@easybits.cloud/html-tailwind-generator 0.2.104 → 0.2.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -315,7 +315,8 @@ async function generateDocumentParallel(options) {
315
315
  onPageComplete,
316
316
  onImageUpdate,
317
317
  onDone,
318
- onError
318
+ onError,
319
+ onUsage
319
320
  } = options;
320
321
  const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;
321
322
  try {
@@ -330,7 +331,9 @@ async function generateDocumentParallel(options) {
330
331
  const extra = extraInstructions ? `
331
332
  Additional instructions: ${extraInstructions}` : "";
332
333
  const pageCountHint = skipCover ? `Generate exactly ${Math.max(1, (pageCount || 5) - 1)} content pages (NO cover \u2014 it already exists).` : pageCount ? `Generate exactly ${pageCount} pages including a cover page.` : "Generate 3-8 pages including a cover page.";
333
- const { object: rawOutline } = await generateObject({
334
+ let totalInputTokens = 0;
335
+ let totalOutputTokens = 0;
336
+ const { object: rawOutline, usage: outlineUsage } = await generateObject({
334
337
  model: outlineModel,
335
338
  schema: DocumentOutlineSchema,
336
339
  prompt: `You are planning a professional document. Create a detailed page-by-page outline.
@@ -349,6 +352,8 @@ ${skipCover ? "- CRITICAL: Do NOT include a cover/title page. The cover already
349
352
  - If source content is provided, assign specific portions to each page in the contentBrief
350
353
  ${direction ? `- Design mood: ${direction.mood}, layout approach: ${direction.layoutHint}` : ""}`
351
354
  });
355
+ totalInputTokens += outlineUsage?.inputTokens || 0;
356
+ totalOutputTokens += outlineUsage?.outputTokens || 0;
352
357
  const outline = skipCover ? { pages: rawOutline.pages.filter((p) => p.type !== "cover").map((p, i) => ({ ...p, pageNumber: i + 1 })) } : rawOutline;
353
358
  onOutline?.(outline);
354
359
  const directionInstruction = direction ? buildDirectionInstruction(direction) : "";
@@ -414,6 +419,9 @@ OUTPUT: A single JSON object on ONE line, no markdown fences:
414
419
  if (partial) onPageChunk?.(pageIdx, partial);
415
420
  }
416
421
  }
422
+ const pageUsage = await result.usage;
423
+ totalInputTokens += pageUsage?.inputTokens || 0;
424
+ totalOutputTokens += pageUsage?.outputTokens || 0;
417
425
  const finalPartial = extractPartialHtml(buffer);
418
426
  if (finalPartial) onPageChunk?.(pageIdx, finalPartial);
419
427
  let cleaned = buffer.trim();
@@ -476,6 +484,7 @@ OUTPUT: A single JSON object on ONE line, no markdown fences:
476
484
  onImageUpdate?.(section.id, section.html);
477
485
  }
478
486
  }
487
+ onUsage?.({ inputTokens: totalInputTokens, outputTokens: totalOutputTokens });
479
488
  onDone?.(sections);
480
489
  return sections;
481
490
  } catch (err) {
@@ -491,4 +500,4 @@ export {
491
500
  generateDocument,
492
501
  generateDocumentParallel
493
502
  };
494
- //# sourceMappingURL=chunk-JB3MUQJS.js.map
503
+ //# sourceMappingURL=chunk-AUGNRZDT.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/generateDocument.ts"],"sourcesContent":["import { generateObject, streamText } from \"ai\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport {\n streamGenerate,\n dataUrlToImagePart,\n resolveModel,\n currentDateLine,\n extractJsonObjects,\n addLoadingPlaceholders,\n addSvgLoadingPlaceholders,\n enrichSectionImages,\n enrichSectionSvgCharts,\n} from \"./streamCore\";\nimport { sanitizeSemanticColors } from \"./sanitizeColors\";\nimport type { Section3 } from \"./types\";\nimport type { DesignDirection } from \"./directions\";\n\nexport const DOCUMENT_SYSTEM_PROMPT = `You are a professional document designer who creates stunning letter-sized (8.5\" × 11\") document pages using HTML + Tailwind CSS.\n\nRULES:\n- Each page is a <section> element sized for letter paper\n- Page structure: <section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col\">\n- The section is EXACTLY 11in tall — content MUST fit, never exceed. Use flex flex-col so children can use flex-1.\n- The section itself has NO padding — backgrounds, gradients, and decorative elements go edge-to-edge\n- Use slot layout: shrink-0 for header/footer bands, flex-1 overflow-hidden for main content area\n- For text content, use an inner wrapper: <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">...content...</div>\n- Footer elements (page numbers, decorative bars, contact info) use shrink-0 so they are ALWAYS visible\n- Cover pages and decorative sections can use full-bleed backgrounds (bg-primary, gradients, images that fill the entire page)\n- Content MUST NOT overflow page boundaries — be conservative with spacing\n- Use Tailwind CDN classes ONLY (no custom CSS, no @apply, no @import)\n- NO JavaScript, only HTML+Tailwind\n- All text content in Spanish unless the prompt specifies otherwise\n- Use real content from the source material, not Lorem ipsum\n- NOT responsive — fixed letter size, no breakpoints needed\n- Sections can have ANY background — full-bleed color, gradients, or white. Not limited to white paper.\n\nSTRICT PROHIBITIONS:\n1. **NO EMOJI** — Never use emoji characters (🚀❌✅📊 etc.). Instead use inline SVG icons or colored divs. For bullet decorators use small colored circles (<span class=\"w-2 h-2 rounded-full bg-primary inline-block\"></span>) or simple SVG.\n2. **NO Chart.js / NO JavaScript** — Never reference Chart.js, canvas, or any JS library. For data visualization use pure CSS: progress bars (div with percentage width + bg-primary), horizontal bars, styled tables with colored cells. Never use <canvas> or <script>.\n3. **NO buttons or CTAs** — This is a print document, not a web page. No \"Contactar\", \"Ver más\", \"Comprar\" buttons. Use text with contact info instead.\n4. **CONTRAST IS MANDATORY** — Dark/colored backgrounds (bg-primary, bg-primary-dark, bg-secondary, dark gradients) MUST use text-white or text-on-primary. Light backgrounds (white, bg-surface, bg-surface-alt) MUST use text-gray-900 or text-on-surface. NEVER use dark text on dark backgrounds or light text on light backgrounds.\n5. **Max 2 font weights per page** — Pick 2 (e.g. font-semibold + font-normal, or font-bold + font-light). Don't mix 4-5 weights.\n6. **Generous whitespace** — Don't fill every centimeter. Leave breathing room. Use py-8, py-12, gap-6, gap-8 liberally. Less content per page = more professional.\n\nLAYOUT OVERFLOW PREVENTION — CRITICAL:\n- Max 2 columns side by side — each with w-1/2. NEVER use 3+ columns.\n- Decorative sidebars: max w-16 (4rem). NEVER use w-[2.5in] or wider sidebars — they steal too much space.\n- Stats/metric grids: max 3 items per row (grid-cols-3). Use gap-4 or gap-6.\n- Tables: max 4 columns, use text-xs or text-sm for cell text, px-3 py-2 cell padding.\n- Images: always w-full or max-w-[50%] — never fixed pixel widths.\n- Text: never use text-6xl or larger except for cover page title. Body text: text-sm or text-base.\n- NEVER use absolute positioning that could overflow — prefer flex/grid layouts.\n- Decorative shapes with absolute positioning MUST stay fully inside the page. Use overflow-hidden on parent AND keep coordinates positive (no negative right/left values).\n- Large decorative text (text-[200px], text-[10rem] etc.) MUST have opacity-5 or lower AND overflow-hidden on its container. These giant texts frequently overflow — be extra careful.\n- NEVER place elements beyond the right edge — all content and decorations must fit within 8.5in width.\n\nDESIGN — ADAPT to the document type. Read the prompt carefully and match the visual style:\n\nGENERAL PRINCIPLES (apply to ALL documents):\n- First page is ALWAYS a stunning cover/title page with impactful design\n- Typography: strong hierarchy with just 2 weights, clear headings vs body\n- Each page visually distinct — different layouts, accent placements\n- Use the full page creatively — backgrounds, sidebars, geometric shapes\n- Professional and polished, never generic or template-looking\n- Icons: use simple inline SVG (12-20px) for visual accents. Keep SVGs minimal (single path, no complex illustrations)\n\nADAPT YOUR STYLE to what the user is creating:\n- Reports/Data: structured grids, tables with alternating rows (bg-surface-alt), progress bars, stat cards, clean data hierarchy\n- Brochures/Marketing: bold hero images, large headlines, feature grids, testimonial-style quotes, visual storytelling\n- Catalogs/Products: product cards with images, specs grids, price highlights, category headers with full-bleed color\n- Invitations/Events: centered dramatic typography, decorative borders, elegant spacing, date/location prominently styled\n- Proposals/Pitches: problem→solution flow, metric highlights, team/about sections, pricing tables\n- CVs/Resumes: clean sidebar layouts, skill bars, timeline for experience, contact info header\n- Creative/General: mix techniques — bento grids, full-bleed images, overlapping elements, bold color blocking\n\nVISUAL TECHNIQUES available to you:\n- Full-bleed colored pages (bg-primary, gradients)\n- Geometric accent shapes (CSS divs with clip-path or rotation)\n- Asymmetric layouts (grid with unequal columns)\n- Large stat numbers as visual anchors (text-5xl font-black)\n- Header/footer bands with contrasting color\n- Sidebar accents (thin, max w-16)\n- Image + text compositions\n- Bento-grid mixing content blocks of different sizes\n- Tables: alternating row colors, clean borders, generous cell padding (px-4 py-3)\n- For numerical data: CSS progress bars, styled tables with colored cells, large stat numbers — NEVER canvas/charts\n\nCSS PROGRESS BARS — use this pattern for data visualization:\n<div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-primary h-3 rounded-full\" style=\"width: 75%\"></div></div>\n\nCOLOR SYSTEM — use semantic classes:\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- Cover pages should use bold full-bleed backgrounds (bg-primary, gradients from-primary to-primary-dark)\n- CONTRAST: bg-primary/bg-primary-dark/bg-secondary → text-white or text-on-primary. White/bg-surface → text-gray-900 or text-on-surface\n\nIMAGES — USE GENEROUSLY:\n- EVERY image MUST use: <img data-image-query=\"english search query\" alt=\"description\" class=\"w-full h-auto object-cover rounded-xl\"/>\n- NEVER include a src attribute — the system auto-replaces data-image-query with a real image\n- For avatar-like elements, use colored divs with initials instead of img tags\n- Include at LEAST 3-5 images across the document — hero images, section illustrations, backgrounds, product photos\n- Each data-image-query should be a UNIQUE, specific search query in English (e.g. \"modern office workspace aerial view\", \"team brainstorming whiteboard\", \"abstract blue technology network\")\n- Use images to break up text-heavy pages and add visual interest\n\nCHARTS & DATA VISUALIZATION (SVG):\n- For charts, diagrams, and decorative data graphics, use:\n <div data-svg-chart=\"bar chart showing Q1 revenue: Jan $45K, Feb $52K, Mar $61K\" class=\"w-full\"></div>\n- The system generates professional SVG charts with a specialized tool — NEVER draw SVGs yourself\n- Use descriptive prompts with data points: type of chart + what it shows + actual values\n- Examples: \"donut chart: 40% Marketing, 30% Sales, 20% R&D, 10% Admin\", \"line chart showing growth: Q1 $100K, Q2 $150K, Q3 $220K, Q4 $310K\"\n- For simple metrics, still prefer CSS progress bars (they render faster)\n\nTAILWIND v3 NOTES:\n- Standard Tailwind v3 classes (shadow-sm, shadow-md, rounded-md, etc.)\n- Borders: border + border-gray-200 for visible borders\n\nEXAMPLE — Cover page (simple, no wide sidebars):\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-white\">\n <div class=\"absolute left-0 top-0 w-2 h-full bg-primary\"></div>\n <div class=\"flex-1 overflow-hidden flex flex-col justify-center px-[1in]\">\n <div class=\"text-sm font-normal text-primary mb-4\">Marzo 2026 · Versión 1.0</div>\n <h1 class=\"text-5xl font-bold text-gray-900 leading-tight\">Reporte<br/>Trimestral</h1>\n <div class=\"w-16 h-1 bg-primary mt-6 mb-4\"></div>\n <p class=\"text-lg font-normal text-gray-500\">Resultados y análisis del primer trimestre</p>\n </div>\n</section>\n\nEXAMPLE — Marketing/brochure page (bold, visual):\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-primary\">\n <div class=\"flex flex-1 overflow-hidden\">\n <div class=\"w-1/2 flex flex-col justify-center px-[0.75in]\">\n <span class=\"text-sm font-normal text-on-primary opacity-70 uppercase tracking-widest mb-3\">Solución Premium</span>\n <h2 class=\"text-4xl font-bold text-on-primary leading-tight mb-6\">Transforma tu negocio digital</h2>\n <p class=\"text-base font-normal text-on-primary opacity-80 mb-8\">Herramientas inteligentes que simplifican la gestión de tus activos digitales.</p>\n <div class=\"flex gap-6\">\n <div><div class=\"text-3xl font-bold text-accent\">98%</div><div class=\"text-xs text-on-primary opacity-70\">Satisfacción</div></div>\n <div><div class=\"text-3xl font-bold text-accent\">2.4K</div><div class=\"text-xs text-on-primary opacity-70\">Empresas</div></div>\n </div>\n </div>\n <div class=\"w-1/2 relative\">\n <img data-image-query=\"modern office team collaboration technology\" alt=\"Team working\" class=\"absolute inset-0 w-full h-full object-cover\" />\n </div>\n </div>\n</section>\n\nEXAMPLE — Catalog/product grid page:\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-surface\">\n <div class=\"shrink-0 h-1 bg-primary w-full\"></div>\n <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">\n <div class=\"flex justify-between items-baseline mb-6\">\n <h2 class=\"text-2xl font-bold text-on-surface\">Colección Primavera</h2>\n <span class=\"text-xs font-normal text-on-surface-muted uppercase tracking-wider\">Página 3 de 8</span>\n </div>\n <div class=\"grid grid-cols-2 gap-6\">\n <div class=\"bg-surface-alt rounded-xl overflow-hidden\">\n <img data-image-query=\"minimalist product on white background\" alt=\"Product\" class=\"w-full h-48 object-cover\" />\n <div class=\"p-4\"><h3 class=\"font-bold text-on-surface text-sm\">Producto Alpha</h3><p class=\"text-xs text-on-surface-muted mt-1\">Diseño ergonómico premium</p><div class=\"text-lg font-bold text-primary mt-2\">$2,490</div></div>\n </div>\n <div class=\"bg-surface-alt rounded-xl overflow-hidden\">\n <img data-image-query=\"elegant product photography studio\" alt=\"Product\" class=\"w-full h-48 object-cover\" />\n <div class=\"p-4\"><h3 class=\"font-bold text-on-surface text-sm\">Producto Beta</h3><p class=\"text-xs text-on-surface-muted mt-1\">Tecnología de vanguardia</p><div class=\"text-lg font-bold text-primary mt-2\">$3,190</div></div>\n </div>\n </div>\n </div>\n</section>\n\nEXAMPLE — Content page with table + progress bars:\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-white\">\n <div class=\"shrink-0 h-1.5 bg-primary w-full\"></div>\n <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">\n <h2 class=\"text-2xl font-bold text-gray-900 mb-1\">Métricas de Rendimiento</h2>\n <p class=\"text-sm font-normal text-gray-500 mb-8\">Indicadores clave del periodo enero—marzo</p>\n <table class=\"w-full text-sm mb-10\">\n <thead><tr class=\"bg-primary text-white\"><th class=\"px-4 py-3 text-left font-semibold\">Indicador</th><th class=\"px-4 py-3 text-left font-semibold\">Valor</th><th class=\"px-4 py-3 text-left font-semibold\">Meta</th></tr></thead>\n <tbody>\n <tr class=\"bg-surface-alt\"><td class=\"px-4 py-3 text-gray-900\">Ingresos</td><td class=\"px-4 py-3 text-gray-900\">$1.2M</td><td class=\"px-4 py-3 text-gray-900\">$1.5M</td></tr>\n <tr><td class=\"px-4 py-3 text-gray-900\">Clientes nuevos</td><td class=\"px-4 py-3 text-gray-900\">340</td><td class=\"px-4 py-3 text-gray-900\">300</td></tr>\n <tr class=\"bg-surface-alt\"><td class=\"px-4 py-3 text-gray-900\">Retención</td><td class=\"px-4 py-3 text-gray-900\">92%</td><td class=\"px-4 py-3 text-gray-900\">90%</td></tr>\n </tbody>\n </table>\n <h3 class=\"text-lg font-bold text-gray-900 mb-4\">Progreso por Área</h3>\n <div class=\"space-y-4\">\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Ventas</span><span class=\"text-gray-500\">80%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-primary h-3 rounded-full\" style=\"width: 80%\"></div></div></div>\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Marketing</span><span class=\"text-gray-500\">65%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-secondary h-3 rounded-full\" style=\"width: 65%\"></div></div></div>\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Producto</span><span class=\"text-gray-500\">95%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-accent h-3 rounded-full\" style=\"width: 95%\"></div></div></div>\n </div>\n </div>\n</section>`;\n\nexport const DOCUMENT_PROMPT_SUFFIX = `\n\nOUTPUT FORMAT: NDJSON — one JSON object per line, NO wrapper array, NO markdown fences.\nEach line: {\"label\": \"Page Title\", \"html\": \"<section class='w-[8.5in] h-[11in] relative overflow-hidden flex flex-col'>...</section>\"}\n\nGenerate 3-8 pages depending on content length. First page = cover/title page.\nEach page must fit within letter size (8.5\" × 11\"). Be conservative with spacing.\nMake each page visually distinct — different layouts, different accent placements.\nIMPORTANT: Adapt your design style to match the type of document — not everything is a report. Brochures should feel bold and visual, catalogs should showcase products, invitations should feel elegant, etc.`;\n\nexport interface GenerateDocumentOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n logoUrl?: string;\n referenceImage?: string;\n extraInstructions?: string;\n model?: string | import(\"ai\").LanguageModel;\n pexelsApiKey?: string;\n /** Design direction — injects Google Fonts + hex colors into the prompt */\n direction?: DesignDirection;\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n onSection?: (section: Section3) => void;\n onImageUpdate?: (sectionId: string, html: string) => void;\n onRawChunk?: (buffer: string, completedCount: number) => void;\n onDone?: (sections: Section3[]) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Generate a multi-page document with streaming AI + image enrichment.\n */\nexport async function generateDocument(options: GenerateDocumentOptions): Promise<Section3[]> {\n const {\n prompt,\n logoUrl,\n referenceImage,\n extraInstructions,\n direction,\n ...rest\n } = options;\n\n const extra = extraInstructions ? `\\nAdditional instructions: ${extraInstructions}` : \"\";\n\n // Build direction style instructions if provided\n let directionInstruction = \"\";\n if (direction) {\n const fontsUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(direction.headingFont).replace(/%20/g, \"+\")}:wght@400;700;900&family=${encodeURIComponent(direction.bodyFont).replace(/%20/g, \"+\")}:wght@400;500;600&display=swap`;\n directionInstruction = `\nDESIGN DIRECTION: \"${direction.name}\" — ${direction.tagline}\nTYPOGRAPHY: Use these Google Fonts via <link href=\"${fontsUrl}\" rel=\"stylesheet\"> on the first page.\n- Headings: font-family: '${direction.headingFont}', sans-serif (via inline style)\n- Body: font-family: '${direction.bodyFont}', sans-serif (via inline style)\nCOLORS — use ONLY semantic Tailwind classes (the editor injects CSS variables that resolve these):\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- NEVER use hardcoded hex colors like bg-[#xxx] or text-[#xxx] — always use semantic classes\n- The palette is: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}\nMood: ${direction.mood}\nLayout approach: ${direction.layoutHint}\nIMPORTANT: Apply inline style=\"font-family: '${direction.headingFont}'\" on ALL heading elements and style=\"font-family: '${direction.bodyFont}'\" on ALL body text elements. Include the Google Fonts <link> tag inside the FIRST <section> only.`;\n }\n // Truncate prompt to prevent token overflow (max ~15K chars ≈ 5K tokens)\n const safePrompt = prompt.length > 15_000 ? prompt.substring(0, 15_000) + \"\\n[...content truncated...]\" : prompt;\n const logoInstruction = logoUrl\n ? `\\nLOGO: Include this logo on the cover page and as a small header on other pages:\\n<img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-12 object-contain\" />\\nUse this exact <img> tag with this exact src URL — do NOT invent a different URL or modify it.`\n : \"\";\n\n const content: any[] = [];\n\n if (referenceImage) {\n const converted = dataUrlToImagePart(referenceImage);\n if (converted) {\n content.push({ type: \"image\", ...converted });\n } else {\n content.push({ type: \"image\", image: referenceImage });\n }\n content.push({\n type: \"text\",\n text: `Create a professional document inspired by this reference image for: ${safePrompt}${logoInstruction}${directionInstruction}${extra}${DOCUMENT_PROMPT_SUFFIX}`,\n });\n } else {\n content.push({\n type: \"text\",\n text: `Create a professional document for: ${safePrompt}${logoInstruction}${directionInstruction}${extra}${DOCUMENT_PROMPT_SUFFIX}`,\n });\n }\n\n return streamGenerate({\n ...rest,\n systemPrompt: DOCUMENT_SYSTEM_PROMPT,\n userContent: content,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Parallel Document Generation\n// ---------------------------------------------------------------------------\n\nconst DocumentOutlineSchema = z.object({\n pages: z.array(z.object({\n pageNumber: z.number(),\n label: z.string().describe(\"Page title for sidebar\"),\n type: z.enum([\"cover\", \"content\", \"data\", \"visual\", \"closing\"]),\n layoutHint: z.string().describe(\"Layout approach: split, full-bleed, grid, editorial, table-heavy, sidebar\"),\n contentBrief: z.string().describe(\"2-4 sentences describing exactly what goes on this page\"),\n keyElements: z.array(z.string()).describe(\"Specific elements: stats grid, table, hero image, timeline, etc.\"),\n backgroundStyle: z.enum([\"white\", \"primary\", \"gradient\", \"surface-alt\", \"image\"]),\n continuesFrom: z.string().optional().describe(\"How this page relates to the previous one\"),\n })),\n});\n\nexport type DocumentOutline = z.infer<typeof DocumentOutlineSchema>;\n\nexport interface GenerateDocumentParallelOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n logoUrl?: string;\n referenceImage?: string;\n extraInstructions?: string;\n /** Model for page generation (quality model) */\n model?: string | import(\"ai\").LanguageModel;\n /** Model for outline generation (fast model) */\n outlineModel?: string | import(\"ai\").LanguageModel;\n pexelsApiKey?: string;\n direction?: DesignDirection;\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n pageCount?: number;\n skipCover?: boolean;\n onOutline?: (outline: DocumentOutline) => void;\n onPageChunk?: (pageIndex: number, partialHtml: string) => void;\n onPageComplete?: (pageIndex: number, section: Section3) => void;\n onImageUpdate?: (sectionId: string, html: string) => void;\n onDone?: (sections: Section3[]) => void;\n onError?: (error: Error) => void;\n}\n\nfunction buildDirectionInstruction(direction: DesignDirection): string {\n const fontsUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(direction.headingFont).replace(/%20/g, \"+\")}:wght@400;700;900&family=${encodeURIComponent(direction.bodyFont).replace(/%20/g, \"+\")}:wght@400;500;600&display=swap`;\n return `\nDESIGN DIRECTION: \"${direction.name}\" — ${direction.tagline}\nTYPOGRAPHY: Use these Google Fonts via <link href=\"${fontsUrl}\" rel=\"stylesheet\"> on the first page.\n- Headings: font-family: '${direction.headingFont}', sans-serif (via inline style)\n- Body: font-family: '${direction.bodyFont}', sans-serif (via inline style)\nCOLORS — use ONLY semantic Tailwind classes (the editor injects CSS variables that resolve these):\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- NEVER use hardcoded hex colors like bg-[#xxx] or text-[#xxx] — always use semantic classes\n- The palette is: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}\nMood: ${direction.mood}\nLayout approach: ${direction.layoutHint}\nIMPORTANT: Apply inline style=\"font-family: '${direction.headingFont}'\" on ALL heading elements and style=\"font-family: '${direction.bodyFont}'\" on ALL body text elements. Include the Google Fonts <link> tag inside the FIRST <section> only.`;\n}\n\n/** Extract partial HTML from a raw JSON buffer (same pattern as onRawChunk) */\nfunction extractPartialHtml(buffer: string): string | null {\n const htmlMatch = buffer.match(/\"html\"\\s*:\\s*\"([\\s\\S]*)/);\n if (!htmlMatch) return null;\n let partial = htmlMatch[1]\n .replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n if (partial.endsWith('\\\\')) partial = partial.slice(0, -1);\n const lastQuote = partial.lastIndexOf('\"');\n if (lastQuote > 0) partial = partial.slice(0, lastQuote);\n if (/<section/i.test(partial) && !/<\\/section>/i.test(partial)) {\n partial += '</section>';\n }\n return partial.length > 20 ? partial : null;\n}\n\nexport async function generateDocumentParallel(options: GenerateDocumentParallelOptions): Promise<Section3[]> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n prompt,\n logoUrl,\n referenceImage,\n extraInstructions,\n model: pageModelId,\n outlineModel: outlineModelId,\n pexelsApiKey,\n direction,\n persistImage,\n pageCount,\n skipCover,\n onOutline,\n onPageChunk,\n onPageComplete,\n onImageUpdate,\n onDone,\n onError,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n\n try {\n // --- Phase 1: Generate outline ---\n const outlineModel = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId: outlineModelId,\n defaultOpenai: \"gpt-4.1-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\n const safePrompt = prompt.length > 15_000 ? prompt.substring(0, 15_000) + \"\\n[...content truncated...]\" : prompt;\n const extra = extraInstructions ? `\\nAdditional instructions: ${extraInstructions}` : \"\";\n const pageCountHint = skipCover\n ? `Generate exactly ${Math.max(1, (pageCount || 5) - 1)} content pages (NO cover — it already exists).`\n : pageCount\n ? `Generate exactly ${pageCount} pages including a cover page.`\n : \"Generate 3-8 pages including a cover page.\";\n\n const { object: rawOutline } = await generateObject({\n model: outlineModel,\n schema: DocumentOutlineSchema,\n prompt: `You are planning a professional document. Create a detailed page-by-page outline.\n\nDOCUMENT BRIEF: ${safePrompt}${extra}\n\nRULES:\n- ${pageCountHint}\n${skipCover ? \"- CRITICAL: Do NOT include a cover/title page. The cover already exists. Start with page type 'content', 'data', or 'visual'. NEVER use type 'cover'.\" : \"- First page is ALWAYS a stunning cover/title page.\"}\n- Distribute content EVENLY — no page should be overloaded\n- Each page must have a DISTINCT layout (mix split, full-bleed, grid, editorial, sidebar, table-heavy)\n- Narrative flows naturally: ${skipCover ? \"introduction → detail → data → closing\" : \"cover → introduction → detail → data → closing\"}\n- contentBrief must be detailed enough that a separate AI can generate the page independently\n- keyElements must list specific visual elements (not vague descriptions)\n- Vary backgroundStyle across pages — not all white\n- If source content is provided, assign specific portions to each page in the contentBrief\n${direction ? `- Design mood: ${direction.mood}, layout approach: ${direction.layoutHint}` : \"\"}`,\n });\n\n // Filter out any cover pages if skipCover (AI sometimes ignores instructions)\n const outline: DocumentOutline = skipCover\n ? { pages: rawOutline.pages.filter(p => p.type !== \"cover\").map((p, i) => ({ ...p, pageNumber: i + 1 })) }\n : rawOutline;\n\n onOutline?.(outline);\n\n // --- Phase 2: Generate pages in parallel ---\n const directionInstruction = direction ? buildDirectionInstruction(direction) : \"\";\n const logoInstruction = logoUrl\n ? `\\nLOGO: Include this logo on the cover page and as a small header on other pages:\\n<img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-12 object-contain\" />\\nUse this exact <img> tag with this exact src URL.`\n : \"\";\n\n const outlineJson = JSON.stringify(outline.pages, null, 2);\n\n const pageModel = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId: pageModelId,\n defaultOpenai: \"gpt-4o\",\n defaultAnthropic: \"claude-sonnet-4-6\",\n });\n\n async function generateSinglePage(\n page: DocumentOutline[\"pages\"][number],\n retryCount = 0\n ): Promise<Section3> {\n const pageIdx = page.pageNumber - 1;\n const isCover = page.type === \"cover\";\n\n const userContent: any[] = [];\n\n // Reference image only for cover/visual pages\n if (referenceImage && (isCover || page.type === \"visual\")) {\n const converted = dataUrlToImagePart(referenceImage);\n if (converted) {\n userContent.push({ type: \"image\", ...converted });\n } else {\n userContent.push({ type: \"image\", image: referenceImage });\n }\n }\n\n userContent.push({\n type: \"text\",\n text: `You are generating PAGE ${page.pageNumber} of ${outline.pages.length} for a professional document.\n\nFULL DOCUMENT OUTLINE (for context — you are generating ONLY page ${page.pageNumber}):\n${outlineJson}\n\nYOUR PAGE ASSIGNMENT:\n- Label: ${page.label}\n- Type: ${page.type}\n- Layout: ${page.layoutHint}\n- Background: ${page.backgroundStyle}\n- Content: ${page.contentBrief}\n- Key elements: ${page.keyElements.join(\", \")}\n${page.continuesFrom ? `- Continues from: ${page.continuesFrom}` : \"\"}\n${isCover ? logoInstruction : logoUrl ? `\\nSmall logo header: <img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-8 object-contain\" />` : \"\"}\n${directionInstruction}\n\nOUTPUT: A single JSON object on ONE line, no markdown fences:\n{\"label\": \"${page.label}\", \"html\": \"<section class='w-[8.5in] h-[11in] relative overflow-hidden flex flex-col'>...</section>\"}`,\n });\n\n try {\n const result = streamText({\n model: pageModel,\n system: DOCUMENT_SYSTEM_PROMPT + currentDateLine(),\n messages: [{ role: \"user\", content: userContent }],\n });\n\n let buffer = \"\";\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n buffer += chunk;\n chunkCount++;\n if (chunkCount % 5 === 0) {\n const partial = extractPartialHtml(buffer);\n if (partial) onPageChunk?.(pageIdx, partial);\n }\n }\n\n // Final partial before parse\n const finalPartial = extractPartialHtml(buffer);\n if (finalPartial) onPageChunk?.(pageIdx, finalPartial);\n\n // Parse the JSON object\n let cleaned = buffer.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n const [objects] = extractJsonObjects(cleaned);\n const obj = objects[0];\n if (!obj?.html) throw new Error(`No valid HTML output for page ${page.pageNumber}`);\n\n const section: Section3 = {\n id: nanoid(8),\n order: pageIdx,\n html: sanitizeSemanticColors(addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html))),\n label: obj.label || page.label,\n };\n\n onPageComplete?.(pageIdx, section);\n return section;\n } catch (err) {\n if (retryCount < 1) {\n console.warn(`Page ${page.pageNumber} failed, retrying:`, (err as Error).message);\n return generateSinglePage(page, retryCount + 1);\n }\n // Return error placeholder\n const section: Section3 = {\n id: nanoid(8),\n order: pageIdx,\n html: `<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-gray-50 items-center justify-center\"><div class=\"text-center text-gray-400\"><p class=\"text-lg font-semibold\">Error generando página</p><p class=\"text-sm mt-2\">${(err as Error).message?.slice(0, 100) || \"Error desconocido\"}</p></div></section>`,\n label: page.label,\n };\n onPageComplete?.(pageIdx, section);\n return section;\n }\n }\n\n const results = await Promise.allSettled(\n outline.pages.map((page) => generateSinglePage(page))\n );\n\n const sections: Section3[] = results\n .map((r) => r.status === \"fulfilled\" ? r.value : null)\n .filter((s): s is Section3 => s !== null)\n .sort((a, b) => a.order - b.order);\n\n // --- Phase 3: Image enrichment (sequential to respect Pexels rate limits) ---\n for (const section of sections) {\n await enrichSectionImages(section, {\n pexelsApiKey,\n openaiApiKey,\n persistImage,\n onImageUpdate,\n });\n await enrichSectionSvgCharts(section, {\n anthropicApiKey,\n onImageUpdate,\n });\n }\n\n // Final fallback for images without src\n for (const section of sections) {\n const before = section.html;\n section.html = section.html.replace(\n /<img\\s(?![^>]*\\bsrc=)([^>]*?)>/gi,\n (_match, attrs) => {\n const altMatch = attrs.match(/alt=\"([^\"]*?)\"/);\n const query = altMatch?.[1] || \"image\";\n return `<img src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" ${attrs}>`;\n }\n );\n if (section.html !== before) {\n onImageUpdate?.(section.id, section.html);\n }\n }\n\n onDone?.(sections);\n return sections;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Parallel generation failed\");\n onError?.(error);\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;AAAA,SAAS,gBAAgB,kBAAkB;AAC3C,SAAS,SAAS;AAClB,SAAS,cAAc;AAgBhB,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA6K/B,IAAM,yBAAyB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAgCtC,eAAsB,iBAAiB,SAAuD;AAC5F,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,GAAG;AAAA,EACL,IAAI;AAEJ,QAAM,QAAQ,oBAAoB;AAAA,2BAA8B,iBAAiB,KAAK;AAGtF,MAAI,uBAAuB;AAC3B,MAAI,WAAW;AACb,UAAM,WAAW,4CAA4C,mBAAmB,UAAU,WAAW,EAAE,QAAQ,QAAQ,GAAG,CAAC,4BAA4B,mBAAmB,UAAU,QAAQ,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAClN,2BAAuB;AAAA,qBACN,UAAU,IAAI,YAAO,UAAU,OAAO;AAAA,qDACN,QAAQ;AAAA,4BACjC,UAAU,WAAW;AAAA,wBACzB,UAAU,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAMd,UAAU,OAAO,OAAO,YAAY,UAAU,OAAO,MAAM,aAAa,UAAU,OAAO,OAAO;AAAA,QACpH,UAAU,IAAI;AAAA,mBACH,UAAU,UAAU;AAAA,+CACQ,UAAU,WAAW,uDAAuD,UAAU,QAAQ;AAAA,EAC3I;AAEA,QAAM,aAAa,OAAO,SAAS,OAAS,OAAO,UAAU,GAAG,IAAM,IAAI,gCAAgC;AAC1G,QAAM,kBAAkB,UACpB;AAAA;AAAA,YAAgG,OAAO;AAAA,uGACvG;AAEJ,QAAM,UAAiB,CAAC;AAExB,MAAI,gBAAgB;AAClB,UAAM,YAAY,mBAAmB,cAAc;AACnD,QAAI,WAAW;AACb,cAAQ,KAAK,EAAE,MAAM,SAAS,GAAG,UAAU,CAAC;AAAA,IAC9C,OAAO;AACL,cAAQ,KAAK,EAAE,MAAM,SAAS,OAAO,eAAe,CAAC;AAAA,IACvD;AACA,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,wEAAwE,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,KAAK,GAAG,sBAAsB;AAAA,IACpK,CAAC;AAAA,EACH,OAAO;AACL,YAAQ,KAAK;AAAA,MACX,MAAM;AAAA,MACN,MAAM,uCAAuC,UAAU,GAAG,eAAe,GAAG,oBAAoB,GAAG,KAAK,GAAG,sBAAsB;AAAA,IACnI,CAAC;AAAA,EACH;AAEA,SAAO,eAAe;AAAA,IACpB,GAAG;AAAA,IACH,cAAc;AAAA,IACd,aAAa;AAAA,EACf,CAAC;AACH;AAMA,IAAM,wBAAwB,EAAE,OAAO;AAAA,EACrC,OAAO,EAAE,MAAM,EAAE,OAAO;AAAA,IACtB,YAAY,EAAE,OAAO;AAAA,IACrB,OAAO,EAAE,OAAO,EAAE,SAAS,wBAAwB;AAAA,IACnD,MAAM,EAAE,KAAK,CAAC,SAAS,WAAW,QAAQ,UAAU,SAAS,CAAC;AAAA,IAC9D,YAAY,EAAE,OAAO,EAAE,SAAS,2EAA2E;AAAA,IAC3G,cAAc,EAAE,OAAO,EAAE,SAAS,yDAAyD;AAAA,IAC3F,aAAa,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,SAAS,kEAAkE;AAAA,IAC5G,iBAAiB,EAAE,KAAK,CAAC,SAAS,WAAW,YAAY,eAAe,OAAO,CAAC;AAAA,IAChF,eAAe,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,2CAA2C;AAAA,EAC3F,CAAC,CAAC;AACJ,CAAC;AA4BD,SAAS,0BAA0B,WAAoC;AACrE,QAAM,WAAW,4CAA4C,mBAAmB,UAAU,WAAW,EAAE,QAAQ,QAAQ,GAAG,CAAC,4BAA4B,mBAAmB,UAAU,QAAQ,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAClN,SAAO;AAAA,qBACY,UAAU,IAAI,YAAO,UAAU,OAAO;AAAA,qDACN,QAAQ;AAAA,4BACjC,UAAU,WAAW;AAAA,wBACzB,UAAU,QAAQ;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAMd,UAAU,OAAO,OAAO,YAAY,UAAU,OAAO,MAAM,aAAa,UAAU,OAAO,OAAO;AAAA,QACpH,UAAU,IAAI;AAAA,mBACH,UAAU,UAAU;AAAA,+CACQ,UAAU,WAAW,uDAAuD,UAAU,QAAQ;AAC7I;AAGA,SAAS,mBAAmB,QAA+B;AACzD,QAAM,YAAY,OAAO,MAAM,yBAAyB;AACxD,MAAI,CAAC,UAAW,QAAO;AACvB,MAAI,UAAU,UAAU,CAAC,EACtB,QAAQ,QAAQ,IAAI,EAAE,QAAQ,QAAQ,GAAG,EAAE,QAAQ,SAAS,IAAI;AACnE,MAAI,QAAQ,SAAS,IAAI,EAAG,WAAU,QAAQ,MAAM,GAAG,EAAE;AACzD,QAAM,YAAY,QAAQ,YAAY,GAAG;AACzC,MAAI,YAAY,EAAG,WAAU,QAAQ,MAAM,GAAG,SAAS;AACvD,MAAI,YAAY,KAAK,OAAO,KAAK,CAAC,eAAe,KAAK,OAAO,GAAG;AAC9D,eAAW;AAAA,EACb;AACA,SAAO,QAAQ,SAAS,KAAK,UAAU;AACzC;AAEA,eAAsB,yBAAyB,SAA+D;AAC5G,QAAM;AAAA,IACJ;AAAA,IACA,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,OAAO;AAAA,IACP,cAAc;AAAA,IACd;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF,IAAI;AAEJ,QAAM,eAAe,iBAAiB,QAAQ,IAAI;AAElD,MAAI;AAEF,UAAM,eAAe,MAAM,aAAa;AAAA,MACtC;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,eAAe;AAAA,MACf,kBAAkB;AAAA,IACpB,CAAC;AAED,UAAM,aAAa,OAAO,SAAS,OAAS,OAAO,UAAU,GAAG,IAAM,IAAI,gCAAgC;AAC1G,UAAM,QAAQ,oBAAoB;AAAA,2BAA8B,iBAAiB,KAAK;AACtF,UAAM,gBAAgB,YAClB,oBAAoB,KAAK,IAAI,IAAI,aAAa,KAAK,CAAC,CAAC,wDACrD,YACE,oBAAoB,SAAS,mCAC7B;AAEN,UAAM,EAAE,QAAQ,WAAW,IAAI,MAAM,eAAe;AAAA,MAClD,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,QAAQ;AAAA;AAAA,kBAEI,UAAU,GAAG,KAAK;AAAA;AAAA;AAAA,IAGhC,aAAa;AAAA,EACf,YAAY,0JAA0J,qDAAqD;AAAA;AAAA;AAAA,+BAG9L,YAAY,0DAA2C,oEAAgD;AAAA;AAAA;AAAA;AAAA;AAAA,EAKpI,YAAY,kBAAkB,UAAU,IAAI,sBAAsB,UAAU,UAAU,KAAK,EAAE;AAAA,IAC3F,CAAC;AAGD,UAAM,UAA2B,YAC7B,EAAE,OAAO,WAAW,MAAM,OAAO,OAAK,EAAE,SAAS,OAAO,EAAE,IAAI,CAAC,GAAG,OAAO,EAAE,GAAG,GAAG,YAAY,IAAI,EAAE,EAAE,EAAE,IACvG;AAEJ,gBAAY,OAAO;AAGnB,UAAM,uBAAuB,YAAY,0BAA0B,SAAS,IAAI;AAChF,UAAM,kBAAkB,UACpB;AAAA;AAAA,YAAgG,OAAO;AAAA,qDACvG;AAEJ,UAAM,cAAc,KAAK,UAAU,QAAQ,OAAO,MAAM,CAAC;AAEzD,UAAM,YAAY,MAAM,aAAa;AAAA,MACnC;AAAA,MACA;AAAA,MACA,SAAS;AAAA,MACT,eAAe;AAAA,MACf,kBAAkB;AAAA,IACpB,CAAC;AAED,mBAAe,mBACb,MACA,aAAa,GACM;AACnB,YAAM,UAAU,KAAK,aAAa;AAClC,YAAM,UAAU,KAAK,SAAS;AAE9B,YAAM,cAAqB,CAAC;AAG5B,UAAI,mBAAmB,WAAW,KAAK,SAAS,WAAW;AACzD,cAAM,YAAY,mBAAmB,cAAc;AACnD,YAAI,WAAW;AACb,sBAAY,KAAK,EAAE,MAAM,SAAS,GAAG,UAAU,CAAC;AAAA,QAClD,OAAO;AACL,sBAAY,KAAK,EAAE,MAAM,SAAS,OAAO,eAAe,CAAC;AAAA,QAC3D;AAAA,MACF;AAEA,kBAAY,KAAK;AAAA,QACf,MAAM;AAAA,QACN,MAAM,2BAA2B,KAAK,UAAU,OAAO,QAAQ,MAAM,MAAM;AAAA;AAAA,yEAEf,KAAK,UAAU;AAAA,EACjF,WAAW;AAAA;AAAA;AAAA,WAGF,KAAK,KAAK;AAAA,UACX,KAAK,IAAI;AAAA,YACP,KAAK,UAAU;AAAA,gBACX,KAAK,eAAe;AAAA,aACvB,KAAK,YAAY;AAAA,kBACZ,KAAK,YAAY,KAAK,IAAI,CAAC;AAAA,EAC3C,KAAK,gBAAgB,qBAAqB,KAAK,aAAa,KAAK,EAAE;AAAA,EACnE,UAAU,kBAAkB,UAAU;AAAA,+BAAkC,OAAO,+CAA+C,EAAE;AAAA,EAChI,oBAAoB;AAAA;AAAA;AAAA,aAGT,KAAK,KAAK;AAAA,MACjB,CAAC;AAED,UAAI;AACF,cAAM,SAAS,WAAW;AAAA,UACxB,OAAO;AAAA,UACP,QAAQ,yBAAyB,gBAAgB;AAAA,UACjD,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,YAAY,CAAC;AAAA,QACnD,CAAC;AAED,YAAI,SAAS;AACb,YAAI,aAAa;AACjB,yBAAiB,SAAS,OAAO,YAAY;AAC3C,oBAAU;AACV;AACA,cAAI,aAAa,MAAM,GAAG;AACxB,kBAAM,UAAU,mBAAmB,MAAM;AACzC,gBAAI,QAAS,eAAc,SAAS,OAAO;AAAA,UAC7C;AAAA,QACF;AAGA,cAAM,eAAe,mBAAmB,MAAM;AAC9C,YAAI,aAAc,eAAc,SAAS,YAAY;AAGrD,YAAI,UAAU,OAAO,KAAK;AAC1B,YAAI,QAAQ,WAAW,KAAK,GAAG;AAC7B,oBAAU,QAAQ,QAAQ,oBAAoB,EAAE,EAAE,QAAQ,WAAW,EAAE;AAAA,QACzE;AACA,cAAM,CAAC,OAAO,IAAI,mBAAmB,OAAO;AAC5C,cAAM,MAAM,QAAQ,CAAC;AACrB,YAAI,CAAC,KAAK,KAAM,OAAM,IAAI,MAAM,iCAAiC,KAAK,UAAU,EAAE;AAElF,cAAM,UAAoB;AAAA,UACxB,IAAI,OAAO,CAAC;AAAA,UACZ,OAAO;AAAA,UACP,MAAM,uBAAuB,0BAA0B,uBAAuB,IAAI,IAAI,CAAC,CAAC;AAAA,UACxF,OAAO,IAAI,SAAS,KAAK;AAAA,QAC3B;AAEA,yBAAiB,SAAS,OAAO;AACjC,eAAO;AAAA,MACT,SAAS,KAAK;AACZ,YAAI,aAAa,GAAG;AAClB,kBAAQ,KAAK,QAAQ,KAAK,UAAU,sBAAuB,IAAc,OAAO;AAChF,iBAAO,mBAAmB,MAAM,aAAa,CAAC;AAAA,QAChD;AAEA,cAAM,UAAoB;AAAA,UACxB,IAAI,OAAO,CAAC;AAAA,UACZ,OAAO;AAAA,UACP,MAAM,kPAAgP,IAAc,SAAS,MAAM,GAAG,GAAG,KAAK,mBAAmB;AAAA,UACjT,OAAO,KAAK;AAAA,QACd;AACA,yBAAiB,SAAS,OAAO;AACjC,eAAO;AAAA,MACT;AAAA,IACF;AAEA,UAAM,UAAU,MAAM,QAAQ;AAAA,MAC5B,QAAQ,MAAM,IAAI,CAAC,SAAS,mBAAmB,IAAI,CAAC;AAAA,IACtD;AAEA,UAAM,WAAuB,QAC1B,IAAI,CAAC,MAAM,EAAE,WAAW,cAAc,EAAE,QAAQ,IAAI,EACpD,OAAO,CAAC,MAAqB,MAAM,IAAI,EACvC,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAGnC,eAAW,WAAW,UAAU;AAC9B,YAAM,oBAAoB,SAAS;AAAA,QACjC;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AACD,YAAM,uBAAuB,SAAS;AAAA,QACpC;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,eAAW,WAAW,UAAU;AAC9B,YAAM,SAAS,QAAQ;AACvB,cAAQ,OAAO,QAAQ,KAAK;AAAA,QAC1B;AAAA,QACA,CAAC,QAAQ,UAAU;AACjB,gBAAM,WAAW,MAAM,MAAM,gBAAgB;AAC7C,gBAAM,QAAQ,WAAW,CAAC,KAAK;AAC/B,iBAAO,6DAA6D,mBAAmB,MAAM,MAAM,GAAG,EAAE,CAAC,CAAC,KAAK,KAAK;AAAA,QACtH;AAAA,MACF;AACA,UAAI,QAAQ,SAAS,QAAQ;AAC3B,wBAAgB,QAAQ,IAAI,QAAQ,IAAI;AAAA,MAC1C;AAAA,IACF;AAEA,aAAS,QAAQ;AACjB,WAAO;AAAA,EACT,SAAS,KAAU;AACjB,UAAM,QAAQ,eAAe,QAAQ,MAAM,IAAI,MAAM,KAAK,WAAW,4BAA4B;AACjG,cAAU,KAAK;AACf,UAAM;AAAA,EACR;AACF;","names":[]}
1
+ {"version":3,"sources":["../src/generateDocument.ts"],"sourcesContent":["import { generateObject, streamText } from \"ai\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport {\n streamGenerate,\n dataUrlToImagePart,\n resolveModel,\n currentDateLine,\n extractJsonObjects,\n addLoadingPlaceholders,\n addSvgLoadingPlaceholders,\n enrichSectionImages,\n enrichSectionSvgCharts,\n} from \"./streamCore\";\nimport { sanitizeSemanticColors } from \"./sanitizeColors\";\nimport type { Section3 } from \"./types\";\nimport type { DesignDirection } from \"./directions\";\n\nexport const DOCUMENT_SYSTEM_PROMPT = `You are a professional document designer who creates stunning letter-sized (8.5\" × 11\") document pages using HTML + Tailwind CSS.\n\nRULES:\n- Each page is a <section> element sized for letter paper\n- Page structure: <section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col\">\n- The section is EXACTLY 11in tall — content MUST fit, never exceed. Use flex flex-col so children can use flex-1.\n- The section itself has NO padding — backgrounds, gradients, and decorative elements go edge-to-edge\n- Use slot layout: shrink-0 for header/footer bands, flex-1 overflow-hidden for main content area\n- For text content, use an inner wrapper: <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">...content...</div>\n- Footer elements (page numbers, decorative bars, contact info) use shrink-0 so they are ALWAYS visible\n- Cover pages and decorative sections can use full-bleed backgrounds (bg-primary, gradients, images that fill the entire page)\n- Content MUST NOT overflow page boundaries — be conservative with spacing\n- Use Tailwind CDN classes ONLY (no custom CSS, no @apply, no @import)\n- NO JavaScript, only HTML+Tailwind\n- All text content in Spanish unless the prompt specifies otherwise\n- Use real content from the source material, not Lorem ipsum\n- NOT responsive — fixed letter size, no breakpoints needed\n- Sections can have ANY background — full-bleed color, gradients, or white. Not limited to white paper.\n\nSTRICT PROHIBITIONS:\n1. **NO EMOJI** — Never use emoji characters (🚀❌✅📊 etc.). Instead use inline SVG icons or colored divs. For bullet decorators use small colored circles (<span class=\"w-2 h-2 rounded-full bg-primary inline-block\"></span>) or simple SVG.\n2. **NO Chart.js / NO JavaScript** — Never reference Chart.js, canvas, or any JS library. For data visualization use pure CSS: progress bars (div with percentage width + bg-primary), horizontal bars, styled tables with colored cells. Never use <canvas> or <script>.\n3. **NO buttons or CTAs** — This is a print document, not a web page. No \"Contactar\", \"Ver más\", \"Comprar\" buttons. Use text with contact info instead.\n4. **CONTRAST IS MANDATORY** — Dark/colored backgrounds (bg-primary, bg-primary-dark, bg-secondary, dark gradients) MUST use text-white or text-on-primary. Light backgrounds (white, bg-surface, bg-surface-alt) MUST use text-gray-900 or text-on-surface. NEVER use dark text on dark backgrounds or light text on light backgrounds.\n5. **Max 2 font weights per page** — Pick 2 (e.g. font-semibold + font-normal, or font-bold + font-light). Don't mix 4-5 weights.\n6. **Generous whitespace** — Don't fill every centimeter. Leave breathing room. Use py-8, py-12, gap-6, gap-8 liberally. Less content per page = more professional.\n\nLAYOUT OVERFLOW PREVENTION — CRITICAL:\n- Max 2 columns side by side — each with w-1/2. NEVER use 3+ columns.\n- Decorative sidebars: max w-16 (4rem). NEVER use w-[2.5in] or wider sidebars — they steal too much space.\n- Stats/metric grids: max 3 items per row (grid-cols-3). Use gap-4 or gap-6.\n- Tables: max 4 columns, use text-xs or text-sm for cell text, px-3 py-2 cell padding.\n- Images: always w-full or max-w-[50%] — never fixed pixel widths.\n- Text: never use text-6xl or larger except for cover page title. Body text: text-sm or text-base.\n- NEVER use absolute positioning that could overflow — prefer flex/grid layouts.\n- Decorative shapes with absolute positioning MUST stay fully inside the page. Use overflow-hidden on parent AND keep coordinates positive (no negative right/left values).\n- Large decorative text (text-[200px], text-[10rem] etc.) MUST have opacity-5 or lower AND overflow-hidden on its container. These giant texts frequently overflow — be extra careful.\n- NEVER place elements beyond the right edge — all content and decorations must fit within 8.5in width.\n\nDESIGN — ADAPT to the document type. Read the prompt carefully and match the visual style:\n\nGENERAL PRINCIPLES (apply to ALL documents):\n- First page is ALWAYS a stunning cover/title page with impactful design\n- Typography: strong hierarchy with just 2 weights, clear headings vs body\n- Each page visually distinct — different layouts, accent placements\n- Use the full page creatively — backgrounds, sidebars, geometric shapes\n- Professional and polished, never generic or template-looking\n- Icons: use simple inline SVG (12-20px) for visual accents. Keep SVGs minimal (single path, no complex illustrations)\n\nADAPT YOUR STYLE to what the user is creating:\n- Reports/Data: structured grids, tables with alternating rows (bg-surface-alt), progress bars, stat cards, clean data hierarchy\n- Brochures/Marketing: bold hero images, large headlines, feature grids, testimonial-style quotes, visual storytelling\n- Catalogs/Products: product cards with images, specs grids, price highlights, category headers with full-bleed color\n- Invitations/Events: centered dramatic typography, decorative borders, elegant spacing, date/location prominently styled\n- Proposals/Pitches: problem→solution flow, metric highlights, team/about sections, pricing tables\n- CVs/Resumes: clean sidebar layouts, skill bars, timeline for experience, contact info header\n- Creative/General: mix techniques — bento grids, full-bleed images, overlapping elements, bold color blocking\n\nVISUAL TECHNIQUES available to you:\n- Full-bleed colored pages (bg-primary, gradients)\n- Geometric accent shapes (CSS divs with clip-path or rotation)\n- Asymmetric layouts (grid with unequal columns)\n- Large stat numbers as visual anchors (text-5xl font-black)\n- Header/footer bands with contrasting color\n- Sidebar accents (thin, max w-16)\n- Image + text compositions\n- Bento-grid mixing content blocks of different sizes\n- Tables: alternating row colors, clean borders, generous cell padding (px-4 py-3)\n- For numerical data: CSS progress bars, styled tables with colored cells, large stat numbers — NEVER canvas/charts\n\nCSS PROGRESS BARS — use this pattern for data visualization:\n<div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-primary h-3 rounded-full\" style=\"width: 75%\"></div></div>\n\nCOLOR SYSTEM — use semantic classes:\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- Cover pages should use bold full-bleed backgrounds (bg-primary, gradients from-primary to-primary-dark)\n- CONTRAST: bg-primary/bg-primary-dark/bg-secondary → text-white or text-on-primary. White/bg-surface → text-gray-900 or text-on-surface\n\nIMAGES — USE GENEROUSLY:\n- EVERY image MUST use: <img data-image-query=\"english search query\" alt=\"description\" class=\"w-full h-auto object-cover rounded-xl\"/>\n- NEVER include a src attribute — the system auto-replaces data-image-query with a real image\n- For avatar-like elements, use colored divs with initials instead of img tags\n- Include at LEAST 3-5 images across the document — hero images, section illustrations, backgrounds, product photos\n- Each data-image-query should be a UNIQUE, specific search query in English (e.g. \"modern office workspace aerial view\", \"team brainstorming whiteboard\", \"abstract blue technology network\")\n- Use images to break up text-heavy pages and add visual interest\n\nCHARTS & DATA VISUALIZATION (SVG):\n- For charts, diagrams, and decorative data graphics, use:\n <div data-svg-chart=\"bar chart showing Q1 revenue: Jan $45K, Feb $52K, Mar $61K\" class=\"w-full\"></div>\n- The system generates professional SVG charts with a specialized tool — NEVER draw SVGs yourself\n- Use descriptive prompts with data points: type of chart + what it shows + actual values\n- Examples: \"donut chart: 40% Marketing, 30% Sales, 20% R&D, 10% Admin\", \"line chart showing growth: Q1 $100K, Q2 $150K, Q3 $220K, Q4 $310K\"\n- For simple metrics, still prefer CSS progress bars (they render faster)\n\nTAILWIND v3 NOTES:\n- Standard Tailwind v3 classes (shadow-sm, shadow-md, rounded-md, etc.)\n- Borders: border + border-gray-200 for visible borders\n\nEXAMPLE — Cover page (simple, no wide sidebars):\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-white\">\n <div class=\"absolute left-0 top-0 w-2 h-full bg-primary\"></div>\n <div class=\"flex-1 overflow-hidden flex flex-col justify-center px-[1in]\">\n <div class=\"text-sm font-normal text-primary mb-4\">Marzo 2026 · Versión 1.0</div>\n <h1 class=\"text-5xl font-bold text-gray-900 leading-tight\">Reporte<br/>Trimestral</h1>\n <div class=\"w-16 h-1 bg-primary mt-6 mb-4\"></div>\n <p class=\"text-lg font-normal text-gray-500\">Resultados y análisis del primer trimestre</p>\n </div>\n</section>\n\nEXAMPLE — Marketing/brochure page (bold, visual):\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-primary\">\n <div class=\"flex flex-1 overflow-hidden\">\n <div class=\"w-1/2 flex flex-col justify-center px-[0.75in]\">\n <span class=\"text-sm font-normal text-on-primary opacity-70 uppercase tracking-widest mb-3\">Solución Premium</span>\n <h2 class=\"text-4xl font-bold text-on-primary leading-tight mb-6\">Transforma tu negocio digital</h2>\n <p class=\"text-base font-normal text-on-primary opacity-80 mb-8\">Herramientas inteligentes que simplifican la gestión de tus activos digitales.</p>\n <div class=\"flex gap-6\">\n <div><div class=\"text-3xl font-bold text-accent\">98%</div><div class=\"text-xs text-on-primary opacity-70\">Satisfacción</div></div>\n <div><div class=\"text-3xl font-bold text-accent\">2.4K</div><div class=\"text-xs text-on-primary opacity-70\">Empresas</div></div>\n </div>\n </div>\n <div class=\"w-1/2 relative\">\n <img data-image-query=\"modern office team collaboration technology\" alt=\"Team working\" class=\"absolute inset-0 w-full h-full object-cover\" />\n </div>\n </div>\n</section>\n\nEXAMPLE — Catalog/product grid page:\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-surface\">\n <div class=\"shrink-0 h-1 bg-primary w-full\"></div>\n <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">\n <div class=\"flex justify-between items-baseline mb-6\">\n <h2 class=\"text-2xl font-bold text-on-surface\">Colección Primavera</h2>\n <span class=\"text-xs font-normal text-on-surface-muted uppercase tracking-wider\">Página 3 de 8</span>\n </div>\n <div class=\"grid grid-cols-2 gap-6\">\n <div class=\"bg-surface-alt rounded-xl overflow-hidden\">\n <img data-image-query=\"minimalist product on white background\" alt=\"Product\" class=\"w-full h-48 object-cover\" />\n <div class=\"p-4\"><h3 class=\"font-bold text-on-surface text-sm\">Producto Alpha</h3><p class=\"text-xs text-on-surface-muted mt-1\">Diseño ergonómico premium</p><div class=\"text-lg font-bold text-primary mt-2\">$2,490</div></div>\n </div>\n <div class=\"bg-surface-alt rounded-xl overflow-hidden\">\n <img data-image-query=\"elegant product photography studio\" alt=\"Product\" class=\"w-full h-48 object-cover\" />\n <div class=\"p-4\"><h3 class=\"font-bold text-on-surface text-sm\">Producto Beta</h3><p class=\"text-xs text-on-surface-muted mt-1\">Tecnología de vanguardia</p><div class=\"text-lg font-bold text-primary mt-2\">$3,190</div></div>\n </div>\n </div>\n </div>\n</section>\n\nEXAMPLE — Content page with table + progress bars:\n<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-white\">\n <div class=\"shrink-0 h-1.5 bg-primary w-full\"></div>\n <div class=\"flex-1 overflow-hidden px-[0.75in] py-[0.5in]\">\n <h2 class=\"text-2xl font-bold text-gray-900 mb-1\">Métricas de Rendimiento</h2>\n <p class=\"text-sm font-normal text-gray-500 mb-8\">Indicadores clave del periodo enero—marzo</p>\n <table class=\"w-full text-sm mb-10\">\n <thead><tr class=\"bg-primary text-white\"><th class=\"px-4 py-3 text-left font-semibold\">Indicador</th><th class=\"px-4 py-3 text-left font-semibold\">Valor</th><th class=\"px-4 py-3 text-left font-semibold\">Meta</th></tr></thead>\n <tbody>\n <tr class=\"bg-surface-alt\"><td class=\"px-4 py-3 text-gray-900\">Ingresos</td><td class=\"px-4 py-3 text-gray-900\">$1.2M</td><td class=\"px-4 py-3 text-gray-900\">$1.5M</td></tr>\n <tr><td class=\"px-4 py-3 text-gray-900\">Clientes nuevos</td><td class=\"px-4 py-3 text-gray-900\">340</td><td class=\"px-4 py-3 text-gray-900\">300</td></tr>\n <tr class=\"bg-surface-alt\"><td class=\"px-4 py-3 text-gray-900\">Retención</td><td class=\"px-4 py-3 text-gray-900\">92%</td><td class=\"px-4 py-3 text-gray-900\">90%</td></tr>\n </tbody>\n </table>\n <h3 class=\"text-lg font-bold text-gray-900 mb-4\">Progreso por Área</h3>\n <div class=\"space-y-4\">\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Ventas</span><span class=\"text-gray-500\">80%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-primary h-3 rounded-full\" style=\"width: 80%\"></div></div></div>\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Marketing</span><span class=\"text-gray-500\">65%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-secondary h-3 rounded-full\" style=\"width: 65%\"></div></div></div>\n <div><div class=\"flex justify-between text-sm mb-1\"><span class=\"text-gray-900 font-semibold\">Producto</span><span class=\"text-gray-500\">95%</span></div><div class=\"w-full bg-gray-200 rounded-full h-3\"><div class=\"bg-accent h-3 rounded-full\" style=\"width: 95%\"></div></div></div>\n </div>\n </div>\n</section>`;\n\nexport const DOCUMENT_PROMPT_SUFFIX = `\n\nOUTPUT FORMAT: NDJSON — one JSON object per line, NO wrapper array, NO markdown fences.\nEach line: {\"label\": \"Page Title\", \"html\": \"<section class='w-[8.5in] h-[11in] relative overflow-hidden flex flex-col'>...</section>\"}\n\nGenerate 3-8 pages depending on content length. First page = cover/title page.\nEach page must fit within letter size (8.5\" × 11\"). Be conservative with spacing.\nMake each page visually distinct — different layouts, different accent placements.\nIMPORTANT: Adapt your design style to match the type of document — not everything is a report. Brochures should feel bold and visual, catalogs should showcase products, invitations should feel elegant, etc.`;\n\nexport interface GenerateDocumentOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n logoUrl?: string;\n referenceImage?: string;\n extraInstructions?: string;\n model?: string | import(\"ai\").LanguageModel;\n pexelsApiKey?: string;\n /** Design direction — injects Google Fonts + hex colors into the prompt */\n direction?: DesignDirection;\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n onSection?: (section: Section3) => void;\n onImageUpdate?: (sectionId: string, html: string) => void;\n onRawChunk?: (buffer: string, completedCount: number) => void;\n onDone?: (sections: Section3[]) => void;\n onError?: (error: Error) => void;\n}\n\n/**\n * Generate a multi-page document with streaming AI + image enrichment.\n */\nexport async function generateDocument(options: GenerateDocumentOptions): Promise<Section3[]> {\n const {\n prompt,\n logoUrl,\n referenceImage,\n extraInstructions,\n direction,\n ...rest\n } = options;\n\n const extra = extraInstructions ? `\\nAdditional instructions: ${extraInstructions}` : \"\";\n\n // Build direction style instructions if provided\n let directionInstruction = \"\";\n if (direction) {\n const fontsUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(direction.headingFont).replace(/%20/g, \"+\")}:wght@400;700;900&family=${encodeURIComponent(direction.bodyFont).replace(/%20/g, \"+\")}:wght@400;500;600&display=swap`;\n directionInstruction = `\nDESIGN DIRECTION: \"${direction.name}\" — ${direction.tagline}\nTYPOGRAPHY: Use these Google Fonts via <link href=\"${fontsUrl}\" rel=\"stylesheet\"> on the first page.\n- Headings: font-family: '${direction.headingFont}', sans-serif (via inline style)\n- Body: font-family: '${direction.bodyFont}', sans-serif (via inline style)\nCOLORS — use ONLY semantic Tailwind classes (the editor injects CSS variables that resolve these):\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- NEVER use hardcoded hex colors like bg-[#xxx] or text-[#xxx] — always use semantic classes\n- The palette is: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}\nMood: ${direction.mood}\nLayout approach: ${direction.layoutHint}\nIMPORTANT: Apply inline style=\"font-family: '${direction.headingFont}'\" on ALL heading elements and style=\"font-family: '${direction.bodyFont}'\" on ALL body text elements. Include the Google Fonts <link> tag inside the FIRST <section> only.`;\n }\n // Truncate prompt to prevent token overflow (max ~15K chars ≈ 5K tokens)\n const safePrompt = prompt.length > 15_000 ? prompt.substring(0, 15_000) + \"\\n[...content truncated...]\" : prompt;\n const logoInstruction = logoUrl\n ? `\\nLOGO: Include this logo on the cover page and as a small header on other pages:\\n<img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-12 object-contain\" />\\nUse this exact <img> tag with this exact src URL — do NOT invent a different URL or modify it.`\n : \"\";\n\n const content: any[] = [];\n\n if (referenceImage) {\n const converted = dataUrlToImagePart(referenceImage);\n if (converted) {\n content.push({ type: \"image\", ...converted });\n } else {\n content.push({ type: \"image\", image: referenceImage });\n }\n content.push({\n type: \"text\",\n text: `Create a professional document inspired by this reference image for: ${safePrompt}${logoInstruction}${directionInstruction}${extra}${DOCUMENT_PROMPT_SUFFIX}`,\n });\n } else {\n content.push({\n type: \"text\",\n text: `Create a professional document for: ${safePrompt}${logoInstruction}${directionInstruction}${extra}${DOCUMENT_PROMPT_SUFFIX}`,\n });\n }\n\n return streamGenerate({\n ...rest,\n systemPrompt: DOCUMENT_SYSTEM_PROMPT,\n userContent: content,\n });\n}\n\n// ---------------------------------------------------------------------------\n// Parallel Document Generation\n// ---------------------------------------------------------------------------\n\nconst DocumentOutlineSchema = z.object({\n pages: z.array(z.object({\n pageNumber: z.number(),\n label: z.string().describe(\"Page title for sidebar\"),\n type: z.enum([\"cover\", \"content\", \"data\", \"visual\", \"closing\"]),\n layoutHint: z.string().describe(\"Layout approach: split, full-bleed, grid, editorial, table-heavy, sidebar\"),\n contentBrief: z.string().describe(\"2-4 sentences describing exactly what goes on this page\"),\n keyElements: z.array(z.string()).describe(\"Specific elements: stats grid, table, hero image, timeline, etc.\"),\n backgroundStyle: z.enum([\"white\", \"primary\", \"gradient\", \"surface-alt\", \"image\"]),\n continuesFrom: z.string().optional().describe(\"How this page relates to the previous one\"),\n })),\n});\n\nexport type DocumentOutline = z.infer<typeof DocumentOutlineSchema>;\n\nexport interface GenerateDocumentParallelOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n logoUrl?: string;\n referenceImage?: string;\n extraInstructions?: string;\n /** Model for page generation (quality model) */\n model?: string | import(\"ai\").LanguageModel;\n /** Model for outline generation (fast model) */\n outlineModel?: string | import(\"ai\").LanguageModel;\n pexelsApiKey?: string;\n direction?: DesignDirection;\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n pageCount?: number;\n skipCover?: boolean;\n onOutline?: (outline: DocumentOutline) => void;\n onPageChunk?: (pageIndex: number, partialHtml: string) => void;\n onPageComplete?: (pageIndex: number, section: Section3) => void;\n onImageUpdate?: (sectionId: string, html: string) => void;\n onDone?: (sections: Section3[]) => void;\n onError?: (error: Error) => void;\n /** Called with accumulated token usage from outline + all pages */\n onUsage?: (usage: { inputTokens: number; outputTokens: number }) => void;\n}\n\nfunction buildDirectionInstruction(direction: DesignDirection): string {\n const fontsUrl = `https://fonts.googleapis.com/css2?family=${encodeURIComponent(direction.headingFont).replace(/%20/g, \"+\")}:wght@400;700;900&family=${encodeURIComponent(direction.bodyFont).replace(/%20/g, \"+\")}:wght@400;500;600&display=swap`;\n return `\nDESIGN DIRECTION: \"${direction.name}\" — ${direction.tagline}\nTYPOGRAPHY: Use these Google Fonts via <link href=\"${fontsUrl}\" rel=\"stylesheet\"> on the first page.\n- Headings: font-family: '${direction.headingFont}', sans-serif (via inline style)\n- Body: font-family: '${direction.bodyFont}', sans-serif (via inline style)\nCOLORS — use ONLY semantic Tailwind classes (the editor injects CSS variables that resolve these):\n- bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary\n- bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted\n- bg-secondary, text-secondary, bg-accent, text-accent\n- NEVER use hardcoded hex colors like bg-[#xxx] or text-[#xxx] — always use semantic classes\n- The palette is: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}\nMood: ${direction.mood}\nLayout approach: ${direction.layoutHint}\nIMPORTANT: Apply inline style=\"font-family: '${direction.headingFont}'\" on ALL heading elements and style=\"font-family: '${direction.bodyFont}'\" on ALL body text elements. Include the Google Fonts <link> tag inside the FIRST <section> only.`;\n}\n\n/** Extract partial HTML from a raw JSON buffer (same pattern as onRawChunk) */\nfunction extractPartialHtml(buffer: string): string | null {\n const htmlMatch = buffer.match(/\"html\"\\s*:\\s*\"([\\s\\S]*)/);\n if (!htmlMatch) return null;\n let partial = htmlMatch[1]\n .replace(/\\\\n/g, '\\n').replace(/\\\\\"/g, '\"').replace(/\\\\\\\\/g, '\\\\');\n if (partial.endsWith('\\\\')) partial = partial.slice(0, -1);\n const lastQuote = partial.lastIndexOf('\"');\n if (lastQuote > 0) partial = partial.slice(0, lastQuote);\n if (/<section/i.test(partial) && !/<\\/section>/i.test(partial)) {\n partial += '</section>';\n }\n return partial.length > 20 ? partial : null;\n}\n\nexport async function generateDocumentParallel(options: GenerateDocumentParallelOptions): Promise<Section3[]> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n prompt,\n logoUrl,\n referenceImage,\n extraInstructions,\n model: pageModelId,\n outlineModel: outlineModelId,\n pexelsApiKey,\n direction,\n persistImage,\n pageCount,\n skipCover,\n onOutline,\n onPageChunk,\n onPageComplete,\n onImageUpdate,\n onDone,\n onError,\n onUsage,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n\n try {\n // --- Phase 1: Generate outline ---\n const outlineModel = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId: outlineModelId,\n defaultOpenai: \"gpt-4.1-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\n const safePrompt = prompt.length > 15_000 ? prompt.substring(0, 15_000) + \"\\n[...content truncated...]\" : prompt;\n const extra = extraInstructions ? `\\nAdditional instructions: ${extraInstructions}` : \"\";\n const pageCountHint = skipCover\n ? `Generate exactly ${Math.max(1, (pageCount || 5) - 1)} content pages (NO cover — it already exists).`\n : pageCount\n ? `Generate exactly ${pageCount} pages including a cover page.`\n : \"Generate 3-8 pages including a cover page.\";\n\n let totalInputTokens = 0;\n let totalOutputTokens = 0;\n\n const { object: rawOutline, usage: outlineUsage } = await generateObject({\n model: outlineModel,\n schema: DocumentOutlineSchema,\n prompt: `You are planning a professional document. Create a detailed page-by-page outline.\n\nDOCUMENT BRIEF: ${safePrompt}${extra}\n\nRULES:\n- ${pageCountHint}\n${skipCover ? \"- CRITICAL: Do NOT include a cover/title page. The cover already exists. Start with page type 'content', 'data', or 'visual'. NEVER use type 'cover'.\" : \"- First page is ALWAYS a stunning cover/title page.\"}\n- Distribute content EVENLY — no page should be overloaded\n- Each page must have a DISTINCT layout (mix split, full-bleed, grid, editorial, sidebar, table-heavy)\n- Narrative flows naturally: ${skipCover ? \"introduction → detail → data → closing\" : \"cover → introduction → detail → data → closing\"}\n- contentBrief must be detailed enough that a separate AI can generate the page independently\n- keyElements must list specific visual elements (not vague descriptions)\n- Vary backgroundStyle across pages — not all white\n- If source content is provided, assign specific portions to each page in the contentBrief\n${direction ? `- Design mood: ${direction.mood}, layout approach: ${direction.layoutHint}` : \"\"}`,\n });\n\n totalInputTokens += outlineUsage?.inputTokens || 0;\n totalOutputTokens += outlineUsage?.outputTokens || 0;\n\n // Filter out any cover pages if skipCover (AI sometimes ignores instructions)\n const outline: DocumentOutline = skipCover\n ? { pages: rawOutline.pages.filter(p => p.type !== \"cover\").map((p, i) => ({ ...p, pageNumber: i + 1 })) }\n : rawOutline;\n\n onOutline?.(outline);\n\n // --- Phase 2: Generate pages in parallel ---\n const directionInstruction = direction ? buildDirectionInstruction(direction) : \"\";\n const logoInstruction = logoUrl\n ? `\\nLOGO: Include this logo on the cover page and as a small header on other pages:\\n<img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-12 object-contain\" />\\nUse this exact <img> tag with this exact src URL.`\n : \"\";\n\n const outlineJson = JSON.stringify(outline.pages, null, 2);\n\n const pageModel = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId: pageModelId,\n defaultOpenai: \"gpt-4o\",\n defaultAnthropic: \"claude-sonnet-4-6\",\n });\n\n async function generateSinglePage(\n page: DocumentOutline[\"pages\"][number],\n retryCount = 0\n ): Promise<Section3> {\n const pageIdx = page.pageNumber - 1;\n const isCover = page.type === \"cover\";\n\n const userContent: any[] = [];\n\n // Reference image only for cover/visual pages\n if (referenceImage && (isCover || page.type === \"visual\")) {\n const converted = dataUrlToImagePart(referenceImage);\n if (converted) {\n userContent.push({ type: \"image\", ...converted });\n } else {\n userContent.push({ type: \"image\", image: referenceImage });\n }\n }\n\n userContent.push({\n type: \"text\",\n text: `You are generating PAGE ${page.pageNumber} of ${outline.pages.length} for a professional document.\n\nFULL DOCUMENT OUTLINE (for context — you are generating ONLY page ${page.pageNumber}):\n${outlineJson}\n\nYOUR PAGE ASSIGNMENT:\n- Label: ${page.label}\n- Type: ${page.type}\n- Layout: ${page.layoutHint}\n- Background: ${page.backgroundStyle}\n- Content: ${page.contentBrief}\n- Key elements: ${page.keyElements.join(\", \")}\n${page.continuesFrom ? `- Continues from: ${page.continuesFrom}` : \"\"}\n${isCover ? logoInstruction : logoUrl ? `\\nSmall logo header: <img src=\"${logoUrl}\" alt=\"Logo\" class=\"h-8 object-contain\" />` : \"\"}\n${directionInstruction}\n\nOUTPUT: A single JSON object on ONE line, no markdown fences:\n{\"label\": \"${page.label}\", \"html\": \"<section class='w-[8.5in] h-[11in] relative overflow-hidden flex flex-col'>...</section>\"}`,\n });\n\n try {\n const result = streamText({\n model: pageModel,\n system: DOCUMENT_SYSTEM_PROMPT + currentDateLine(),\n messages: [{ role: \"user\", content: userContent }],\n });\n\n let buffer = \"\";\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n buffer += chunk;\n chunkCount++;\n if (chunkCount % 5 === 0) {\n const partial = extractPartialHtml(buffer);\n if (partial) onPageChunk?.(pageIdx, partial);\n }\n }\n\n // Accumulate token usage\n const pageUsage = await result.usage;\n totalInputTokens += pageUsage?.inputTokens || 0;\n totalOutputTokens += pageUsage?.outputTokens || 0;\n\n // Final partial before parse\n const finalPartial = extractPartialHtml(buffer);\n if (finalPartial) onPageChunk?.(pageIdx, finalPartial);\n\n // Parse the JSON object\n let cleaned = buffer.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n const [objects] = extractJsonObjects(cleaned);\n const obj = objects[0];\n if (!obj?.html) throw new Error(`No valid HTML output for page ${page.pageNumber}`);\n\n const section: Section3 = {\n id: nanoid(8),\n order: pageIdx,\n html: sanitizeSemanticColors(addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html))),\n label: obj.label || page.label,\n };\n\n onPageComplete?.(pageIdx, section);\n return section;\n } catch (err) {\n if (retryCount < 1) {\n console.warn(`Page ${page.pageNumber} failed, retrying:`, (err as Error).message);\n return generateSinglePage(page, retryCount + 1);\n }\n // Return error placeholder\n const section: Section3 = {\n id: nanoid(8),\n order: pageIdx,\n html: `<section class=\"w-[8.5in] h-[11in] relative overflow-hidden flex flex-col bg-gray-50 items-center justify-center\"><div class=\"text-center text-gray-400\"><p class=\"text-lg font-semibold\">Error generando página</p><p class=\"text-sm mt-2\">${(err as Error).message?.slice(0, 100) || \"Error desconocido\"}</p></div></section>`,\n label: page.label,\n };\n onPageComplete?.(pageIdx, section);\n return section;\n }\n }\n\n const results = await Promise.allSettled(\n outline.pages.map((page) => generateSinglePage(page))\n );\n\n const sections: Section3[] = results\n .map((r) => r.status === \"fulfilled\" ? r.value : null)\n .filter((s): s is Section3 => s !== null)\n .sort((a, b) => a.order - b.order);\n\n // --- Phase 3: Image enrichment (sequential to respect Pexels rate limits) ---\n for (const section of sections) {\n await enrichSectionImages(section, {\n pexelsApiKey,\n openaiApiKey,\n persistImage,\n onImageUpdate,\n });\n await enrichSectionSvgCharts(section, {\n anthropicApiKey,\n onImageUpdate,\n });\n }\n\n // Final fallback for images without src\n for (const section of sections) {\n const before = section.html;\n section.html = section.html.replace(\n /<img\\s(?![^>]*\\bsrc=)([^>]*?)>/gi,\n (_match, attrs) => {\n const altMatch = attrs.match(/alt=\"([^\"]*?)\"/);\n const query = altMatch?.[1] || \"image\";\n return `<img src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" ${attrs}>`;\n }\n );\n if (section.html !== before) {\n onImageUpdate?.(section.id, section.html);\n }\n }\n\n onUsage?.({ inputTokens: totalInputTokens, outputTokens: totalOutputTokens });\n onDone?.(sections);\n return sections;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Parallel generation failed\");\n onError?.(error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
@@ -832,16 +832,7 @@ function FloatingToolbar({
832
832
  className: "fixed z-50 flex flex-col bg-gray-900 text-white rounded-xl shadow-2xl px-2 py-1.5 border border-gray-700",
833
833
  style: { top: finalTop, left: clampedLeft, maxWidth: "min(600px, calc(100vw - 16px))" },
834
834
  children: [
835
- /* @__PURE__ */ jsx3(
836
- "button",
837
- {
838
- onClick: onClose,
839
- className: "absolute top-1.5 right-2 w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 text-gray-400 hover:text-white transition-colors z-10",
840
- title: "Cerrar (ESC)",
841
- children: "\u2715"
842
- }
843
- ),
844
- /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 flex-wrap pr-8", children: [
835
+ /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1.5 flex-wrap", children: [
845
836
  selection.tagName && (() => {
846
837
  const tag = selection.tagName.toUpperCase();
847
838
  const HEADINGS = ["H1", "H2", "H3", "H4", "H5", "H6"];
@@ -989,7 +980,17 @@ function FloatingToolbar({
989
980
  ] })
990
981
  }
991
982
  )
992
- ] })
983
+ ] }),
984
+ /* @__PURE__ */ jsx3("div", { className: "w-px h-5 bg-gray-700" }),
985
+ /* @__PURE__ */ jsx3(
986
+ "button",
987
+ {
988
+ onClick: onClose,
989
+ className: "w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 text-gray-400 hover:text-white transition-colors shrink-0",
990
+ title: "Cerrar (ESC)",
991
+ children: "\u2715"
992
+ }
993
+ )
993
994
  ] }),
994
995
  refImage && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 pt-0.5 pb-0.5 border-t border-gray-700/50", children: [
995
996
  /* @__PURE__ */ jsx3("img", { src: refImage, alt: "Referencia", className: "w-10 h-10 rounded object-cover border border-gray-600" }),
@@ -1379,4 +1380,4 @@ export {
1379
1380
  ViewportToggle,
1380
1381
  useUndoStack
1381
1382
  };
1382
- //# sourceMappingURL=chunk-BK2DAZIT.js.map
1383
+ //# sourceMappingURL=chunk-FK4XJ7XI.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/components/Canvas.tsx","../src/components/SectionList.tsx","../src/components/FloatingToolbar.tsx","../src/components/CodeEditor.tsx","../src/components/ViewportToggle.tsx","../src/hooks/useUndoStack.ts"],"sourcesContent":["import React, { useRef, useEffect, useCallback, useState, forwardRef, useImperativeHandle } from \"react\";\nimport type { Section3, IframeMessage } from \"../types\";\nimport { buildPreviewHtml } from \"../buildHtml\";\n\nexport interface CanvasHandle {\n scrollToSection: (id: string) => void;\n postMessage: (msg: Record<string, unknown>) => void;\n}\n\ninterface CanvasProps {\n sections: Section3[];\n theme?: string;\n onMessage: (msg: IframeMessage) => void;\n iframeRectRef: React.MutableRefObject<DOMRect | null>;\n onReady?: () => void;\n}\n\nexport const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas({ sections, theme, onMessage, iframeRectRef, onReady: onReadyProp }, ref) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [ready, setReady] = useState(false);\n // Track what the iframe currently has so we can diff\n const knownSectionsRef = useRef<Map<string, string>>(new Map());\n const initializedRef = useRef(false);\n const onReadyRef = useRef(onReadyProp);\n onReadyRef.current = onReadyProp;\n\n // Post a message to the iframe\n const postToIframe = useCallback((msg: Record<string, unknown>) => {\n iframeRef.current?.contentWindow?.postMessage(msg, \"*\");\n }, []);\n\n useImperativeHandle(ref, () => ({\n scrollToSection(id: string) {\n postToIframe({ action: \"scroll-to-section\", id });\n },\n postMessage(msg: Record<string, unknown>) {\n postToIframe(msg);\n },\n }), [postToIframe]);\n\n // Initial write: set up the iframe shell (empty body + script + tailwind)\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe || initializedRef.current) return;\n initializedRef.current = true;\n\n const html = buildPreviewHtml([]);\n const doc = iframe.contentDocument;\n if (!doc) return;\n doc.open();\n doc.write(html);\n doc.close();\n }, []);\n\n // Handle \"ready\" from iframe — then inject current sections\n const handleReady = useCallback(() => {\n setReady(true);\n // Inject all current sections\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n for (const s of sorted) {\n postToIframe({ action: \"add-section\", id: s.id, html: s.html, scroll: false });\n knownSectionsRef.current.set(s.id, s.html);\n }\n // Restore scroll position from sessionStorage\n const savedY = sessionStorage.getItem(\"landing-v3-iframe-scrollY\");\n if (savedY) {\n setTimeout(() => postToIframe({ action: \"restore-scroll\", y: Number(savedY) }), 100);\n }\n onReadyRef.current?.();\n }, [sections, postToIframe]);\n\n // Incremental diff: detect added/updated/removed/renamed sections\n useEffect(() => {\n if (!ready) return;\n\n const known = knownSectionsRef.current;\n const currentIds = new Set(sections.map((s) => s.id));\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n\n // Detect renames: a known id disappears and a new id appears at the same index\n const removedIds = [...known.keys()].filter((id) => !currentIds.has(id));\n const addedSections = sorted.filter((s) => !known.has(s.id));\n\n // Match removed → added by position for rename (e.g. __building__ → real id)\n const renamedSet = new Set<string>();\n for (const removedId of removedIds) {\n if (addedSections.length > 0) {\n const added = addedSections.shift()!;\n postToIframe({ action: \"rename-section\", oldId: removedId, newId: added.id, html: added.html });\n known.delete(removedId);\n known.set(added.id, added.html);\n renamedSet.add(added.id);\n }\n }\n\n // Add truly new sections (not renamed)\n for (const s of sorted) {\n if (renamedSet.has(s.id)) continue;\n if (!known.has(s.id)) {\n postToIframe({ action: \"add-section\", id: s.id, html: s.html, scroll: s.id !== \"__building__\" });\n known.set(s.id, s.html);\n } else if (known.get(s.id) !== s.html) {\n // Update changed sections\n postToIframe({ action: \"update-section\", id: s.id, html: s.html });\n known.set(s.id, s.html);\n }\n }\n\n // Remove deleted sections (that weren't renamed)\n for (const id of [...known.keys()]) {\n if (!currentIds.has(id)) {\n postToIframe({ action: \"remove-section\", id });\n known.delete(id);\n }\n }\n\n // Reorder if needed\n const knownOrder = [...known.keys()];\n const desiredOrder = sorted.map((s) => s.id);\n if (knownOrder.length !== desiredOrder.length || knownOrder.some((id, i) => id !== desiredOrder[i])) {\n postToIframe({ action: \"reorder-sections\", order: desiredOrder });\n // Rebuild Map in new order so subsequent diffs compare correctly\n const reordered = new Map<string, string>();\n for (const id of desiredOrder) {\n const html = known.get(id);\n if (html !== undefined) reordered.set(id, html);\n }\n knownSectionsRef.current = reordered;\n }\n }, [sections, ready, postToIframe]);\n\n // Send theme changes to iframe\n useEffect(() => {\n if (!ready) return;\n postToIframe({ action: \"set-theme\", theme: theme || \"default\" });\n }, [theme, ready, postToIframe]);\n\n // Update iframe rect on resize/scroll\n const updateRect = useCallback(() => {\n if (iframeRef.current) {\n iframeRectRef.current = iframeRef.current.getBoundingClientRect();\n }\n }, [iframeRectRef]);\n\n useEffect(() => {\n updateRect();\n window.addEventListener(\"resize\", updateRect);\n window.addEventListener(\"scroll\", updateRect, true);\n return () => {\n window.removeEventListener(\"resize\", updateRect);\n window.removeEventListener(\"scroll\", updateRect, true);\n };\n }, [updateRect]);\n\n // Periodically save iframe scroll position\n useEffect(() => {\n if (!ready) return;\n const interval = setInterval(() => {\n postToIframe({ action: \"get-scroll\" });\n }, 2000);\n return () => clearInterval(interval);\n }, [ready, postToIframe]);\n\n // Listen for postMessage from iframe\n useEffect(() => {\n function handleMessage(e: MessageEvent) {\n const data = e.data;\n if (!data || typeof data.type !== \"string\") return;\n\n if (data.type === \"ready\") {\n handleReady();\n return;\n }\n\n if (data.type === \"scroll-position\") {\n sessionStorage.setItem(\"landing-v3-iframe-scrollY\", String(data.y));\n return;\n }\n\n if (\n [\"element-selected\", \"element-contextmenu\", \"text-edited\", \"element-deselected\", \"section-html-updated\", \"undo\", \"redo\"].includes(\n data.type\n )\n ) {\n // Sync known HTML so the diff doesn't re-send to iframe\n if (data.type === \"section-html-updated\" && data.sectionId && data.sectionHtml) {\n knownSectionsRef.current.set(data.sectionId, data.sectionHtml);\n }\n updateRect();\n onMessage(data as IframeMessage);\n }\n }\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [onMessage, updateRect, handleReady]);\n\n return (\n <div className=\"flex-1 overflow-hidden relative\">\n <iframe\n ref={iframeRef}\n title=\"Landing preview\"\n className=\"w-full h-full border-0\"\n sandbox=\"allow-scripts allow-same-origin\"\n style={{ minHeight: \"calc(100vh - 120px)\" }}\n />\n {!ready && sections.length > 0 && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-white/80\">\n <span className=\"w-6 h-6 border-2 border-gray-400 border-t-gray-800 rounded-full animate-spin\" />\n </div>\n )}\n <a href=\"https://www.easybits.cloud\" target=\"_blank\" rel=\"noopener noreferrer\"\n className=\"absolute bottom-2 right-3 text-xs text-gray-400 hover:text-gray-600 transition-colors\">\n Powered by easybits.cloud\n </a>\n </div>\n );\n});\n","import React, { useRef, useState } from \"react\";\nimport type { Section3 } from \"../types\";\nimport { LANDING_THEMES, type CustomColors } from \"../themes\";\n\ninterface SectionListProps {\n sections: Section3[];\n selectedSectionId: string | null;\n theme: string;\n customColors?: CustomColors;\n onThemeChange: (themeId: string) => void;\n onCustomColorChange?: (colors: Partial<CustomColors>) => void;\n onSelect: (id: string) => void;\n onOpenCode: (id: string) => void;\n onReorder: (fromIndex: number, toIndex: number) => void;\n onDelete: (id: string) => void;\n onRename: (id: string, label: string) => void;\n onAdd: () => void;\n}\n\nexport function SectionList({\n sections,\n selectedSectionId,\n theme,\n customColors,\n onThemeChange,\n onCustomColorChange,\n onSelect,\n onOpenCode,\n onReorder,\n onDelete,\n onRename,\n onAdd,\n}: SectionListProps) {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const colorInputRef = useRef<HTMLInputElement>(null);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingLabel, setEditingLabel] = useState(\"\");\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n <div className=\"p-3 border-b border-gray-200\">\n <h3 className=\"text-xs font-black uppercase tracking-wider text-gray-500 mb-2\">\n Tema\n </h3>\n <div className=\"flex gap-1.5 flex-wrap\">\n {LANDING_THEMES.map((t) => (\n <button\n key={t.id}\n onClick={() => onThemeChange(t.id)}\n title={t.label}\n className={`w-6 h-6 rounded-full border-2 transition-all ${\n theme === t.id\n ? \"border-black scale-110 shadow-sm\"\n : \"border-gray-300 hover:border-gray-400\"\n }`}\n style={{ backgroundColor: t.colors.primary }}\n />\n ))}\n {/* Custom color picker */}\n <button\n onClick={() => colorInputRef.current?.click()}\n title=\"Color personalizado\"\n className={`w-6 h-6 rounded-full border-2 transition-all relative overflow-hidden ${\n theme === \"custom\"\n ? \"border-black scale-110 shadow-sm\"\n : \"border-gray-300 hover:border-gray-400\"\n }`}\n style={theme === \"custom\" && customColors?.primary ? { backgroundColor: customColors.primary } : undefined}\n >\n {theme !== \"custom\" && (\n <span className=\"absolute inset-0 rounded-full\"\n style={{ background: \"conic-gradient(#ef4444, #eab308, #22c55e, #3b82f6, #a855f7, #ef4444)\" }}\n />\n )}\n </button>\n <input\n ref={colorInputRef}\n type=\"color\"\n value={customColors?.primary || \"#6366f1\"}\n onChange={(e) => onCustomColorChange?.({ primary: e.target.value })}\n className=\"sr-only\"\n />\n </div>\n {/* Multi-color pickers when custom theme is active */}\n {theme === \"custom\" && (\n <div className=\"flex items-center gap-2 mt-2\">\n {([\n { key: \"primary\" as const, label: \"Pri\", fallback: \"#6366f1\" },\n { key: \"secondary\" as const, label: \"Sec\", fallback: \"#f59e0b\" },\n { key: \"accent\" as const, label: \"Acc\", fallback: \"#06b6d4\" },\n { key: \"surface\" as const, label: \"Sur\", fallback: \"#ffffff\" },\n ]).map((c) => (\n <label key={c.key} className=\"flex flex-col items-center gap-0.5 cursor-pointer\">\n <input\n type=\"color\"\n value={customColors?.[c.key] || c.fallback}\n onChange={(e) => onCustomColorChange?.({ [c.key]: e.target.value })}\n className=\"w-5 h-5 rounded border border-gray-300 cursor-pointer p-0 [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:border-none [&::-webkit-color-swatch]:rounded\"\n />\n <span className=\"text-[9px] font-bold text-gray-400 uppercase\">{c.label}</span>\n </label>\n ))}\n </div>\n )}\n </div>\n <div className=\"p-3 border-b border-gray-200\">\n <h3 className=\"text-xs font-black uppercase tracking-wider text-gray-500\">\n Secciones\n </h3>\n </div>\n\n <div className=\"flex-1 overflow-y-auto py-1\">\n {sorted.map((section, i) => (\n <div\n key={section.id}\n onClick={() => onSelect(section.id)}\n className={`group flex items-center gap-2 px-3 py-2 cursor-pointer transition-colors ${\n selectedSectionId === section.id\n ? \"bg-blue-50 border-l-2 border-blue-500\"\n : \"hover:bg-gray-50 border-l-2 border-transparent\"\n }`}\n >\n <span className=\"text-[10px] font-mono text-gray-400 w-4 text-right\">\n {i + 1}\n </span>\n {editingId === section.id ? (\n <input\n type=\"text\"\n value={editingLabel}\n onChange={(e) => setEditingLabel(e.target.value)}\n onBlur={() => {\n if (editingLabel.trim()) onRename(section.id, editingLabel.trim());\n setEditingId(null);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n if (editingLabel.trim()) onRename(section.id, editingLabel.trim());\n setEditingId(null);\n } else if (e.key === \"Escape\") {\n setEditingId(null);\n }\n }}\n className=\"text-sm font-bold flex-1 min-w-0 bg-transparent border-b border-blue-500 outline-none px-0 py-0\"\n autoFocus\n onClick={(e) => e.stopPropagation()}\n />\n ) : (\n <span\n className=\"text-sm font-bold truncate flex-1\"\n onDoubleClick={(e) => {\n e.stopPropagation();\n setEditingId(section.id);\n setEditingLabel(section.label);\n }}\n >\n {section.label}\n </span>\n )}\n <div className=\"hidden group-hover:flex gap-0.5 shrink-0\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n onOpenCode(section.id);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200\"\n title=\"Editar HTML\"\n >\n <svg className=\"w-3 h-3\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M5.854 4.854a.5.5 0 1 0-.708-.708l-3.5 3.5a.5.5 0 0 0 0 .708l3.5 3.5a.5.5 0 0 0 .708-.708L2.707 8l3.147-3.146zm4.292 0a.5.5 0 0 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L13.293 8l-3.147-3.146z\"/></svg>\n </button>\n {i > 0 && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onReorder(i, i - 1);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200 text-[10px]\"\n >\n ↑\n </button>\n )}\n {i < sorted.length - 1 && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onReorder(i, i + 1);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200 text-[10px]\"\n >\n ↓\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n onDelete(section.id);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-red-600 hover:bg-red-50 text-[10px]\"\n title=\"Eliminar seccion\"\n >\n ✕\n </button>\n </div>\n </div>\n ))}\n </div>\n\n <div className=\"p-3 border-t border-gray-200\">\n <button\n onClick={onAdd}\n className=\"w-full text-center py-2 text-sm font-bold text-blue-600 hover:bg-blue-50 rounded-lg transition-colors\"\n >\n + Agregar seccion\n </button>\n </div>\n </div>\n );\n}\n","import React, { useState, useRef, useEffect } from \"react\";\nimport { HiSparkles } from \"react-icons/hi2\";\nimport type { IframeMessage } from \"../types\";\nimport type { LandingTheme } from \"../themes\";\n\nconst STYLE_PRESETS = [\n { label: \"Minimal\", icon: \"○\", instruction: \"Redisena esta seccion con estetica minimal: mucho espacio en blanco, tipografia limpia, sin bordes ni sombras innecesarias. Manten el mismo contenido.\" },\n { label: \"Cards\", icon: \"▦\", instruction: \"Redisena esta seccion usando layout de cards en grid: cada item en su propia card con padding, sombra sutil y bordes redondeados. Manten el mismo contenido.\" },\n { label: \"Bold\", icon: \"■\", instruction: \"Redisena esta seccion con estilo bold/brutalist: tipografia grande y gruesa, colores de alto contraste, bordes solidos, sin gradientes. Manten el mismo contenido.\" },\n { label: \"Glass\", icon: \"◇\", instruction: \"Redisena esta seccion con glassmorphism: fondos translucidos con backdrop-blur, bordes sutiles blancos, sombras suaves. Usa un fondo oscuro o con gradiente detras. Manten el mismo contenido.\" },\n { label: \"Dark\", icon: \"●\", instruction: \"Redisena esta seccion con fondo oscuro (#111 o similar), texto claro, acentos de color vibrantes. Manten el mismo contenido.\" },\n];\n\n// SVG icons for tab bar\nconst PaletteIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"13.5\" cy=\"6.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"17.5\" cy=\"10.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"8.5\" cy=\"7.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"6.5\" cy=\"12\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 011.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\" />\n </svg>\n);\n\nconst RulerIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21.3 15.3a2.4 2.4 0 010 3.4l-2.6 2.6a2.4 2.4 0 01-3.4 0L2.7 8.7a2.4 2.4 0 010-3.4l2.6-2.6a2.4 2.4 0 013.4 0z\" />\n <path d=\"M14.5 12.5l2-2\" />\n <path d=\"M11.5 9.5l2-2\" />\n <path d=\"M8.5 6.5l2-2\" />\n <path d=\"M17.5 15.5l2-2\" />\n </svg>\n);\n\nconst LinkIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71\" />\n </svg>\n);\n\ntype TabType = \"style\" | \"size\" | \"attrs\" | null;\n\ninterface FloatingToolbarProps {\n selection: IframeMessage | null;\n iframeRect: DOMRect | null;\n onRefine: (instruction: string, referenceImage?: string, opts?: { isVariant?: boolean }) => void;\n onMoveUp: () => void;\n onMoveDown: () => void;\n onDelete: () => void;\n onClose: () => void;\n onViewCode: () => void;\n onUpdateAttribute?: (sectionId: string, elementPath: string, attr: string, value: string) => void;\n onChangeTag?: (sectionId: string, elementPath: string, newTag: string) => void;\n onReplaceClass?: (sectionId: string, elementPath: string, removePrefixes: string[], addClass: string) => void;\n onDeleteElement?: (sectionId: string, elementPath: string) => void;\n isRefining: boolean;\n hideStylePresets?: boolean;\n themeColors?: LandingTheme[\"colors\"];\n}\n\nexport function FloatingToolbar({\n selection,\n iframeRect,\n onRefine,\n onMoveUp,\n onMoveDown,\n onDelete,\n onClose,\n onViewCode,\n onUpdateAttribute,\n onChangeTag,\n onReplaceClass,\n onDeleteElement,\n isRefining,\n hideStylePresets,\n themeColors,\n}: FloatingToolbarProps) {\n const [prompt, setPrompt] = useState(\"\");\n const [showCode, setShowCode] = useState(false);\n const [refImage, setRefImage] = useState<string | null>(null);\n const [refImageName, setRefImageName] = useState<string | null>(null);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const toolbarRef = useRef<HTMLDivElement>(null);\n\n const [showTagPicker, setShowTagPicker] = useState(false);\n const [activeTab, setActiveTab] = useState<TabType>(null);\n\n // Local attr editing state\n const [imgSrc, setImgSrc] = useState(\"\");\n const [imgAlt, setImgAlt] = useState(\"\");\n const [linkHref, setLinkHref] = useState(\"\");\n\n useEffect(() => {\n setPrompt(\"\");\n setShowCode(false);\n setRefImage(null);\n setRefImageName(null);\n setShowTagPicker(false);\n setActiveTab(null);\n // Auto-focus the AI prompt input when toolbar appears\n setTimeout(() => inputRef.current?.focus(), 50);\n }, [selection?.sectionId, selection?.elementPath]);\n\n // Sync attr inputs when selection changes\n useEffect(() => {\n if (selection?.attrs) {\n setImgSrc(selection.attrs.src || \"\");\n setImgAlt(selection.attrs.alt || \"\");\n setLinkHref(selection.attrs.href || \"\");\n }\n }, [selection?.attrs, selection?.elementPath]);\n\n // ESC closes toolbar\n useEffect(() => {\n function handleKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n document.addEventListener(\"keydown\", handleKey);\n return () => document.removeEventListener(\"keydown\", handleKey);\n }, [onClose]);\n\n if (!selection || !selection.rect || !iframeRect) return null;\n\n const toolbarWidth = toolbarRef.current?.offsetWidth || 600;\n const toolbarHeight = toolbarRef.current?.offsetHeight || 60;\n const top = iframeRect.top + selection.rect.top + selection.rect.height + 8;\n const left = iframeRect.left + selection.rect.left;\n const clampedLeft = Math.max(8, Math.min(left, window.innerWidth - toolbarWidth - 8));\n const showAbove = top + toolbarHeight + 8 > window.innerHeight;\n const finalTop = Math.max(8, showAbove\n ? iframeRect.top + selection.rect.top - toolbarHeight - 8\n : top);\n\n function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n if (isRefining || !selection) return;\n if (prompt.trim()) {\n onRefine(prompt.trim(), refImage || undefined);\n } else {\n // No text — request variant\n const tag = selection.tagName?.toLowerCase();\n const text = selection.text?.substring(0, 80);\n const variantPrompt = selection.isSectionRoot\n ? \"Genera una variante completamente diferente de esta seccion. Manten el mismo contenido/informacion pero cambia radicalmente el layout, la estructura visual, y el estilo. Sorprendeme con un diseno creativo e inesperado.\"\n : `Modifica SOLO el elemento <${tag}> que contiene \"${text}\". Genera una variante visual diferente de ESE elemento (diferente estilo, layout, tipografia). NO modifiques ningun otro elemento de la seccion.`;\n onRefine(variantPrompt, refImage || undefined, { isVariant: selection.isSectionRoot ? true : undefined });\n }\n setPrompt(\"\");\n if (inputRef.current) inputRef.current.style.height = \"auto\";\n setRefImage(null);\n setRefImageName(null);\n }\n\n function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const file = e.target.files?.[0];\n if (!file) return;\n setRefImageName(file.name);\n\n const img = new Image();\n img.onload = () => {\n const MAX = 1024;\n let { width, height } = img;\n if (width > MAX || height > MAX) {\n const ratio = Math.min(MAX / width, MAX / height);\n width = Math.round(width * ratio);\n height = Math.round(height * ratio);\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n canvas.getContext(\"2d\")!.drawImage(img, 0, 0, width, height);\n canvas.toBlob(\n (blob) => {\n if (!blob) return;\n const reader = new FileReader();\n reader.onload = () => setRefImage(reader.result as string);\n reader.readAsDataURL(blob);\n },\n \"image/jpeg\",\n 0.7\n );\n URL.revokeObjectURL(img.src);\n };\n img.src = URL.createObjectURL(file);\n e.target.value = \"\";\n }\n\n function handleSetAttr(attr: string, value: string) {\n if (!selection?.sectionId || !selection?.elementPath || !onUpdateAttribute) return;\n onUpdateAttribute(selection.sectionId, selection.elementPath, attr, value);\n }\n\n const isImg = selection.tagName === \"IMG\";\n const isLink = selection.tagName === \"A\";\n const hasAttrEditing = (isImg || isLink) && onUpdateAttribute;\n\n function handleReplaceClass(removePrefixes: string[], addClass: string) {\n if (!selection?.sectionId || !selection?.elementPath || !onUpdateAttribute) return;\n const currentClasses = (selection.className || \"\").split(/\\s+/).filter(Boolean);\n const filtered = currentClasses.filter(cls => {\n const bare = cls.includes(\":\") ? cls.substring(cls.lastIndexOf(\":\") + 1) : cls;\n return !removePrefixes.some(pfx => bare === pfx || bare.startsWith(pfx));\n });\n if (addClass) {\n for (const c of addClass.split(/\\s+/).filter(Boolean)) {\n if (!filtered.includes(c)) filtered.push(c);\n }\n }\n onUpdateAttribute(selection.sectionId, selection.elementPath, \"class\", filtered.join(\" \"));\n }\n\n // Determine size presets based on element type\n const sizePresets = (() => {\n if (!onUpdateAttribute || !selection.tagName) return null;\n const tag = selection.tagName.toUpperCase();\n const CONTAINERS = [\"DIV\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"NAV\", \"MAIN\"];\n const TEXT_TAGS = [\"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\", \"P\", \"SPAN\", \"BLOCKQUOTE\"];\n const currentClasses = (selection.className || \"\").split(/\\s+/);\n\n if (CONTAINERS.includes(tag)) {\n const W_PREFIXES = [\"w-\"];\n const MAX_W_PREFIXES = [\"max-w-\"];\n const P_PREFIXES = [\"p-\", \"px-\", \"py-\"];\n const widthOptions = [\n { label: \"Full\", cls: \"w-full\", prefixes: W_PREFIXES },\n { label: \"3/4\", cls: \"w-3/4\", prefixes: W_PREFIXES },\n { label: \"2/3\", cls: \"w-2/3\", prefixes: W_PREFIXES },\n { label: \"1/2\", cls: \"w-1/2\", prefixes: W_PREFIXES },\n { label: \"1/3\", cls: \"w-1/3\", prefixes: W_PREFIXES },\n ];\n const maxWOptions = [\n { label: \"sm\", cls: \"max-w-sm\", prefixes: MAX_W_PREFIXES },\n { label: \"md\", cls: \"max-w-md\", prefixes: MAX_W_PREFIXES },\n { label: \"lg\", cls: \"max-w-lg\", prefixes: MAX_W_PREFIXES },\n { label: \"xl\", cls: \"max-w-xl\", prefixes: MAX_W_PREFIXES },\n { label: \"2xl\", cls: \"max-w-2xl\", prefixes: MAX_W_PREFIXES },\n { label: \"full\", cls: \"max-w-full\", prefixes: MAX_W_PREFIXES },\n ];\n const paddingOptions = [\n { label: \"0\", cls: \"p-0\", prefixes: P_PREFIXES },\n { label: \"4\", cls: \"p-4\", prefixes: P_PREFIXES },\n { label: \"8\", cls: \"p-8\", prefixes: P_PREFIXES },\n { label: \"12\", cls: \"p-12\", prefixes: P_PREFIXES },\n { label: \"16\", cls: \"p-16\", prefixes: P_PREFIXES },\n ];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n { label: \"8\", cls: \"m-8\", prefixes: M_PREFIXES },\n ];\n return { width: widthOptions, maxW: maxWOptions, padding: paddingOptions, margin: marginOptions, currentClasses };\n }\n\n if (TEXT_TAGS.includes(tag)) {\n const TEXT_SIZE_EXACT = [\"text-xs\", \"text-sm\", \"text-base\", \"text-lg\", \"text-xl\", \"text-2xl\", \"text-3xl\", \"text-4xl\", \"text-5xl\", \"text-6xl\", \"text-7xl\", \"text-8xl\", \"text-9xl\"];\n const FONT_WEIGHT_EXACT = [\"font-thin\", \"font-extralight\", \"font-light\", \"font-normal\", \"font-medium\", \"font-semibold\", \"font-bold\", \"font-extrabold\", \"font-black\"];\n const textSizes = [\n { label: \"sm\", cls: \"text-sm\", prefixes: TEXT_SIZE_EXACT },\n { label: \"base\", cls: \"text-base\", prefixes: TEXT_SIZE_EXACT },\n { label: \"lg\", cls: \"text-lg\", prefixes: TEXT_SIZE_EXACT },\n { label: \"xl\", cls: \"text-xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"2xl\", cls: \"text-2xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"3xl\", cls: \"text-3xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"4xl\", cls: \"text-4xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"5xl\", cls: \"text-5xl\", prefixes: TEXT_SIZE_EXACT },\n ];\n const fontWeight = [\n { label: \"light\", cls: \"font-light\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"normal\", cls: \"font-normal\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"medium\", cls: \"font-medium\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"semibold\", cls: \"font-semibold\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"bold\", cls: \"font-bold\", prefixes: FONT_WEIGHT_EXACT },\n ];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n { label: \"8\", cls: \"m-8\", prefixes: M_PREFIXES },\n ];\n return { textSize: textSizes, fontWeight, margin: marginOptions, currentClasses };\n }\n\n if (tag === \"BUTTON\" || tag === \"A\") {\n const W_PREFIXES = [\"w-\"];\n const P_PREFIXES = [\"p-\", \"px-\", \"py-\"];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const widthOptions = [\n { label: \"auto\", cls: \"w-auto\", prefixes: W_PREFIXES },\n { label: \"Full\", cls: \"w-full\", prefixes: W_PREFIXES },\n { label: \"1/2\", cls: \"w-1/2\", prefixes: W_PREFIXES },\n { label: \"1/3\", cls: \"w-1/3\", prefixes: W_PREFIXES },\n ];\n const paddingOptions = [\n { label: \"0\", cls: \"p-0\", prefixes: P_PREFIXES },\n { label: \"2\", cls: \"px-2 py-1\", prefixes: P_PREFIXES },\n { label: \"4\", cls: \"px-4 py-2\", prefixes: P_PREFIXES },\n { label: \"6\", cls: \"px-6 py-3\", prefixes: P_PREFIXES },\n { label: \"8\", cls: \"px-8 py-4\", prefixes: P_PREFIXES },\n ];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n ];\n return { width: widthOptions, padding: paddingOptions, margin: marginOptions, currentClasses };\n }\n\n if (tag === \"IMG\") {\n const IMG_SIZE_PREFIXES = [\"w-\", \"max-w-\"];\n const ROUNDED_PREFIXES = [\"rounded\"];\n const imgSizes = [\n { label: \"sm\", cls: \"max-w-xs\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"md\", cls: \"max-w-md\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"lg\", cls: \"max-w-lg\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"xl\", cls: \"max-w-xl\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"full\", cls: \"w-full\", prefixes: IMG_SIZE_PREFIXES },\n ];\n const rounded = [\n { label: \"none\", cls: \"rounded-none\", prefixes: ROUNDED_PREFIXES },\n { label: \"md\", cls: \"rounded-md\", prefixes: ROUNDED_PREFIXES },\n { label: \"lg\", cls: \"rounded-lg\", prefixes: ROUNDED_PREFIXES },\n { label: \"xl\", cls: \"rounded-xl\", prefixes: ROUNDED_PREFIXES },\n { label: \"full\", cls: \"rounded-full\", prefixes: ROUNDED_PREFIXES },\n ];\n return { imgSize: imgSizes, rounded, currentClasses };\n }\n\n return null;\n })();\n\n // Determine which tabs are available\n const hasStyleTab = true; // Always available (colors for non-section, style presets for section)\n const hasSizeTab = sizePresets && !selection.isSectionRoot;\n const hasAttrsTab = hasAttrEditing;\n\n function toggleTab(tab: TabType) {\n setActiveTab(prev => prev === tab ? null : tab);\n }\n\n // Render color swatches panel\n function renderColorPanel() {\n if (selection!.isSectionRoot) {\n // Style presets for section root\n if (hideStylePresets) return null;\n return (\n <div className=\"flex items-center gap-1 pt-1 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0\">Estilo</span>\n {STYLE_PRESETS.map((preset) => (\n <button\n key={preset.label}\n onClick={() => onRefine(preset.instruction)}\n disabled={isRefining}\n className=\"px-2 py-0.5 text-[11px] font-medium rounded-md bg-gray-800 hover:bg-gray-700 disabled:opacity-30 transition-colors whitespace-nowrap\"\n title={preset.label}\n >\n <span className=\"mr-1\">{preset.icon}</span>\n {preset.label}\n </button>\n ))}\n </div>\n );\n }\n\n // Color swatches for non-section elements\n if (!onUpdateAttribute || selection!.tagName === 'IMG') return null;\n\n const containerTags = ['DIV', 'SECTION', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'MAIN', 'ARTICLE'];\n const isContainer = containerTags.includes(selection!.tagName ?? '');\n\n const TEXT_COLOR_PREFIXES = [\"text-primary\", \"text-secondary\", \"text-accent\", \"text-on-surface\", \"text-on-primary\", \"text-on-surface-muted\", \"text-white\", \"text-black\", \"text-transparent\"];\n const BG_COLOR_PREFIXES = [\"bg-primary\", \"bg-primary-dark\", \"bg-secondary\", \"bg-accent\", \"bg-surface\", \"bg-surface-alt\", \"bg-white\", \"bg-black\", \"bg-transparent\"];\n\n const themeSwatches = themeColors ? [\n { color: themeColors.primary, textCls: \"text-primary\", bgCls: \"bg-primary\", label: \"Primary\" },\n { color: themeColors.secondary, textCls: \"text-secondary\", bgCls: \"bg-secondary\", label: \"Secondary\" },\n { color: themeColors.accent, textCls: \"text-accent\", bgCls: \"bg-accent\", label: \"Accent\" },\n { color: themeColors.surface, textCls: \"text-on-surface\", bgCls: \"bg-surface\", label: \"Surface\" },\n ] : [];\n\n const fixedSwatches = [\n { color: \"#ffffff\", textCls: \"text-white\", bgCls: \"bg-white\", label: \"Blanco\" },\n { color: \"#000000\", textCls: \"text-black\", bgCls: \"bg-black\", label: \"Negro\" },\n { color: \"transparent\", textCls: \"text-transparent\", bgCls: \"bg-transparent\", label: \"Transparente\" },\n ];\n\n const renderColorRow = (label: string, mode: \"text\" | \"bg\") => (\n <div key={mode} className=\"flex items-center gap-1 pt-0.5 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0 w-10\">{label}</span>\n {fixedSwatches.map(({ color, textCls, bgCls, label: swatchLabel }) => (\n <button\n key={swatchLabel}\n onClick={() => {\n const prefixes = mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES;\n const cls = mode === \"text\" ? textCls : bgCls;\n handleReplaceClass(prefixes, cls);\n handleSetAttr(\"style\", \"\");\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 hover:scale-125 transition-transform shrink-0\"\n style={color === \"transparent\" ? {\n backgroundImage: \"repeating-conic-gradient(#808080 0% 25%, #c0c0c0 0% 50%)\",\n backgroundSize: \"8px 8px\",\n } : { backgroundColor: color }}\n title={swatchLabel}\n />\n ))}\n {themeSwatches.map(({ color, textCls, bgCls, label: swatchLabel }) => (\n <button\n key={swatchLabel}\n onClick={() => {\n const prefixes = mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES;\n const cls = mode === \"text\" ? textCls : bgCls;\n handleReplaceClass(prefixes, cls);\n handleSetAttr(\"style\", \"\");\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 hover:scale-125 transition-transform shrink-0\"\n style={{ backgroundColor: color }}\n title={swatchLabel}\n />\n ))}\n <input\n type=\"color\"\n onChange={(e) => {\n handleReplaceClass(mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES, \"\");\n const cssProp = mode === \"text\" ? \"color\" : \"background-color\";\n handleSetAttr(\"style\", `${cssProp}: ${e.target.value}`);\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 cursor-pointer shrink-0 p-0 bg-transparent [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:rounded-full [&::-webkit-color-swatch]:border-0\"\n title=\"Color personalizado\"\n />\n </div>\n );\n\n return (\n <div className=\"pt-1 pb-0.5\">\n {renderColorRow(\"Color\", \"text\")}\n {isContainer && renderColorRow(\"Fondo\", \"bg\")}\n </div>\n );\n }\n\n // Render size presets panel\n function renderSizePanel() {\n if (!sizePresets || selection!.isSectionRoot) return null;\n const groups = Object.entries(sizePresets).filter(([k]) => k !== 'currentClasses') as [string, { label: string; cls: string; prefixes: string[] }[]][];\n const labels: Record<string, string> = { width: \"Ancho\", maxW: \"Max\", padding: \"Padding\", margin: \"Margin\", textSize: \"Texto\", fontWeight: \"Peso\", imgSize: \"Tamaño\", rounded: \"Borde\" };\n return (\n <div className=\"pt-1 pb-0.5\">\n {groups.map(([key, options]) => (\n <div key={key} className=\"flex items-center gap-1 pt-0.5 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0 w-10\">{labels[key] || key}</span>\n {options.map((opt) => {\n const isActive = sizePresets.currentClasses.includes(opt.cls);\n return (\n <button\n key={opt.cls}\n onClick={() => handleReplaceClass(opt.prefixes, opt.cls)}\n className={`px-2 py-0.5 text-[10px] font-mono font-bold rounded-md transition-colors whitespace-nowrap ${\n isActive ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-300\"\n }`}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n );\n }\n\n // Render attributes panel\n function renderAttrsPanel() {\n if (!hasAttrEditing) return null;\n return (\n <div className=\"pt-1 pb-0.5\">\n {isImg && (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">src</span>\n <input\n type=\"text\"\n value={imgSrc}\n onChange={(e) => setImgSrc(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"src\", imgSrc); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"URL de imagen...\"\n />\n <button\n onClick={() => handleSetAttr(\"src\", imgSrc)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">alt</span>\n <input\n type=\"text\"\n value={imgAlt}\n onChange={(e) => setImgAlt(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"alt\", imgAlt); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"Alt text...\"\n />\n <button\n onClick={() => handleSetAttr(\"alt\", imgAlt)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n </div>\n )}\n {isLink && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">href</span>\n <input\n type=\"text\"\n value={linkHref}\n onChange={(e) => setLinkHref(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"href\", linkHref); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"URL del enlace...\"\n />\n <button\n onClick={() => handleSetAttr(\"href\", linkHref)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={toolbarRef}\n className=\"fixed z-50 flex flex-col bg-gray-900 text-white rounded-xl shadow-2xl px-2 py-1.5 border border-gray-700\"\n style={{ top: finalTop, left: clampedLeft, maxWidth: \"min(600px, calc(100vw - 16px))\" }}\n >\n {/* Main row */}\n <div className=\"flex items-center gap-1.5 flex-wrap\">\n {/* Tag badge / switcher */}\n {selection.tagName && (() => {\n const tag = selection.tagName.toUpperCase();\n const HEADINGS = [\"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\"];\n const TEXT = [\"P\", \"SPAN\", \"DIV\", \"BLOCKQUOTE\"];\n const CONTAINERS = [\"DIV\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"NAV\", \"MAIN\"];\n const NO_SWITCH = [\"A\", \"IMG\", \"INPUT\", \"BUTTON\", \"SVG\", \"VIDEO\", \"IFRAME\", \"TABLE\", \"UL\", \"OL\", \"LI\", \"FORM\"];\n\n let tagOptions: string[] = [];\n if (HEADINGS.includes(tag)) tagOptions = [...HEADINGS, \"P\"];\n else if (TEXT.includes(tag) && !CONTAINERS.includes(tag)) tagOptions = [...TEXT, \"H1\", \"H2\", \"H3\"];\n else if (CONTAINERS.includes(tag)) tagOptions = [...CONTAINERS, \"P\", \"SPAN\"];\n tagOptions = tagOptions.filter((t) => t !== tag);\n const canSwitch = !NO_SWITCH.includes(tag) && tagOptions.length > 0 && onChangeTag;\n\n return canSwitch ? (\n <div className=\"relative shrink-0\">\n <button\n onClick={() => setShowTagPicker(!showTagPicker)}\n className=\"px-2 py-0.5 rounded-md bg-blue-600 hover:bg-blue-500 text-[10px] font-mono font-bold uppercase tracking-wider transition-colors flex items-center gap-0.5\"\n >\n {tag.toLowerCase()}\n <svg className=\"w-2.5 h-2.5 opacity-60\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\" clipRule=\"evenodd\" />\n </svg>\n </button>\n {showTagPicker && (\n <div className={`absolute left-0 bg-gray-800 border border-gray-600 rounded-lg shadow-xl py-1 z-50 min-w-[4rem] max-h-[200px] overflow-y-auto ${showAbove ? \"bottom-full mb-1\" : \"top-full mt-1\"}`}>\n {tagOptions.map((t) => (\n <button\n key={t}\n onClick={() => {\n if (selection.sectionId && selection.elementPath) {\n onChangeTag(selection.sectionId, selection.elementPath, t.toLowerCase());\n }\n setShowTagPicker(false);\n }}\n className=\"block w-full text-left px-3 py-1 text-[11px] font-mono font-bold uppercase hover:bg-gray-700 transition-colors\"\n >\n {t.toLowerCase()}\n </button>\n ))}\n </div>\n )}\n </div>\n ) : (\n <span className=\"px-2 py-0.5 rounded-md bg-blue-600 text-[10px] font-mono font-bold uppercase tracking-wider shrink-0\">\n {tag.toLowerCase()}\n </span>\n );\n })()}\n\n {/* AI prompt input */}\n <form onSubmit={handleSubmit} className=\"flex items-start gap-1 flex-1\">\n <textarea\n ref={inputRef}\n value={prompt}\n onChange={(e) => {\n setPrompt(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit(e);\n }\n }}\n placeholder={refImage ? \"Instruccion + imagen...\" : \"Editar con AI...\"}\n disabled={isRefining}\n rows={1}\n className=\"bg-transparent text-sm text-white placeholder:text-gray-500 outline-none min-w-[6rem] sm:min-w-[10rem] flex-1 px-2 py-1 resize-none overflow-hidden\"\n />\n <button\n type=\"submit\"\n disabled={isRefining}\n className={`flex items-center justify-center rounded-lg bg-blue-500 hover:bg-blue-600 disabled:opacity-30 transition-colors shrink-0 ${\n prompt.trim() ? \"w-7 h-7\" : \"px-2.5 py-1 text-[11px] font-bold whitespace-nowrap\"\n }`}\n >\n {isRefining ? (\n <span className=\"w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin\" />\n ) : prompt.trim() ? (\n <HiSparkles className=\"w-3.5 h-3.5\" />\n ) : (\n \"✦ Variante\"\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isRefining}\n className={`w-7 h-7 flex items-center justify-center rounded-lg transition-colors shrink-0 ${\n refImage\n ? \"bg-blue-600 text-white\"\n : \"hover:bg-gray-800 text-gray-400 hover:text-white\"\n }`}\n title={refImage ? `Imagen: ${refImageName}` : \"Adjuntar imagen de referencia\"}\n >\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M1 5.25A2.25 2.25 0 013.25 3h13.5A2.25 2.25 0 0119 5.25v9.5A2.25 2.25 0 0116.75 17H3.25A2.25 2.25 0 011 14.75v-9.5zm1.5 5.81V14.75c0 .414.336.75.75.75h13.5a.75.75 0 00.75-.75v-2.06l-2.22-2.22a.75.75 0 00-1.06 0L8.56 16.1l-3.28-3.28a.75.75 0 00-1.06 0l-1.72 1.72zm12-4.06a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z\" clipRule=\"evenodd\"/>\n </svg>\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n </form>\n\n {/* Section-level actions (move/delete) */}\n {selection.isSectionRoot && (\n <span className=\"hidden sm:contents\">\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={onMoveUp}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 transition-colors text-xs\"\n title=\"Mover arriba\"\n >\n ↑\n </button>\n <button\n onClick={onMoveDown}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 transition-colors text-xs\"\n title=\"Mover abajo\"\n >\n ↓\n </button>\n </span>\n )}\n\n {selection.isSectionRoot ? (\n <>\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={onDelete}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-red-900/50 text-red-400 transition-colors\"\n title=\"Eliminar seccion\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\" />\n </svg>\n </button>\n </>\n ) : onDeleteElement && (\n <>\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={() => {\n if (selection.sectionId && selection.elementPath) {\n onDeleteElement(selection.sectionId, selection.elementPath);\n }\n }}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-red-900/50 text-red-400 transition-colors\"\n title=\"Eliminar elemento\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\" />\n </svg>\n </button>\n </>\n )}\n\n {/* Close button */}\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={onClose}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 text-gray-400 hover:text-white transition-colors shrink-0\"\n title=\"Cerrar (ESC)\"\n >\n ✕\n </button>\n </div>\n\n {/* Reference image preview */}\n {refImage && (\n <div className=\"flex items-center gap-2 pt-0.5 pb-0.5 border-t border-gray-700/50\">\n <img src={refImage} alt=\"Referencia\" className=\"w-10 h-10 rounded object-cover border border-gray-600\" />\n <span className=\"text-[10px] text-gray-400 truncate flex-1\">{refImageName}</span>\n <button\n onClick={() => { setRefImage(null); setRefImageName(null); }}\n className=\"text-[10px] text-gray-500 hover:text-white px-1\"\n >\n ✕\n </button>\n </div>\n )}\n\n {/* Tab bar */}\n <div className=\"flex items-center gap-1 pt-1 border-t border-gray-700/50 overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\">\n {hasStyleTab && (\n <button\n onClick={() => toggleTab(\"style\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"style\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Estilo\"\n >\n <PaletteIcon />\n <span>Estilo</span>\n </button>\n )}\n {hasSizeTab && (\n <button\n onClick={() => toggleTab(\"size\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"size\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Tamaño\"\n >\n <RulerIcon />\n <span>Tamaño</span>\n </button>\n )}\n {hasAttrsTab && (\n <button\n onClick={() => toggleTab(\"attrs\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"attrs\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Atributos\"\n >\n <LinkIcon />\n <span>Attrs</span>\n </button>\n )}\n <button\n onClick={onViewCode}\n className=\"hidden sm:flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium bg-gray-800 hover:bg-gray-700 text-gray-400 transition-colors font-mono\"\n title=\"Ver código\"\n >\n &lt;/&gt;\n </button>\n </div>\n\n {/* Active tab panel */}\n {activeTab === \"style\" && renderColorPanel()}\n {activeTab === \"size\" && renderSizePanel()}\n {activeTab === \"attrs\" && renderAttrsPanel()}\n </div>\n );\n}\n","import React, { useEffect, useRef, useCallback, useState } from \"react\";\nimport { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, Decoration, type DecorationSet } from \"@codemirror/view\";\nimport { EditorState, StateField, StateEffect } from \"@codemirror/state\";\nimport { html } from \"@codemirror/lang-html\";\nimport { oneDark } from \"@codemirror/theme-one-dark\";\nimport { defaultKeymap, indentWithTab, history, historyKeymap } from \"@codemirror/commands\";\nimport { searchKeymap, highlightSelectionMatches } from \"@codemirror/search\";\nimport { bracketMatching, foldGutter, foldKeymap } from \"@codemirror/language\";\nimport { closeBrackets, closeBracketsKeymap } from \"@codemirror/autocomplete\";\n\ninterface CodeEditorProps {\n code: string;\n label: string;\n scrollToText?: string;\n onSave: (code: string) => void;\n onClose: () => void;\n}\n\nfunction formatHtml(html: string): string {\n let result = html.replace(/>\\s*</g, \">\\n<\");\n const lines = result.split(\"\\n\");\n const output: string[] = [];\n let indent = 0;\n for (const raw of lines) {\n const line = raw.trim();\n if (!line) continue;\n const isClosing = /^<\\//.test(line);\n const isSelfClosing =\n /\\/>$/.test(line) ||\n /^<(img|br|hr|input|meta|link|col|area|base|embed|source|track|wbr)\\b/i.test(line);\n const hasInlineClose = /^<[^/][^>]*>.*<\\//.test(line);\n if (isClosing) indent = Math.max(0, indent - 1);\n output.push(\" \".repeat(indent) + line);\n if (!isClosing && !isSelfClosing && !hasInlineClose && /^<[a-zA-Z]/.test(line)) {\n indent++;\n }\n }\n return output.join(\"\\n\");\n}\n\n// Flash highlight effect for scroll-to-code\nconst flashLineEffect = StateEffect.define<{ from: number; to: number }>();\nconst clearFlashEffect = StateEffect.define<null>();\n\nconst flashLineDeco = Decoration.line({ class: \"cm-flash-line\" });\n\nconst flashLineField = StateField.define<DecorationSet>({\n create: () => Decoration.none,\n update(decos, tr) {\n for (const e of tr.effects) {\n if (e.is(flashLineEffect)) {\n return Decoration.set([flashLineDeco.range(e.value.from)]);\n }\n if (e.is(clearFlashEffect)) {\n return Decoration.none;\n }\n }\n return decos;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n\nfunction scrollToTarget(view: EditorView, target?: string) {\n if (!target) return;\n const docText = view.state.doc.toString();\n const normalized = target.replace(/\"/g, \"'\");\n let idx = docText.indexOf(normalized);\n if (idx === -1) idx = docText.indexOf(target);\n\n // If exact match fails, extract tag+class and search line by line\n if (idx === -1) {\n const tagMatch = target.match(/^<(\\w+)/);\n const classMatch = target.match(/class=[\"']([^\"']*?)[\"']/);\n if (tagMatch) {\n const searchTag = tagMatch[0];\n const searchClass = classMatch ? classMatch[1].split(\" \")[0] : null;\n for (let i = 1; i <= view.state.doc.lines; i++) {\n const line = view.state.doc.line(i);\n if (line.text.includes(searchTag) && (!searchClass || line.text.includes(searchClass))) {\n idx = line.from;\n break;\n }\n }\n }\n }\n\n if (idx !== -1) {\n const line = view.state.doc.lineAt(idx);\n view.dispatch({\n selection: { anchor: line.from },\n effects: [\n EditorView.scrollIntoView(line.from, { y: \"center\" }),\n flashLineEffect.of({ from: line.from, to: line.to }),\n ],\n });\n // Clear flash after 2s\n setTimeout(() => {\n view.dispatch({ effects: clearFlashEffect.of(null) });\n }, 2000);\n }\n}\n\nexport function CodeEditor({ code, label, scrollToText, onSave, onClose }: CodeEditorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const viewRef = useRef<EditorView | null>(null);\n const [stats, setStats] = useState({ lines: 0, kb: \"0.0\" });\n\n const onSaveRef = useRef(onSave);\n const onCloseRef = useRef(onClose);\n onSaveRef.current = onSave;\n onCloseRef.current = onClose;\n\n const updateStats = useCallback((doc: { length: number; lines: number }) => {\n setStats({ lines: doc.lines, kb: (doc.length / 1024).toFixed(1) });\n }, []);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const initialDoc = code.includes(\"\\n\") ? code : formatHtml(code);\n\n const state = EditorState.create({\n doc: initialDoc,\n extensions: [\n lineNumbers(),\n highlightActiveLine(),\n highlightActiveLineGutter(),\n bracketMatching(),\n closeBrackets(),\n foldGutter(),\n highlightSelectionMatches(),\n html(),\n oneDark,\n history(),\n EditorView.lineWrapping,\n keymap.of([\n { key: \"Mod-s\", run: (v) => { onSaveRef.current(v.state.doc.toString()); return true; } },\n { key: \"Escape\", run: () => { onCloseRef.current(); return true; } },\n indentWithTab,\n ...closeBracketsKeymap,\n ...searchKeymap,\n ...foldKeymap,\n ...historyKeymap,\n ...defaultKeymap,\n ]),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n updateStats(update.state.doc);\n }\n }),\n flashLineField,\n EditorView.theme({\n \"&\": { height: \"100%\", fontSize: \"13px\" },\n \".cm-scroller\": { overflow: \"auto\", fontFamily: \"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace\" },\n \".cm-content\": { padding: \"8px 0\" },\n \".cm-gutters\": { borderRight: \"1px solid #21262d\" },\n \".cm-flash-line\": { backgroundColor: \"rgba(250, 204, 21, 0.25)\", transition: \"background-color 2s ease-out\" },\n }),\n ],\n });\n\n const view = new EditorView({ state, parent: containerRef.current });\n viewRef.current = view;\n\n updateStats(view.state.doc);\n scrollToTarget(view, scrollToText);\n view.focus();\n\n return () => { view.destroy(); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Re-scroll when scrollToText changes while editor is already open\n useEffect(() => {\n const view = viewRef.current;\n if (!view || !scrollToText) return;\n scrollToTarget(view, scrollToText);\n }, [scrollToText]);\n\n function handleFormat() {\n const view = viewRef.current;\n if (!view) return;\n const formatted = formatHtml(view.state.doc.toString());\n view.dispatch({\n changes: { from: 0, to: view.state.doc.length, insert: formatted },\n });\n }\n\n function handleSave() {\n const view = viewRef.current;\n if (!view) return;\n onSave(view.state.doc.toString());\n }\n\n return (\n <div className=\"flex flex-col h-full bg-[#0d1117]\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-gray-800 shrink-0\">\n <div className=\"flex items-center gap-3\">\n <span className=\"px-2 py-0.5 rounded bg-orange-600/20 text-orange-400 text-[10px] font-mono font-bold uppercase tracking-wider\">\n HTML\n </span>\n <span className=\"text-sm font-bold text-gray-300\">{label}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleFormat}\n className=\"px-3 py-1.5 text-xs font-bold rounded-lg bg-gray-800 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors\"\n >\n Formatear\n </button>\n <button\n onClick={handleSave}\n className=\"px-4 py-1.5 text-xs font-bold rounded-lg bg-blue-500 text-white hover:bg-blue-600 transition-colors\"\n >\n Guardar\n </button>\n <button\n onClick={onClose}\n className=\"px-3 py-1.5 text-xs font-bold rounded-lg bg-gray-800 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors\"\n >\n Cerrar\n </button>\n </div>\n </div>\n\n {/* Editor */}\n <div ref={containerRef} className=\"flex-1 overflow-hidden\" />\n\n {/* Footer */}\n <div className=\"flex items-center justify-between px-4 py-1.5 border-t border-gray-800 text-[10px] text-gray-500 font-mono shrink-0\">\n <span>{stats.lines} lineas</span>\n <span>Tab = indentar &middot; Cmd+S = guardar &middot; Esc = cerrar</span>\n <span>{stats.kb} KB</span>\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\nexport type Viewport = \"desktop\" | \"tablet\" | \"mobile\";\n\nconst VIEWPORTS: { id: Viewport; label: string; icon: React.ReactElement }[] = [\n {\n id: \"desktop\",\n label: \"Desktop\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M2 4.25A2.25 2.25 0 014.25 2h11.5A2.25 2.25 0 0118 4.25v8.5A2.25 2.25 0 0115.75 15h-3.105a3.501 3.501 0 001.1 1.677A.75.75 0 0113.26 18H6.74a.75.75 0 01-.484-1.323A3.501 3.501 0 007.355 15H4.25A2.25 2.25 0 012 12.75v-8.5zm1.5 0a.75.75 0 01.75-.75h11.5a.75.75 0 01.75.75v8.5a.75.75 0 01-.75.75H4.25a.75.75 0 01-.75-.75v-8.5z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n {\n id: \"tablet\",\n label: \"Tablet\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2H5zm0 1.5h10a.5.5 0 01.5.5v14a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V3a.5.5 0 01.5-.5zm4 14a1 1 0 112 0 1 1 0 01-2 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n {\n id: \"mobile\",\n label: \"Mobile\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V4a2 2 0 00-2-2H6zm0 1.5h8a.5.5 0 01.5.5v12a.5.5 0 01-.5.5H6a.5.5 0 01-.5-.5V4a.5.5 0 01.5-.5zm3 13a1 1 0 112 0 1 1 0 01-2 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n];\n\nexport function ViewportToggle({\n value,\n onChange,\n activeClass = \"bg-blue-100 text-blue-700\",\n inactiveClass = \"text-gray-400 hover:text-gray-600 hover:bg-gray-100\",\n}: {\n value: Viewport;\n onChange: (v: Viewport) => void;\n activeClass?: string;\n inactiveClass?: string;\n}) {\n return (\n <div className=\"flex items-center justify-center gap-1 py-2 shrink-0 bg-gray-50 border-b border-gray-200\">\n {VIEWPORTS.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => onChange(v.id)}\n title={v.label}\n className={`p-1.5 rounded-lg transition-colors ${\n value === v.id ? activeClass : inactiveClass\n }`}\n >\n {v.icon}\n </button>\n ))}\n </div>\n );\n}\n","import { useState, useCallback, useRef } from \"react\";\n\nconst MAX_STACK = 30;\n\nexport function useUndoStack<T>() {\n const undoStack = useRef<T[]>([]);\n const redoStack = useRef<T[]>([]);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const pushUndo = useCallback((snapshot: T) => {\n undoStack.current.push(JSON.parse(JSON.stringify(snapshot)));\n if (undoStack.current.length > MAX_STACK) undoStack.current.shift();\n redoStack.current = [];\n setCanUndo(true);\n setCanRedo(false);\n }, []);\n\n const undo = useCallback((current: T): T | null => {\n if (undoStack.current.length === 0) return null;\n redoStack.current.push(JSON.parse(JSON.stringify(current)));\n const prev = undoStack.current.pop()!;\n setCanUndo(undoStack.current.length > 0);\n setCanRedo(true);\n return prev;\n }, []);\n\n const redo = useCallback((current: T): T | null => {\n if (redoStack.current.length === 0) return null;\n undoStack.current.push(JSON.parse(JSON.stringify(current)));\n const next = redoStack.current.pop()!;\n setCanUndo(true);\n setCanRedo(redoStack.current.length > 0);\n return next;\n }, []);\n\n return { pushUndo, undo, redo, canUndo, canRedo };\n}\n"],"mappings":"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","names":["Canvas","html","useRef","useState","jsx","jsxs","useRef","useState","useState","useRef","useEffect","jsx","jsxs","useEffect","useRef","useCallback","useState","jsx","jsxs","html","jsx","useState","useCallback","useRef"]}
@@ -5,7 +5,7 @@ import {
5
5
  SectionList,
6
6
  ViewportToggle,
7
7
  useUndoStack
8
- } from "./chunk-BK2DAZIT.js";
8
+ } from "./chunk-FK4XJ7XI.js";
9
9
  import "./chunk-OZ3RKRER.js";
10
10
  export {
11
11
  Canvas,
@@ -74,6 +74,11 @@ interface GenerateDocumentParallelOptions {
74
74
  onImageUpdate?: (sectionId: string, html: string) => void;
75
75
  onDone?: (sections: Section3[]) => void;
76
76
  onError?: (error: Error) => void;
77
+ /** Called with accumulated token usage from outline + all pages */
78
+ onUsage?: (usage: {
79
+ inputTokens: number;
80
+ outputTokens: number;
81
+ }) => void;
77
82
  }
78
83
  declare function generateDocumentParallel(options: GenerateDocumentParallelOptions): Promise<Section3[]>;
79
84
 
@@ -3,7 +3,7 @@ import {
3
3
  DOCUMENT_SYSTEM_PROMPT,
4
4
  generateDocument,
5
5
  generateDocumentParallel
6
- } from "./chunk-JB3MUQJS.js";
6
+ } from "./chunk-AUGNRZDT.js";
7
7
  import "./chunk-HTGS346J.js";
8
8
  export {
9
9
  DOCUMENT_PROMPT_SUFFIX,
package/dist/index.js CHANGED
@@ -4,7 +4,7 @@ import {
4
4
  FloatingToolbar,
5
5
  SectionList,
6
6
  ViewportToggle
7
- } from "./chunk-BK2DAZIT.js";
7
+ } from "./chunk-FK4XJ7XI.js";
8
8
  import "./chunk-MJ34S5ZC.js";
9
9
  import {
10
10
  PROMPT_SUFFIX,
@@ -15,7 +15,7 @@ import {
15
15
  DOCUMENT_PROMPT_SUFFIX,
16
16
  DOCUMENT_SYSTEM_PROMPT,
17
17
  generateDocument
18
- } from "./chunk-JB3MUQJS.js";
18
+ } from "./chunk-AUGNRZDT.js";
19
19
  import {
20
20
  REFINE_SYSTEM,
21
21
  refineLanding
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@easybits.cloud/html-tailwind-generator",
3
- "version": "0.2.104",
3
+ "version": "0.2.106",
4
4
  "description": "AI-powered landing page generator with Tailwind CSS — canvas editor, streaming generation, and one-click deploy",
5
5
  "license": "PolyForm-Noncommercial-1.0.0",
6
6
  "type": "module",
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/components/Canvas.tsx","../src/components/SectionList.tsx","../src/components/FloatingToolbar.tsx","../src/components/CodeEditor.tsx","../src/components/ViewportToggle.tsx","../src/hooks/useUndoStack.ts"],"sourcesContent":["import React, { useRef, useEffect, useCallback, useState, forwardRef, useImperativeHandle } from \"react\";\nimport type { Section3, IframeMessage } from \"../types\";\nimport { buildPreviewHtml } from \"../buildHtml\";\n\nexport interface CanvasHandle {\n scrollToSection: (id: string) => void;\n postMessage: (msg: Record<string, unknown>) => void;\n}\n\ninterface CanvasProps {\n sections: Section3[];\n theme?: string;\n onMessage: (msg: IframeMessage) => void;\n iframeRectRef: React.MutableRefObject<DOMRect | null>;\n onReady?: () => void;\n}\n\nexport const Canvas = forwardRef<CanvasHandle, CanvasProps>(function Canvas({ sections, theme, onMessage, iframeRectRef, onReady: onReadyProp }, ref) {\n const iframeRef = useRef<HTMLIFrameElement>(null);\n const [ready, setReady] = useState(false);\n // Track what the iframe currently has so we can diff\n const knownSectionsRef = useRef<Map<string, string>>(new Map());\n const initializedRef = useRef(false);\n const onReadyRef = useRef(onReadyProp);\n onReadyRef.current = onReadyProp;\n\n // Post a message to the iframe\n const postToIframe = useCallback((msg: Record<string, unknown>) => {\n iframeRef.current?.contentWindow?.postMessage(msg, \"*\");\n }, []);\n\n useImperativeHandle(ref, () => ({\n scrollToSection(id: string) {\n postToIframe({ action: \"scroll-to-section\", id });\n },\n postMessage(msg: Record<string, unknown>) {\n postToIframe(msg);\n },\n }), [postToIframe]);\n\n // Initial write: set up the iframe shell (empty body + script + tailwind)\n useEffect(() => {\n const iframe = iframeRef.current;\n if (!iframe || initializedRef.current) return;\n initializedRef.current = true;\n\n const html = buildPreviewHtml([]);\n const doc = iframe.contentDocument;\n if (!doc) return;\n doc.open();\n doc.write(html);\n doc.close();\n }, []);\n\n // Handle \"ready\" from iframe — then inject current sections\n const handleReady = useCallback(() => {\n setReady(true);\n // Inject all current sections\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n for (const s of sorted) {\n postToIframe({ action: \"add-section\", id: s.id, html: s.html, scroll: false });\n knownSectionsRef.current.set(s.id, s.html);\n }\n // Restore scroll position from sessionStorage\n const savedY = sessionStorage.getItem(\"landing-v3-iframe-scrollY\");\n if (savedY) {\n setTimeout(() => postToIframe({ action: \"restore-scroll\", y: Number(savedY) }), 100);\n }\n onReadyRef.current?.();\n }, [sections, postToIframe]);\n\n // Incremental diff: detect added/updated/removed/renamed sections\n useEffect(() => {\n if (!ready) return;\n\n const known = knownSectionsRef.current;\n const currentIds = new Set(sections.map((s) => s.id));\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n\n // Detect renames: a known id disappears and a new id appears at the same index\n const removedIds = [...known.keys()].filter((id) => !currentIds.has(id));\n const addedSections = sorted.filter((s) => !known.has(s.id));\n\n // Match removed → added by position for rename (e.g. __building__ → real id)\n const renamedSet = new Set<string>();\n for (const removedId of removedIds) {\n if (addedSections.length > 0) {\n const added = addedSections.shift()!;\n postToIframe({ action: \"rename-section\", oldId: removedId, newId: added.id, html: added.html });\n known.delete(removedId);\n known.set(added.id, added.html);\n renamedSet.add(added.id);\n }\n }\n\n // Add truly new sections (not renamed)\n for (const s of sorted) {\n if (renamedSet.has(s.id)) continue;\n if (!known.has(s.id)) {\n postToIframe({ action: \"add-section\", id: s.id, html: s.html, scroll: s.id !== \"__building__\" });\n known.set(s.id, s.html);\n } else if (known.get(s.id) !== s.html) {\n // Update changed sections\n postToIframe({ action: \"update-section\", id: s.id, html: s.html });\n known.set(s.id, s.html);\n }\n }\n\n // Remove deleted sections (that weren't renamed)\n for (const id of [...known.keys()]) {\n if (!currentIds.has(id)) {\n postToIframe({ action: \"remove-section\", id });\n known.delete(id);\n }\n }\n\n // Reorder if needed\n const knownOrder = [...known.keys()];\n const desiredOrder = sorted.map((s) => s.id);\n if (knownOrder.length !== desiredOrder.length || knownOrder.some((id, i) => id !== desiredOrder[i])) {\n postToIframe({ action: \"reorder-sections\", order: desiredOrder });\n // Rebuild Map in new order so subsequent diffs compare correctly\n const reordered = new Map<string, string>();\n for (const id of desiredOrder) {\n const html = known.get(id);\n if (html !== undefined) reordered.set(id, html);\n }\n knownSectionsRef.current = reordered;\n }\n }, [sections, ready, postToIframe]);\n\n // Send theme changes to iframe\n useEffect(() => {\n if (!ready) return;\n postToIframe({ action: \"set-theme\", theme: theme || \"default\" });\n }, [theme, ready, postToIframe]);\n\n // Update iframe rect on resize/scroll\n const updateRect = useCallback(() => {\n if (iframeRef.current) {\n iframeRectRef.current = iframeRef.current.getBoundingClientRect();\n }\n }, [iframeRectRef]);\n\n useEffect(() => {\n updateRect();\n window.addEventListener(\"resize\", updateRect);\n window.addEventListener(\"scroll\", updateRect, true);\n return () => {\n window.removeEventListener(\"resize\", updateRect);\n window.removeEventListener(\"scroll\", updateRect, true);\n };\n }, [updateRect]);\n\n // Periodically save iframe scroll position\n useEffect(() => {\n if (!ready) return;\n const interval = setInterval(() => {\n postToIframe({ action: \"get-scroll\" });\n }, 2000);\n return () => clearInterval(interval);\n }, [ready, postToIframe]);\n\n // Listen for postMessage from iframe\n useEffect(() => {\n function handleMessage(e: MessageEvent) {\n const data = e.data;\n if (!data || typeof data.type !== \"string\") return;\n\n if (data.type === \"ready\") {\n handleReady();\n return;\n }\n\n if (data.type === \"scroll-position\") {\n sessionStorage.setItem(\"landing-v3-iframe-scrollY\", String(data.y));\n return;\n }\n\n if (\n [\"element-selected\", \"element-contextmenu\", \"text-edited\", \"element-deselected\", \"section-html-updated\", \"undo\", \"redo\"].includes(\n data.type\n )\n ) {\n // Sync known HTML so the diff doesn't re-send to iframe\n if (data.type === \"section-html-updated\" && data.sectionId && data.sectionHtml) {\n knownSectionsRef.current.set(data.sectionId, data.sectionHtml);\n }\n updateRect();\n onMessage(data as IframeMessage);\n }\n }\n window.addEventListener(\"message\", handleMessage);\n return () => window.removeEventListener(\"message\", handleMessage);\n }, [onMessage, updateRect, handleReady]);\n\n return (\n <div className=\"flex-1 overflow-hidden relative\">\n <iframe\n ref={iframeRef}\n title=\"Landing preview\"\n className=\"w-full h-full border-0\"\n sandbox=\"allow-scripts allow-same-origin\"\n style={{ minHeight: \"calc(100vh - 120px)\" }}\n />\n {!ready && sections.length > 0 && (\n <div className=\"absolute inset-0 flex items-center justify-center bg-white/80\">\n <span className=\"w-6 h-6 border-2 border-gray-400 border-t-gray-800 rounded-full animate-spin\" />\n </div>\n )}\n <a href=\"https://www.easybits.cloud\" target=\"_blank\" rel=\"noopener noreferrer\"\n className=\"absolute bottom-2 right-3 text-xs text-gray-400 hover:text-gray-600 transition-colors\">\n Powered by easybits.cloud\n </a>\n </div>\n );\n});\n","import React, { useRef, useState } from \"react\";\nimport type { Section3 } from \"../types\";\nimport { LANDING_THEMES, type CustomColors } from \"../themes\";\n\ninterface SectionListProps {\n sections: Section3[];\n selectedSectionId: string | null;\n theme: string;\n customColors?: CustomColors;\n onThemeChange: (themeId: string) => void;\n onCustomColorChange?: (colors: Partial<CustomColors>) => void;\n onSelect: (id: string) => void;\n onOpenCode: (id: string) => void;\n onReorder: (fromIndex: number, toIndex: number) => void;\n onDelete: (id: string) => void;\n onRename: (id: string, label: string) => void;\n onAdd: () => void;\n}\n\nexport function SectionList({\n sections,\n selectedSectionId,\n theme,\n customColors,\n onThemeChange,\n onCustomColorChange,\n onSelect,\n onOpenCode,\n onReorder,\n onDelete,\n onRename,\n onAdd,\n}: SectionListProps) {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const colorInputRef = useRef<HTMLInputElement>(null);\n const [editingId, setEditingId] = useState<string | null>(null);\n const [editingLabel, setEditingLabel] = useState(\"\");\n\n return (\n <div className=\"flex flex-col flex-1 overflow-hidden\">\n <div className=\"p-3 border-b border-gray-200\">\n <h3 className=\"text-xs font-black uppercase tracking-wider text-gray-500 mb-2\">\n Tema\n </h3>\n <div className=\"flex gap-1.5 flex-wrap\">\n {LANDING_THEMES.map((t) => (\n <button\n key={t.id}\n onClick={() => onThemeChange(t.id)}\n title={t.label}\n className={`w-6 h-6 rounded-full border-2 transition-all ${\n theme === t.id\n ? \"border-black scale-110 shadow-sm\"\n : \"border-gray-300 hover:border-gray-400\"\n }`}\n style={{ backgroundColor: t.colors.primary }}\n />\n ))}\n {/* Custom color picker */}\n <button\n onClick={() => colorInputRef.current?.click()}\n title=\"Color personalizado\"\n className={`w-6 h-6 rounded-full border-2 transition-all relative overflow-hidden ${\n theme === \"custom\"\n ? \"border-black scale-110 shadow-sm\"\n : \"border-gray-300 hover:border-gray-400\"\n }`}\n style={theme === \"custom\" && customColors?.primary ? { backgroundColor: customColors.primary } : undefined}\n >\n {theme !== \"custom\" && (\n <span className=\"absolute inset-0 rounded-full\"\n style={{ background: \"conic-gradient(#ef4444, #eab308, #22c55e, #3b82f6, #a855f7, #ef4444)\" }}\n />\n )}\n </button>\n <input\n ref={colorInputRef}\n type=\"color\"\n value={customColors?.primary || \"#6366f1\"}\n onChange={(e) => onCustomColorChange?.({ primary: e.target.value })}\n className=\"sr-only\"\n />\n </div>\n {/* Multi-color pickers when custom theme is active */}\n {theme === \"custom\" && (\n <div className=\"flex items-center gap-2 mt-2\">\n {([\n { key: \"primary\" as const, label: \"Pri\", fallback: \"#6366f1\" },\n { key: \"secondary\" as const, label: \"Sec\", fallback: \"#f59e0b\" },\n { key: \"accent\" as const, label: \"Acc\", fallback: \"#06b6d4\" },\n { key: \"surface\" as const, label: \"Sur\", fallback: \"#ffffff\" },\n ]).map((c) => (\n <label key={c.key} className=\"flex flex-col items-center gap-0.5 cursor-pointer\">\n <input\n type=\"color\"\n value={customColors?.[c.key] || c.fallback}\n onChange={(e) => onCustomColorChange?.({ [c.key]: e.target.value })}\n className=\"w-5 h-5 rounded border border-gray-300 cursor-pointer p-0 [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:border-none [&::-webkit-color-swatch]:rounded\"\n />\n <span className=\"text-[9px] font-bold text-gray-400 uppercase\">{c.label}</span>\n </label>\n ))}\n </div>\n )}\n </div>\n <div className=\"p-3 border-b border-gray-200\">\n <h3 className=\"text-xs font-black uppercase tracking-wider text-gray-500\">\n Secciones\n </h3>\n </div>\n\n <div className=\"flex-1 overflow-y-auto py-1\">\n {sorted.map((section, i) => (\n <div\n key={section.id}\n onClick={() => onSelect(section.id)}\n className={`group flex items-center gap-2 px-3 py-2 cursor-pointer transition-colors ${\n selectedSectionId === section.id\n ? \"bg-blue-50 border-l-2 border-blue-500\"\n : \"hover:bg-gray-50 border-l-2 border-transparent\"\n }`}\n >\n <span className=\"text-[10px] font-mono text-gray-400 w-4 text-right\">\n {i + 1}\n </span>\n {editingId === section.id ? (\n <input\n type=\"text\"\n value={editingLabel}\n onChange={(e) => setEditingLabel(e.target.value)}\n onBlur={() => {\n if (editingLabel.trim()) onRename(section.id, editingLabel.trim());\n setEditingId(null);\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\") {\n if (editingLabel.trim()) onRename(section.id, editingLabel.trim());\n setEditingId(null);\n } else if (e.key === \"Escape\") {\n setEditingId(null);\n }\n }}\n className=\"text-sm font-bold flex-1 min-w-0 bg-transparent border-b border-blue-500 outline-none px-0 py-0\"\n autoFocus\n onClick={(e) => e.stopPropagation()}\n />\n ) : (\n <span\n className=\"text-sm font-bold truncate flex-1\"\n onDoubleClick={(e) => {\n e.stopPropagation();\n setEditingId(section.id);\n setEditingLabel(section.label);\n }}\n >\n {section.label}\n </span>\n )}\n <div className=\"hidden group-hover:flex gap-0.5 shrink-0\">\n <button\n onClick={(e) => {\n e.stopPropagation();\n onOpenCode(section.id);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200\"\n title=\"Editar HTML\"\n >\n <svg className=\"w-3 h-3\" viewBox=\"0 0 16 16\" fill=\"currentColor\"><path d=\"M5.854 4.854a.5.5 0 1 0-.708-.708l-3.5 3.5a.5.5 0 0 0 0 .708l3.5 3.5a.5.5 0 0 0 .708-.708L2.707 8l3.147-3.146zm4.292 0a.5.5 0 0 1 .708-.708l3.5 3.5a.5.5 0 0 1 0 .708l-3.5 3.5a.5.5 0 0 1-.708-.708L13.293 8l-3.147-3.146z\"/></svg>\n </button>\n {i > 0 && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onReorder(i, i - 1);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200 text-[10px]\"\n >\n ↑\n </button>\n )}\n {i < sorted.length - 1 && (\n <button\n onClick={(e) => {\n e.stopPropagation();\n onReorder(i, i + 1);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-gray-700 hover:bg-gray-200 text-[10px]\"\n >\n ↓\n </button>\n )}\n <button\n onClick={(e) => {\n e.stopPropagation();\n onDelete(section.id);\n }}\n className=\"w-5 h-5 flex items-center justify-center rounded text-gray-400 hover:text-red-600 hover:bg-red-50 text-[10px]\"\n title=\"Eliminar seccion\"\n >\n ✕\n </button>\n </div>\n </div>\n ))}\n </div>\n\n <div className=\"p-3 border-t border-gray-200\">\n <button\n onClick={onAdd}\n className=\"w-full text-center py-2 text-sm font-bold text-blue-600 hover:bg-blue-50 rounded-lg transition-colors\"\n >\n + Agregar seccion\n </button>\n </div>\n </div>\n );\n}\n","import React, { useState, useRef, useEffect } from \"react\";\nimport { HiSparkles } from \"react-icons/hi2\";\nimport type { IframeMessage } from \"../types\";\nimport type { LandingTheme } from \"../themes\";\n\nconst STYLE_PRESETS = [\n { label: \"Minimal\", icon: \"○\", instruction: \"Redisena esta seccion con estetica minimal: mucho espacio en blanco, tipografia limpia, sin bordes ni sombras innecesarias. Manten el mismo contenido.\" },\n { label: \"Cards\", icon: \"▦\", instruction: \"Redisena esta seccion usando layout de cards en grid: cada item en su propia card con padding, sombra sutil y bordes redondeados. Manten el mismo contenido.\" },\n { label: \"Bold\", icon: \"■\", instruction: \"Redisena esta seccion con estilo bold/brutalist: tipografia grande y gruesa, colores de alto contraste, bordes solidos, sin gradientes. Manten el mismo contenido.\" },\n { label: \"Glass\", icon: \"◇\", instruction: \"Redisena esta seccion con glassmorphism: fondos translucidos con backdrop-blur, bordes sutiles blancos, sombras suaves. Usa un fondo oscuro o con gradiente detras. Manten el mismo contenido.\" },\n { label: \"Dark\", icon: \"●\", instruction: \"Redisena esta seccion con fondo oscuro (#111 o similar), texto claro, acentos de color vibrantes. Manten el mismo contenido.\" },\n];\n\n// SVG icons for tab bar\nconst PaletteIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <circle cx=\"13.5\" cy=\"6.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"17.5\" cy=\"10.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"8.5\" cy=\"7.5\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <circle cx=\"6.5\" cy=\"12\" r=\"0.5\" fill=\"currentColor\" stroke=\"none\" />\n <path d=\"M12 2C6.5 2 2 6.5 2 12s4.5 10 10 10c.926 0 1.648-.746 1.648-1.688 0-.437-.18-.835-.437-1.125-.29-.289-.438-.652-.438-1.125a1.64 1.64 0 011.668-1.668h1.996c3.051 0 5.555-2.503 5.555-5.554C21.965 6.012 17.461 2 12 2z\" />\n </svg>\n);\n\nconst RulerIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M21.3 15.3a2.4 2.4 0 010 3.4l-2.6 2.6a2.4 2.4 0 01-3.4 0L2.7 8.7a2.4 2.4 0 010-3.4l2.6-2.6a2.4 2.4 0 013.4 0z\" />\n <path d=\"M14.5 12.5l2-2\" />\n <path d=\"M11.5 9.5l2-2\" />\n <path d=\"M8.5 6.5l2-2\" />\n <path d=\"M17.5 15.5l2-2\" />\n </svg>\n);\n\nconst LinkIcon = () => (\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <path d=\"M10 13a5 5 0 007.54.54l3-3a5 5 0 00-7.07-7.07l-1.72 1.71\" />\n <path d=\"M14 11a5 5 0 00-7.54-.54l-3 3a5 5 0 007.07 7.07l1.71-1.71\" />\n </svg>\n);\n\ntype TabType = \"style\" | \"size\" | \"attrs\" | null;\n\ninterface FloatingToolbarProps {\n selection: IframeMessage | null;\n iframeRect: DOMRect | null;\n onRefine: (instruction: string, referenceImage?: string, opts?: { isVariant?: boolean }) => void;\n onMoveUp: () => void;\n onMoveDown: () => void;\n onDelete: () => void;\n onClose: () => void;\n onViewCode: () => void;\n onUpdateAttribute?: (sectionId: string, elementPath: string, attr: string, value: string) => void;\n onChangeTag?: (sectionId: string, elementPath: string, newTag: string) => void;\n onReplaceClass?: (sectionId: string, elementPath: string, removePrefixes: string[], addClass: string) => void;\n onDeleteElement?: (sectionId: string, elementPath: string) => void;\n isRefining: boolean;\n hideStylePresets?: boolean;\n themeColors?: LandingTheme[\"colors\"];\n}\n\nexport function FloatingToolbar({\n selection,\n iframeRect,\n onRefine,\n onMoveUp,\n onMoveDown,\n onDelete,\n onClose,\n onViewCode,\n onUpdateAttribute,\n onChangeTag,\n onReplaceClass,\n onDeleteElement,\n isRefining,\n hideStylePresets,\n themeColors,\n}: FloatingToolbarProps) {\n const [prompt, setPrompt] = useState(\"\");\n const [showCode, setShowCode] = useState(false);\n const [refImage, setRefImage] = useState<string | null>(null);\n const [refImageName, setRefImageName] = useState<string | null>(null);\n const inputRef = useRef<HTMLTextAreaElement>(null);\n const fileInputRef = useRef<HTMLInputElement>(null);\n const toolbarRef = useRef<HTMLDivElement>(null);\n\n const [showTagPicker, setShowTagPicker] = useState(false);\n const [activeTab, setActiveTab] = useState<TabType>(null);\n\n // Local attr editing state\n const [imgSrc, setImgSrc] = useState(\"\");\n const [imgAlt, setImgAlt] = useState(\"\");\n const [linkHref, setLinkHref] = useState(\"\");\n\n useEffect(() => {\n setPrompt(\"\");\n setShowCode(false);\n setRefImage(null);\n setRefImageName(null);\n setShowTagPicker(false);\n setActiveTab(null);\n // Auto-focus the AI prompt input when toolbar appears\n setTimeout(() => inputRef.current?.focus(), 50);\n }, [selection?.sectionId, selection?.elementPath]);\n\n // Sync attr inputs when selection changes\n useEffect(() => {\n if (selection?.attrs) {\n setImgSrc(selection.attrs.src || \"\");\n setImgAlt(selection.attrs.alt || \"\");\n setLinkHref(selection.attrs.href || \"\");\n }\n }, [selection?.attrs, selection?.elementPath]);\n\n // ESC closes toolbar\n useEffect(() => {\n function handleKey(e: KeyboardEvent) {\n if (e.key === \"Escape\") onClose();\n }\n document.addEventListener(\"keydown\", handleKey);\n return () => document.removeEventListener(\"keydown\", handleKey);\n }, [onClose]);\n\n if (!selection || !selection.rect || !iframeRect) return null;\n\n const toolbarWidth = toolbarRef.current?.offsetWidth || 600;\n const toolbarHeight = toolbarRef.current?.offsetHeight || 60;\n const top = iframeRect.top + selection.rect.top + selection.rect.height + 8;\n const left = iframeRect.left + selection.rect.left;\n const clampedLeft = Math.max(8, Math.min(left, window.innerWidth - toolbarWidth - 8));\n const showAbove = top + toolbarHeight + 8 > window.innerHeight;\n const finalTop = Math.max(8, showAbove\n ? iframeRect.top + selection.rect.top - toolbarHeight - 8\n : top);\n\n function handleSubmit(e: React.FormEvent) {\n e.preventDefault();\n if (isRefining || !selection) return;\n if (prompt.trim()) {\n onRefine(prompt.trim(), refImage || undefined);\n } else {\n // No text — request variant\n const tag = selection.tagName?.toLowerCase();\n const text = selection.text?.substring(0, 80);\n const variantPrompt = selection.isSectionRoot\n ? \"Genera una variante completamente diferente de esta seccion. Manten el mismo contenido/informacion pero cambia radicalmente el layout, la estructura visual, y el estilo. Sorprendeme con un diseno creativo e inesperado.\"\n : `Modifica SOLO el elemento <${tag}> que contiene \"${text}\". Genera una variante visual diferente de ESE elemento (diferente estilo, layout, tipografia). NO modifiques ningun otro elemento de la seccion.`;\n onRefine(variantPrompt, refImage || undefined, { isVariant: selection.isSectionRoot ? true : undefined });\n }\n setPrompt(\"\");\n if (inputRef.current) inputRef.current.style.height = \"auto\";\n setRefImage(null);\n setRefImageName(null);\n }\n\n function handleFileSelect(e: React.ChangeEvent<HTMLInputElement>) {\n const file = e.target.files?.[0];\n if (!file) return;\n setRefImageName(file.name);\n\n const img = new Image();\n img.onload = () => {\n const MAX = 1024;\n let { width, height } = img;\n if (width > MAX || height > MAX) {\n const ratio = Math.min(MAX / width, MAX / height);\n width = Math.round(width * ratio);\n height = Math.round(height * ratio);\n }\n const canvas = document.createElement(\"canvas\");\n canvas.width = width;\n canvas.height = height;\n canvas.getContext(\"2d\")!.drawImage(img, 0, 0, width, height);\n canvas.toBlob(\n (blob) => {\n if (!blob) return;\n const reader = new FileReader();\n reader.onload = () => setRefImage(reader.result as string);\n reader.readAsDataURL(blob);\n },\n \"image/jpeg\",\n 0.7\n );\n URL.revokeObjectURL(img.src);\n };\n img.src = URL.createObjectURL(file);\n e.target.value = \"\";\n }\n\n function handleSetAttr(attr: string, value: string) {\n if (!selection?.sectionId || !selection?.elementPath || !onUpdateAttribute) return;\n onUpdateAttribute(selection.sectionId, selection.elementPath, attr, value);\n }\n\n const isImg = selection.tagName === \"IMG\";\n const isLink = selection.tagName === \"A\";\n const hasAttrEditing = (isImg || isLink) && onUpdateAttribute;\n\n function handleReplaceClass(removePrefixes: string[], addClass: string) {\n if (!selection?.sectionId || !selection?.elementPath || !onUpdateAttribute) return;\n const currentClasses = (selection.className || \"\").split(/\\s+/).filter(Boolean);\n const filtered = currentClasses.filter(cls => {\n const bare = cls.includes(\":\") ? cls.substring(cls.lastIndexOf(\":\") + 1) : cls;\n return !removePrefixes.some(pfx => bare === pfx || bare.startsWith(pfx));\n });\n if (addClass) {\n for (const c of addClass.split(/\\s+/).filter(Boolean)) {\n if (!filtered.includes(c)) filtered.push(c);\n }\n }\n onUpdateAttribute(selection.sectionId, selection.elementPath, \"class\", filtered.join(\" \"));\n }\n\n // Determine size presets based on element type\n const sizePresets = (() => {\n if (!onUpdateAttribute || !selection.tagName) return null;\n const tag = selection.tagName.toUpperCase();\n const CONTAINERS = [\"DIV\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"NAV\", \"MAIN\"];\n const TEXT_TAGS = [\"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\", \"P\", \"SPAN\", \"BLOCKQUOTE\"];\n const currentClasses = (selection.className || \"\").split(/\\s+/);\n\n if (CONTAINERS.includes(tag)) {\n const W_PREFIXES = [\"w-\"];\n const MAX_W_PREFIXES = [\"max-w-\"];\n const P_PREFIXES = [\"p-\", \"px-\", \"py-\"];\n const widthOptions = [\n { label: \"Full\", cls: \"w-full\", prefixes: W_PREFIXES },\n { label: \"3/4\", cls: \"w-3/4\", prefixes: W_PREFIXES },\n { label: \"2/3\", cls: \"w-2/3\", prefixes: W_PREFIXES },\n { label: \"1/2\", cls: \"w-1/2\", prefixes: W_PREFIXES },\n { label: \"1/3\", cls: \"w-1/3\", prefixes: W_PREFIXES },\n ];\n const maxWOptions = [\n { label: \"sm\", cls: \"max-w-sm\", prefixes: MAX_W_PREFIXES },\n { label: \"md\", cls: \"max-w-md\", prefixes: MAX_W_PREFIXES },\n { label: \"lg\", cls: \"max-w-lg\", prefixes: MAX_W_PREFIXES },\n { label: \"xl\", cls: \"max-w-xl\", prefixes: MAX_W_PREFIXES },\n { label: \"2xl\", cls: \"max-w-2xl\", prefixes: MAX_W_PREFIXES },\n { label: \"full\", cls: \"max-w-full\", prefixes: MAX_W_PREFIXES },\n ];\n const paddingOptions = [\n { label: \"0\", cls: \"p-0\", prefixes: P_PREFIXES },\n { label: \"4\", cls: \"p-4\", prefixes: P_PREFIXES },\n { label: \"8\", cls: \"p-8\", prefixes: P_PREFIXES },\n { label: \"12\", cls: \"p-12\", prefixes: P_PREFIXES },\n { label: \"16\", cls: \"p-16\", prefixes: P_PREFIXES },\n ];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n { label: \"8\", cls: \"m-8\", prefixes: M_PREFIXES },\n ];\n return { width: widthOptions, maxW: maxWOptions, padding: paddingOptions, margin: marginOptions, currentClasses };\n }\n\n if (TEXT_TAGS.includes(tag)) {\n const TEXT_SIZE_EXACT = [\"text-xs\", \"text-sm\", \"text-base\", \"text-lg\", \"text-xl\", \"text-2xl\", \"text-3xl\", \"text-4xl\", \"text-5xl\", \"text-6xl\", \"text-7xl\", \"text-8xl\", \"text-9xl\"];\n const FONT_WEIGHT_EXACT = [\"font-thin\", \"font-extralight\", \"font-light\", \"font-normal\", \"font-medium\", \"font-semibold\", \"font-bold\", \"font-extrabold\", \"font-black\"];\n const textSizes = [\n { label: \"sm\", cls: \"text-sm\", prefixes: TEXT_SIZE_EXACT },\n { label: \"base\", cls: \"text-base\", prefixes: TEXT_SIZE_EXACT },\n { label: \"lg\", cls: \"text-lg\", prefixes: TEXT_SIZE_EXACT },\n { label: \"xl\", cls: \"text-xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"2xl\", cls: \"text-2xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"3xl\", cls: \"text-3xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"4xl\", cls: \"text-4xl\", prefixes: TEXT_SIZE_EXACT },\n { label: \"5xl\", cls: \"text-5xl\", prefixes: TEXT_SIZE_EXACT },\n ];\n const fontWeight = [\n { label: \"light\", cls: \"font-light\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"normal\", cls: \"font-normal\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"medium\", cls: \"font-medium\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"semibold\", cls: \"font-semibold\", prefixes: FONT_WEIGHT_EXACT },\n { label: \"bold\", cls: \"font-bold\", prefixes: FONT_WEIGHT_EXACT },\n ];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n { label: \"8\", cls: \"m-8\", prefixes: M_PREFIXES },\n ];\n return { textSize: textSizes, fontWeight, margin: marginOptions, currentClasses };\n }\n\n if (tag === \"BUTTON\" || tag === \"A\") {\n const W_PREFIXES = [\"w-\"];\n const P_PREFIXES = [\"p-\", \"px-\", \"py-\"];\n const M_PREFIXES = [\"m-\", \"mx-\", \"my-\", \"mt-\", \"mb-\"];\n const widthOptions = [\n { label: \"auto\", cls: \"w-auto\", prefixes: W_PREFIXES },\n { label: \"Full\", cls: \"w-full\", prefixes: W_PREFIXES },\n { label: \"1/2\", cls: \"w-1/2\", prefixes: W_PREFIXES },\n { label: \"1/3\", cls: \"w-1/3\", prefixes: W_PREFIXES },\n ];\n const paddingOptions = [\n { label: \"0\", cls: \"p-0\", prefixes: P_PREFIXES },\n { label: \"2\", cls: \"px-2 py-1\", prefixes: P_PREFIXES },\n { label: \"4\", cls: \"px-4 py-2\", prefixes: P_PREFIXES },\n { label: \"6\", cls: \"px-6 py-3\", prefixes: P_PREFIXES },\n { label: \"8\", cls: \"px-8 py-4\", prefixes: P_PREFIXES },\n ];\n const marginOptions = [\n { label: \"0\", cls: \"m-0\", prefixes: M_PREFIXES },\n { label: \"auto\", cls: \"mx-auto\", prefixes: M_PREFIXES },\n { label: \"2\", cls: \"m-2\", prefixes: M_PREFIXES },\n { label: \"4\", cls: \"m-4\", prefixes: M_PREFIXES },\n ];\n return { width: widthOptions, padding: paddingOptions, margin: marginOptions, currentClasses };\n }\n\n if (tag === \"IMG\") {\n const IMG_SIZE_PREFIXES = [\"w-\", \"max-w-\"];\n const ROUNDED_PREFIXES = [\"rounded\"];\n const imgSizes = [\n { label: \"sm\", cls: \"max-w-xs\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"md\", cls: \"max-w-md\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"lg\", cls: \"max-w-lg\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"xl\", cls: \"max-w-xl\", prefixes: IMG_SIZE_PREFIXES },\n { label: \"full\", cls: \"w-full\", prefixes: IMG_SIZE_PREFIXES },\n ];\n const rounded = [\n { label: \"none\", cls: \"rounded-none\", prefixes: ROUNDED_PREFIXES },\n { label: \"md\", cls: \"rounded-md\", prefixes: ROUNDED_PREFIXES },\n { label: \"lg\", cls: \"rounded-lg\", prefixes: ROUNDED_PREFIXES },\n { label: \"xl\", cls: \"rounded-xl\", prefixes: ROUNDED_PREFIXES },\n { label: \"full\", cls: \"rounded-full\", prefixes: ROUNDED_PREFIXES },\n ];\n return { imgSize: imgSizes, rounded, currentClasses };\n }\n\n return null;\n })();\n\n // Determine which tabs are available\n const hasStyleTab = true; // Always available (colors for non-section, style presets for section)\n const hasSizeTab = sizePresets && !selection.isSectionRoot;\n const hasAttrsTab = hasAttrEditing;\n\n function toggleTab(tab: TabType) {\n setActiveTab(prev => prev === tab ? null : tab);\n }\n\n // Render color swatches panel\n function renderColorPanel() {\n if (selection!.isSectionRoot) {\n // Style presets for section root\n if (hideStylePresets) return null;\n return (\n <div className=\"flex items-center gap-1 pt-1 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0\">Estilo</span>\n {STYLE_PRESETS.map((preset) => (\n <button\n key={preset.label}\n onClick={() => onRefine(preset.instruction)}\n disabled={isRefining}\n className=\"px-2 py-0.5 text-[11px] font-medium rounded-md bg-gray-800 hover:bg-gray-700 disabled:opacity-30 transition-colors whitespace-nowrap\"\n title={preset.label}\n >\n <span className=\"mr-1\">{preset.icon}</span>\n {preset.label}\n </button>\n ))}\n </div>\n );\n }\n\n // Color swatches for non-section elements\n if (!onUpdateAttribute || selection!.tagName === 'IMG') return null;\n\n const containerTags = ['DIV', 'SECTION', 'HEADER', 'FOOTER', 'NAV', 'ASIDE', 'MAIN', 'ARTICLE'];\n const isContainer = containerTags.includes(selection!.tagName ?? '');\n\n const TEXT_COLOR_PREFIXES = [\"text-primary\", \"text-secondary\", \"text-accent\", \"text-on-surface\", \"text-on-primary\", \"text-on-surface-muted\", \"text-white\", \"text-black\", \"text-transparent\"];\n const BG_COLOR_PREFIXES = [\"bg-primary\", \"bg-primary-dark\", \"bg-secondary\", \"bg-accent\", \"bg-surface\", \"bg-surface-alt\", \"bg-white\", \"bg-black\", \"bg-transparent\"];\n\n const themeSwatches = themeColors ? [\n { color: themeColors.primary, textCls: \"text-primary\", bgCls: \"bg-primary\", label: \"Primary\" },\n { color: themeColors.secondary, textCls: \"text-secondary\", bgCls: \"bg-secondary\", label: \"Secondary\" },\n { color: themeColors.accent, textCls: \"text-accent\", bgCls: \"bg-accent\", label: \"Accent\" },\n { color: themeColors.surface, textCls: \"text-on-surface\", bgCls: \"bg-surface\", label: \"Surface\" },\n ] : [];\n\n const fixedSwatches = [\n { color: \"#ffffff\", textCls: \"text-white\", bgCls: \"bg-white\", label: \"Blanco\" },\n { color: \"#000000\", textCls: \"text-black\", bgCls: \"bg-black\", label: \"Negro\" },\n { color: \"transparent\", textCls: \"text-transparent\", bgCls: \"bg-transparent\", label: \"Transparente\" },\n ];\n\n const renderColorRow = (label: string, mode: \"text\" | \"bg\") => (\n <div key={mode} className=\"flex items-center gap-1 pt-0.5 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0 w-10\">{label}</span>\n {fixedSwatches.map(({ color, textCls, bgCls, label: swatchLabel }) => (\n <button\n key={swatchLabel}\n onClick={() => {\n const prefixes = mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES;\n const cls = mode === \"text\" ? textCls : bgCls;\n handleReplaceClass(prefixes, cls);\n handleSetAttr(\"style\", \"\");\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 hover:scale-125 transition-transform shrink-0\"\n style={color === \"transparent\" ? {\n backgroundImage: \"repeating-conic-gradient(#808080 0% 25%, #c0c0c0 0% 50%)\",\n backgroundSize: \"8px 8px\",\n } : { backgroundColor: color }}\n title={swatchLabel}\n />\n ))}\n {themeSwatches.map(({ color, textCls, bgCls, label: swatchLabel }) => (\n <button\n key={swatchLabel}\n onClick={() => {\n const prefixes = mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES;\n const cls = mode === \"text\" ? textCls : bgCls;\n handleReplaceClass(prefixes, cls);\n handleSetAttr(\"style\", \"\");\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 hover:scale-125 transition-transform shrink-0\"\n style={{ backgroundColor: color }}\n title={swatchLabel}\n />\n ))}\n <input\n type=\"color\"\n onChange={(e) => {\n handleReplaceClass(mode === \"text\" ? TEXT_COLOR_PREFIXES : BG_COLOR_PREFIXES, \"\");\n const cssProp = mode === \"text\" ? \"color\" : \"background-color\";\n handleSetAttr(\"style\", `${cssProp}: ${e.target.value}`);\n }}\n className=\"w-5 h-5 rounded-full border border-gray-600 cursor-pointer shrink-0 p-0 bg-transparent [&::-webkit-color-swatch-wrapper]:p-0 [&::-webkit-color-swatch]:rounded-full [&::-webkit-color-swatch]:border-0\"\n title=\"Color personalizado\"\n />\n </div>\n );\n\n return (\n <div className=\"pt-1 pb-0.5\">\n {renderColorRow(\"Color\", \"text\")}\n {isContainer && renderColorRow(\"Fondo\", \"bg\")}\n </div>\n );\n }\n\n // Render size presets panel\n function renderSizePanel() {\n if (!sizePresets || selection!.isSectionRoot) return null;\n const groups = Object.entries(sizePresets).filter(([k]) => k !== 'currentClasses') as [string, { label: string; cls: string; prefixes: string[] }[]][];\n const labels: Record<string, string> = { width: \"Ancho\", maxW: \"Max\", padding: \"Padding\", margin: \"Margin\", textSize: \"Texto\", fontWeight: \"Peso\", imgSize: \"Tamaño\", rounded: \"Borde\" };\n return (\n <div className=\"pt-1 pb-0.5\">\n {groups.map(([key, options]) => (\n <div key={key} className=\"flex items-center gap-1 pt-0.5 pb-0.5\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider mr-1 shrink-0 w-10\">{labels[key] || key}</span>\n {options.map((opt) => {\n const isActive = sizePresets.currentClasses.includes(opt.cls);\n return (\n <button\n key={opt.cls}\n onClick={() => handleReplaceClass(opt.prefixes, opt.cls)}\n className={`px-2 py-0.5 text-[10px] font-mono font-bold rounded-md transition-colors whitespace-nowrap ${\n isActive ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-300\"\n }`}\n >\n {opt.label}\n </button>\n );\n })}\n </div>\n ))}\n </div>\n );\n }\n\n // Render attributes panel\n function renderAttrsPanel() {\n if (!hasAttrEditing) return null;\n return (\n <div className=\"pt-1 pb-0.5\">\n {isImg && (\n <div className=\"flex flex-col gap-1\">\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">src</span>\n <input\n type=\"text\"\n value={imgSrc}\n onChange={(e) => setImgSrc(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"src\", imgSrc); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"URL de imagen...\"\n />\n <button\n onClick={() => handleSetAttr(\"src\", imgSrc)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">alt</span>\n <input\n type=\"text\"\n value={imgAlt}\n onChange={(e) => setImgAlt(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"alt\", imgAlt); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"Alt text...\"\n />\n <button\n onClick={() => handleSetAttr(\"alt\", imgAlt)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n </div>\n )}\n {isLink && (\n <div className=\"flex items-center gap-1\">\n <span className=\"text-[10px] text-gray-500 uppercase tracking-wider w-8 shrink-0\">href</span>\n <input\n type=\"text\"\n value={linkHref}\n onChange={(e) => setLinkHref(e.target.value)}\n onKeyDown={(e) => { if (e.key === \"Enter\") handleSetAttr(\"href\", linkHref); }}\n className=\"flex-1 bg-gray-800 text-xs text-white rounded px-2 py-1 outline-none min-w-0\"\n placeholder=\"URL del enlace...\"\n />\n <button\n onClick={() => handleSetAttr(\"href\", linkHref)}\n className=\"px-2 py-1 text-[10px] font-bold rounded bg-blue-500 hover:bg-blue-600 transition-colors shrink-0\"\n >\n Set\n </button>\n </div>\n )}\n </div>\n );\n }\n\n return (\n <div\n ref={toolbarRef}\n className=\"fixed z-50 flex flex-col bg-gray-900 text-white rounded-xl shadow-2xl px-2 py-1.5 border border-gray-700\"\n style={{ top: finalTop, left: clampedLeft, maxWidth: \"min(600px, calc(100vw - 16px))\" }}\n >\n {/* Close button — pinned top-right */}\n <button\n onClick={onClose}\n className=\"absolute top-1.5 right-2 w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 text-gray-400 hover:text-white transition-colors z-10\"\n title=\"Cerrar (ESC)\"\n >\n ✕\n </button>\n {/* Main row */}\n <div className=\"flex items-center gap-1.5 flex-wrap pr-8\">\n {/* Tag badge / switcher */}\n {selection.tagName && (() => {\n const tag = selection.tagName.toUpperCase();\n const HEADINGS = [\"H1\", \"H2\", \"H3\", \"H4\", \"H5\", \"H6\"];\n const TEXT = [\"P\", \"SPAN\", \"DIV\", \"BLOCKQUOTE\"];\n const CONTAINERS = [\"DIV\", \"SECTION\", \"ARTICLE\", \"ASIDE\", \"HEADER\", \"FOOTER\", \"NAV\", \"MAIN\"];\n const NO_SWITCH = [\"A\", \"IMG\", \"INPUT\", \"BUTTON\", \"SVG\", \"VIDEO\", \"IFRAME\", \"TABLE\", \"UL\", \"OL\", \"LI\", \"FORM\"];\n\n let tagOptions: string[] = [];\n if (HEADINGS.includes(tag)) tagOptions = [...HEADINGS, \"P\"];\n else if (TEXT.includes(tag) && !CONTAINERS.includes(tag)) tagOptions = [...TEXT, \"H1\", \"H2\", \"H3\"];\n else if (CONTAINERS.includes(tag)) tagOptions = [...CONTAINERS, \"P\", \"SPAN\"];\n tagOptions = tagOptions.filter((t) => t !== tag);\n const canSwitch = !NO_SWITCH.includes(tag) && tagOptions.length > 0 && onChangeTag;\n\n return canSwitch ? (\n <div className=\"relative shrink-0\">\n <button\n onClick={() => setShowTagPicker(!showTagPicker)}\n className=\"px-2 py-0.5 rounded-md bg-blue-600 hover:bg-blue-500 text-[10px] font-mono font-bold uppercase tracking-wider transition-colors flex items-center gap-0.5\"\n >\n {tag.toLowerCase()}\n <svg className=\"w-2.5 h-2.5 opacity-60\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M5.23 7.21a.75.75 0 011.06.02L10 11.168l3.71-3.938a.75.75 0 111.08 1.04l-4.25 4.5a.75.75 0 01-1.08 0l-4.25-4.5a.75.75 0 01.02-1.06z\" clipRule=\"evenodd\" />\n </svg>\n </button>\n {showTagPicker && (\n <div className={`absolute left-0 bg-gray-800 border border-gray-600 rounded-lg shadow-xl py-1 z-50 min-w-[4rem] max-h-[200px] overflow-y-auto ${showAbove ? \"bottom-full mb-1\" : \"top-full mt-1\"}`}>\n {tagOptions.map((t) => (\n <button\n key={t}\n onClick={() => {\n if (selection.sectionId && selection.elementPath) {\n onChangeTag(selection.sectionId, selection.elementPath, t.toLowerCase());\n }\n setShowTagPicker(false);\n }}\n className=\"block w-full text-left px-3 py-1 text-[11px] font-mono font-bold uppercase hover:bg-gray-700 transition-colors\"\n >\n {t.toLowerCase()}\n </button>\n ))}\n </div>\n )}\n </div>\n ) : (\n <span className=\"px-2 py-0.5 rounded-md bg-blue-600 text-[10px] font-mono font-bold uppercase tracking-wider shrink-0\">\n {tag.toLowerCase()}\n </span>\n );\n })()}\n\n {/* AI prompt input */}\n <form onSubmit={handleSubmit} className=\"flex items-start gap-1 flex-1\">\n <textarea\n ref={inputRef}\n value={prompt}\n onChange={(e) => {\n setPrompt(e.target.value);\n e.target.style.height = \"auto\";\n e.target.style.height = Math.min(e.target.scrollHeight, 96) + \"px\";\n }}\n onKeyDown={(e) => {\n if (e.key === \"Enter\" && !e.shiftKey) {\n e.preventDefault();\n handleSubmit(e);\n }\n }}\n placeholder={refImage ? \"Instruccion + imagen...\" : \"Editar con AI...\"}\n disabled={isRefining}\n rows={1}\n className=\"bg-transparent text-sm text-white placeholder:text-gray-500 outline-none min-w-[6rem] sm:min-w-[10rem] flex-1 px-2 py-1 resize-none overflow-hidden\"\n />\n <button\n type=\"submit\"\n disabled={isRefining}\n className={`flex items-center justify-center rounded-lg bg-blue-500 hover:bg-blue-600 disabled:opacity-30 transition-colors shrink-0 ${\n prompt.trim() ? \"w-7 h-7\" : \"px-2.5 py-1 text-[11px] font-bold whitespace-nowrap\"\n }`}\n >\n {isRefining ? (\n <span className=\"w-3.5 h-3.5 border-2 border-white/30 border-t-white rounded-full animate-spin\" />\n ) : prompt.trim() ? (\n <HiSparkles className=\"w-3.5 h-3.5\" />\n ) : (\n \"✦ Variante\"\n )}\n </button>\n <button\n type=\"button\"\n onClick={() => fileInputRef.current?.click()}\n disabled={isRefining}\n className={`w-7 h-7 flex items-center justify-center rounded-lg transition-colors shrink-0 ${\n refImage\n ? \"bg-blue-600 text-white\"\n : \"hover:bg-gray-800 text-gray-400 hover:text-white\"\n }`}\n title={refImage ? `Imagen: ${refImageName}` : \"Adjuntar imagen de referencia\"}\n >\n <svg className=\"w-3.5 h-3.5\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path fillRule=\"evenodd\" d=\"M1 5.25A2.25 2.25 0 013.25 3h13.5A2.25 2.25 0 0119 5.25v9.5A2.25 2.25 0 0116.75 17H3.25A2.25 2.25 0 011 14.75v-9.5zm1.5 5.81V14.75c0 .414.336.75.75.75h13.5a.75.75 0 00.75-.75v-2.06l-2.22-2.22a.75.75 0 00-1.06 0L8.56 16.1l-3.28-3.28a.75.75 0 00-1.06 0l-1.72 1.72zm12-4.06a1.5 1.5 0 11-3 0 1.5 1.5 0 013 0z\" clipRule=\"evenodd\"/>\n </svg>\n </button>\n <input\n ref={fileInputRef}\n type=\"file\"\n accept=\"image/*\"\n onChange={handleFileSelect}\n className=\"hidden\"\n />\n </form>\n\n {/* Section-level actions (move/delete) */}\n {selection.isSectionRoot && (\n <span className=\"hidden sm:contents\">\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={onMoveUp}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 transition-colors text-xs\"\n title=\"Mover arriba\"\n >\n ↑\n </button>\n <button\n onClick={onMoveDown}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-gray-800 transition-colors text-xs\"\n title=\"Mover abajo\"\n >\n ↓\n </button>\n </span>\n )}\n\n {selection.isSectionRoot ? (\n <>\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={onDelete}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-red-900/50 text-red-400 transition-colors\"\n title=\"Eliminar seccion\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\" />\n </svg>\n </button>\n </>\n ) : onDeleteElement && (\n <>\n <div className=\"w-px h-5 bg-gray-700\" />\n <button\n onClick={() => {\n if (selection.sectionId && selection.elementPath) {\n onDeleteElement(selection.sectionId, selection.elementPath);\n }\n }}\n className=\"w-7 h-7 flex items-center justify-center rounded-lg hover:bg-red-900/50 text-red-400 transition-colors\"\n title=\"Eliminar elemento\"\n >\n <svg width=\"14\" height=\"14\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"currentColor\" strokeWidth=\"2\" strokeLinecap=\"round\" strokeLinejoin=\"round\">\n <polyline points=\"3 6 5 6 21 6\" />\n <path d=\"M19 6v14a2 2 0 01-2 2H7a2 2 0 01-2-2V6m3 0V4a2 2 0 012-2h4a2 2 0 012 2v2\" />\n </svg>\n </button>\n </>\n )}\n\n </div>\n\n {/* Reference image preview */}\n {refImage && (\n <div className=\"flex items-center gap-2 pt-0.5 pb-0.5 border-t border-gray-700/50\">\n <img src={refImage} alt=\"Referencia\" className=\"w-10 h-10 rounded object-cover border border-gray-600\" />\n <span className=\"text-[10px] text-gray-400 truncate flex-1\">{refImageName}</span>\n <button\n onClick={() => { setRefImage(null); setRefImageName(null); }}\n className=\"text-[10px] text-gray-500 hover:text-white px-1\"\n >\n ✕\n </button>\n </div>\n )}\n\n {/* Tab bar */}\n <div className=\"flex items-center gap-1 pt-1 border-t border-gray-700/50 overflow-x-auto [&::-webkit-scrollbar]:hidden [-ms-overflow-style:none] [scrollbar-width:none]\">\n {hasStyleTab && (\n <button\n onClick={() => toggleTab(\"style\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"style\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Estilo\"\n >\n <PaletteIcon />\n <span>Estilo</span>\n </button>\n )}\n {hasSizeTab && (\n <button\n onClick={() => toggleTab(\"size\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"size\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Tamaño\"\n >\n <RulerIcon />\n <span>Tamaño</span>\n </button>\n )}\n {hasAttrsTab && (\n <button\n onClick={() => toggleTab(\"attrs\")}\n className={`flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium transition-colors ${\n activeTab === \"attrs\" ? \"bg-blue-600 text-white\" : \"bg-gray-800 hover:bg-gray-700 text-gray-400\"\n }`}\n title=\"Atributos\"\n >\n <LinkIcon />\n <span>Attrs</span>\n </button>\n )}\n <button\n onClick={onViewCode}\n className=\"hidden sm:flex items-center gap-1 px-2 py-1 rounded-md text-[10px] font-medium bg-gray-800 hover:bg-gray-700 text-gray-400 transition-colors font-mono\"\n title=\"Ver código\"\n >\n &lt;/&gt;\n </button>\n </div>\n\n {/* Active tab panel */}\n {activeTab === \"style\" && renderColorPanel()}\n {activeTab === \"size\" && renderSizePanel()}\n {activeTab === \"attrs\" && renderAttrsPanel()}\n </div>\n );\n}\n","import React, { useEffect, useRef, useCallback, useState } from \"react\";\nimport { EditorView, keymap, lineNumbers, highlightActiveLine, highlightActiveLineGutter, Decoration, type DecorationSet } from \"@codemirror/view\";\nimport { EditorState, StateField, StateEffect } from \"@codemirror/state\";\nimport { html } from \"@codemirror/lang-html\";\nimport { oneDark } from \"@codemirror/theme-one-dark\";\nimport { defaultKeymap, indentWithTab, history, historyKeymap } from \"@codemirror/commands\";\nimport { searchKeymap, highlightSelectionMatches } from \"@codemirror/search\";\nimport { bracketMatching, foldGutter, foldKeymap } from \"@codemirror/language\";\nimport { closeBrackets, closeBracketsKeymap } from \"@codemirror/autocomplete\";\n\ninterface CodeEditorProps {\n code: string;\n label: string;\n scrollToText?: string;\n onSave: (code: string) => void;\n onClose: () => void;\n}\n\nfunction formatHtml(html: string): string {\n let result = html.replace(/>\\s*</g, \">\\n<\");\n const lines = result.split(\"\\n\");\n const output: string[] = [];\n let indent = 0;\n for (const raw of lines) {\n const line = raw.trim();\n if (!line) continue;\n const isClosing = /^<\\//.test(line);\n const isSelfClosing =\n /\\/>$/.test(line) ||\n /^<(img|br|hr|input|meta|link|col|area|base|embed|source|track|wbr)\\b/i.test(line);\n const hasInlineClose = /^<[^/][^>]*>.*<\\//.test(line);\n if (isClosing) indent = Math.max(0, indent - 1);\n output.push(\" \".repeat(indent) + line);\n if (!isClosing && !isSelfClosing && !hasInlineClose && /^<[a-zA-Z]/.test(line)) {\n indent++;\n }\n }\n return output.join(\"\\n\");\n}\n\n// Flash highlight effect for scroll-to-code\nconst flashLineEffect = StateEffect.define<{ from: number; to: number }>();\nconst clearFlashEffect = StateEffect.define<null>();\n\nconst flashLineDeco = Decoration.line({ class: \"cm-flash-line\" });\n\nconst flashLineField = StateField.define<DecorationSet>({\n create: () => Decoration.none,\n update(decos, tr) {\n for (const e of tr.effects) {\n if (e.is(flashLineEffect)) {\n return Decoration.set([flashLineDeco.range(e.value.from)]);\n }\n if (e.is(clearFlashEffect)) {\n return Decoration.none;\n }\n }\n return decos;\n },\n provide: (f) => EditorView.decorations.from(f),\n});\n\n\nfunction scrollToTarget(view: EditorView, target?: string) {\n if (!target) return;\n const docText = view.state.doc.toString();\n const normalized = target.replace(/\"/g, \"'\");\n let idx = docText.indexOf(normalized);\n if (idx === -1) idx = docText.indexOf(target);\n\n // If exact match fails, extract tag+class and search line by line\n if (idx === -1) {\n const tagMatch = target.match(/^<(\\w+)/);\n const classMatch = target.match(/class=[\"']([^\"']*?)[\"']/);\n if (tagMatch) {\n const searchTag = tagMatch[0];\n const searchClass = classMatch ? classMatch[1].split(\" \")[0] : null;\n for (let i = 1; i <= view.state.doc.lines; i++) {\n const line = view.state.doc.line(i);\n if (line.text.includes(searchTag) && (!searchClass || line.text.includes(searchClass))) {\n idx = line.from;\n break;\n }\n }\n }\n }\n\n if (idx !== -1) {\n const line = view.state.doc.lineAt(idx);\n view.dispatch({\n selection: { anchor: line.from },\n effects: [\n EditorView.scrollIntoView(line.from, { y: \"center\" }),\n flashLineEffect.of({ from: line.from, to: line.to }),\n ],\n });\n // Clear flash after 2s\n setTimeout(() => {\n view.dispatch({ effects: clearFlashEffect.of(null) });\n }, 2000);\n }\n}\n\nexport function CodeEditor({ code, label, scrollToText, onSave, onClose }: CodeEditorProps) {\n const containerRef = useRef<HTMLDivElement>(null);\n const viewRef = useRef<EditorView | null>(null);\n const [stats, setStats] = useState({ lines: 0, kb: \"0.0\" });\n\n const onSaveRef = useRef(onSave);\n const onCloseRef = useRef(onClose);\n onSaveRef.current = onSave;\n onCloseRef.current = onClose;\n\n const updateStats = useCallback((doc: { length: number; lines: number }) => {\n setStats({ lines: doc.lines, kb: (doc.length / 1024).toFixed(1) });\n }, []);\n\n useEffect(() => {\n if (!containerRef.current) return;\n\n const initialDoc = code.includes(\"\\n\") ? code : formatHtml(code);\n\n const state = EditorState.create({\n doc: initialDoc,\n extensions: [\n lineNumbers(),\n highlightActiveLine(),\n highlightActiveLineGutter(),\n bracketMatching(),\n closeBrackets(),\n foldGutter(),\n highlightSelectionMatches(),\n html(),\n oneDark,\n history(),\n EditorView.lineWrapping,\n keymap.of([\n { key: \"Mod-s\", run: (v) => { onSaveRef.current(v.state.doc.toString()); return true; } },\n { key: \"Escape\", run: () => { onCloseRef.current(); return true; } },\n indentWithTab,\n ...closeBracketsKeymap,\n ...searchKeymap,\n ...foldKeymap,\n ...historyKeymap,\n ...defaultKeymap,\n ]),\n EditorView.updateListener.of((update) => {\n if (update.docChanged) {\n updateStats(update.state.doc);\n }\n }),\n flashLineField,\n EditorView.theme({\n \"&\": { height: \"100%\", fontSize: \"13px\" },\n \".cm-scroller\": { overflow: \"auto\", fontFamily: \"ui-monospace, SFMono-Regular, 'SF Mono', Menlo, Consolas, monospace\" },\n \".cm-content\": { padding: \"8px 0\" },\n \".cm-gutters\": { borderRight: \"1px solid #21262d\" },\n \".cm-flash-line\": { backgroundColor: \"rgba(250, 204, 21, 0.25)\", transition: \"background-color 2s ease-out\" },\n }),\n ],\n });\n\n const view = new EditorView({ state, parent: containerRef.current });\n viewRef.current = view;\n\n updateStats(view.state.doc);\n scrollToTarget(view, scrollToText);\n view.focus();\n\n return () => { view.destroy(); };\n // eslint-disable-next-line react-hooks/exhaustive-deps\n }, []);\n\n // Re-scroll when scrollToText changes while editor is already open\n useEffect(() => {\n const view = viewRef.current;\n if (!view || !scrollToText) return;\n scrollToTarget(view, scrollToText);\n }, [scrollToText]);\n\n function handleFormat() {\n const view = viewRef.current;\n if (!view) return;\n const formatted = formatHtml(view.state.doc.toString());\n view.dispatch({\n changes: { from: 0, to: view.state.doc.length, insert: formatted },\n });\n }\n\n function handleSave() {\n const view = viewRef.current;\n if (!view) return;\n onSave(view.state.doc.toString());\n }\n\n return (\n <div className=\"flex flex-col h-full bg-[#0d1117]\">\n {/* Header */}\n <div className=\"flex items-center justify-between px-4 py-3 border-b border-gray-800 shrink-0\">\n <div className=\"flex items-center gap-3\">\n <span className=\"px-2 py-0.5 rounded bg-orange-600/20 text-orange-400 text-[10px] font-mono font-bold uppercase tracking-wider\">\n HTML\n </span>\n <span className=\"text-sm font-bold text-gray-300\">{label}</span>\n </div>\n <div className=\"flex items-center gap-2\">\n <button\n onClick={handleFormat}\n className=\"px-3 py-1.5 text-xs font-bold rounded-lg bg-gray-800 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors\"\n >\n Formatear\n </button>\n <button\n onClick={handleSave}\n className=\"px-4 py-1.5 text-xs font-bold rounded-lg bg-blue-500 text-white hover:bg-blue-600 transition-colors\"\n >\n Guardar\n </button>\n <button\n onClick={onClose}\n className=\"px-3 py-1.5 text-xs font-bold rounded-lg bg-gray-800 text-gray-400 hover:text-white hover:bg-gray-700 transition-colors\"\n >\n Cerrar\n </button>\n </div>\n </div>\n\n {/* Editor */}\n <div ref={containerRef} className=\"flex-1 overflow-hidden\" />\n\n {/* Footer */}\n <div className=\"flex items-center justify-between px-4 py-1.5 border-t border-gray-800 text-[10px] text-gray-500 font-mono shrink-0\">\n <span>{stats.lines} lineas</span>\n <span>Tab = indentar &middot; Cmd+S = guardar &middot; Esc = cerrar</span>\n <span>{stats.kb} KB</span>\n </div>\n </div>\n );\n}\n","import React from \"react\";\n\nexport type Viewport = \"desktop\" | \"tablet\" | \"mobile\";\n\nconst VIEWPORTS: { id: Viewport; label: string; icon: React.ReactElement }[] = [\n {\n id: \"desktop\",\n label: \"Desktop\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M2 4.25A2.25 2.25 0 014.25 2h11.5A2.25 2.25 0 0118 4.25v8.5A2.25 2.25 0 0115.75 15h-3.105a3.501 3.501 0 001.1 1.677A.75.75 0 0113.26 18H6.74a.75.75 0 01-.484-1.323A3.501 3.501 0 007.355 15H4.25A2.25 2.25 0 012 12.75v-8.5zm1.5 0a.75.75 0 01.75-.75h11.5a.75.75 0 01.75.75v8.5a.75.75 0 01-.75.75H4.25a.75.75 0 01-.75-.75v-8.5z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n {\n id: \"tablet\",\n label: \"Tablet\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M5 1a2 2 0 00-2 2v14a2 2 0 002 2h10a2 2 0 002-2V3a2 2 0 00-2-2H5zm0 1.5h10a.5.5 0 01.5.5v14a.5.5 0 01-.5.5H5a.5.5 0 01-.5-.5V3a.5.5 0 01.5-.5zm4 14a1 1 0 112 0 1 1 0 01-2 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n {\n id: \"mobile\",\n label: \"Mobile\",\n icon: (\n <svg className=\"w-4 h-4\" viewBox=\"0 0 20 20\" fill=\"currentColor\">\n <path\n fillRule=\"evenodd\"\n d=\"M6 2a2 2 0 00-2 2v12a2 2 0 002 2h8a2 2 0 002-2V4a2 2 0 00-2-2H6zm0 1.5h8a.5.5 0 01.5.5v12a.5.5 0 01-.5.5H6a.5.5 0 01-.5-.5V4a.5.5 0 01.5-.5zm3 13a1 1 0 112 0 1 1 0 01-2 0z\"\n clipRule=\"evenodd\"\n />\n </svg>\n ),\n },\n];\n\nexport function ViewportToggle({\n value,\n onChange,\n activeClass = \"bg-blue-100 text-blue-700\",\n inactiveClass = \"text-gray-400 hover:text-gray-600 hover:bg-gray-100\",\n}: {\n value: Viewport;\n onChange: (v: Viewport) => void;\n activeClass?: string;\n inactiveClass?: string;\n}) {\n return (\n <div className=\"flex items-center justify-center gap-1 py-2 shrink-0 bg-gray-50 border-b border-gray-200\">\n {VIEWPORTS.map((v) => (\n <button\n key={v.id}\n type=\"button\"\n onClick={() => onChange(v.id)}\n title={v.label}\n className={`p-1.5 rounded-lg transition-colors ${\n value === v.id ? activeClass : inactiveClass\n }`}\n >\n {v.icon}\n </button>\n ))}\n </div>\n );\n}\n","import { useState, useCallback, useRef } from \"react\";\n\nconst MAX_STACK = 30;\n\nexport function useUndoStack<T>() {\n const undoStack = useRef<T[]>([]);\n const redoStack = useRef<T[]>([]);\n const [canUndo, setCanUndo] = useState(false);\n const [canRedo, setCanRedo] = useState(false);\n\n const pushUndo = useCallback((snapshot: T) => {\n undoStack.current.push(JSON.parse(JSON.stringify(snapshot)));\n if (undoStack.current.length > MAX_STACK) undoStack.current.shift();\n redoStack.current = [];\n setCanUndo(true);\n setCanRedo(false);\n }, []);\n\n const undo = useCallback((current: T): T | null => {\n if (undoStack.current.length === 0) return null;\n redoStack.current.push(JSON.parse(JSON.stringify(current)));\n const prev = undoStack.current.pop()!;\n setCanUndo(undoStack.current.length > 0);\n setCanRedo(true);\n return prev;\n }, []);\n\n const redo = useCallback((current: T): T | null => {\n if (redoStack.current.length === 0) return null;\n undoStack.current.push(JSON.parse(JSON.stringify(current)));\n const next = redoStack.current.pop()!;\n setCanUndo(true);\n setCanRedo(redoStack.current.length > 0);\n return next;\n }, []);\n\n return { pushUndo, undo, redo, canUndo, canRedo };\n}\n"],"mappings":"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","names":["Canvas","html","useRef","useState","jsx","jsxs","useRef","useState","useState","useRef","useEffect","jsx","jsxs","useEffect","useRef","useCallback","useState","jsx","jsxs","html","jsx","useState","useCallback","useRef"]}