@easybits.cloud/html-tailwind-generator 0.2.99 → 0.2.102
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.
- package/dist/{chunk-Q6BLHD6O.js → chunk-2YEIWCVP.js} +13 -11
- package/dist/chunk-2YEIWCVP.js.map +1 -0
- package/dist/{chunk-IQWKMIAW.js → chunk-GBDM7BJL.js} +2 -2
- package/dist/{chunk-AJGAO7UP.js → chunk-HTGS346J.js} +260 -11
- package/dist/chunk-HTGS346J.js.map +1 -0
- package/dist/{chunk-ROHHDJBH.js → chunk-JB3MUQJS.js} +26 -21
- package/dist/chunk-JB3MUQJS.js.map +1 -0
- package/dist/{chunk-3QER6Z5B.js → chunk-OQOOZCFY.js} +5 -6
- package/dist/chunk-OQOOZCFY.js.map +1 -0
- package/dist/{chunk-DL5PA47F.js → chunk-WDOPQECR.js} +2 -2
- package/dist/chunk-WDOPQECR.js.map +1 -0
- package/dist/{chunk-T6UZ3FVJ.js → chunk-ZWOEBRPC.js} +2 -2
- package/dist/components.js +2 -2
- package/dist/deploy.js +2 -2
- package/dist/directions.js +4 -4
- package/dist/directions.js.map +1 -1
- package/dist/generate.js +2 -3
- package/dist/generateDocument.d.ts +2 -2
- package/dist/generateDocument.js +2 -3
- package/dist/images.js +1 -1
- package/dist/index.js +8 -10
- package/dist/refine.js +2 -3
- package/package.json +1 -1
- package/dist/chunk-3QER6Z5B.js.map +0 -1
- package/dist/chunk-AJGAO7UP.js.map +0 -1
- package/dist/chunk-DL5PA47F.js.map +0 -1
- package/dist/chunk-G5QRFGPZ.js +0 -249
- package/dist/chunk-G5QRFGPZ.js.map +0 -1
- package/dist/chunk-Q6BLHD6O.js.map +0 -1
- package/dist/chunk-ROHHDJBH.js.map +0 -1
- /package/dist/{chunk-IQWKMIAW.js.map → chunk-GBDM7BJL.js.map} +0 -0
- /package/dist/{chunk-T6UZ3FVJ.js.map → chunk-ZWOEBRPC.js.map} +0 -0
package/dist/directions.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
|
+
currentDateLine,
|
|
2
3
|
resolveModel
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-G5QRFGPZ.js";
|
|
4
|
+
} from "./chunk-HTGS346J.js";
|
|
5
5
|
|
|
6
6
|
// src/directions.ts
|
|
7
7
|
import { generateObject, streamText } from "ai";
|
|
@@ -55,7 +55,7 @@ async function generateDirections(options) {
|
|
|
55
55
|
schema: z.object({
|
|
56
56
|
directions: z.array(DesignDirectionSchema).describe(`Exactly ${count} design directions`)
|
|
57
57
|
}),
|
|
58
|
-
system: DIRECTIONS_SYSTEM,
|
|
58
|
+
system: DIRECTIONS_SYSTEM + currentDateLine(),
|
|
59
59
|
prompt: `Project brief: "${prompt}"
|
|
60
60
|
|
|
61
61
|
Generate ${count} design directions. Make them as visually distinct as possible.`
|
|
@@ -139,7 +139,7 @@ ${textPrompt}` }
|
|
|
139
139
|
}];
|
|
140
140
|
const result = streamText({
|
|
141
141
|
model,
|
|
142
|
-
system: systemPrompt,
|
|
142
|
+
system: systemPrompt + currentDateLine(),
|
|
143
143
|
messages
|
|
144
144
|
});
|
|
145
145
|
let html = "";
|
package/dist/directions.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/directions.ts"],"sourcesContent":["import { generateObject, streamText } from \"ai\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport { resolveModel } from \"./streamCore\";\n\nexport const DesignDirectionSchema = z.object({\n name: z.string().describe(\"Creative direction name, e.g. 'The Editorial'\"),\n tagline: z\n .string()\n .describe(\"One-line vibe description, e.g. 'Bold serif typography meets minimalist space'\"),\n headingFont: z\n .string()\n .describe(\"Google Font name for headings, e.g. 'Playfair Display'\"),\n bodyFont: z\n .string()\n .describe(\"Google Font name for body text, e.g. 'Inter'\"),\n colors: z.object({\n primary: z.string().describe(\"Main brand color as hex, e.g. '#6366f1'\"),\n accent: z.string().describe(\"Accent/CTA color as hex, e.g. '#f59e0b'\"),\n surface: z.string().describe(\"Background surface color as hex, e.g. '#ffffff'\"),\n surfaceAlt: z.string().describe(\"Alt surface (cards, alternating sections) as hex, e.g. '#f8fafc'\"),\n text: z.string().describe(\"Main text color as hex, e.g. '#0f172a'\"),\n }),\n mood: z.enum([\"dark\", \"light\", \"warm\", \"cool\", \"vibrant\"]),\n layoutHint: z\n .string()\n .describe(\"Layout archetype: 'split-screen', 'editorial', 'immersive-gallery', 'community-feed', 'bento-grid', 'magazine'\"),\n});\n\nexport type DesignDirection = z.infer<typeof DesignDirectionSchema>;\n\nexport interface DirectionsOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n count?: number;\n /** \"landing\" generates hero sections, \"document\" generates cover pages */\n product?: \"landing\" | \"document\";\n /** Override the model ID or pass a pre-built LanguageModel object */\n model?: string | import(\"ai\").LanguageModel;\n}\n\nconst DIRECTIONS_SYSTEM = `You are an elite creative director at a top design agency (Pentagram, Sagmeister, Collins).\n\nGiven a project brief, propose design directions that are MAXIMALLY diverse from each other.\n\nRULES:\n- Each direction must feel like a completely DIFFERENT design agency made it\n- Vary these axes: typography style (geometric sans, humanist sans, serif, slab, display/decorative), color mood (dark, light, warm, cool, vibrant), layout approach (editorial, split-screen, immersive, bento-grid, community)\n- Fonts MUST be popular Google Fonts that render well. Good examples:\n SANS: Inter, DM Sans, Space Grotesk, Outfit, Plus Jakarta Sans, Manrope, Sora, Figtree, Urbanist\n SERIF: Playfair Display, Lora, Merriweather, Source Serif 4, Cormorant Garamond, Libre Baskerville, DM Serif Display\n DISPLAY: Bebas Neue, Oswald, Archivo Black, Righteous, Anton, Alfa Slab One\n MONO: JetBrains Mono, Space Mono, IBM Plex Mono\n- NEVER use the same heading font twice\n- NEVER use the same color palette twice\n- AT LEAST one direction must be dark-mode\n- AT LEAST one must use serif headings\n- AT LEAST one must be bold/editorial with huge typography\n- AT LEAST one layoutHint must include \"photo\" (e.g. \"split-screen-photo\", \"immersive-gallery\") — this tells the preview generator to use a real image\n- Colors must be cohesive palettes, not random. Think Dribbble-worthy.\n- Names should be evocative (\"The Chronicle\", \"Neon Pulse\", \"Warm Atelier\")`;\n\n/**\n * Generate N design directions for a landing page.\n * Uses generateObject for type-safe structured output.\n */\nexport async function generateDirections(\n options: DirectionsOptions\n): Promise<DesignDirection[]> {\n const { prompt, count = 4, openaiApiKey, anthropicApiKey, model: modelId } = options;\n\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\n const { object } = await generateObject({\n model,\n schema: z.object({\n directions: z.array(DesignDirectionSchema).describe(`Exactly ${count} design directions`),\n }),\n system: DIRECTIONS_SYSTEM,\n prompt: `Project brief: \"${prompt}\"\n\nGenerate ${count} design directions. Make them as visually distinct as possible.`,\n });\n\n return object.directions;\n}\n\n/**\n * Generate a hero section preview for a given design direction.\n * Fast Haiku call, returns raw HTML string with Google Fonts link.\n */\nexport async function generateHeroPreview(options: {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n direction: DesignDirection;\n product?: \"landing\" | \"document\";\n /** Override model ID or pass a pre-built LanguageModel object */\n model?: string | import(\"ai\").LanguageModel;\n /** Called with partial HTML as it streams in */\n onChunk?: (partialHtml: string) => void;\n /** Reference image data URL — AI will replicate this design style */\n referenceImage?: string;\n}): Promise<string> {\n const { prompt, direction, anthropicApiKey, openaiApiKey, product = \"landing\", model: modelId, onChunk, referenceImage } = options;\n\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\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\n const isDocument = product === \"document\";\n\n const systemPrompt = isDocument\n ? `You create stunning document cover pages with HTML + Tailwind CSS.\nOutput ONLY the raw HTML — no markdown fences, no explanation.\nThe HTML must include a <link> tag for Google Fonts and a <section> tag.\nThis is a LETTER-SIZE document cover page (8.5\" × 11\"), NOT a website hero.\nUse the EXACT fonts and colors provided. The cover must feel premium and print-ready.\nUse real-looking content specific to the brief (Spanish text).\n\nCRITICAL — TEXT MUST BE VISIBLE AND READABLE:\n- The document title MUST be large (text-4xl or bigger), bold, and clearly visible\n- Include: document title, subtitle or description, date, optional author/company name\n- ALL text must have strong contrast against its background — if background is dark, text MUST be white/light\n- NEVER let text disappear into the background\n\nDESIGN APPROACHES (vary across directions):\n- Approach A: Solid color background (bg-[primary]) with large white text — NO image needed\n- Approach B: Split layout — image on one half, text on solid color half\n- Approach C: Full-bleed image WITH a solid overlay (bg-black/50 or bg-[primary]/80) AND white text on top\n- Approach D: Geometric/abstract design with color blocks, no image\n- If the layout hint contains \"photo\", you MUST use Approach B or C (with a real image).\n- Otherwise, choose the approach that best fits the mood. NOT every cover needs a full-bleed image.\n\nIf using images:\n- Pattern: <img data-image-query=\"specific english search query\" alt=\"description\" class=\"absolute inset-0 w-full h-full object-cover\"/>\n- ALWAYS add a dark overlay on top: <div class=\"absolute inset-0 bg-black/50\"></div>\n- Text goes ABOVE the overlay with z-10 and text-white\n- NEVER include a src attribute — ONLY data-image-query\n\nNO buttons or CTAs — this is a print document cover.\nNO emoji — use geometric shapes or SVG icons for decoration.`\n : `You create stunning hero sections with HTML + Tailwind CSS.\nOutput ONLY the raw HTML — no markdown fences, no explanation.\nThe HTML must include a <link> tag for Google Fonts and a <section> tag.\nUse the EXACT fonts and colors provided. The hero must feel premium and polished.\nUse real-looking content specific to the brief (Spanish text).\nInclude: headline (huge), subtitle, 1-2 CTAs, and optionally a hero image via <img data-image-query=\"...\" alt=\"...\" class=\"...\">.\nNEVER use src on img tags. Use data-image-query with English search terms.`;\n\n const sectionInstruction = isDocument\n ? `Generate a document cover page. Use inline style for font-family referencing the Google Fonts.\nStart with: <link href=\"${fontsUrl}\" rel=\"stylesheet\">\nThen a <section class=\"w-[8.5in] h-[11in] relative overflow-hidden\"> sized for letter paper.\nUse the exact hex colors in Tailwind arbitrary values like bg-[${direction.colors.primary}] text-[${direction.colors.text}] etc.\nUse full-bleed backgrounds, geometric accents, elegant typography hierarchy.\nMake it look like a $50K design agency document cover.`\n : `Generate a hero section. Use inline style for font-family referencing the Google Fonts.\nStart with: <link href=\"${fontsUrl}\" rel=\"stylesheet\">\nThen a <section> with min-h-[80vh].\nUse the exact hex colors in Tailwind arbitrary values like bg-[${direction.colors.primary}] text-[${direction.colors.text}] etc.\nMake it look like a $50K agency landing page hero.`;\n\n const textPrompt = `Brief: \"${prompt}\"\n\nDesign direction: \"${direction.name}\" — ${direction.tagline}\nLayout: ${direction.layoutHint}\nHeading font: ${direction.headingFont}\nBody font: ${direction.bodyFont}\nColors: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}, surfaceAlt=${direction.colors.surfaceAlt}, text=${direction.colors.text}\nMood: ${direction.mood}\n\n${sectionInstruction}`;\n\n const messages: any[] = [{\n role: \"user\" as const,\n content: referenceImage\n ? [\n { type: \"image\" as const, image: referenceImage },\n { type: \"text\" as const, text: `Replicate the visual design style, layout, and aesthetic from the reference image above.\\n\\n${textPrompt}` },\n ]\n : textPrompt,\n }];\n\n const result = streamText({\n model,\n system: systemPrompt,\n messages,\n });\n\n let html = \"\";\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n html += chunk;\n chunkCount++;\n if (onChunk && chunkCount % 3 === 0) {\n onChunk(html);\n }\n }\n if (onChunk) onChunk(html);\n\n // Clean markdown fences if present\n html = html.trim();\n if (html.startsWith(\"```\")) {\n html = html.replace(/^```(?:html|xml)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n\n return html;\n}\n"],"mappings":"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","names":[]}
|
|
1
|
+
{"version":3,"sources":["../src/directions.ts"],"sourcesContent":["import { generateObject, streamText } from \"ai\";\nimport { z } from \"zod\";\nimport { nanoid } from \"nanoid\";\nimport { resolveModel, currentDateLine } from \"./streamCore\";\n\nexport const DesignDirectionSchema = z.object({\n name: z.string().describe(\"Creative direction name, e.g. 'The Editorial'\"),\n tagline: z\n .string()\n .describe(\"One-line vibe description, e.g. 'Bold serif typography meets minimalist space'\"),\n headingFont: z\n .string()\n .describe(\"Google Font name for headings, e.g. 'Playfair Display'\"),\n bodyFont: z\n .string()\n .describe(\"Google Font name for body text, e.g. 'Inter'\"),\n colors: z.object({\n primary: z.string().describe(\"Main brand color as hex, e.g. '#6366f1'\"),\n accent: z.string().describe(\"Accent/CTA color as hex, e.g. '#f59e0b'\"),\n surface: z.string().describe(\"Background surface color as hex, e.g. '#ffffff'\"),\n surfaceAlt: z.string().describe(\"Alt surface (cards, alternating sections) as hex, e.g. '#f8fafc'\"),\n text: z.string().describe(\"Main text color as hex, e.g. '#0f172a'\"),\n }),\n mood: z.enum([\"dark\", \"light\", \"warm\", \"cool\", \"vibrant\"]),\n layoutHint: z\n .string()\n .describe(\"Layout archetype: 'split-screen', 'editorial', 'immersive-gallery', 'community-feed', 'bento-grid', 'magazine'\"),\n});\n\nexport type DesignDirection = z.infer<typeof DesignDirectionSchema>;\n\nexport interface DirectionsOptions {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n count?: number;\n /** \"landing\" generates hero sections, \"document\" generates cover pages */\n product?: \"landing\" | \"document\";\n /** Override the model ID or pass a pre-built LanguageModel object */\n model?: string | import(\"ai\").LanguageModel;\n}\n\nconst DIRECTIONS_SYSTEM = `You are an elite creative director at a top design agency (Pentagram, Sagmeister, Collins).\n\nGiven a project brief, propose design directions that are MAXIMALLY diverse from each other.\n\nRULES:\n- Each direction must feel like a completely DIFFERENT design agency made it\n- Vary these axes: typography style (geometric sans, humanist sans, serif, slab, display/decorative), color mood (dark, light, warm, cool, vibrant), layout approach (editorial, split-screen, immersive, bento-grid, community)\n- Fonts MUST be popular Google Fonts that render well. Good examples:\n SANS: Inter, DM Sans, Space Grotesk, Outfit, Plus Jakarta Sans, Manrope, Sora, Figtree, Urbanist\n SERIF: Playfair Display, Lora, Merriweather, Source Serif 4, Cormorant Garamond, Libre Baskerville, DM Serif Display\n DISPLAY: Bebas Neue, Oswald, Archivo Black, Righteous, Anton, Alfa Slab One\n MONO: JetBrains Mono, Space Mono, IBM Plex Mono\n- NEVER use the same heading font twice\n- NEVER use the same color palette twice\n- AT LEAST one direction must be dark-mode\n- AT LEAST one must use serif headings\n- AT LEAST one must be bold/editorial with huge typography\n- AT LEAST one layoutHint must include \"photo\" (e.g. \"split-screen-photo\", \"immersive-gallery\") — this tells the preview generator to use a real image\n- Colors must be cohesive palettes, not random. Think Dribbble-worthy.\n- Names should be evocative (\"The Chronicle\", \"Neon Pulse\", \"Warm Atelier\")`;\n\n/**\n * Generate N design directions for a landing page.\n * Uses generateObject for type-safe structured output.\n */\nexport async function generateDirections(\n options: DirectionsOptions\n): Promise<DesignDirection[]> {\n const { prompt, count = 4, openaiApiKey, anthropicApiKey, model: modelId } = options;\n\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\n const { object } = await generateObject({\n model,\n schema: z.object({\n directions: z.array(DesignDirectionSchema).describe(`Exactly ${count} design directions`),\n }),\n system: DIRECTIONS_SYSTEM + currentDateLine(),\n prompt: `Project brief: \"${prompt}\"\n\nGenerate ${count} design directions. Make them as visually distinct as possible.`,\n });\n\n return object.directions;\n}\n\n/**\n * Generate a hero section preview for a given design direction.\n * Fast Haiku call, returns raw HTML string with Google Fonts link.\n */\nexport async function generateHeroPreview(options: {\n anthropicApiKey?: string;\n openaiApiKey?: string;\n prompt: string;\n direction: DesignDirection;\n product?: \"landing\" | \"document\";\n /** Override model ID or pass a pre-built LanguageModel object */\n model?: string | import(\"ai\").LanguageModel;\n /** Called with partial HTML as it streams in */\n onChunk?: (partialHtml: string) => void;\n /** Reference image data URL — AI will replicate this design style */\n referenceImage?: string;\n}): Promise<string> {\n const { prompt, direction, anthropicApiKey, openaiApiKey, product = \"landing\", model: modelId, onChunk, referenceImage } = options;\n\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o-mini\",\n defaultAnthropic: \"claude-haiku-4-5-20251001\",\n });\n\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\n const isDocument = product === \"document\";\n\n const systemPrompt = isDocument\n ? `You create stunning document cover pages with HTML + Tailwind CSS.\nOutput ONLY the raw HTML — no markdown fences, no explanation.\nThe HTML must include a <link> tag for Google Fonts and a <section> tag.\nThis is a LETTER-SIZE document cover page (8.5\" × 11\"), NOT a website hero.\nUse the EXACT fonts and colors provided. The cover must feel premium and print-ready.\nUse real-looking content specific to the brief (Spanish text).\n\nCRITICAL — TEXT MUST BE VISIBLE AND READABLE:\n- The document title MUST be large (text-4xl or bigger), bold, and clearly visible\n- Include: document title, subtitle or description, date, optional author/company name\n- ALL text must have strong contrast against its background — if background is dark, text MUST be white/light\n- NEVER let text disappear into the background\n\nDESIGN APPROACHES (vary across directions):\n- Approach A: Solid color background (bg-[primary]) with large white text — NO image needed\n- Approach B: Split layout — image on one half, text on solid color half\n- Approach C: Full-bleed image WITH a solid overlay (bg-black/50 or bg-[primary]/80) AND white text on top\n- Approach D: Geometric/abstract design with color blocks, no image\n- If the layout hint contains \"photo\", you MUST use Approach B or C (with a real image).\n- Otherwise, choose the approach that best fits the mood. NOT every cover needs a full-bleed image.\n\nIf using images:\n- Pattern: <img data-image-query=\"specific english search query\" alt=\"description\" class=\"absolute inset-0 w-full h-full object-cover\"/>\n- ALWAYS add a dark overlay on top: <div class=\"absolute inset-0 bg-black/50\"></div>\n- Text goes ABOVE the overlay with z-10 and text-white\n- NEVER include a src attribute — ONLY data-image-query\n\nNO buttons or CTAs — this is a print document cover.\nNO emoji — use geometric shapes or SVG icons for decoration.`\n : `You create stunning hero sections with HTML + Tailwind CSS.\nOutput ONLY the raw HTML — no markdown fences, no explanation.\nThe HTML must include a <link> tag for Google Fonts and a <section> tag.\nUse the EXACT fonts and colors provided. The hero must feel premium and polished.\nUse real-looking content specific to the brief (Spanish text).\nInclude: headline (huge), subtitle, 1-2 CTAs, and optionally a hero image via <img data-image-query=\"...\" alt=\"...\" class=\"...\">.\nNEVER use src on img tags. Use data-image-query with English search terms.`;\n\n const sectionInstruction = isDocument\n ? `Generate a document cover page. Use inline style for font-family referencing the Google Fonts.\nStart with: <link href=\"${fontsUrl}\" rel=\"stylesheet\">\nThen a <section class=\"w-[8.5in] h-[11in] relative overflow-hidden\"> sized for letter paper.\nUse the exact hex colors in Tailwind arbitrary values like bg-[${direction.colors.primary}] text-[${direction.colors.text}] etc.\nUse full-bleed backgrounds, geometric accents, elegant typography hierarchy.\nMake it look like a $50K design agency document cover.`\n : `Generate a hero section. Use inline style for font-family referencing the Google Fonts.\nStart with: <link href=\"${fontsUrl}\" rel=\"stylesheet\">\nThen a <section> with min-h-[80vh].\nUse the exact hex colors in Tailwind arbitrary values like bg-[${direction.colors.primary}] text-[${direction.colors.text}] etc.\nMake it look like a $50K agency landing page hero.`;\n\n const textPrompt = `Brief: \"${prompt}\"\n\nDesign direction: \"${direction.name}\" — ${direction.tagline}\nLayout: ${direction.layoutHint}\nHeading font: ${direction.headingFont}\nBody font: ${direction.bodyFont}\nColors: primary=${direction.colors.primary}, accent=${direction.colors.accent}, surface=${direction.colors.surface}, surfaceAlt=${direction.colors.surfaceAlt}, text=${direction.colors.text}\nMood: ${direction.mood}\n\n${sectionInstruction}`;\n\n const messages: any[] = [{\n role: \"user\" as const,\n content: referenceImage\n ? [\n { type: \"image\" as const, image: referenceImage },\n { type: \"text\" as const, text: `Replicate the visual design style, layout, and aesthetic from the reference image above.\\n\\n${textPrompt}` },\n ]\n : textPrompt,\n }];\n\n const result = streamText({\n model,\n system: systemPrompt + currentDateLine(),\n messages,\n });\n\n let html = \"\";\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n html += chunk;\n chunkCount++;\n if (onChunk && chunkCount % 3 === 0) {\n onChunk(html);\n }\n }\n if (onChunk) onChunk(html);\n\n // Clean markdown fences if present\n html = html.trim();\n if (html.startsWith(\"```\")) {\n html = html.replace(/^```(?:html|xml)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n\n return html;\n}\n"],"mappings":";;;;;;AAAA,SAAS,gBAAgB,kBAAkB;AAC3C,SAAS,SAAS;AAIX,IAAM,wBAAwB,EAAE,OAAO;AAAA,EAC5C,MAAM,EAAE,OAAO,EAAE,SAAS,+CAA+C;AAAA,EACzE,SAAS,EACN,OAAO,EACP,SAAS,gFAAgF;AAAA,EAC5F,aAAa,EACV,OAAO,EACP,SAAS,wDAAwD;AAAA,EACpE,UAAU,EACP,OAAO,EACP,SAAS,8CAA8C;AAAA,EAC1D,QAAQ,EAAE,OAAO;AAAA,IACf,SAAS,EAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,IACtE,QAAQ,EAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,IACrE,SAAS,EAAE,OAAO,EAAE,SAAS,iDAAiD;AAAA,IAC9E,YAAY,EAAE,OAAO,EAAE,SAAS,kEAAkE;AAAA,IAClG,MAAM,EAAE,OAAO,EAAE,SAAS,wCAAwC;AAAA,EACpE,CAAC;AAAA,EACD,MAAM,EAAE,KAAK,CAAC,QAAQ,SAAS,QAAQ,QAAQ,SAAS,CAAC;AAAA,EACzD,YAAY,EACT,OAAO,EACP,SAAS,gHAAgH;AAC9H,CAAC;AAeD,IAAM,oBAAoB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAyB1B,eAAsB,mBACpB,SAC4B;AAC5B,QAAM,EAAE,QAAQ,QAAQ,GAAG,cAAc,iBAAiB,OAAO,QAAQ,IAAI;AAE7E,QAAM,QAAQ,MAAM,aAAa;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,kBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,EAAE,OAAO,IAAI,MAAM,eAAe;AAAA,IACtC;AAAA,IACA,QAAQ,EAAE,OAAO;AAAA,MACf,YAAY,EAAE,MAAM,qBAAqB,EAAE,SAAS,WAAW,KAAK,oBAAoB;AAAA,IAC1F,CAAC;AAAA,IACD,QAAQ,oBAAoB,gBAAgB;AAAA,IAC5C,QAAQ,mBAAmB,MAAM;AAAA;AAAA,WAE1B,KAAK;AAAA,EACd,CAAC;AAED,SAAO,OAAO;AAChB;AAMA,eAAsB,oBAAoB,SAYtB;AAClB,QAAM,EAAE,QAAQ,WAAW,iBAAiB,cAAc,UAAU,WAAW,OAAO,SAAS,SAAS,eAAe,IAAI;AAE3H,QAAM,QAAQ,MAAM,aAAa;AAAA,IAC/B;AAAA,IACA;AAAA,IACA;AAAA,IACA,eAAe;AAAA,IACf,kBAAkB;AAAA,EACpB,CAAC;AAED,QAAM,WAAW,4CAA4C,mBAAmB,UAAU,WAAW,EAAE,QAAQ,QAAQ,GAAG,CAAC,4BAA4B,mBAAmB,UAAU,QAAQ,EAAE,QAAQ,QAAQ,GAAG,CAAC;AAElN,QAAM,aAAa,YAAY;AAE/B,QAAM,eAAe,aACjB;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,qEA6BA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAQJ,QAAM,qBAAqB,aACvB;AAAA,0BACoB,QAAQ;AAAA;AAAA,iEAE+B,UAAU,OAAO,OAAO,WAAW,UAAU,OAAO,IAAI;AAAA;AAAA,0DAGnH;AAAA,0BACoB,QAAQ;AAAA;AAAA,iEAE+B,UAAU,OAAO,OAAO,WAAW,UAAU,OAAO,IAAI;AAAA;AAGvH,QAAM,aAAa,WAAW,MAAM;AAAA;AAAA,qBAEjB,UAAU,IAAI,YAAO,UAAU,OAAO;AAAA,UACjD,UAAU,UAAU;AAAA,gBACd,UAAU,WAAW;AAAA,aACxB,UAAU,QAAQ;AAAA,kBACb,UAAU,OAAO,OAAO,YAAY,UAAU,OAAO,MAAM,aAAa,UAAU,OAAO,OAAO,gBAAgB,UAAU,OAAO,UAAU,UAAU,UAAU,OAAO,IAAI;AAAA,QACpL,UAAU,IAAI;AAAA;AAAA,EAEpB,kBAAkB;AAElB,QAAM,WAAkB,CAAC;AAAA,IACvB,MAAM;AAAA,IACN,SAAS,iBACL;AAAA,MACE,EAAE,MAAM,SAAkB,OAAO,eAAe;AAAA,MAChD,EAAE,MAAM,QAAiB,MAAM;AAAA;AAAA,EAA+F,UAAU,GAAG;AAAA,IAC7I,IACA;AAAA,EACN,CAAC;AAED,QAAM,SAAS,WAAW;AAAA,IACxB;AAAA,IACA,QAAQ,eAAe,gBAAgB;AAAA,IACvC;AAAA,EACF,CAAC;AAED,MAAI,OAAO;AACX,MAAI,aAAa;AACjB,mBAAiB,SAAS,OAAO,YAAY;AAC3C,YAAQ;AACR;AACA,QAAI,WAAW,aAAa,MAAM,GAAG;AACnC,cAAQ,IAAI;AAAA,IACd;AAAA,EACF;AACA,MAAI,QAAS,SAAQ,IAAI;AAGzB,SAAO,KAAK,KAAK;AACjB,MAAI,KAAK,WAAW,KAAK,GAAG;AAC1B,WAAO,KAAK,QAAQ,wBAAwB,EAAE,EAAE,QAAQ,WAAW,EAAE;AAAA,EACvE;AAEA,SAAO;AACT;","names":[]}
|
package/dist/generate.js
CHANGED
|
@@ -2,11 +2,10 @@ import {
|
|
|
2
2
|
PROMPT_SUFFIX,
|
|
3
3
|
SYSTEM_PROMPT,
|
|
4
4
|
generateLanding
|
|
5
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-ZWOEBRPC.js";
|
|
6
6
|
import {
|
|
7
7
|
extractJsonObjects
|
|
8
|
-
} from "./chunk-
|
|
9
|
-
import "./chunk-G5QRFGPZ.js";
|
|
8
|
+
} from "./chunk-HTGS346J.js";
|
|
10
9
|
export {
|
|
11
10
|
PROMPT_SUFFIX,
|
|
12
11
|
SYSTEM_PROMPT,
|
|
@@ -3,8 +3,8 @@ import { z } from 'zod';
|
|
|
3
3
|
import { S as Section3 } from './types-BIpbpCJr.js';
|
|
4
4
|
import { DesignDirection } from './directions.js';
|
|
5
5
|
|
|
6
|
-
declare const DOCUMENT_SYSTEM_PROMPT = "You are a professional document designer who creates stunning letter-sized (8.5\" \u00D7 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\">\n- The section itself has NO padding \u2014 backgrounds, gradients, and decorative elements go edge-to-edge\n- For text content, use an inner wrapper: <div class=\"px-[0.75in] py-[0.5in]\">...content...</div>\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 \u2014 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 \u2014 fixed letter size, no breakpoints needed\n- Sections can have ANY background \u2014 full-bleed color, gradients, or white. Not limited to white paper.\n\nSTRICT PROHIBITIONS:\n1. **NO EMOJI** \u2014 Never use emoji characters (\uD83D\uDE80\u274C\u2705\uD83D\uDCCA 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** \u2014 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** \u2014 This is a print document, not a web page. No \"Contactar\", \"Ver m\u00E1s\", \"Comprar\" buttons. Use text with contact info instead.\n4. **CONTRAST IS MANDATORY** \u2014 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** \u2014 Pick 2 (e.g. font-semibold + font-normal, or font-bold + font-light). Don't mix 4-5 weights.\n6. **Generous whitespace** \u2014 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 \u2014 CRITICAL:\n- Max 2 columns side by side \u2014 each with w-1/2. NEVER use 3+ columns.\n- Decorative sidebars: max w-16 (4rem). NEVER use w-[2.5in] or wider sidebars \u2014 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%] \u2014 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 \u2014 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 \u2014 be extra careful.\n- NEVER place elements beyond the right edge \u2014 all content and decorations must fit within 8.5in width.\n\nDESIGN \u2014 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 \u2014 different layouts, accent placements\n- Use the full page creatively \u2014 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\u2192solution 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 \u2014 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 \u2014 NEVER canvas/charts\n\nCSS PROGRESS BARS \u2014 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 \u2014 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 \u2192 text-white or text-on-primary. White/bg-surface \u2192 text-gray-900 or text-on-surface\n\nIMAGES \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 Cover page (simple, no wide sidebars):\n<section class=\"w-[8.5in] min-h-[11in] relative overflow-hidden bg-white\">\n <div class=\"absolute left-0 top-0 w-2 h-full bg-primary\"></div>\n <div class=\"flex flex-col justify-center h-[11in] px-[1in]\">\n <div class=\"text-sm font-normal text-primary mb-4\">Marzo 2026 \u00B7 Versi\u00F3n 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\u00E1lisis del primer trimestre</p>\n </div>\n</section>\n\nEXAMPLE \u2014 Marketing/brochure page (bold, visual):\n<section class=\"w-[8.5in] min-h-[11in] relative overflow-hidden bg-primary\">\n <div class=\"flex h-[11in]\">\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\u00F3n 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\u00F3n 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\u00F3n</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 \u2014 Catalog/product grid page:\n<section class=\"w-[8.5in] min-h-[11in] relative overflow-hidden bg-surface\">\n <div class=\"h-1 bg-primary w-full\"></div>\n <div class=\"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\u00F3n Primavera</h2>\n <span class=\"text-xs font-normal text-on-surface-muted uppercase tracking-wider\">P\u00E1gina 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\u00F1o ergon\u00F3mico 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\u00EDa 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 \u2014 Content page with table + progress bars:\n<section class=\"w-[8.5in] min-h-[11in] relative overflow-hidden bg-white\">\n <div class=\"h-1.5 bg-primary w-full\"></div>\n <div class=\"px-[0.75in] py-[0.5in]\">\n <h2 class=\"text-2xl font-bold text-gray-900 mb-1\">M\u00E9tricas de Rendimiento</h2>\n <p class=\"text-sm font-normal text-gray-500 mb-8\">Indicadores clave del periodo enero\u2014marzo</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\u00F3n</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 \u00C1rea</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>";
|
|
7
|
-
declare const DOCUMENT_PROMPT_SUFFIX = "\n\nOUTPUT FORMAT: NDJSON \u2014 one JSON object per line, NO wrapper array, NO markdown fences.\nEach line: {\"label\": \"Page Title\", \"html\": \"<section class='w-[8.5in]
|
|
6
|
+
declare const DOCUMENT_SYSTEM_PROMPT = "You are a professional document designer who creates stunning letter-sized (8.5\" \u00D7 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 \u2014 content MUST fit, never exceed. Use flex flex-col so children can use flex-1.\n- The section itself has NO padding \u2014 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 \u2014 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 \u2014 fixed letter size, no breakpoints needed\n- Sections can have ANY background \u2014 full-bleed color, gradients, or white. Not limited to white paper.\n\nSTRICT PROHIBITIONS:\n1. **NO EMOJI** \u2014 Never use emoji characters (\uD83D\uDE80\u274C\u2705\uD83D\uDCCA 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** \u2014 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** \u2014 This is a print document, not a web page. No \"Contactar\", \"Ver m\u00E1s\", \"Comprar\" buttons. Use text with contact info instead.\n4. **CONTRAST IS MANDATORY** \u2014 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** \u2014 Pick 2 (e.g. font-semibold + font-normal, or font-bold + font-light). Don't mix 4-5 weights.\n6. **Generous whitespace** \u2014 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 \u2014 CRITICAL:\n- Max 2 columns side by side \u2014 each with w-1/2. NEVER use 3+ columns.\n- Decorative sidebars: max w-16 (4rem). NEVER use w-[2.5in] or wider sidebars \u2014 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%] \u2014 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 \u2014 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 \u2014 be extra careful.\n- NEVER place elements beyond the right edge \u2014 all content and decorations must fit within 8.5in width.\n\nDESIGN \u2014 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 \u2014 different layouts, accent placements\n- Use the full page creatively \u2014 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\u2192solution 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 \u2014 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 \u2014 NEVER canvas/charts\n\nCSS PROGRESS BARS \u2014 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 \u2014 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 \u2192 text-white or text-on-primary. White/bg-surface \u2192 text-gray-900 or text-on-surface\n\nIMAGES \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u2014 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 \u00B7 Versi\u00F3n 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\u00E1lisis del primer trimestre</p>\n </div>\n</section>\n\nEXAMPLE \u2014 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\u00F3n 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\u00F3n 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\u00F3n</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 \u2014 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\u00F3n Primavera</h2>\n <span class=\"text-xs font-normal text-on-surface-muted uppercase tracking-wider\">P\u00E1gina 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\u00F1o ergon\u00F3mico 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\u00EDa 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 \u2014 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\u00E9tricas de Rendimiento</h2>\n <p class=\"text-sm font-normal text-gray-500 mb-8\">Indicadores clave del periodo enero\u2014marzo</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\u00F3n</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 \u00C1rea</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>";
|
|
7
|
+
declare const DOCUMENT_PROMPT_SUFFIX = "\n\nOUTPUT FORMAT: NDJSON \u2014 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\" \u00D7 11\"). Be conservative with spacing.\nMake each page visually distinct \u2014 different layouts, different accent placements.\nIMPORTANT: Adapt your design style to match the type of document \u2014 not everything is a report. Brochures should feel bold and visual, catalogs should showcase products, invitations should feel elegant, etc.";
|
|
8
8
|
interface GenerateDocumentOptions {
|
|
9
9
|
anthropicApiKey?: string;
|
|
10
10
|
openaiApiKey?: string;
|
package/dist/generateDocument.js
CHANGED
|
@@ -3,9 +3,8 @@ import {
|
|
|
3
3
|
DOCUMENT_SYSTEM_PROMPT,
|
|
4
4
|
generateDocument,
|
|
5
5
|
generateDocumentParallel
|
|
6
|
-
} from "./chunk-
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-G5QRFGPZ.js";
|
|
6
|
+
} from "./chunk-JB3MUQJS.js";
|
|
7
|
+
import "./chunk-HTGS346J.js";
|
|
9
8
|
export {
|
|
10
9
|
DOCUMENT_PROMPT_SUFFIX,
|
|
11
10
|
DOCUMENT_SYSTEM_PROMPT,
|
package/dist/images.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -4,26 +4,26 @@ import {
|
|
|
4
4
|
FloatingToolbar,
|
|
5
5
|
SectionList,
|
|
6
6
|
ViewportToggle
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-2YEIWCVP.js";
|
|
8
8
|
import "./chunk-MJ34S5ZC.js";
|
|
9
9
|
import {
|
|
10
10
|
PROMPT_SUFFIX,
|
|
11
11
|
SYSTEM_PROMPT,
|
|
12
12
|
generateLanding
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-ZWOEBRPC.js";
|
|
14
14
|
import {
|
|
15
15
|
DOCUMENT_PROMPT_SUFFIX,
|
|
16
16
|
DOCUMENT_SYSTEM_PROMPT,
|
|
17
17
|
generateDocument
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-JB3MUQJS.js";
|
|
19
19
|
import {
|
|
20
20
|
REFINE_SYSTEM,
|
|
21
21
|
refineLanding
|
|
22
|
-
} from "./chunk-
|
|
22
|
+
} from "./chunk-OQOOZCFY.js";
|
|
23
23
|
import {
|
|
24
24
|
deployToEasyBits,
|
|
25
25
|
deployToS3
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-GBDM7BJL.js";
|
|
27
27
|
import {
|
|
28
28
|
LANDING_THEMES,
|
|
29
29
|
buildCustomTheme,
|
|
@@ -33,17 +33,15 @@ import {
|
|
|
33
33
|
buildSingleThemeCss,
|
|
34
34
|
buildThemeCss,
|
|
35
35
|
getIframeScript
|
|
36
|
-
} from "./chunk-
|
|
37
|
-
import {
|
|
38
|
-
extractJsonObjects
|
|
39
|
-
} from "./chunk-AJGAO7UP.js";
|
|
36
|
+
} from "./chunk-WDOPQECR.js";
|
|
40
37
|
import {
|
|
41
38
|
enrichImages,
|
|
39
|
+
extractJsonObjects,
|
|
42
40
|
findImageSlots,
|
|
43
41
|
generateImage,
|
|
44
42
|
generateSvg,
|
|
45
43
|
searchImage
|
|
46
|
-
} from "./chunk-
|
|
44
|
+
} from "./chunk-HTGS346J.js";
|
|
47
45
|
export {
|
|
48
46
|
Canvas,
|
|
49
47
|
CodeEditor,
|
package/dist/refine.js
CHANGED
|
@@ -2,9 +2,8 @@ import {
|
|
|
2
2
|
REFINE_SYSTEM,
|
|
3
3
|
extractSectionDescription,
|
|
4
4
|
refineLanding
|
|
5
|
-
} from "./chunk-
|
|
6
|
-
import "./chunk-
|
|
7
|
-
import "./chunk-G5QRFGPZ.js";
|
|
5
|
+
} from "./chunk-OQOOZCFY.js";
|
|
6
|
+
import "./chunk-HTGS346J.js";
|
|
8
7
|
export {
|
|
9
8
|
REFINE_SYSTEM,
|
|
10
9
|
extractSectionDescription,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@easybits.cloud/html-tailwind-generator",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.102",
|
|
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/refine.ts"],"sourcesContent":["import { streamText } from \"ai\";\nimport { enrichImages } from \"./images/enrichImages\";\nimport { sanitizeSemanticColors } from \"./sanitizeColors\";\nimport { resolveModel } from \"./streamCore\";\n\nexport const REFINE_SYSTEM = `You are an expert HTML/Tailwind CSS developer. You receive the current HTML of a landing page section and a user instruction.\n\nRULES:\n- Return ONLY the modified HTML — no full page, no <html>/<head>/<body> tags\n- Use Tailwind CSS classes (CDN loaded)\n- You may use inline styles for specific adjustments\n- Images: use data-image-query=\"english search query\" for new images\n- Keep all text in its original language unless asked to translate\n- Be creative — don't just make minimal changes, improve the design\n- Return raw HTML only — no markdown fences, no explanations\n\nCOLOR SYSTEM — CRITICAL:\n- Use semantic color classes: bg-primary, text-primary, bg-primary-light, bg-primary-dark, text-on-primary, bg-surface, bg-surface-alt, text-on-surface, text-on-surface-muted, bg-secondary, text-secondary, bg-accent, text-accent\n- NEVER use hardcoded colors: NO bg-gray-*, bg-black, bg-white, text-gray-*, text-black, text-white, etc.\n- The ONLY exception: border-gray-200 or border-gray-700 for subtle dividers.\n- ALL text MUST use: text-on-surface, text-on-surface-muted, text-on-primary, text-accent. Use text-primary ONLY on bg-surface/bg-surface-alt (it's the same hue as bg-primary — invisible on primary backgrounds).\n- CONTRAST RULE: on bg-primary or bg-primary-dark → use ONLY text-on-primary. On bg-surface or bg-surface-alt → use text-on-surface, text-on-surface-muted, or text-primary. NEVER use text-primary on bg-primary — they are the SAME COLOR. NEVER put text-on-surface on bg-primary or text-on-primary on bg-surface.\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\n/**\n * Extract a text description from HTML for variant generation.\n * Instead of sending full HTML to the model, we send a content summary\n * so the model generates a completely new layout rather than tweaking colors.\n */\nexport function extractSectionDescription(html: string, label?: string): { content: string; layoutHint: string } {\n // Extract headings\n const headings = [...html.matchAll(/<h[1-6][^>]*>([\\s\\S]*?)<\\/h[1-6]>/gi)]\n .map(m => m[1].replace(/<[^>]+>/g, \"\").trim())\n .filter(Boolean);\n\n // Extract paragraphs\n const paragraphs = [...html.matchAll(/<p[^>]*>([\\s\\S]*?)<\\/p>/gi)]\n .map(m => m[1].replace(/<[^>]+>/g, \"\").trim())\n .filter(Boolean);\n\n // Extract button/CTA text\n const buttons = [...html.matchAll(/<(?:button|a)[^>]*>([\\s\\S]*?)<\\/(?:button|a)>/gi)]\n .map(m => m[1].replace(/<[^>]+>/g, \"\").trim())\n .filter(t => t.length > 0 && t.length < 60);\n\n // Count items (cards, list items, grid children)\n const listItems = (html.match(/<li[\\s>]/gi) || []).length;\n const gridMatch = html.match(/grid-cols-(\\d)/);\n const gridCols = gridMatch ? parseInt(gridMatch[1]) : 0;\n\n // Detect layout patterns for negative prompt\n const layouts: string[] = [];\n if (html.includes(\"grid\")) layouts.push(\"grid\");\n if (html.includes(\"flex-col\")) layouts.push(\"vertical-stack\");\n if (html.includes(\"flex-row\") || html.includes(\"md:flex-row\")) layouts.push(\"horizontal-flex\");\n if (html.includes(\"text-center\") && !html.includes(\"text-left\")) layouts.push(\"centered\");\n if (gridCols) layouts.push(`${gridCols}-column-grid`);\n\n const content = [\n label ? `Section: ${label}` : \"\",\n headings.length ? `Headings: ${headings.join(\" | \")}` : \"\",\n paragraphs.length ? `Text: ${paragraphs.slice(0, 3).join(\" \")}` : \"\",\n buttons.length ? `CTAs: ${buttons.join(\", \")}` : \"\",\n listItems > 0 ? `${listItems} list/card items` : \"\",\n ].filter(Boolean).join(\"\\n\");\n\n return { content, layoutHint: layouts.join(\", \") };\n}\n\nexport interface RefineOptions {\n /** Anthropic API key. Falls back to ANTHROPIC_API_KEY env var */\n anthropicApiKey?: string;\n /** OpenAI API key. If provided, uses GPT-4o-mini instead of Claude */\n openaiApiKey?: string;\n /** Current HTML of the section being refined */\n currentHtml: string;\n /** User instruction for refinement */\n instruction: string;\n /** Reference image (base64 data URI) for vision-based refinement */\n referenceImage?: string;\n /** When true, generates a completely new layout variant instead of refining */\n isVariant?: boolean;\n /** Custom system prompt (overrides default REFINE_SYSTEM) */\n systemPrompt?: string;\n /** Model ID (default: gpt-4o-mini/gpt-4o for OpenAI, claude-haiku/claude-sonnet for Anthropic) */\n model?: string;\n /** Pexels API key for image enrichment. Falls back to PEXELS_API_KEY env var */\n pexelsApiKey?: string;\n /** Called with temp DALL-E URL + query, returns permanent URL. Use to persist to S3/etc. */\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n /** Called with accumulated HTML as it streams */\n onChunk?: (html: string) => void;\n /** Called when refinement is complete with final enriched HTML */\n onDone?: (html: string) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\n/**\n * Refine a landing page section with streaming AI.\n * Returns the final enriched HTML.\n */\nexport async function refineLanding(options: RefineOptions): Promise<string> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n currentHtml,\n instruction,\n referenceImage,\n isVariant,\n systemPrompt = REFINE_SYSTEM,\n model: modelId,\n pexelsApiKey,\n persistImage,\n onChunk,\n onDone,\n onError,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n const useVision = !!referenceImage;\n const defaultOpenai = useVision ? \"gpt-4o\" : \"gpt-4o-mini\";\n const defaultAnthropic = useVision ? \"claude-sonnet-4-6\" : \"claude-haiku-4-5-20251001\";\n const model = await resolveModel({ openaiApiKey, anthropicApiKey, modelId, defaultOpenai, defaultAnthropic });\n\n // Build content (supports multimodal with reference image)\n const content: any[] = [];\n if (referenceImage) {\n content.push({ type: \"image\", image: referenceImage });\n }\n\n if (isVariant && !referenceImage) {\n // Variant mode: send description instead of HTML to force creative layout\n const { content: desc, layoutHint } = extractSectionDescription(currentHtml);\n content.push({\n type: \"text\",\n text: `Generate a COMPLETELY NEW section with the following content. Create an original, creative layout.\\n\\nContent:\\n${desc}\\n\\n${layoutHint ? `DO NOT use these layout patterns (the current design already uses them): ${layoutHint}. Choose a radically different structure.` : \"\"}\\n\\nReturn ONLY the <section>...</section> HTML with Tailwind classes.`,\n });\n } else {\n content.push({\n type: \"text\",\n text: `Current HTML:\\n${currentHtml}\\n\\nInstruction: ${instruction}\\n\\nReturn the updated HTML.`,\n });\n }\n\n const result = streamText({\n model,\n system: systemPrompt,\n messages: [{ role: \"user\", content }],\n ...(isVariant && !referenceImage ? { temperature: 1.2 } : {}),\n });\n\n try {\n let accumulated = \"\";\n\n for await (const chunk of result.textStream) {\n accumulated += chunk;\n onChunk?.(accumulated);\n }\n\n // Clean up markdown fences if present\n let html = accumulated.trim();\n if (html.startsWith(\"```\")) {\n html = html.replace(/^```(?:html|xml)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n\n // Sanitize hardcoded colors to semantic classes\n html = sanitizeSemanticColors(html);\n\n // Enrich images (DALL-E if openaiApiKey, otherwise Pexels)\n html = await enrichImages(html, { pexelsApiKey, openaiApiKey, persistImage });\n\n onDone?.(html);\n return html;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Refine failed\");\n onError?.(error);\n throw error;\n }\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/sanitizeColors.ts","../src/streamCore.ts"],"sourcesContent":["/**\n * Replace hardcoded Tailwind color classes with semantic color classes\n * (bg-primary, text-primary, etc.). Gray/white/black/slate/zinc/neutral stay intact.\n *\n * Covers ALL chromatic Tailwind colors the model might generate.\n */\n\n// All chromatic colors Tailwind ships (excluding neutrals: slate, gray, zinc, neutral, stone)\nconst COLORS =\n \"red|orange|amber|yellow|lime|green|emerald|teal|cyan|sky|blue|indigo|violet|purple|fuchsia|pink|rose\";\n\nfunction re(prefix: string, shades: string): RegExp {\n return new RegExp(`\\\\b${prefix}-(${COLORS})-(${shades})\\\\b`, \"g\");\n}\n\nconst replacements: [RegExp, string][] = [\n // Background\n [re(\"bg\", \"500|600|700\"), \"bg-primary\"],\n [re(\"bg\", \"50|100\"), \"bg-primary-light\"],\n [re(\"bg\", \"800|900|950\"), \"bg-primary-dark\"],\n [re(\"bg\", \"200|300|400\"), \"bg-primary\"],\n\n // Text\n [re(\"text\", \"500|600|700\"), \"text-primary\"],\n [re(\"text\", \"800|900|950\"), \"text-primary-dark\"],\n [re(\"text\", \"50|100|200|300\"), \"text-on-primary\"],\n [re(\"text\", \"400\"), \"text-primary\"],\n\n // Border\n [re(\"border\", \"\\\\d{2,3}\"), \"border-primary\"],\n\n // Ring\n [re(\"ring\", \"\\\\d{2,3}\"), \"ring-primary\"],\n\n // Gradients\n [re(\"from\", \"\\\\d{2,3}\"), \"from-primary\"],\n [re(\"to\", \"\\\\d{2,3}\"), \"to-primary\"],\n [re(\"via\", \"\\\\d{2,3}\"), \"via-primary\"],\n\n // Hover/focus variants\n [new RegExp(`\\\\bhover:bg-(${COLORS})-(500|600|700|800|900|950)\\\\b`, \"g\"), \"hover:bg-primary-dark\"],\n [new RegExp(`\\\\bhover:bg-(${COLORS})-(50|100|200|300|400)\\\\b`, \"g\"), \"hover:bg-primary-light\"],\n [new RegExp(`\\\\bhover:text-(${COLORS})-\\\\d{2,3}\\\\b`, \"g\"), \"hover:text-primary\"],\n [new RegExp(`\\\\bfocus:ring-(${COLORS})-\\\\d{2,3}\\\\b`, \"g\"), \"focus:ring-primary\"],\n [new RegExp(`\\\\bfocus:border-(${COLORS})-\\\\d{2,3}\\\\b`, \"g\"), \"focus:border-primary\"],\n\n // Divide\n [re(\"divide\", \"\\\\d{2,3}\"), \"divide-primary\"],\n\n // Placeholder\n [re(\"placeholder\", \"\\\\d{2,3}\"), \"placeholder-primary\"],\n\n // Outline\n [re(\"outline\", \"\\\\d{2,3}\"), \"outline-primary\"],\n\n // Shadow colored\n [re(\"shadow\", \"\\\\d{2,3}\"), \"shadow-primary\"],\n\n // Decoration\n [re(\"decoration\", \"\\\\d{2,3}\"), \"decoration-primary\"],\n\n // Accent\n [re(\"accent\", \"\\\\d{2,3}\"), \"accent-primary\"],\n];\n\nexport function sanitizeSemanticColors(html: string): string {\n let result = html;\n for (const [pattern, replacement] of replacements) {\n result = result.replace(pattern, replacement);\n }\n return result;\n}\n","import { streamText } from \"ai\";\nimport { createAnthropic } from \"@ai-sdk/anthropic\";\nimport { nanoid } from \"nanoid\";\nimport { findImageSlots } from \"./images/enrichImages\";\nimport { searchImage } from \"./images/pexels\";\nimport { generateImage } from \"./images/dalleImages\";\nimport { generateSvg } from \"./images/svgGenerator\";\nimport type { Section3 } from \"./types\";\nimport { sanitizeSemanticColors } from \"./sanitizeColors\";\n\n/**\n * Resolve AI model from available keys.\n * If modelId is already a LanguageModel object, return it directly.\n * Prefers Anthropic, falls back to OpenAI.\n */\nfunction isOpenAiModel(id: string): boolean {\n return /^(gpt-|o[1-9]|dall-e|tts-|whisper|chatgpt-)/.test(id);\n}\n\nfunction isLanguageModel(value: unknown): value is import(\"ai\").LanguageModel {\n return typeof value === \"object\" && value !== null && \"modelId\" in value && \"provider\" in value;\n}\n\nexport async function resolveModel(opts: {\n openaiApiKey?: string;\n anthropicApiKey?: string;\n modelId?: string | import(\"ai\").LanguageModel;\n defaultOpenai: string;\n defaultAnthropic: string;\n}) {\n // If modelId is already a model object, return it directly\n if (opts.modelId && isLanguageModel(opts.modelId)) {\n return opts.modelId;\n }\n\n const modelId = opts.modelId as string | undefined;\n\n if (modelId && isOpenAiModel(modelId)) {\n const openaiKey = opts.openaiApiKey || process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const { createOpenAI } = await import(\"@ai-sdk/openai\");\n return createOpenAI({ apiKey: openaiKey })(modelId);\n }\n // OpenAI model requested but no key — fall through to Anthropic default\n } else if (modelId) {\n const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n if (anthropicKey) {\n return createAnthropic({ apiKey: anthropicKey })(modelId);\n }\n }\n // No explicit modelId — prefer Anthropic, fallback to OpenAI\n const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n if (anthropicKey) {\n return createAnthropic({ apiKey: anthropicKey })(opts.defaultAnthropic);\n }\n const openaiKey = opts.openaiApiKey || process.env.OPENAI_API_KEY;\n if (openaiKey) {\n const { createOpenAI } = await import(\"@ai-sdk/openai\");\n return createOpenAI({ apiKey: openaiKey })(opts.defaultOpenai);\n }\n return createAnthropic()(opts.defaultAnthropic);\n}\n\n/**\n * Convert data URL to Uint8Array for AI SDK vision.\n */\nexport function dataUrlToImagePart(dataUrl: string): { image: Uint8Array; mimeType: string } | null {\n const match = dataUrl.match(/^data:([^;]+);base64,(.+)$/);\n if (!match) return null;\n return {\n image: new Uint8Array(Buffer.from(match[2], \"base64\")),\n mimeType: match[1],\n };\n}\n\n/**\n * Extract complete JSON objects from accumulated text using brace-depth tracking.\n */\nexport function extractJsonObjects(text: string): [any[], string] {\n const objects: any[] = [];\n let remaining = text;\n\n while (remaining.length > 0) {\n remaining = remaining.trimStart();\n if (!remaining.startsWith(\"{\")) {\n const nextBrace = remaining.indexOf(\"{\");\n if (nextBrace === -1) break;\n remaining = remaining.slice(nextBrace);\n continue;\n }\n\n let depth = 0;\n let inString = false;\n let escape = false;\n let end = -1;\n\n for (let i = 0; i < remaining.length; i++) {\n const ch = remaining[i];\n if (escape) { escape = false; continue; }\n if (ch === \"\\\\\") { escape = true; continue; }\n if (ch === '\"') { inString = !inString; continue; }\n if (inString) continue;\n if (ch === \"{\") depth++;\n if (ch === \"}\") { depth--; if (depth === 0) { end = i; break; } }\n }\n\n if (end === -1) break;\n\n const candidate = remaining.slice(0, end + 1);\n remaining = remaining.slice(end + 1);\n\n try {\n objects.push(JSON.parse(candidate));\n } catch {\n // malformed, skip\n }\n }\n\n return [objects, remaining];\n}\n\n/** Inline shimmer SVG used as src for loading image placeholders */\nconst LOADING_PLACEHOLDER_SRC = `data:image/svg+xml,${encodeURIComponent('<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"800\" height=\"500\" viewBox=\"0 0 800 500\"><rect fill=\"#f3f4f6\" width=\"800\" height=\"500\" rx=\"12\"/><g opacity=\".4\"><rect x=\"320\" y=\"200\" width=\"160\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" repeatCount=\"indefinite\"/></rect><rect x=\"280\" y=\"215\" width=\"240\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".3s\" repeatCount=\"indefinite\"/></rect><rect x=\"340\" y=\"230\" width=\"120\" height=\"4\" rx=\"2\" fill=\"#d1d5db\"><animate attributeName=\"opacity\" values=\".3;.8;.3\" dur=\"1.5s\" begin=\".6s\" repeatCount=\"indefinite\"/></rect></g><g transform=\"translate(376,150)\" opacity=\".3\"><path d=\"M0 28V4a4 4 0 014-4h40a4 4 0 014 4v24a4 4 0 01-4 4H4a4 4 0 01-4-4z\" fill=\"#d1d5db\"/><circle cx=\"14\" cy=\"12\" r=\"4\" fill=\"#9ca3af\"/><path d=\"M4 28l10-10 6 6 8-8 16 16H4z\" fill=\"#9ca3af\" opacity=\".5\"/></g></svg>')}`;\n\n/** Inline SVG placeholder for loading charts */\nconst SVG_LOADING_PLACEHOLDER = `<div class=\"w-full h-48 bg-gray-50 rounded-lg flex items-center justify-center animate-pulse\"><svg xmlns=\"http://www.w3.org/2000/svg\" width=\"48\" height=\"48\" viewBox=\"0 0 24 24\" fill=\"none\" stroke=\"#9ca3af\" stroke-width=\"1.5\"><rect x=\"3\" y=\"12\" width=\"4\" height=\"9\" rx=\"1\"/><rect x=\"10\" y=\"7\" width=\"4\" height=\"14\" rx=\"1\"/><rect x=\"17\" y=\"3\" width=\"4\" height=\"18\" rx=\"1\"/></svg></div>`;\n\n/** Replace data-svg-chart divs with loading placeholders */\nexport function addSvgLoadingPlaceholders(html: string): string {\n return html.replace(\n /<div\\s([^>]*?)data-svg-chart=\"([^\"]+)\"([^>]*?)>[\\s\\S]*?<\\/div>/gi,\n (_match, before, chart, after) => {\n return `<div ${before}data-svg-chart=\"${chart}\"${after}>${SVG_LOADING_PLACEHOLDER}</div>`;\n }\n );\n}\n\n/** Replace data-image-query attrs with animated loading placeholders */\nexport function addLoadingPlaceholders(html: string): string {\n return html.replace(\n /(<img\\s[^>]*)data-image-query=\"([^\"]+)\"([^>]*?)(?:\\s*\\/?>)/gi,\n (_match, before, query, after) => {\n if (before.includes('src=') || after.includes('src=')) return _match;\n return `${before}src=\"${LOADING_PLACEHOLDER_SRC}\" data-image-query=\"${query}\" alt=\"${query}\"${after}>`;\n }\n );\n}\n\n/** Enrich a section's images (Pexels → DALL-E → placeholder fallback). Mutates section.html in place. */\nexport async function enrichSectionImages(\n section: Section3,\n opts: {\n pexelsApiKey?: string;\n openaiApiKey?: string;\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n onImageUpdate?: (sectionId: string, html: string) => void;\n }\n): Promise<void> {\n const slots = findImageSlots(section.html);\n if (slots.length === 0) return;\n const results = await Promise.allSettled(\n slots.map(async (slot) => {\n let url: string | null = null;\n if (opts.pexelsApiKey) {\n const img = await searchImage(slot.query, opts.pexelsApiKey).catch(() => null);\n url = img?.url || null;\n }\n if (!url && opts.openaiApiKey) {\n try {\n const tempUrl = await generateImage(slot.query, opts.openaiApiKey);\n url = opts.persistImage ? await opts.persistImage(tempUrl, slot.query) : tempUrl;\n } catch (e) {\n console.warn(`[dalle] failed for \"${slot.query}\":`, e);\n }\n }\n url ??= `https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(slot.query.slice(0, 30))}`;\n return { slot, url };\n })\n );\n let html = section.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n const { slot, url } = r.value;\n const replacement = slot.replaceStr.replace(\"{url}\", url);\n html = html.replaceAll(slot.searchStr, replacement);\n }\n }\n if (html !== section.html) {\n section.html = html;\n opts.onImageUpdate?.(section.id, html);\n }\n}\n\n/** Enrich a section's SVG chart placeholders. Mutates section.html in place. */\nexport async function enrichSectionSvgCharts(\n section: Section3,\n opts: {\n anthropicApiKey?: string;\n onImageUpdate?: (sectionId: string, html: string) => void;\n }\n): Promise<void> {\n const svgRegex = /<div\\s[^>]*data-svg-chart=\"([^\"]+)\"[^>]*>[\\s\\S]*?<\\/div>/gi;\n const svgMatches: { fullMatch: string; prompt: string }[] = [];\n let svgM: RegExpExecArray | null;\n while ((svgM = svgRegex.exec(section.html)) !== null) {\n svgMatches.push({ fullMatch: svgM[0], prompt: svgM[1] });\n }\n if (svgMatches.length === 0) return;\n const anthropicKey = opts.anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n const results = await Promise.allSettled(\n svgMatches.map(async ({ fullMatch, prompt }) => {\n try {\n const svg = await generateSvg(prompt, anthropicKey);\n return { fullMatch, svg };\n } catch (e) {\n console.warn(`[svg] failed for \"${prompt}\":`, e);\n return { fullMatch, svg: `<div class=\"w-full h-48 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-sm\">${prompt}</div>` };\n }\n })\n );\n let html = section.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n html = html.replace(r.value.fullMatch, r.value.svg);\n }\n }\n if (html !== section.html) {\n section.html = html;\n opts.onImageUpdate?.(section.id, html);\n }\n}\n\nexport interface StreamGenerateOptions {\n /** Anthropic API key */\n anthropicApiKey?: string;\n /** OpenAI API key */\n openaiApiKey?: string;\n /** Model ID override or pre-built LanguageModel object */\n model?: string | import(\"ai\").LanguageModel;\n /** System prompt */\n systemPrompt: string;\n /** User message content (text or multimodal parts) */\n userContent: any[];\n /** Pexels API key for image enrichment */\n pexelsApiKey?: string;\n /** Persist DALL-E images to permanent storage */\n persistImage?: (tempUrl: string, query: string) => Promise<string>;\n /** Called when a new section is parsed */\n onSection?: (section: Section3) => void;\n /** Called when a section's images are enriched */\n onImageUpdate?: (sectionId: string, html: string) => void;\n /** Called with raw text buffer for real-time partial streaming */\n onRawChunk?: (buffer: string, completedCount: number) => void;\n /** Called when generation is complete */\n onDone?: (sections: Section3[]) => void;\n /** Called on error */\n onError?: (error: Error) => void;\n}\n\n/**\n * Core streaming generation: stream AI text → parse NDJSON → emit sections → enrich images.\n * Used by both generateLanding and generateDocument.\n */\nexport async function streamGenerate(options: StreamGenerateOptions): Promise<Section3[]> {\n const {\n anthropicApiKey,\n openaiApiKey: _openaiApiKey,\n model: modelId,\n systemPrompt,\n userContent,\n pexelsApiKey,\n persistImage,\n onSection,\n onImageUpdate,\n onRawChunk,\n onDone,\n onError,\n } = options;\n\n const openaiApiKey = _openaiApiKey || process.env.OPENAI_API_KEY;\n const model = await resolveModel({\n openaiApiKey,\n anthropicApiKey,\n modelId,\n defaultOpenai: \"gpt-4o\",\n defaultAnthropic: \"claude-sonnet-4-6\",\n });\n\n const result = streamText({\n model,\n system: systemPrompt,\n messages: [{ role: \"user\", content: userContent }],\n });\n\n const allSections: Section3[] = [];\n const imagePromises: Promise<void>[] = [];\n let sectionOrder = 0;\n let buffer = \"\";\n\n function enrichSvgCharts(sectionRef: Section3) {\n const svgRegex = /<div\\s[^>]*data-svg-chart=\"([^\"]+)\"[^>]*>[\\s\\S]*?<\\/div>/gi;\n const svgMatches: { fullMatch: string; prompt: string }[] = [];\n let svgM: RegExpExecArray | null;\n while ((svgM = svgRegex.exec(sectionRef.html)) !== null) {\n svgMatches.push({ fullMatch: svgM[0], prompt: svgM[1] });\n }\n if (svgMatches.length === 0) return;\n\n const anthropicKey = anthropicApiKey || process.env.ANTHROPIC_API_KEY;\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n svgMatches.map(async ({ fullMatch, prompt }) => {\n try {\n const svg = await generateSvg(prompt, anthropicKey);\n return { fullMatch, svg };\n } catch (e) {\n console.warn(`[svg] failed for \"${prompt}\":`, e);\n return { fullMatch, svg: `<div class=\"w-full h-48 bg-gray-100 rounded-lg flex items-center justify-center text-gray-400 text-sm\">${prompt}</div>` };\n }\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n html = html.replace(r.value.fullMatch, r.value.svg);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function enrichSection(sectionRef: Section3) {\n const slots = findImageSlots(sectionRef.html);\n if (slots.length === 0) return;\n const slotsSnapshot = slots.map((s) => ({ ...s }));\n imagePromises.push(\n (async () => {\n const results = await Promise.allSettled(\n slotsSnapshot.map(async (slot) => {\n let url: string | null = null;\n // 1. Pexels first (free, fast)\n if (pexelsApiKey) {\n const img = await searchImage(slot.query, pexelsApiKey).catch(() => null);\n url = img?.url || null;\n }\n // 2. DALL-E fallback\n if (!url && openaiApiKey) {\n try {\n const tempUrl = await generateImage(slot.query, openaiApiKey);\n url = persistImage ? await persistImage(tempUrl, slot.query) : tempUrl;\n } catch (e) {\n console.warn(`[dalle] failed for \"${slot.query}\":`, e);\n }\n }\n url ??= `https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(slot.query.slice(0, 30))}`;\n return { slot, url };\n })\n );\n let html = sectionRef.html;\n for (const r of results) {\n if (r.status === \"fulfilled\" && r.value) {\n const { slot, url } = r.value;\n const replacement = slot.replaceStr.replace(\"{url}\", url);\n html = html.replaceAll(slot.searchStr, replacement);\n }\n }\n if (html !== sectionRef.html) {\n sectionRef.html = html;\n onImageUpdate?.(sectionRef.id, html);\n }\n })()\n );\n }\n\n function processObject(obj: any) {\n if (!obj.html || !obj.label) return;\n const section: Section3 = {\n id: nanoid(8),\n order: sectionOrder++,\n html: sanitizeSemanticColors(addSvgLoadingPlaceholders(addLoadingPlaceholders(obj.html))),\n label: obj.label,\n };\n allSections.push(section);\n onSection?.(section);\n enrichSection(section);\n enrichSvgCharts(section);\n }\n\n try {\n let chunkCount = 0;\n for await (const chunk of result.textStream) {\n buffer += chunk;\n chunkCount++;\n\n const [objects, remaining] = extractJsonObjects(buffer);\n buffer = remaining;\n for (const obj of objects) {\n chunkCount = 0;\n processObject(obj);\n }\n\n if (onRawChunk && chunkCount % 5 === 0 && buffer.length > 20) {\n onRawChunk(buffer, allSections.length);\n }\n }\n\n // Parse remaining buffer\n if (buffer.trim()) {\n let cleaned = buffer.trim();\n if (cleaned.startsWith(\"```\")) {\n cleaned = cleaned.replace(/^```(?:json)?\\s*/, \"\").replace(/\\s*```$/, \"\");\n }\n const [lastObjects] = extractJsonObjects(cleaned);\n for (const obj of lastObjects) processObject(obj);\n }\n\n // Wait for image enrichment\n await Promise.allSettled(imagePromises);\n\n // Final fallback for images without src\n for (const section of allSections) {\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 section.html = section.html.replace(\n /data-image-query=\"([^\"]+)\"/g,\n (_match, query) => {\n return `src=\"https://placehold.co/800x500/1f2937/9ca3af?text=${encodeURIComponent(query.slice(0, 30))}\" data-enriched=\"placeholder\"`;\n }\n );\n if (section.html !== before) {\n onImageUpdate?.(section.id, section.html);\n }\n }\n\n onDone?.(allSections);\n return allSections;\n } catch (err: any) {\n const error = err instanceof Error ? err : new Error(err?.message || \"Generation failed\");\n onError?.(error);\n throw error;\n }\n}\n"],"mappings":"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","names":["openaiKey","anthropicKey"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/themes.ts","../src/iframeScript.ts","../src/buildHtml.ts"],"sourcesContent":["export interface LandingTheme {\n id: string;\n label: string;\n colors: {\n primary: string;\n \"primary-light\": string;\n \"primary-dark\": string;\n secondary: string;\n accent: string;\n surface: string;\n \"surface-alt\": string;\n \"on-surface\": string;\n \"on-surface-muted\": string;\n \"on-primary\": string;\n };\n}\n\nexport const LANDING_THEMES: LandingTheme[] = [\n {\n id: \"minimal\",\n label: \"Minimal\",\n colors: {\n primary: \"#18181b\",\n \"primary-light\": \"#3f3f46\",\n \"primary-dark\": \"#09090b\",\n secondary: \"#71717a\",\n accent: \"#2563eb\",\n surface: \"#ffffff\",\n \"surface-alt\": \"#f4f4f5\",\n \"on-surface\": \"#18181b\",\n \"on-surface-muted\": \"#71717a\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"calido\",\n label: \"Cálido\",\n colors: {\n primary: \"#9a3412\",\n \"primary-light\": \"#c2410c\",\n \"primary-dark\": \"#7c2d12\",\n secondary: \"#78716c\",\n accent: \"#d97706\",\n surface: \"#fafaf9\",\n \"surface-alt\": \"#f5f5f4\",\n \"on-surface\": \"#1c1917\",\n \"on-surface-muted\": \"#78716c\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"oceano\",\n label: \"Océano\",\n colors: {\n primary: \"#0f4c75\",\n \"primary-light\": \"#1a6fa0\",\n \"primary-dark\": \"#0a3555\",\n secondary: \"#6b7280\",\n accent: \"#0d9488\",\n surface: \"#ffffff\",\n \"surface-alt\": \"#f0fdfa\",\n \"on-surface\": \"#0f172a\",\n \"on-surface-muted\": \"#64748b\",\n \"on-primary\": \"#ffffff\",\n },\n },\n {\n id: \"noche\",\n label: \"Noche\",\n colors: {\n primary: \"#a78bfa\",\n \"primary-light\": \"#c4b5fd\",\n \"primary-dark\": \"#8b5cf6\",\n secondary: \"#9ca3af\",\n accent: \"#a78bfa\",\n surface: \"#111827\",\n \"surface-alt\": \"#1f2937\",\n \"on-surface\": \"#f9fafb\",\n \"on-surface-muted\": \"#9ca3af\",\n \"on-primary\": \"#111827\",\n },\n },\n {\n id: \"bosque\",\n label: \"Bosque\",\n colors: {\n primary: \"#16a34a\",\n \"primary-light\": \"#22c55e\",\n \"primary-dark\": \"#15803d\",\n secondary: \"#a3a3a3\",\n accent: \"#16a34a\",\n surface: \"#0a0a0a\",\n \"surface-alt\": \"#171717\",\n \"on-surface\": \"#fafafa\",\n \"on-surface-muted\": \"#a3a3a3\",\n \"on-primary\": \"#000000\",\n },\n },\n {\n id: \"rosa\",\n label: \"Rosa\",\n colors: {\n primary: \"#be185d\",\n \"primary-light\": \"#db2777\",\n \"primary-dark\": \"#9d174d\",\n secondary: \"#6b7280\",\n accent: \"#be185d\",\n surface: \"#ffffff\",\n \"surface-alt\": \"#fdf2f8\",\n \"on-surface\": \"#1f2937\",\n \"on-surface-muted\": \"#6b7280\",\n \"on-primary\": \"#ffffff\",\n },\n },\n];\n\nexport interface CustomColors {\n primary: string;\n secondary?: string;\n accent?: string;\n surface?: string;\n}\n\nfunction parseHex(hex: string) {\n return {\n r: parseInt(hex.slice(1, 3), 16),\n g: parseInt(hex.slice(3, 5), 16),\n b: parseInt(hex.slice(5, 7), 16),\n };\n}\n\nfunction toHex(r: number, g: number, b: number) {\n return `#${[r, g, b].map((c) => Math.max(0, Math.min(255, c)).toString(16).padStart(2, \"0\")).join(\"\")}`;\n}\n\nfunction luminance(hex: string) {\n const { r, g, b } = parseHex(hex);\n return (0.299 * r + 0.587 * g + 0.114 * b) / 255;\n}\n\nfunction lighten(hex: string, amount = 40) {\n const { r, g, b } = parseHex(hex);\n return toHex(r + amount, g + amount, b + amount);\n}\n\nfunction darken(hex: string, amount = 40) {\n const { r, g, b } = parseHex(hex);\n return toHex(r - amount, g - amount, b - amount);\n}\n\nexport function buildCustomTheme(colors: CustomColors): LandingTheme {\n const { primary, secondary = \"#f59e0b\", accent = \"#06b6d4\", surface = \"#ffffff\" } = colors;\n\n const onPrimary = luminance(primary) > 0.5 ? \"#111827\" : \"#ffffff\";\n const surfaceLum = luminance(surface);\n const isDarkSurface = surfaceLum < 0.5;\n\n return {\n id: \"custom\",\n label: \"Custom\",\n colors: {\n primary,\n \"primary-light\": lighten(primary),\n \"primary-dark\": darken(primary),\n secondary,\n accent,\n surface,\n \"surface-alt\": isDarkSurface ? lighten(surface, 20) : darken(surface, 5),\n \"on-surface\": isDarkSurface ? \"#f1f5f9\" : \"#111827\",\n \"on-surface-muted\": isDarkSurface ? \"#94a3b8\" : \"#6b7280\",\n \"on-primary\": onPrimary,\n },\n };\n}\n\nexport function buildCustomThemeCss(colors: CustomColors): string {\n const theme = buildCustomTheme(colors);\n return `[data-theme=\"custom\"] {\\n${buildCssVars(theme.colors)}\\n}`;\n}\n\n/** CSS custom properties for a theme */\nfunction buildCssVars(colors: LandingTheme[\"colors\"]): string {\n return Object.entries(colors)\n .map(([k, v]) => ` --color-${k}: ${v};`)\n .join(\"\\n\");\n}\n\n/** Build the tailwind.config JS object string for TW v3 CDN */\nfunction buildTailwindConfig(): string {\n const colorEntries = Object.keys(LANDING_THEMES[0].colors)\n .map((k) => ` '${k}': 'var(--color-${k})'`)\n .join(\",\\n\");\n\n return `{\n theme: {\n extend: {\n colors: {\n${colorEntries}\n }\n }\n }\n }`;\n}\n\nexport function buildThemeCss(): { css: string; tailwindConfig: string } {\n const defaultTheme = LANDING_THEMES[0];\n\n const overrides = LANDING_THEMES.slice(1)\n .map((t) => `[data-theme=\"${t.id}\"] {\\n${buildCssVars(t.colors)}\\n}`)\n .join(\"\\n\\n\");\n\n const css = `:root {\\n${buildCssVars(defaultTheme.colors)}\\n}\\n\\n${overrides}`;\n return { css, tailwindConfig: buildTailwindConfig() };\n}\n\nexport function buildSingleThemeCss(themeId: string): { css: string; tailwindConfig: string } {\n const theme = LANDING_THEMES.find((t) => t.id === themeId) || LANDING_THEMES[0];\n const css = `:root {\\n${buildCssVars(theme.colors)}\\n}`;\n return { css, tailwindConfig: buildTailwindConfig() };\n}\n","/**\n * JavaScript injected into the landing v3 iframe.\n * Handles hover highlights, click selection, contentEditable text editing,\n * postMessage communication with the parent editor,\n * and incremental section injection from parent.\n */\nexport function getIframeScript(): string {\n return `\n(function() {\n let hoveredEl = null;\n let selectedEl = null;\n const SHADOW_HOVER = 'inset 0 0 0 2px #3B82F6';\n const SHADOW_SELECTED = 'inset 0 0 0 2px #8B5CF6';\n\n function getSectionId(el) {\n let node = el;\n while (node && node !== document.body) {\n if (node.dataset && node.dataset.sectionId) {\n return node.dataset.sectionId;\n }\n node = node.parentElement;\n }\n return null;\n }\n\n function getSectionElement(sectionId) {\n return document.querySelector('[data-section-id=\"' + sectionId + '\"]');\n }\n\n function getElementPath(el) {\n const parts = [];\n let node = el;\n while (node && node !== document.body) {\n let tag = node.tagName.toLowerCase();\n if (node.id) { tag += '#' + node.id; }\n const siblings = node.parentElement ? Array.from(node.parentElement.children).filter(function(c) { return c.tagName === node.tagName; }) : [];\n if (siblings.length > 1) { tag += ':nth(' + siblings.indexOf(node) + ')'; }\n parts.unshift(tag);\n node = node.parentElement;\n }\n return parts.join(' > ');\n }\n\n function getCleanSectionHtml(sectionEl) {\n var els = sectionEl.querySelectorAll('*');\n var saved = [];\n for (var i = 0; i < els.length; i++) {\n var s = els[i].style;\n saved.push({ boxShadow: s.boxShadow, ce: els[i].contentEditable });\n s.boxShadow = '';\n if (els[i].contentEditable === 'true') els[i].removeAttribute('contenteditable');\n }\n // Also clean the section root\n var rootShadow = sectionEl.style.boxShadow;\n sectionEl.style.boxShadow = '';\n var html = sectionEl.innerHTML;\n sectionEl.style.boxShadow = rootShadow;\n for (var i = 0; i < els.length; i++) {\n els[i].style.boxShadow = saved[i].boxShadow;\n if (saved[i].ce === 'true') els[i].contentEditable = 'true';\n }\n return html;\n }\n\n function isTextElement(el) {\n var textTags = ['H1','H2','H3','H4','H5','H6','P','SPAN','LI','A','BLOCKQUOTE','LABEL','TD','TH','FIGCAPTION','BUTTON'];\n return textTags.indexOf(el.tagName) !== -1;\n }\n\n function emitElementSelected(el) {\n var rect = el.getBoundingClientRect();\n var attrs = {};\n if (el.tagName === 'IMG') attrs = { src: el.getAttribute('src') || '', alt: el.getAttribute('alt') || '' };\n if (el.tagName === 'A') attrs = { href: el.getAttribute('href') || '', target: el.getAttribute('target') || '' };\n var ot = el.outerHTML.split('>')[0] + '>';\n if (ot.length > 200) ot = ot.substring(0, 200);\n window.parent.postMessage({\n type: 'element-selected',\n sectionId: getSectionId(el),\n tagName: el.tagName,\n rect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n text: (el.textContent || '').substring(0, 200),\n openTag: ot,\n elementPath: getElementPath(el),\n isSectionRoot: el.dataset && el.dataset.sectionId ? true : false,\n attrs: attrs,\n className: (typeof el.className === 'string' ? el.className : '') || '',\n }, '*');\n }\n\n // Hover\n document.addEventListener('mouseover', function(e) {\n var el = e.target;\n while (el && el !== document.body && (el instanceof SVGElement) && el.tagName !== 'svg') {\n el = el.parentElement;\n }\n if (el && el.tagName === 'svg' && el.parentElement) el = el.parentElement;\n if (el === document.body || el === document.documentElement) return;\n if (el === selectedEl) return;\n if (hoveredEl && hoveredEl !== selectedEl) {\n hoveredEl.style.boxShadow = '';\n }\n hoveredEl = el;\n if (el !== selectedEl) {\n el.style.boxShadow = SHADOW_HOVER;\n }\n });\n\n document.addEventListener('mouseout', function(e) {\n if (hoveredEl && hoveredEl !== selectedEl) {\n hoveredEl.style.boxShadow = '';\n }\n hoveredEl = null;\n });\n\n // Click — select element\n document.addEventListener('click', function(e) {\n e.preventDefault();\n e.stopPropagation();\n var el = e.target;\n\n // Bubble up from SVG internals to the nearest HTML element\n while (el && el !== document.body && (el instanceof SVGElement) && el.tagName !== 'svg') {\n el = el.parentElement;\n }\n // If we landed on an <svg>, bubble up to its HTML parent\n if (el && el.tagName === 'svg' && el.parentElement) {\n el = el.parentElement;\n }\n\n // Deselect previous\n if (selectedEl) {\n selectedEl.style.boxShadow = '';\n }\n\n if (selectedEl === el) {\n selectedEl = null;\n window.parent.postMessage({ type: 'element-deselected' }, '*');\n return;\n }\n\n selectedEl = el;\n\n // Clear hover styles BEFORE capturing openTag (so it matches source HTML)\n el.style.boxShadow = '';\n var openTag = el.outerHTML.substring(0, el.outerHTML.indexOf('>') + 1).substring(0, 120);\n\n el.style.boxShadow = SHADOW_SELECTED;\n\n var rect = el.getBoundingClientRect();\n var attrs = {};\n if (el.tagName === 'IMG') {\n attrs = { src: el.getAttribute('src') || '', alt: el.getAttribute('alt') || '' };\n }\n if (el.tagName === 'A') {\n attrs = { href: el.getAttribute('href') || '', target: el.getAttribute('target') || '' };\n }\n\n window.parent.postMessage({\n type: 'element-selected',\n sectionId: getSectionId(el),\n tagName: el.tagName,\n rect: { top: rect.top, left: rect.left, width: rect.width, height: rect.height },\n text: (el.textContent || '').substring(0, 200),\n openTag: openTag,\n elementPath: getElementPath(el),\n isSectionRoot: el.dataset && el.dataset.sectionId ? true : false,\n attrs: attrs,\n className: el.className || '',\n }, '*');\n }, true);\n\n // Double-click — contentEditable for text\n document.addEventListener('dblclick', function(e) {\n e.preventDefault();\n e.stopPropagation();\n var el = e.target;\n if (!isTextElement(el)) return;\n\n el.contentEditable = 'true';\n el.focus();\n el.style.boxShadow = 'inset 0 0 0 2px #F59E0B';\n\n function onBlur() {\n el.contentEditable = 'false';\n el.style.boxShadow = '';\n el.removeEventListener('blur', onBlur);\n el.removeEventListener('keydown', onKeydown);\n\n var sid = getSectionId(el);\n var sectionEl = sid ? getSectionElement(sid) : null;\n window.parent.postMessage({\n type: 'text-edited',\n sectionId: sid,\n elementPath: getElementPath(el),\n newText: el.innerHTML,\n sectionHtml: sectionEl ? getCleanSectionHtml(sectionEl) : null,\n }, '*');\n\n selectedEl = null;\n }\n\n function onKeydown(ev) {\n if (ev.key === 'Escape') {\n el.blur();\n }\n }\n\n el.addEventListener('blur', onBlur);\n el.addEventListener('keydown', onKeydown);\n }, true);\n\n // Listen for messages FROM parent (incremental section injection)\n window.addEventListener('message', function(e) {\n var msg = e.data;\n if (!msg || !msg.action) return;\n\n if (msg.action === 'add-section') {\n var wrapper = document.createElement('div');\n wrapper.setAttribute('data-section-id', msg.id);\n wrapper.innerHTML = msg.html;\n wrapper.style.animation = 'fadeInUp 0.4s ease-out';\n document.body.appendChild(wrapper);\n if (msg.scroll) {\n wrapper.scrollIntoView({ behavior: 'smooth', block: 'end' });\n }\n }\n\n if (msg.action === 'update-section') {\n var el = getSectionElement(msg.id);\n if (el && typeof window.morphdom === 'function') {\n var tmp = document.createElement('div');\n tmp.innerHTML = msg.html;\n window.morphdom(el, tmp, {\n childrenOnly: true,\n onBeforeElUpdated: function(fromEl, toEl) {\n if (fromEl.isEqualNode(toEl)) return false;\n return true;\n }\n });\n } else if (el) {\n el.innerHTML = msg.html;\n }\n }\n\n if (msg.action === 'rename-section') {\n var el = getSectionElement(msg.oldId);\n if (el) {\n el.setAttribute('data-section-id', msg.newId);\n if (msg.html) {\n if (typeof window.morphdom === 'function') {\n var tmp = document.createElement('div');\n tmp.innerHTML = msg.html;\n window.morphdom(el, tmp, { childrenOnly: true, onBeforeElUpdated: function(f,t){ return !f.isEqualNode(t); } });\n } else {\n el.innerHTML = msg.html;\n }\n }\n }\n }\n\n if (msg.action === 'remove-section') {\n var el = getSectionElement(msg.id);\n if (el) { el.remove(); }\n }\n\n if (msg.action === 'reorder-sections') {\n // msg.order = [id1, id2, id3, ...]\n var order = msg.order;\n for (var i = 0; i < order.length; i++) {\n var el = getSectionElement(order[i]);\n if (el) { document.body.appendChild(el); }\n }\n }\n\n if (msg.action === 'update-attribute') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl) {\n var target = null;\n if (msg.elementPath) {\n // Find element by matching path\n var allEls = sectionEl.querySelectorAll(msg.tagName || '*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n target = allEls[i];\n break;\n }\n }\n }\n if (target) {\n if (msg.attr === 'style' && msg.value.indexOf(':') !== -1) {\n // Merge style property instead of replacing entire style\n var parts = msg.value.split(':');\n var prop = parts[0].trim();\n var val = parts.slice(1).join(':').trim();\n target.style.setProperty(prop, val);\n } else {\n target.setAttribute(msg.attr, msg.value);\n }\n window.parent.postMessage({\n type: 'section-html-updated',\n sectionId: msg.sectionId,\n sectionHtml: getCleanSectionHtml(sectionEl),\n }, '*');\n emitElementSelected(target);\n }\n }\n }\n\n if (msg.action === 'replace-class') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl && msg.elementPath) {\n var target = null;\n var allEls = sectionEl.querySelectorAll('*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n target = allEls[i];\n break;\n }\n }\n if (target) {\n // Remove classes matching prefixes (supports responsive variants like md:p-4)\n var prefixes = msg.removePrefixes || [];\n var toRemove = [];\n for (var ci = 0; ci < target.classList.length; ci++) {\n var cls = target.classList[ci];\n var bare = cls.indexOf(':') !== -1 ? cls.substring(cls.lastIndexOf(':') + 1) : cls;\n for (var pi = 0; pi < prefixes.length; pi++) {\n if (bare === prefixes[pi] || bare.indexOf(prefixes[pi]) === 0) {\n toRemove.push(cls);\n break;\n }\n }\n }\n for (var ri = 0; ri < toRemove.length; ri++) {\n target.classList.remove(toRemove[ri]);\n }\n // Add new class\n if (msg.addClass && !target.classList.contains(msg.addClass)) {\n target.classList.add(msg.addClass);\n }\n window.parent.postMessage({\n type: 'section-html-updated',\n sectionId: msg.sectionId,\n sectionHtml: getCleanSectionHtml(sectionEl),\n }, '*');\n emitElementSelected(target);\n }\n }\n }\n\n if (msg.action === 'delete-element') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl && msg.elementPath) {\n var target = null;\n var allEls = sectionEl.querySelectorAll('*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n target = allEls[i];\n break;\n }\n }\n if (target && target.parentNode) {\n target.parentNode.removeChild(target);\n window.parent.postMessage({\n type: 'section-html-updated',\n sectionId: msg.sectionId,\n sectionHtml: getCleanSectionHtml(sectionEl),\n }, '*');\n window.parent.postMessage({ type: 'element-deselected' }, '*');\n }\n }\n }\n\n if (msg.action === 'change-tag') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl && msg.elementPath && msg.newTag) {\n var target = null;\n var allEls = sectionEl.querySelectorAll('*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n target = allEls[i];\n break;\n }\n }\n if (target && target.parentNode) {\n var newEl = document.createElement(msg.newTag);\n for (var a = 0; a < target.attributes.length; a++) {\n newEl.setAttribute(target.attributes[a].name, target.attributes[a].value);\n }\n while (target.firstChild) newEl.appendChild(target.firstChild);\n target.parentNode.replaceChild(newEl, target);\n window.parent.postMessage({\n type: 'section-html-updated',\n sectionId: msg.sectionId,\n sectionHtml: getCleanSectionHtml(sectionEl),\n }, '*');\n emitElementSelected(newEl);\n }\n }\n }\n\n if (msg.action === 'set-theme') {\n if (msg.theme && msg.theme !== 'default') {\n document.documentElement.setAttribute('data-theme', msg.theme);\n } else {\n document.documentElement.removeAttribute('data-theme');\n }\n }\n\n if (msg.action === 'set-custom-css') {\n var customStyle = document.getElementById('custom-theme-css');\n if (!customStyle) {\n customStyle = document.createElement('style');\n customStyle.id = 'custom-theme-css';\n document.head.appendChild(customStyle);\n }\n customStyle.textContent = msg.css || '';\n }\n\n if (msg.action === 'scroll-to-section') {\n var el = getSectionElement(msg.id);\n if (el) { el.scrollIntoView({ behavior: 'smooth', block: 'start' }); }\n }\n\n if (msg.action === 'get-scroll') {\n window.parent.postMessage({ type: 'scroll-position', y: window.scrollY }, '*');\n }\n\n if (msg.action === 'restore-scroll') {\n window.scrollTo(0, msg.y);\n }\n\n if (msg.action === 'element-loading') {\n var sectionEl = getSectionElement(msg.sectionId);\n if (sectionEl && msg.elementPath) {\n var allEls = sectionEl.querySelectorAll('*');\n for (var i = 0; i < allEls.length; i++) {\n if (getElementPath(allEls[i]) === msg.elementPath) {\n var el = allEls[i];\n el.style.position = 'relative';\n el.style.overflow = 'hidden';\n var overlay = document.createElement('div');\n overlay.setAttribute('data-loading-overlay', 'true');\n overlay.style.cssText = 'position:absolute;inset:0;z-index:999;border-radius:inherit;background:linear-gradient(90deg,rgba(255,255,255,0) 0%,rgba(255,255,255,0.6) 50%,rgba(255,255,255,0) 100%);background-size:200% 100%;animation:shimmer 1.5s infinite;pointer-events:none;';\n el.appendChild(overlay);\n break;\n }\n }\n }\n }\n\n if (msg.action === 'element-loading-clear') {\n var overlays = document.querySelectorAll('[data-loading-overlay]');\n for (var i = 0; i < overlays.length; i++) {\n overlays[i].remove();\n }\n }\n\n if (msg.action === 'full-rewrite') {\n // Fallback: rewrite everything\n document.body.innerHTML = msg.html;\n }\n });\n\n // Forward Cmd/Ctrl+Z undo/redo to parent (iframe captures keyboard focus)\n document.addEventListener('keydown', function(e) {\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'z') {\n e.preventDefault();\n window.parent.postMessage({ type: e.shiftKey ? 'redo' : 'undo' }, '*');\n }\n if ((e.metaKey || e.ctrlKey) && e.key.toLowerCase() === 'y') {\n e.preventDefault();\n window.parent.postMessage({ type: 'redo' }, '*');\n }\n });\n\n // Image loading placeholders\n function setupImagePlaceholder(img) {\n if (img.complete && img.naturalWidth > 0) return;\n img.style.background = 'linear-gradient(90deg, #e5e7eb 25%, #f3f4f6 50%, #e5e7eb 75%)';\n img.style.backgroundSize = '200% 100%';\n img.style.animation = 'shimmer 1.5s infinite';\n if (!img.style.minHeight && !img.getAttribute('height')) img.style.minHeight = '120px';\n function onDone() {\n img.style.background = '';\n img.style.backgroundSize = '';\n img.style.animation = '';\n if (img.style.minHeight === '120px') img.style.minHeight = '';\n img.removeEventListener('load', onDone);\n img.removeEventListener('error', onDone);\n }\n img.addEventListener('load', onDone);\n img.addEventListener('error', onDone);\n }\n // Observe new/changed images\n var imgObserver = new MutationObserver(function(mutations) {\n for (var m = 0; m < mutations.length; m++) {\n // New nodes\n for (var n = 0; n < mutations[m].addedNodes.length; n++) {\n var node = mutations[m].addedNodes[n];\n if (node.tagName === 'IMG') setupImagePlaceholder(node);\n if (node.querySelectorAll) {\n var imgs = node.querySelectorAll('img');\n for (var i = 0; i < imgs.length; i++) setupImagePlaceholder(imgs[i]);\n }\n }\n // src attribute changed\n if (mutations[m].type === 'attributes' && mutations[m].attributeName === 'src' && mutations[m].target.tagName === 'IMG') {\n setupImagePlaceholder(mutations[m].target);\n }\n }\n });\n imgObserver.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['src'] });\n // Initial images\n var existingImgs = document.querySelectorAll('img');\n for (var ii = 0; ii < existingImgs.length; ii++) setupImagePlaceholder(existingImgs[ii]);\n\n // Inject animation keyframes\n var style = document.createElement('style');\n style.textContent = '@keyframes fadeInUp { from { opacity:0; transform:translateY(20px); } to { opacity:1; transform:translateY(0); } } @keyframes shimmer { 0% { background-position: -200% 0; } 100% { background-position: 200% 0; } }';\n document.head.appendChild(style);\n\n // Notify parent we're ready\n window.parent.postMessage({ type: 'ready' }, '*');\n})();\n`;\n}\n","import type { Section3 } from \"./types\";\nimport { getIframeScript } from \"./iframeScript\";\nimport { buildThemeCss, buildSingleThemeCss, buildCustomTheme, type CustomColors } from \"./themes\";\n\n/**\n * Build the full HTML for the iframe preview (with editing script).\n */\nexport function buildPreviewHtml(sections: Section3[], theme?: string): string {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const body = sorted\n .map((s) => `<div data-section-id=\"${s.id}\">${s.html}</div>`)\n .join(\"\\n\");\n\n const dataTheme = theme && theme !== \"default\" ? ` data-theme=\"${theme}\"` : \"\";\n const { css, tailwindConfig } = buildThemeCss();\n\n return `<!DOCTYPE html>\n<html lang=\"es\"${dataTheme}>\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n<script src=\"https://unpkg.com/morphdom@2.7.4/dist/morphdom-umd.min.js\"></script>\n<script>tailwind.config = ${tailwindConfig}</script>\n<style>\n${css}\n*{margin:0;padding:0;box-sizing:border-box}\nhtml{scroll-behavior:smooth}\nbody{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}\nimg{max-width:100%}\nsection{width:100%}\nsection>*{max-width:80rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}\n[contenteditable=\"true\"]{cursor:text}\n</style>\n</head>\n<body class=\"bg-surface text-on-surface\">\n${body}\n<script>${getIframeScript()}</script>\n</body>\n</html>`;\n}\n\n/**\n * Build the deploy HTML (no editing script, clean output).\n */\n/**\n * Remove editor artifacts (outline, outlineOffset, contenteditable) from HTML before deploy.\n */\nfunction stripEditorArtifacts(html: string): string {\n return html\n .replace(/\\s*outline:\\s*[^;\"]+;?/gi, \"\")\n .replace(/\\s*outline-offset:\\s*[^;\"]+;?/gi, \"\")\n .replace(/\\s*style=\"\\s*\"/gi, \"\")\n .replace(/\\s+contenteditable=\"[^\"]*\"/gi, \"\")\n .replace(/\\s+data-section-id=\"[^\"]*\"/gi, \"\")\n}\n\nexport function buildDeployHtml(sections: Section3[], theme?: string, customColors?: CustomColors, showBranding = true): string {\n const sorted = [...sections].sort((a, b) => a.order - b.order);\n const body = sorted.map((s) => stripEditorArtifacts(s.html)).join(\"\\n\");\n\n const isCustom = theme === \"custom\" && customColors;\n const dataTheme = theme && theme !== \"default\" && !isCustom ? ` data-theme=\"${theme}\"` : \"\";\n\n // For custom theme, build CSS from the custom colors directly (no data-theme needed, inject as :root)\n const { css: baseCss, tailwindConfig } = isCustom\n ? (() => {\n const ct = buildCustomTheme(customColors);\n const vars = Object.entries(ct.colors).map(([k, v]) => ` --color-${k}: ${v};`).join(\"\\n\");\n return { css: `:root {\\n${vars}\\n}`, tailwindConfig: buildSingleThemeCss(\"default\").tailwindConfig };\n })()\n : buildSingleThemeCss(theme || \"default\");\n\n return `<!DOCTYPE html>\n<html lang=\"es\"${dataTheme}>\n<head>\n<meta charset=\"UTF-8\"/>\n<meta name=\"viewport\" content=\"width=device-width,initial-scale=1\"/>\n<title>Landing Page</title>\n<script src=\"https://cdn.tailwindcss.com\"></script>\n<script>tailwind.config = ${tailwindConfig}</script>\n<style>\n${baseCss}\n*{margin:0;padding:0;box-sizing:border-box}\nhtml{scroll-behavior:smooth}\nbody{font-family:system-ui,-apple-system,sans-serif;background-color:var(--color-surface);color:var(--color-on-surface)}\nsection{width:100%}\nsection>*{max-width:80rem;margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}\n</style>\n</head>\n<body class=\"bg-surface text-on-surface\">\n${body}\n${showBranding ? `<div style=\"text-align:center;padding:16px 0 12px;font-size:12px\">\n <a href=\"https://www.easybits.cloud\" target=\"_blank\" rel=\"noopener\"\n style=\"color:#9ca3af;text-decoration:none\">\n Powered by easybits.cloud\n </a>\n</div>` : \"\"}\n</body>\n</html>`;\n}\n"],"mappings":";AAiBO,IAAM,iBAAiC;AAAA,EAC5C;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AAAA,EACA;AAAA,IACE,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN,SAAS;AAAA,MACT,iBAAiB;AAAA,MACjB,gBAAgB;AAAA,MAChB,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,SAAS;AAAA,MACT,eAAe;AAAA,MACf,cAAc;AAAA,MACd,oBAAoB;AAAA,MACpB,cAAc;AAAA,IAChB;AAAA,EACF;AACF;AASA,SAAS,SAAS,KAAa;AAC7B,SAAO;AAAA,IACL,GAAG,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC/B,GAAG,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,IAC/B,GAAG,SAAS,IAAI,MAAM,GAAG,CAAC,GAAG,EAAE;AAAA,EACjC;AACF;AAEA,SAAS,MAAM,GAAW,GAAW,GAAW;AAC9C,SAAO,IAAI,CAAC,GAAG,GAAG,CAAC,EAAE,IAAI,CAAC,MAAM,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,CAAC,CAAC,EAAE,SAAS,EAAE,EAAE,SAAS,GAAG,GAAG,CAAC,EAAE,KAAK,EAAE,CAAC;AACvG;AAEA,SAAS,UAAU,KAAa;AAC9B,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,GAAG;AAChC,UAAQ,QAAQ,IAAI,QAAQ,IAAI,QAAQ,KAAK;AAC/C;AAEA,SAAS,QAAQ,KAAa,SAAS,IAAI;AACzC,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,GAAG;AAChC,SAAO,MAAM,IAAI,QAAQ,IAAI,QAAQ,IAAI,MAAM;AACjD;AAEA,SAAS,OAAO,KAAa,SAAS,IAAI;AACxC,QAAM,EAAE,GAAG,GAAG,EAAE,IAAI,SAAS,GAAG;AAChC,SAAO,MAAM,IAAI,QAAQ,IAAI,QAAQ,IAAI,MAAM;AACjD;AAEO,SAAS,iBAAiB,QAAoC;AACnE,QAAM,EAAE,SAAS,YAAY,WAAW,SAAS,WAAW,UAAU,UAAU,IAAI;AAEpF,QAAM,YAAY,UAAU,OAAO,IAAI,MAAM,YAAY;AACzD,QAAM,aAAa,UAAU,OAAO;AACpC,QAAM,gBAAgB,aAAa;AAEnC,SAAO;AAAA,IACL,IAAI;AAAA,IACJ,OAAO;AAAA,IACP,QAAQ;AAAA,MACN;AAAA,MACA,iBAAiB,QAAQ,OAAO;AAAA,MAChC,gBAAgB,OAAO,OAAO;AAAA,MAC9B;AAAA,MACA;AAAA,MACA;AAAA,MACA,eAAe,gBAAgB,QAAQ,SAAS,EAAE,IAAI,OAAO,SAAS,CAAC;AAAA,MACvE,cAAc,gBAAgB,YAAY;AAAA,MAC1C,oBAAoB,gBAAgB,YAAY;AAAA,MAChD,cAAc;AAAA,IAChB;AAAA,EACF;AACF;AAEO,SAAS,oBAAoB,QAA8B;AAChE,QAAM,QAAQ,iBAAiB,MAAM;AACrC,SAAO;AAAA,EAA4B,aAAa,MAAM,MAAM,CAAC;AAAA;AAC/D;AAGA,SAAS,aAAa,QAAwC;AAC5D,SAAO,OAAO,QAAQ,MAAM,EACzB,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,EACvC,KAAK,IAAI;AACd;AAGA,SAAS,sBAA8B;AACrC,QAAM,eAAe,OAAO,KAAK,eAAe,CAAC,EAAE,MAAM,EACtD,IAAI,CAAC,MAAM,cAAc,CAAC,mBAAmB,CAAC,IAAI,EAClD,KAAK,KAAK;AAEb,SAAO;AAAA;AAAA;AAAA;AAAA,EAIP,YAAY;AAAA;AAAA;AAAA;AAAA;AAKd;AAEO,SAAS,gBAAyD;AACvE,QAAM,eAAe,eAAe,CAAC;AAErC,QAAM,YAAY,eAAe,MAAM,CAAC,EACrC,IAAI,CAAC,MAAM,gBAAgB,EAAE,EAAE;AAAA,EAAS,aAAa,EAAE,MAAM,CAAC;AAAA,EAAK,EACnE,KAAK,MAAM;AAEd,QAAM,MAAM;AAAA,EAAY,aAAa,aAAa,MAAM,CAAC;AAAA;AAAA;AAAA,EAAU,SAAS;AAC5E,SAAO,EAAE,KAAK,gBAAgB,oBAAoB,EAAE;AACtD;AAEO,SAAS,oBAAoB,SAA0D;AAC5F,QAAM,QAAQ,eAAe,KAAK,CAAC,MAAM,EAAE,OAAO,OAAO,KAAK,eAAe,CAAC;AAC9E,QAAM,MAAM;AAAA,EAAY,aAAa,MAAM,MAAM,CAAC;AAAA;AAClD,SAAO,EAAE,KAAK,gBAAgB,oBAAoB,EAAE;AACtD;;;ACrNO,SAAS,kBAA0B;AACxC,SAAO;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;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;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;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAwgBT;;;ACxgBO,SAAS,iBAAiB,UAAsB,OAAwB;AAC7E,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC7D,QAAM,OAAO,OACV,IAAI,CAAC,MAAM,yBAAyB,EAAE,EAAE,KAAK,EAAE,IAAI,QAAQ,EAC3D,KAAK,IAAI;AAEZ,QAAM,YAAY,SAAS,UAAU,YAAY,gBAAgB,KAAK,MAAM;AAC5E,QAAM,EAAE,KAAK,eAAe,IAAI,cAAc;AAE9C,SAAO;AAAA,iBACQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAME,cAAc;AAAA;AAAA,EAExC,GAAG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAWH,IAAI;AAAA,UACI,gBAAgB,CAAC;AAAA;AAAA;AAG3B;AAQA,SAAS,qBAAqB,MAAsB;AAClD,SAAO,KACJ,QAAQ,4BAA4B,EAAE,EACtC,QAAQ,mCAAmC,EAAE,EAC7C,QAAQ,oBAAoB,EAAE,EAC9B,QAAQ,gCAAgC,EAAE,EAC1C,QAAQ,gCAAgC,EAAE;AAC/C;AAEO,SAAS,gBAAgB,UAAsB,OAAgB,cAA6B,eAAe,MAAc;AAC9H,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAC7D,QAAM,OAAO,OAAO,IAAI,CAAC,MAAM,qBAAqB,EAAE,IAAI,CAAC,EAAE,KAAK,IAAI;AAEtE,QAAM,WAAW,UAAU,YAAY;AACvC,QAAM,YAAY,SAAS,UAAU,aAAa,CAAC,WAAW,gBAAgB,KAAK,MAAM;AAGzF,QAAM,EAAE,KAAK,SAAS,eAAe,IAAI,YACpC,MAAM;AACL,UAAM,KAAK,iBAAiB,YAAY;AACxC,UAAM,OAAO,OAAO,QAAQ,GAAG,MAAM,EAAE,IAAI,CAAC,CAAC,GAAG,CAAC,MAAM,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,KAAK,IAAI;AACzF,WAAO,EAAE,KAAK;AAAA,EAAY,IAAI;AAAA,IAAO,gBAAgB,oBAAoB,SAAS,EAAE,eAAe;AAAA,EACrG,GAAG,IACH,oBAAoB,SAAS,SAAS;AAE1C,SAAO;AAAA,iBACQ,SAAS;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,4BAME,cAAc;AAAA;AAAA,EAExC,OAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASP,IAAI;AAAA,EACJ,eAAe;AAAA;AAAA;AAAA;AAAA;AAAA,UAKP,EAAE;AAAA;AAAA;AAGZ;","names":[]}
|