Files
qwen3-6-lora/data/gate5_holdout_v2b-nvfp4-spec.json
T
aleleba 66d6c6c410 Phase 6.4.27: gate 5 (holdout + full) on v2b-NVFP4, spec config
Holdout (60 design prompts, cheap proxy, no live Penpot needed): 5.0%
forbidden-API rate (3/60) -- right at the <=5% threshold, APPROVES.

Full mode (10 official prompts against live Penpot): 9/10 measured.
g5-08-reparacion-grises is still pending -- Penpot disconnected mid-run
("No Penpot plugin instances are currently connected", the real
disconnect signature, not the transient "transport dropped mid-call").
Per the established GATE5_ONLY pattern, this prompt gets re-measured
(not accepted as a genuine failure) once Penpot reconnects; the other
9 results are preserved as-is.
2026-08-05 01:23:39 +00:00

890 lines
191 KiB
JSON

{
"modo": "holdout",
"tag": "v2b-nvfp4-spec",
"modelo": "qwen3.6-35b-a3b-mcp-v2b-nvfp4",
"total": 60,
"prompts_en_archivo": 60,
"errores_de_request": 0,
"con_api_prohibida": 3,
"tasa": 0.05,
"umbral": 0.05,
"aprueba": true,
"patrones": {
"import_image / importImage / createImage / filePath": 2,
"propiedad .layout inexistente": 1,
"fontSize/fontWeight/lineHeight/letterSpacing numerico": 1
},
"resultados": [
{
"prompt": "Hazme una landing completa de una academia de tango, 1180 px de ancho, con encabezado, hero, tres beneficios, testimonios y pie. Ponele color, nada de cajas grises.",
"id": "pd-001",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n vino:'#5B1A3C', dorado:'#D4A843', crema:'#FDF6EC', tinta:'#1A0F14', gris:'#C8B8A8'\n};\nconst T = {\n h1:{size:56, weight:'bold', color:P.vino},\n h2:{size:34, weight:'bold', color:P.vino},\n body:{size:18, weight:'normal', color:P.tinta},\n small:{size:14, weight:'normal', color:P.dorado}\n};\nconst mk = (n,w,h,c) => { const s = penpot.createShape('rectangle'); s.name = n; s.fills = [{ fillColor: c, fillOpacity: 1 }]; s.resize(w, h); return s; };\nconst addText = (parent, chars, style, size, align) => {\n const t = penpot.createText(chars);\n t.name = style.name;\n t.fontFamily = 'Playfair Display';\n t.fontSize = size;\n t.fontWeight = style.weight;\n t.lineHeight = '1.3';\n t.characters = chars;\n t.growType = 'auto-width';\n t.fills = [{ fillColor: style.color, fillOpacity: 1 }];\n if (align) { t.textAlign = align; }\n parent.insertChild(parent.children.length, t);\n return t;\n};\nconst W = 1180;\nconst pages = penpotUtils.getPages();\nconst page = pages.length ? penpotUtils.getPageById(pages[0].id) || pages[0] : null;\nif (!page) { return { error: 'no page found', pages: pages.map(p => p.id) }; }\nconst boards = [\n { name:'Encabezado', bg:P.vino, h:80 },\n { name:'Hero', bg:P.crema, h:520 },\n { name:'Beneficios', bg:P.crema, h:440 },\n { name:'Testimonios', bg:P.vino, h:360 },\n { name:'CTA', bg:P.dorado, h:200 },\n { name:'Pie', bg:P.tinta, h:120 }\n];\nconst B = boards.map(b => {\n const board = penpot.createBoard();\n board.name = b.name;\n board.fills = [{ fillColor: b.bg, fillOpacity: 1 }];\n board.resize(W, b.h);\n board.addFlexLayout();\n if (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = 80;\n board.flex.verticalPadding = 40;\n board.flex.rowGap = 24;\n board.flex.alignItems = 'start';\n }\n page.root.insertChild(page.root.children.length, board);\n return board;\n});\nB[0].insertChild(0, addText(B[0], 'Academia El Abrazo', T.h2, 22, 'left'));\nB[1].insertChild(0, addText(B[1], 'Tango para todos los niveles', T.h1, 56, 'left'));\nB[1].insertChild(1, addText(B[1], 'Clases presenciales y online, desde el primer paso hasta el milonga.', T.body, 18, 'left'));\nB[1].insertChild(2, addText(B[1], 'Inscribite', T.small, 14, 'left'));\nB[2].insertChild(0, addText(B[2], 'Por que elegirnos', T.h2, 34, 'left'));\nconst cards = [\n { bg: P.crema, chars: 'Clases grupales max. 12 alumnos', style: T.body },\n { bg: P.crema, chars: 'Profesores con 15+ anos de experiencia', style: T.body },\n { bg: P.crema, chars: 'Recital de fin de curso incluido', style: T.body }\n];\ncards.forEach(c => {\n const card = mk('Card', 320, 200, c.bg);\n card.strokes = [{ strokeColor: P.gris, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.borderRadius = 12;\n card.insertChild(0, addText(card, c.chars, c.style, 18, 'left'));\n B[2].insertChild(2, card);\n});\nB[3].insertChild(0, addText(B[3], 'Lo que dicen nuestros alumnos', T.h2, 34, 'left'));\nB[3].insertChild(1, addText(B[3], '\"Aprendi a bailar en tres meses. El ambiente es muy cálido.\"', T.body, 18, 'left'));\nB[4].insertChild(0, addText(B[4], 'Empezá hoy', T.h1, 40, 'left'));\nB[4].insertChild(1, addText(B[4], 'Primera clase gratis. Sin compromiso.', T.body, 18, 'left'));\nB[5].insertChild(0, addText(B[5], '© 2025 Academia El Abrazo — Buenos Aires', T.small, 14, 'left'));\nconst fills = B.map(b => b.fills?.[0]?.fillColor);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', page.root);\nconst sizes = texts.map(t => t.fontSize);\nreturn { pageId: page.id, boardCount: B.length, boardNames: B.map(b => b.name), fillsUnique: new Set(fills).size, texts: texts.length, fontSizes: [...new Set(sizes)].length, flexBoards: B.filter(b => b.flex).length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "textAlign",
"fragmento": "lor, fillOpacity: 1 }]; if (align) { t.textAlign = align; } parent.insertChild(parent.ch"
}
],
"content": ""
},
{
"prompt": "Necesito la home de un estudio de doblaje en 1180 px: hero con foto de fondo, una franja de numeros y un bloque final de contacto. Paleta a tu criterio, que sea vibrante.",
"id": "pd-002",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#0B0B0F', ink:'#FFFFFF', brand:'#00E5FF', accent:'#FF3B30', card:'#1A1A24', link:'#0B0B0F' };\nconst T = { h1:'64', h2:'28', body:'16', small:'13' };\nconst S = { sm:16, md:24, lg:40, xl:64 };\nconst mk = (chars, size, color, weight, family) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = family; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Dub Lab — Home';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 1020);\nboard.x = 0; board.y = 0;\nconst sections = [\n { name:'Hero', h:420, y:0, color:P.bg },\n { name:'Metricas', h:200, y:420, color:P.card },\n { name:'Contacto', h:400, y:620, color:P.brand }\n];\nconst rects = sections.map(s => {\n const r = penpot.createRectangle();\n r.name = s.name;\n r.fills = [{ fillColor: s.color, fillOpacity: 1 }];\n r.resize(1180, s.h);\n r.x = 0; r.y = s.y;\n return r;\n});\nconst mkSection = (name, y, h, color) => {\n const c = penpot.createBoard();\n c.name = name;\n c.fills = [{ fillColor: color, fillOpacity: 1 }];\n c.resize(1180, h);\n c.x = 0; c.y = y;\n c.addFlexLayout();\n if (c.flex) { c.flex.dir = 'column'; c.flex.horizontalPadding = S.xl; c.flex.verticalPadding = S.xl; c.flex.rowGap = S.md; }\n return c;\n};\nconst hero = mkSection('Hero', 0, 420, P.bg);\nhero.insertChild(hero.children.length, mk('DUB LAB', T.h1, P.brand, '700', 'Space Grotesk'));\nhero.insertChild(hero.children.length, mk('Estudio de doblaje y postproduccion', T.body, P.ink, '400', 'Space Grotesk'));\nconst metrics = mkSection('Metricas', 420, 200, P.card);\nmetrics.flex.dir = 'row';\nmetrics.flex.rowGap = S.sm;\nmetrics.flex.columnGap = S.sm;\nmetrics.flex.alignItems = 'center';\nconst cards = [\n { v:'12.000', l:'Horas de grabacion' },\n { v:'340', l:'Proyectos entregados' },\n { v:'98%', l:'Satisfaccion' },\n { v:'24/7', l:'Turno de grabacion' }\n];\ncards.forEach(c => {\n const card = penpot.createRectangle();\n card.name = c.l;\n card.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\n card.borderRadius = 8;\n card.resize(260, 140);\n card.strokes = [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.insertChild(card.children.length, mk(c.v, T.h2, P.accent, '700', 'Space Grotesk'));\n card.insertChild(card.children.length, mk(c.l, T.small, P.ink, '400', 'Space Grotesk'));\n metrics.appendChild(card);\n});\nconst contact = mkSection('Contacto', 620, 400, P.brand);\ncontact.insertChild(contact.children.length, mk('Trabajemos juntos', T.h2, P.link, '700', 'Space Grotesk'));\ncontact.insertChild(contact.children.length, mk('hola@dublab.studio', T.body, P.link, '400', 'Space Grotesk'));\nrects.forEach(r => board.appendChild(r));\nboard.appendChild(hero);\nboard.appendChild(metrics);\nboard.appendChild(contact);\nstorage.dublab = { boardId: board.id, P, T, S, mk };\nreturn { boardId: board.id, sections: sections.length, shapes: penpotUtils.shapeStructure(board, 2).children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Armame algo lindo para un museo de maquinas de escribir, una pagina de presentacion. Vos elegis todo.",
"id": "pd-003",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#F5F0E6', ink: '#1A1A2E', accent: '#C05A3E', inkGreen: '#3A6B4F', card: '#FFFFFF', line: '#D4C5A9' };\nconst S = { h1: '48', h2: '28', body: '16', small: '13' };\nconst T = { heading: 'Georgia, serif', ui: 'system-ui, sans-serif' };\nconst mk = (name, w, h, fill, radius) => { const s = penpot.createShape('rectangle'); s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }]; s.resize(w, h); s.borderRadius = radius; return s; };\nconst mkT = (chars, size, color, family, weight, align) => { const t = penpot.createText(chars); t.fontFamily = family; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.align = align; t.growType = 'auto-width'; return t; };\nconst mkIcon = (name, color) => { const s = penpot.createShape('path'); s.name = name; s.fills = [{ fillColor: color, fillOpacity: 1, fillStyle: 'solid' }]; s.path = 'M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 15h-2v-6h2v6zm0-8h-2V7h2v2z'; s.pathSize = '24'; s.strokeWidth = '0'; return s; };\nconst mkCard = (name, icon, title, desc) => { const b = penpot.createShape('board'); b.name = name; b.fills = [{ fillColor: P.card, fillOpacity: 1 }]; b.resize(280, 320); b.borderRadius = 12; b.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 16, spread: 0, color: { color: P.ink, opacity: 0.1 } }]; b.strokes = [{ strokeStyle: 'solid', strokeWidth: 1, strokeColor: P.line, strokeOpacity: 1 }]; const iconS = mkIcon(icon, P.accent); iconS.resize(40, 40); const t1 = mkT(title, S.h2, P.ink, T.heading, '700'); const t2 = mkT(desc, S.body, P.inkGreen, T.ui, '400'); [iconS, t1, t2].forEach(c => b.appendChild(c)); [iconS, t1, t2].forEach(c => { c.horizontalPadding = 24; c.verticalPadding = 24; }); return b; };\nconst hero = penpot.createShape('board'); hero.name = 'Hero / Coleccion'; hero.fills = [{ fillColor: P.bg, fillOpacity: 1 }]; hero.resize(1100, 400); hero.borderRadius = 0;\nconst heroBg = mk('Hero / fondo', 1100, 120, P.accent, 0); heroBg.x = 0; heroBg.y = 280; hero.insertChild(0, heroBg);\nconst h1 = mkT('Coleccion de Maquinas de Escribir', S.h1, P.ink, T.heading, '700', 'center'); const h2 = mkT('Desde 1874 hasta hoy — cada una con su historia', '20', P.inkGreen, T.ui, '400', 'center');\n[heroBg, h1, h2].forEach(s => hero.appendChild(s));\nconst gallery = penpot.createShape('board'); gallery.name = 'Galeria'; gallery.fills = [{ fillColor: P.bg, fillOpacity: 1 }]; gallery.resize(1100, 520); gallery.addFlexLayout();\nif (gallery.flex) { gallery.flex.dir = 'column'; gallery.flex.rowGap = 32; gallery.flex.horizontalPadding = 48; gallery.flex.verticalPadding = 48; gallery.flex.alignItems = 'center'; gallery.flex.justifyContent = 'center'; }\nconst cards = [ mkCard('Maquina 1', 'keyboard', 'Underwood Standard, 1932', 'La maquina que escribio los telegramas de la Segunda Guerra Mundial.'), mkCard('Maquina 2', 'keyboard', 'Olivetti Lettera 32, 1963', 'Portatil, de 3.2 kg. La primera que cabe en una maleta.'), mkCard('Maquina 3', 'keyboard', 'Royal Quiet Deluxe, 1950', 'Silenciosa hasta el punto de que nadie sabia que la estabas usando.') ];\ncards.forEach(c => gallery.appendChild(c));\nconst footer = penpot.createShape('board'); footer.name = 'Footer'; footer.fills = [{ fillColor: P.ink, fillOpacity: 1 }]; footer.resize(1100, 100); footer.borderRadius = 0;\nconst f1 = mkT('Museo de Maquinas de Escribir', '18', P.bg, T.heading, '700', 'center'); const f2 = mkT('Abierto de martes a domingos, 10 a 18 hs. Entrada libre.', '14', P.inkGreen, T.ui, '400', 'center');\n[footer, f1, f2].forEach(s => footer.appendChild(s));\nconst pages = penpotUtils.getPages();\nconst page = pages.find(p => p.name === 'Museo de Maquinas');\nconst target = page ? penpotUtils.getPageByName(page.name) : penpot.root;\nif (target) { target.insertChild(target.children.length, hero); target.insertChild(target.children.length, gallery); target.insertChild(target.children.length, footer); }\nstorage.museum = { hero, gallery, footer, cards, mkCard, mkIcon };\nreturn { heroId: hero.id, galleryId: gallery.id, footerId: footer.id, cardCount: cards.length, heroChildren: hero.children.length, galleryChildren: gallery.children.length, footerChildren: footer.children.length, flexDir: gallery.flex ? gallery.flex.dir : null, flexPadding: gallery.flex ? { top: gallery.flex.topPadding, right: gallery.flex.rightPadding, bottom: gallery.flex.bottomPadding, left: gallery.flex.leftPadding } : null };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pagina de precios de un vivero de orquideas de 900 px: encabezado, tres planes con el del medio destacado en #312e81, preguntas frecuentes y pie.",
"id": "pd-004",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#faf9f6', card: '#ffffff', ink: '#1e1b4b', accent: '#312e81', line: '#e7e5f4', green: '#065f46', white: '#ffffff' };\nconst T = { h1: '56', h2: '32', h3: '22', body: '17', small: '14' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 32, xl: 48 };\nconst mk = (chars, size, color, weight, align) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.35';\n t.characters = chars; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = []; t.shadows = [];\n if (align) t.textAlign = align;\n return t;\n};\nconst mkCard = (w, h, fill) => {\n const r = penpot.createRectangle();\n r.name = 'card'; r.fills = [{ fillColor: fill, fillOpacity: 1 }];\n r.borderRadius = 20; r.strokes = [{ strokeColor: P.line, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n r.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 24, spread: 0, color: { color: P.ink, opacity: 0.08 } }];\n r.resize(w, h); return r;\n};\nconst mkBtn = (label, fill) => {\n const b = penpot.createRectangle();\n b.name = 'boton'; b.fills = [{ fillColor: fill, fillOpacity: 1 }];\n b.borderRadius = 100; b.resize(200, 48);\n b.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: fill, opacity: 0.3 } }];\n b.insertChild(0, mk(label, '15', P.white, '700', 'center'));\n return b;\n};\nconst mkPila = (items) => {\n const p = penpot.createBoard();\n p.name = 'pila'; p.addFlexLayout();\n if (p.flex) { p.flex.dir = 'column'; p.flex.columnGap = S.md; p.flex.alignItems = 'start'; p.flex.horizontalPadding = S.lg; p.flex.verticalPadding = S.lg; }\n items.forEach(i => p.appendChild(i));\n return p;\n};\nconst plan = (name, precio, desc, items, destacado) => {\n const c = mkCard(260, 480, destacado ? P.accent : P.card);\n c.insertChild(0, mk(name, T.h3, destacado ? P.white : P.ink, '700'));\n c.insertChild(1, mk('$' + precio, '40', destacado ? '#c7d2fe' : P.green, '700'));\n c.insertChild(2, mkPila(items.map(i => mk(i, T.body, destacado ? '#e0e7ff' : P.ink, '400'))));\n c.insertChild(3, mkBtn(desc, destacado ? P.white : P.accent));\n if (destacado) { c.fills = [{ fillColor: P.accent, fillOpacity: 1 }]; c.strokes = []; }\n return c;\n};\nconst faq = (q, a) => {\n const c = mkCard(700, 100, P.card);\n c.insertChild(0, mkPila([mk(q, T.h3, P.ink, '600'), mk(a, T.body, P.ink, '400')]));\n return c;\n};\nconst root = penpot.root;\nconst mkSeccion = (titulo, items) => {\n const s = penpot.createBoard();\n s.name = titulo; s.fills = []; s.resize(900, 400);\n s.insertChild(0, mkPila([mk(titulo, T.h2, P.ink, '700'), ...items]));\n return s;\n};\nconst header = mkSeccion('Orquideas a tu medida', [mk('Elige el plan que mejor se adapte a tu espacio', T.body, P.ink, '400')]);\nheader.flex.horizontalPadding = S.xl;\nconst plans = mkPila([plan('Basico', '89', 'Mantenimiento mensual', ['2 orquideas', 'Riego y abono', 'Reemplazo gratis'], false), plan('Premium', '159', 'Cuidado integral', ['5 orquideas', 'Riego y abono', 'Reemplazo gratis', 'Asesor mensual'], true), plan('Elite', '249', 'Coleccion completa', ['10 orquideas', 'Riego y abono', 'Reemplazo gratis', 'Asesor semanal', 'Diseño de arreglo'], false)]);\nconst faqs = mkSeccion('Preguntas frecuentes', [faq('Cada cuanto riegas?', 'Cada 7 dias, segun la estacion del ano.'), faq('Que pasa si una se seca?', 'La reemplazamos sin costo en el plan incluido.'), faq('Puedo cambiar de plan?', 'Si, en cualquier momento desde tu panel.')]);\nconst footer = mkSeccion('Contacto', [mk('hola@viveroquideas.com', T.body, P.ink, '400'), mk('WhatsApp: 11-5555-0000', T.body, P.ink, '400')]);\nfooter.flex.horizontalPadding = S.xl;\nroot.insertChild(root.children.length, header);\nroot.insertChild(root.children.length, plans);\nroot.insertChild(root.children.length, faqs);\nroot.insertChild(root.children.length, footer);\nconst ids = [header.id, plans.id, faqs.id, footer.id];\nreturn { header: header.name, planCount: plans.children.length, faqCount: faqs.children.length, footer: footer.name, planColor: plan(0, 0, '', [], true).fills[0].fillColor, planCount: ids.length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "textAlign",
"fragmento": "kes = []; t.shadows = []; if (align) t.textAlign = align; return t; }; const mkCard = (w"
}
],
"content": ""
},
{
"prompt": "Quiero la pantalla principal del panel interno de un vivero de orquideas, 1320 px, con barra lateral, seis tarjetas de indicadores y una tabla de movimientos.",
"id": "pd-005",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#F4F1E6', ink:'#121A16', side:'#0B3B2E', card:'#FFFFFF', line:'#D9CBB0', ok:'#1A6B4A', warn:'#8BC34A' };\nconst T = { h1:'40', h2:'28', body:'16', small:'14', micro:'12' };\nconst S = { x6:6, x12:12, x16:16, x24:32, x48:48 };\nconst mk = (name, fills, size, weight, color, align) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.characters = name;\n t.fontFamily = 'Work Sans';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = '1.3';\n t.growType = 'auto-width';\n t.fills = fills;\n t.strokes = [];\n t.shadows = [];\n if (align) t.textAlign = align;\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Orquideas — Panel';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1320, 800);\nboard.addFlexLayout();\nif (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = S.x48; board.flex.verticalPadding = S.x48; board.flex.rowGap = S.x24; }\nconst sidebar = penpot.createBoard();\nsidebar.name = 'Sidebar';\nsidebar.fills = [{ fillColor: P.side, fillOpacity: 1 }];\nsidebar.resize(240, 752);\nsidebar.strokes = [];\nconst area = penpot.createBoard();\narea.name = 'Area';\narea.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\narea.resize(1056, 752);\narea.addFlexLayout();\nif (area.flex) { area.flex.dir = 'column'; area.flex.horizontalPadding = S.x24; area.flex.verticalPadding = S.x24; area.flex.rowGap = S.x24; }\nconst header = penpot.createBoard();\nheader.name = 'Header';\nheader.fills = [{ fillColor: P.card, fillOpacity: 1 }];\nheader.resize(1008, 80);\nheader.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\nheader.addFlexLayout();\nif (header.flex) { header.flex.dir = 'row'; header.flex.alignItems = 'center'; header.flex.columnGap = S.x24; }\nconst grid = penpot.createBoard();\ngrid.name = 'Indicadores';\ngrid.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\ngrid.resize(1008, 440);\ngrid.addFlexLayout();\nif (grid.flex) { grid.flex.dir = 'row'; grid.flex.columnGap = S.x16; grid.flex.rowGap = S.x16; grid.flex.alignItems = 'start'; }\nconst cards = [];\nconst datos = [\n { n:'Vivas', v:'1.247', c:P.ok },\n { n:'En cuarentena', v:'38', c:P.warn },\n { n:'En flor', v:'164', c:P.ok },\n { n:'Sin riego (7d)', v:'21', c:'#E65100' },\n { n:'Trasplantes hoy', v:'9', c:P.ok },\n { n:'Ausentes', v:'3', c:'#E65100' }\n];\ndatos.forEach(d => {\n const c = penpot.createBoard();\n c.name = 'Card ' + d.n;\n c.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n c.resize(320, 180);\n c.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: P.ink, opacity: 0.08 } }];\n c.addFlexLayout();\n if (c.flex) { c.flex.dir = 'column'; c.flex.horizontalPadding = S.x24; c.flex.verticalPadding = S.x24; c.flex.rowGap = S.x12; c.flex.alignItems = 'start'; }\n c.appendChild(mk(d.n, [P.ink], T.h2, '600'));\n c.appendChild(mk(d.v, [d.c], T.h1, '700'));\n c.appendChild(mk('ultima actualizacion hace 4 min', [P.ink], T.micro, '400'));\n grid.appendChild(c);\n cards.push(c);\n});\nconst tabla = penpot.createBoard();\ntabla.name = 'Movimientos';\ntabla.fills = [{ fillColor: P.card, fillOpacity: 1 }];\ntabla.resize(1008, 200);\ntabla.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\ntabla.addFlexLayout();\nif (tabla.flex) { tabla.flex.dir = 'column'; tabla.flex.horizontalPadding = S.x24; tabla.flex.verticalPadding = S.x24; tabla.flex.rowGap = S.x12; }\nconst filas = [\n { orq:'Phalaenopsis Amabilis', d:'Invernadero 3', e:'Salida', c:'12' },\n { orq:'Dendrobium Superba', d:'Invernadero 1', e:'Entrada', c:'5' },\n { orq:'Cattleya Intermedia', d:'Invernadero 2', e:'Trasplante', c:'8' },\n { orq:'Vanda Sanderiana', d:'Invernadero 4', e:'Salida', c:'3' }\n];\nfilas.forEach(f => {\n const f2 = penpot.createRectangle();\n f2.name = 'Fila ' + f.orq;\n f2.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\n f2.resize(1008, 40);\n f2.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n f2.addFlexLayout();\n if (f2.flex) { f2.flex.dir = 'row'; f2.flex.columnGap = S.x24; f2.flex.alignItems = 'center'; }\n f2.appendChild(mk(f.orq, [P.ink], T.body, '600'));\n f2.appendChild(mk(f.d, [P.ink], T.body, '400'));\n f2.appendChild(mk(f.e, [f.e === 'Salida' ? '#E65100' : P.ok], T.body, '600'));\n f2.appendChild(mk(f.c, [P.ink], T.body, '600'));\n tabla.appendChild(f2);\n});\nconst sidebarItems = [\n { n:'Panel', c:P.card },\n { n:'Coleccion', c:P.card },\n { n:'Invernaderos', c:P.card },\n { n:'Proveedores', c:P.card },\n { n:'Reportes', c:P.card }\n];\nsidebarItems.forEach(s => {\n const b = penpot.createRectangle();\n b.name = 'Item ' + s.n;\n b.fills = [{ fillColor: s.c, fillOpacity: 1 }];\n b.resize(200, 40);\n b.borderRadius = 8;\n b.strokes = [];\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'row'; b.flex.alignItems = 'center'; b.flex.horizontalPadding = S.x16; b.flex.columnGap = S.x12; }\n b.appendChild(mk(s.n, [s.c === P.card ? P.side : P.card], T.body, s.c === P.card ? '600' : '400'));\n sidebar.appendChild(b);\n});\npenpotUtils.setParentXY(sidebar, 0, 48);\npenpotUtils.setParentXY(area, 240, 48);\nheader.appendChild(mk('Orquideas — Panel', [P.ink], T.h1, '700'));\nheader.appendChild(mk('1.247 orquideas registradas', [P.ink], T.body, '400'));\nheader.appendChild(mk('Exportar', [P.ok], T.body, '600'));\nif (header.flex) header.flex.justifyContent = 'space-between';\narea.appendChild(header);\narea.appendChild(grid);\narea.appendChild(tabla);\nboard.appendChild(sidebar);\nboard.appendChild(area);\nconst ids = [board.id, sidebar.id, area.id, header.id, grid.id, tabla.id];\ncards.forEach(c => ids.push(c.id));\nstorage.orquideas = { boardId: board.id, shapes: ids, P: P, T: T, S: S };\nreturn { boardId: board.id, sidebarWidth: sidebar.width, areaWidth: area.width, cards: cards.length, filas: filas.length, shapes: ids.length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "textAlign",
"fragmento": "s = []; t.shadows = []; if (align) t.textAlign = align; return t; }; const board = pen"
}
],
"content": ""
},
{
"prompt": "Landing de un evento de un laboratorio de analisis de suelos: fecha grande, cuenta regresiva, grilla de oradores y formulario de inscripcion, 1240 px de ancho.",
"id": "pd-006",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const S = {\n bg: '#0B1220', surface: '#111B2E', card: '#162238', border: '#233352',\n accent: '#00D4AA', accentDark: '#00A885', orange: '#FF6B35',\n text: '#EAF0F6', muted: '#8BA3C2'\n};\nconst T = {\n h1: '64', h2: '32', h3: '24', body: '16', small: '12'\n};\nconst P = { xs: '8', sm: '16', md: '24', lg: '40', xl: '80' };\nconst mk = (name, w, h, fills, dir) => {\n const b = penpot.createBoard();\n b.name = name; b.fills = fills; b.resize(w, h);\n if (dir) { b.addFlexLayout(); b.flex.dir = dir; b.flex.rowGap = P.md; b.flex.columnGap = 0; b.flex.horizontalPadding = P.xl; b.flex.verticalPadding = P.lg; b.flex.alignItems = 'start'; }\n return b;\n};\nconst mkT = (chars, size, color, weight, name) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = name; t.fontFamily = name.includes('Titular') ? 'Space Grotesk' : 'Inter';\n t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width'; return t;\n};\nconst mkPill = (chars, fill, color) => {\n const b = mk('Pill', 0, 0, [{ fillOpacity: 1, fillColor: fill }], 'row');\n b.borderRadius = '20'; b.horizontalPadding = P.sm; b.verticalPadding = P.xs;\n b.insertChild(0, mkT(chars, T.small, color, '600', 'Pill'));\n return b;\n};\nconst mkInput = (label, name) => {\n const c = mk(name, 1240, 0, [], 'column');\n c.flex.rowGap = P.xs; c.flex.alignItems = 'start';\n c.insertChild(0, mkT(label, T.small, S.muted, '500', label));\n const i = penpot.createRectangle();\n i.name = name; i.fills = [{ fillOpacity: 1, fillColor: S.card }]; i.strokes = [{ strokeColor: S.border, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }]; i.borderRadius = '8'; i.resize(1240, 48);\n c.insertChild(1, i); return c;\n};\nconst root = penpot.root;\nconst hero = mk('Hero', 1240, 480, [{ fillOpacity: 1, fillColor: S.bg }], 'column');\nconst countdown = mk('Countdown', 1240, 200, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst speakers = mk('Speakers', 1240, 560, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst form = mk('Form', 1240, 440, [{ fillOpacity: 1, fillColor: S.card }], 'column');\nconst grid = mk('Speaker Grid', 1240, 320, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst speakersRow = mk('Speakers Row', 1240, 0, [], 'row');\nconst oradores = [\n { n: 'Dra. Lucia Fernandez', i: 'INTA', e: 'Nitrogeno y carbono' },\n { n: 'Dr. Martin Ruiz', i: 'UNLP', e: 'Microbioma del suelo' },\n { n: 'Ing. Carla Mendoza', i: 'CICPCC', e: 'Remediacion de suelos' },\n { n: 'Dr. Pablo Torres', i: 'CONICET', e: 'Salinidad y drenaje' }\n];\nconst mkSpeaker = (o) => {\n const b = mk('Speaker ' + o.n, 0, 0, [{ fillOpacity: 1, fillColor: S.card }], 'row');\n b.borderRadius = '12'; b.strokes = [{ strokeColor: S.border, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n b.flex.rowGap = P.md; b.flex.columnGap = P.lg; b.flex.horizontalPadding = P.lg; b.flex.verticalPadding = P.lg;\n b.flex.alignItems = 'center';\n const av = penpot.createEllipse();\n av.name = 'Avatar ' + o.n; av.fills = [{ fillOpacity: 1, fillColor: S.accent }]; av.resize(56, 56);\n b.insertChild(0, av);\n const col = mk(o.n, 0, 0, [], 'column');\n col.flex.rowGap = P.xs; col.flex.alignItems = 'start';\n col.insertChild(0, mkT(o.n, T.h3, S.text, '600', o.n));\n col.insertChild(1, mkPill(o.i, S.accentDark, S.bg));\n col.insertChild(2, mkT(o.e, T.body, S.muted, '400', o.e));\n b.insertChild(1, col);\n return b;\n};\nconst fields = ['Nombre', 'Email', 'Institucion'].map((l, i) => mkPill(l, S.accent, S.bg));\nconst btn = mk('Boton', 1240, 56, [{ fillOpacity: 1, fillColor: S.accent }], 'column');\nbtn.borderRadius = '8'; btn.flex.rowGap = 0; btn.flex.horizontalPadding = 0; btn.flex.verticalPadding = 0;\nbtn.insertChild(0, mkT('Inscribirme', T.h3, S.bg, '700', 'Boton'));\nconst mkCountdown = (v, l) => {\n const c = mk('Countdown ' + l, 0, 0, [{ fillOpacity: 1, fillColor: S.card }], 'column');\n c.borderRadius = '12'; c.flex.rowGap = P.xs; c.flex.horizontalPadding = P.md; c.flex.verticalPadding = P.md; c.flex.alignItems = 'center';\n c.insertChild(0, mkT(v, T.h1, S.accent, '700', v));\n c.insertChild(1, mkT(l, T.small, S.muted, '500', l));\n return c;\n};\nconst units = ['Dias', 'Horas', 'Min', 'Seg'];\nconst mk = (chars, size, color, weight, name) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = name; t.fontFamily = name.includes('Titular') ? 'Space Grotesk' : 'Inter';\n t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width'; return t;\n};\nconst mkPill = (chars, fill, color) => {\n const b = mk('Pill', 0, 0, [{ fillOpacity: 1, fillColor: fill }], 'row');\n b.borderRadius = '20'; b.horizontalPadding = P.sm; b.verticalPadding = P.xs;\n b.insertChild(0, mkT(chars, T.small, color, '600', 'Pill'));\n return b;\n};\nconst mkInput = (label, name) => {\n const c = mk(name, 1240, 0, [], 'column');\n c.flex.rowGap = P.xs; c.flex.alignItems = 'start';\n c.insertChild(0, mkT(label, T.small, S.muted, '500', label));\n const i = penpot.createRectangle();\n i.name = name; i.fills = [{ fillOpacity: 1, fillColor: S.card }]; i.strokes = [{ strokeColor: S.border, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }]; i.borderRadius = '8'; i.resize(1240, 48);\n c.insertChild(1, i); return c;\n};\nconst root = penpot.root;\nconst hero = mk('Hero', 1240, 480, [{ fillOpacity: 1, fillColor: S.bg }], 'column');\nconst countdown = mk('Countdown', 1240, 200, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst speakers = mk('Speakers', 1240, 560, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst form = mk('Form', 1240, 440, [{ fillOpacity: 1, fillColor: S.card }], 'column');\nconst grid = mk('Speaker Grid', 1240, 320, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst speakersRow = mk('Speakers Row', 1240, 0, [], 'row');\nconst oradores = [\n { n: 'Dra. Lucia Fernandez', i: 'INTA', e: 'Nitrogeno y carbono' },\n { n: 'Dr. Martin Ruiz', i: 'UNLP', e: 'Microbioma del suelo' },\n { n: 'Ing. Carla Mendoza', i: 'CICPCC', e: 'Remediacion de suelos' },\n { n: 'Dr. Pablo Torres', i: 'CONICET', e: 'Salinidad y drenaje' }\n];\nconst mkSpeaker = (o) => {\n const b = mk('Speaker ' + o.n, 0, 0, [{ fillOpacity: 1, fillColor: S.card }], 'row');\n b.borderRadius = '12'; b.strokes = [{ strokeColor: S.border, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n b.flex.rowGap = P.md; b.flex.columnGap = P.lg; b.flex.horizontalPadding = P.lg; b.flex.verticalPadding = P.lg;\n b.flex.alignItems = 'center';\n const av = penpot.createEllipse();\n av.name = 'Avatar ' + o.n; av.fills = [{ fillOpacity: 1, fillColor: S.accent }]; av.resize(56, 56);\n b.insertChild(0, av);\n const col = mk(o.n, 0, 0, [], 'column');\n col.flex.rowGap = P.xs; col.flex.alignItems = 'start';\n col.insertChild(0, mkT(o.n, T.h3, S.text, '600', o.n));\n col.insertChild(1, mkPill(o.i, S.accentDark, S.bg));\n col.insertChild(2, mkT(o.e, T.body, S.muted, '400', o.e));\n b.insertChild(1, col);\n return b;\n};\nconst fields = ['Nombre', 'Email', 'Institucion'].map((l, i) => mkPill(l, S.accent, S.bg));\nconst btn = mk('Boton', 1240, 56, [{ fillOpacity: 1, fillColor: S.accent }], 'column');\nbtn.borderRadius = '8'; btn.flex.rowGap = 0; btn.flex.horizontalPadding = 0; btn.flex.verticalPadding = 0;\nbtn.insertChild(0, mkT('Inscribirme', T.h3, S.bg, '700', 'Boton'));\nconst mkCountdown = (v, l) => {\n const c = mk('Countdown ' + l, 0, 0, [{ fillOpacity: 1, fillColor: S.card }], 'column');\n c.borderRadius = '12'; c.flex.rowGap = P.xs; c.flex.horizontalPadding = P.md; c.flex.verticalPadding = P.md; c.flex.alignItems = 'center';\n c.insertChild(0, mkT(v, T.h1, S.accent, '700', v));\n c.insertChild(1, mkT(l, T.small, S.muted, '500', l));\n return c;\n};\nconst units = ['Dias', 'Horas', 'Min', 'Seg'];\nconst mk = (chars, size, color, weight, name) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = name; t.fontFamily = name.includes('Titular') ? 'Space Grotesk' : 'Inter';\n t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width'; return t;\n};\nconst mkPill = (chars, fill, color) => {\n const b = mk('Pill', 0, 0, [{ fillOpacity: 1, fillColor: fill }], 'row');\n b.borderRadius = '20'; b.horizontalPadding = P.sm; b.verticalPadding = P.xs;\n b.insertChild(0, mkT(chars, T.small, color, '600', 'Pill'));\n return b;\n};\nconst mkInput = (label, name) => {\n const c = mk(name, 1240, 0, [], 'column');\n c.flex.rowGap = P.xs; c.flex.alignItems = 'start';\n c.insertChild(0, mkT(label, T.small, S.muted, '500', label));\n const i = penpot.createRectangle();\n i.name = name; i.fills = [{ fillOpacity: 1, fillColor: S.card }]; i.strokes = [{ strokeColor: S.border, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }]; i.borderRadius = '8'; i.resize(1240, 48);\n c.insertChild(1, i); return c;\n};\nconst root = penpot.root;\nconst hero = mk('Hero', 1240, 480, [{ fillOpacity: 1, fillColor: S.bg }], 'column');\nconst countdown = mk('Countdown', 1240, 200, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst speakers = mk('Speakers', 1240, 560, [{ fillOpacity: 1, fillColor: S.surface }], 'column');\nconst form = mk('Form', 1240, 440, [{ fillOpacity: 1, fillColor: S.card }], 'column');\nconst grid = mk('Speaker Grid', 1240, "
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Un boton primario y uno secundario para un servicio de reparacion de relojes, ambos con estados normal y deshabilitado, usando #581c87 como acento.",
"id": "pd-007",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { accent: '#581c87', bg: '#f5f0ff', ink: '#1a0f2e', border: '#d4c4f0', white: '#ffffff' };\nconst T = { sm: '16', md: '20', lg: '28' };\nconst S = { xs: '8', sm: '12', md: '16', lg: '24' };\nconst mk = (chars, size, weight, color, align) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.4';\n t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.growType = 'auto-width';\n if (align) t.textAlign = align;\n return t;\n};\nconst btn = (label, filled) => {\n const b = penpot.createRectangle();\n b.name = label; b.fills = [{ fillColor: filled ? P.accent : P.white, fillOpacity: 1 }];\n b.strokes = filled ? [] : [{ strokeColor: P.border, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n b.strokeAlign = 'inside'; b.borderRadius = '12'; b.resize(200, 52);\n b.insertChild(0, mk(label, T.md, '600', filled ? P.white : P.ink, 'center'));\n return b;\n};\nconst page = penpot.createPage('Botones relojero');\npenpot.currentPage = page;\nconst board = penpot.createBoard();\nboard.name = 'Botones'; board.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(320, 200); board.x = 0; board.y = 0;\nboard.addFlexLayout();\nif (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = S.lg; board.flex.verticalPadding = S.lg; board.flex.rowGap = S.sm; board.flex.alignItems = 'center'; }\npage.root.insertChild(0, board);\nconst prim = btn('Pedir reparacion', true);\nconst sec = btn('Consultar precio', false);\nboard.appendChild(prim);\nboard.appendChild(sec);\nconst disabled = penpotUtils.findShapes(s => s.name === 'Boton', board);\ndisabled.forEach(s => {\n s.fills = [{ fillColor: '#c9b0d9', fillOpacity: 1 }];\n s.strokes = [];\n s.fills.forEach(f => { if (f.fillOpacity !== undefined) f.fillOpacity = 1; });\n});\nconst primId = penpotUtils.findShape(s => s.name === 'Pedir reparacion', board)?.id;\nconst secId = penpotUtils.findShape(s => s.name === 'Consultar precio', board)?.id;\nreturn { boardId: board.id, primId, secId, disabledCount: disabled.length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "textAlign",
"fragmento": ".growType = 'auto-width'; if (align) t.textAlign = align; return t; }; const btn = (labe"
}
],
"content": ""
},
{
"prompt": "Card de producto para un servicio de reparacion de relojes: foto arriba, titulo en Karla, precio tachado y precio final, y un boton. 320 de ancho.",
"id": "pd-008",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n bg: '#FDFBF7', card: '#FFFFFF', ink: '#1A1A1A', accent: '#B8860B',\n priceOld: '#9E9E9E', priceNew: '#1A1A1A', btn: '#B8860B', btnText: '#FFFFFF'\n};\nconst T = { xs: '12', sm: '14', md: '18', lg: '26' };\nconst S = { sm: 8, md: 16, lg: 24, xl: 32 };\nconst board = new Board();\nboard.name = 'Card reloj';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(320, 520);\nboard.x = 0;\nboard.y = 0;\nboard.borderRadius = 16;\nboard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 16, spread: 0, color: { color: '#000000', opacity: 0.12 } }];\nboard.flex = board.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.rowGap = S.lg;\n board.flex.horizontalPadding = S.xl;\n board.flex.verticalPadding = S.xl;\n board.flex.alignItems = 'start';\n}\nconst add = (name, fills, fontSize, color, weight) => {\n const t = new Text();\n t.name = name;\n t.characters = name;\n t.fills = fills;\n t.fontFamily = 'Karla';\n t.fontSize = fontSize;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.3';\n t.growType = 'auto-width';\n t.horizontalAlign = 'left';\n board.appendChild(t);\n return t;\n};\nconst foto = new Rectangle();\nfoto.name = 'Foto reloj';\nfoto.fills = [{ fillColor: '#F5F0E6', fillOpacity: 1 }];\nfoto.resize(288, 200);\nfoto.borderRadius = 12;\nboard.appendChild(foto);\nadd('Relojer\\xEDa Su\\xE1rez', [P.ink], T.md, '700');\nadd('Reparaci\\xF3n de relojes mec\\xE1nicos y de cuarzo', [P.accent], T.sm);\nconst precioAnt = add('Antes $120.000', [P.priceOld], T.md, '400');\nprecioAnt.strikethrough = true;\nconst precioHoy = add('Ahora $89.000', [P.priceNew], T.lg, '700');\nconst btn = new Rectangle();\nbtn.name = 'Boton agendar';\nbtn.fills = [{ fillColor: P.btn, fillOpacity: 1 }];\nbtn.resize(288, 48);\nbtn.borderRadius = 24;\nbtn.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 2, blur: 8, spread: 0, color: { color: P.btn, opacity: 0.3 } }];\nconst btnText = new Text();\nbtnText.name = 'Boton texto';\nbtnText.characters = 'Agendar reparaci\\xF3n';\nbtnText.fills = [{ fillColor: P.btnText, fillOpacity: 1 }];\nbtnText.fontFamily = 'Karla';\nbtnText.fontSize = T.md;\nbtnText.fontWeight = '700';\nbtnText.lineHeight = '1.3';\nbtnText.horizontalAlign = 'center';\nbtnText.growType = 'auto-width';\nbtn.insertChild(0, btnText);\npenpotUtils.setParentXY(btnText, 144, 24);\nstorage.card = { boardId: board.id, board: board };\nreturn { boardId: board.id, flex: !!board.flex, dir: board.flex ? board.flex.dir : null, children: board.children.length, shapes: board.descendants.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Hazme una barra de navegacion de una academia de tango de 900x88 con logo, cinco enlaces y un boton de accion a la derecha.",
"id": "pd-009",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#0B0A0F', ink: '#F4F0E8', accent: '#C8102E', hover: '#E83A52' };\nconst S = { sm: '14', md: '16', lg: '20' };\nconst nav = penpot.createBoard();\nnav.name = 'Navbar Academia';\nnav.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nnav.resize(900, 88);\nnav.addFlexLayout();\nif (nav.flex) {\n nav.flex.dir = 'column';\n nav.flex.horizontalPadding = 40;\n nav.flex.verticalPadding = 0;\n nav.flex.rowGap = 0;\n nav.flex.alignItems = 'start';\n}\nconst mk = (name, size, color, weight) => {\n const t = penpot.createText(name);\n t.fontFamily = 'Lora';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = '1.3';\n t.characters = name;\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst links = [\n { name: 'Clases', size: S.md, weight: '500' },\n { name: 'Ponches', size: S.md, weight: '500' },\n { name: 'Profesores', size: S.md, weight: '500' },\n { name: 'Eventos', size: S.md, weight: '500' },\n { name: 'Contacto', size: S.md, weight: '500' }\n];\nlinks.forEach(l => nav.insertChild(nav.children.length, mk(l.name, l.size, P.ink, l.weight)));\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton Inscripcion';\nbtn.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nbtn.borderRadius = 24;\nbtn.resize(160, 40);\nbtn.strokes = [];\nbtn.insertChild(btn.children.length, mk('Inscribite', S.sm, P.ink, '700'));\nif (btn.text) btn.text.alignHorizontal = 'center';\nnav.insertChild(nav.children.length, btn);\nconst items = penpotUtils.findShapes(s => s.name !== 'Navbar Academia', nav);\nconst c = penpotUtils.analyzeDescendants(nav, (root, shape) => {\n if (shape.type === 'text') {\n return shape.fontFamily === 'Lora' ? null : () => { shape.fontFamily = 'Lora'; };\n }\n if (shape.type === 'rectangle' && shape.name === 'Boton Inscripcion') {\n return shape.fills.some(f => f.fillOpacity === 1 && f.fillColor === P.accent) ? null : null;\n }\n return null;\n});\nstorage.navbar = { boardId: nav.id, items: items.map(s => s.id) };\nreturn { boardId: nav.id, flexDir: nav.flex ? nav.flex.dir : null, padding: nav.flex ? { top: nav.flex.topPadding, right: nav.flex.rightPadding, bottom: nav.flex.bottomPadding, left: nav.flex.leftPadding } : null, children: items.length, correctors: c.filter(x => x.result !== null).length, links: links.length, linksOk: links.every(l => penpotUtils.findShape(s => s.name === l.name && s.fontFamily === 'Lora' && s.fontSize === l.size, nav) !== null) };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Un aviso de cookies para el sitio de un estudio de doblaje: texto corto, dos botones y fondo #3b0764.",
"id": "pd-010",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#3b0764', ink: '#fff', accent: '#f5c518', soft: '#c99ae0' };\nconst T = { sm: '14', lg: '20', xl: '28' };\nconst S = { xs: 8, s: 12, m: 20, l: 32 };\nconst mk = (n, w, h, f, c, fs, tc) => {\n const s = penpot.createRectangle();\n s.name = n; s.fills = [{ fillColor: f, fillOpacity: 1, fillStyle: 'solid' }];\n s.resize(w, h); s.borderRadius = tc || 0;\n if (s.type === 'rectangle') {\n const t = penpot.createText();\n t.name = n + '/text'; t.characters = c; t.fontFamily = 'Work Sans';\n t.fontSize = fs; t.fontWeight = '600'; t.lineHeight = '1.35';\n t.fills = [{ fillColor: '#fff', fillOpacity: 1, fillStyle: 'solid' }];\n t.growType = 'auto-width';\n s.insertChild(s.children.length, t);\n }\n return s;\n};\nconst bar = mk('Aviso de cookies', 720, 120, P.bg, P.bg, T.lg, '16');\nbar.x = 0; bar.y = 0;\nconst btn = (n, c) => mk(n, 140, 40, P.accent, P.accent, T.sm, '8');\nconst a = btn('Aceptar', 'Aceptar todo'), b = btn('Configurar', 'Configurar');\na.insertChild(0, penpot.createText());\na.fills[0].fillOpacity = 1;\nb.fills[0].fillOpacity = 1;\nb.strokes = [{ strokeColor: P.soft, strokeOpacity: 1, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeDashArray: [], strokeLineCap: 'square', strokeJoin: 'round' }];\nbar.insertChild(bar.children.length, a);\nbar.insertChild(bar.children.length, b);\nconst fills = bar.fills.map(f => f.fillColor);\nreturn { id: bar.id, name: bar.name, width: bar.width, height: bar.height, children: bar.children.length, fills: fills, btnCount: 2 };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "=== 'rectangle') { const t = penpot.createText(); t.name = n + '/text'; t.characters"
}
],
"content": ""
},
{
"prompt": "Necesito una tarjeta de perfil de una escuela de circo con avatar circular, nombre, rol y tres datos de contacto.",
"id": "pd-011",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#FFF8F0', accent:'#D94F3D', text:'#2B1A10', sec:'#6B4A3A', border:'#F2D5C4' };\nconst T = { h:'22', s:'16', b:'13' };\nconst S = { sm:8, md:16, lg:24 };\nconst mk = (chars, size, color, weight, family) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = family || 'Nunito';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.35';\n t.characters = chars;\n t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = [];\n t.shadows = [];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Tarjeta Circo';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(360, 420);\nboard.borderRadius = 20;\nboard.strokes = [{ strokeColor: P.border, strokeWidth: 2, strokeOpacity: 1, strokeStyle: 'solid' }];\nboard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 6, blur: 16, spread: 0, color: { color: P.accent, opacity: 0.12 } }];\nboard.layoutChild = { horizontalAlignment: 'center', verticalAlignment: 'center' };\nconst avatar = penpot.createBoard();\navatar.name = 'Avatar';\navatar.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\navatar.resize(120, 120);\navatar.borderRadius = 60;\navatar.strokes = [{ strokeColor: P.bg, strokeWidth: 4, strokeOpacity: 1, strokeStyle: 'solid' }];\navatar.shadows = [];\nconst img = { url: 'https://picsum.photos/seed/circo/240', width: 240, height: 240 };\navatar.fills.push({ fillOpacity: 1, fillImage: img });\nconst root = penpot.root;\nroot.insertChild(root.children.length, board);\nroot.insertChild(root.children.length, avatar);\nboard.insertChild(board.children.length, avatar);\nconst info = penpot.createBoard();\ninfo.name = 'Info';\ninfo.fills = [];\ninfo.resize(300, 220);\nboard.insertChild(board.children.length, info);\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'center';\n}\nconst n = mk('Circo Volador', T.h, P.text, '700');\nconst r = mk('Escuela de acrobacia aerea', T.s, P.sec);\nconst c1 = mk('Tel: +34 91 555 0192', T.b, P.text);\nconst c2 = mk('Email: info@circo-volador.es', T.b, P.text);\nconst c3 = mk('Instagram: @circo_volador', T.b, P.text);\nconst items = [n, r, c1, c2, c3];\nitems.forEach(i => {\n info.insertChild(info.children.length, i);\n if (i.layoutChild) i.layoutChild.horizontalAlignment = 'center';\n});\nif (info.flex) {\n info.flex.dir = 'column';\n info.flex.rowGap = S.sm;\n info.flex.alignItems = 'center';\n}\nstorage.tarjeta = { boardId: board.id, avatarId: avatar.id };\nreturn { boardId: board.id, avatarId: avatar.id, nChars: n.characters, c1Chars: c1.characters, flexDir: board.flex ? board.flex.dir : null, infoFlexDir: info.flex ? info.flex.dir : null };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Necesito una tabla de 1320 px para un vivero de orquideas: encabezado oscuro, cinco filas con fondo alternado y una columna de estado con etiquetas de color.",
"id": "pd-012",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n board: '1320', header: '100', row: '80', cell: '40',\n sm: '14', md: '16', lg: '22', xl: '30'\n};\nconst C = {\n ink: '#0D2818', paper: '#FFF8E7', line: '#E8F5E9',\n pri: '#1B5E20', priLight: '#4CAF50',\n pend: '#F57C00', pendBg: '#FFF3E0',\n bloom: '#2E7D32', bloomBg: '#E8F5E9',\n sold: '#607D8B', soldBg: '#ECEFF1'\n};\nconst S = { pri: C.pri, pend: C.pend, bloom: C.bloom, sold: C.sold };\nconst T = { xl: P.xl, lg: P.lg, md: P.md, sm: P.sm };\nconst mk = (name, w, h, fill, fs, color, weight) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Work Sans'; t.fontSize = fs; t.fontWeight = weight;\n t.lineHeight = '1.3'; t.characters = name; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = []; t.shadows = []; t.resize(w, h);\n return t;\n};\nconst mkCell = (name, w, h, fill, stroke, radius) => {\n const s = penpot.createRectangle();\n if (!s) throw new Error('createRectangle returned null');\n s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }];\n s.strokes = stroke ? [{ strokeColor: stroke, strokeWidth: 1, strokeOpacity: 1, strokeStyle: 'solid' }] : [];\n s.borderRadius = radius; s.resize(w, h); return s;\n};\nconst mkBadge = (name, fill, stroke) => {\n const b = penpot.createRectangle();\n if (!b) throw new Error('createRectangle returned null');\n b.name = name; b.fills = [{ fillColor: fill, fillOpacity: 1 }];\n b.strokes = stroke ? [{ strokeColor: stroke, strokeWidth: 2, strokeOpacity: 1, strokeStyle: 'solid' }] : [];\n b.borderRadius = '20'; b.resize(160, 36); return b;\n};\nconst mkRow = (name, cells) => {\n const r = penpot.createBoard();\n if (!r) throw new Error('createBoard returned null');\n r.name = name; r.fills = [{ fillColor: C.paper, fillOpacity: 1 }];\n r.strokes = []; r.resize(1320, 80);\n r.addFlexLayout();\n if (r.flex) {\n r.flex.dir = 'column'; r.flex.rowGap = 0; r.flex.columnGap = 0;\n r.flex.horizontalPadding = 0; r.flex.verticalPadding = 0;\n r.flex.alignItems = 'start'; r.flex.justifyContent = 'start';\n }\n cells.forEach(c => r.appendChild(c));\n return r;\n};\nconst mkCellContent = (name, label, value, size, color, weight) => {\n const c = penpot.createBoard();\n if (!c) throw new Error('createBoard returned null');\n c.name = name; c.fills = []; c.strokes = []; c.resize(280, 80);\n c.addFlexLayout();\n if (c.flex) {\n c.flex.dir = 'row'; c.flex.rowGap = 0; c.flex.columnGap = 16;\n c.flex.horizontalPadding = 24; c.flex.verticalPadding = 20;\n c.flex.alignItems = 'center'; c.flex.justifyContent = 'center';\n }\n const lbl = mk(label, 0, 0, color, size, weight);\n const val = mk(value, 0, 0, color, size, weight);\n c.appendChild(lbl); c.appendChild(val);\n return c;\n};\nconst bd = penpot.createBoard();\nif (!bd) throw new Error('createBoard returned null');\nbd.name = 'Catalogo orquideas';\nbd.fills = [{ fillColor: C.paper, fillOpacity: 1 }];\nbd.strokes = []; bd.resize(1320, 620);\nbd.addFlexLayout();\nif (bd.flex) {\n bd.flex.dir = 'column'; bd.flex.rowGap = 0; bd.flex.columnGap = 0;\n bd.flex.horizontalPadding = 0; bd.flex.verticalPadding = 0;\n bd.flex.alignItems = 'start'; bd.flex.justifyContent = 'start';\n}\nconst header = penpot.createRectangle();\nif (!header) throw new Error('createRectangle returned null');\nheader.name = 'Encabezado';\nheader.fills = [{ fillColor: C.pri, fillOpacity: 1 }];\nheader.strokes = []; header.resize(1320, 100);\nheader.borderRadius = '0';\nconst hTitle = mk('Catalogo de orquideas', 0, 0, C.paper, T.xl, '700');\nconst hSub = mk('Vivero La Selva — 5 variedades', 0, 0, C.priLight, T.md, '400');\nconst hdr = penpot.createGroup();\nif (!hdr) throw new Error('createGroup returned null');\nhdr.name = 'Encabezado grupo';\nhdr.fills = []; hdr.strokes = []; hdr.resize(1320, 100);\nhdr.appendChild(hTitle); hdr.appendChild(hSub);\nif (hdr.flex) {\n hdr.flex.dir = 'column'; hdr.flex.rowGap = 4; hdr.flex.columnGap = 0;\n hdr.flex.horizontalPadding = 48; hdr.flex.verticalPadding = 28;\n hdr.flex.alignItems = 'start'; hdr.flex.justifyContent = 'start';\n}\nbd.appendChild(header); bd.appendChild(hdr);\nconst filas = [\n { n: 'Phalaenopsis Amabilis', p: 'Philippines', s: 'pend', v: 'Pendiente' },\n { n: 'Dendrobium Nobile', p: 'Himalaya', s: 'bloom', v: 'En flor' },\n { n: 'Cattleya Labiata', p: 'Brasil', s: 'bloom', v: 'En flor' },\n { n: 'Miltonia Spectrum', p: 'Colombia', s: 'sold', v: 'Vendida' },\n { n: 'Oncidium Twinkling Star', p: 'Venezuela', s: 'pend', v: 'Pendiente' }\n];\nconst datos = filas.map(f => {\n const badge = mkBadge(f.v, S[f.s], S[f.s]);\n const badgeTxt = mk(f.v, 0, 0, C.paper, T.sm, '700');\n badge.appendChild(badgeTxt);\n if (badge.flex) {\n badge.flex.dir = 'row'; badge.flex.rowGap = 0; badge.flex.columnGap = 0;\n badge.flex.horizontalPadding = 16; badge.flex.verticalPadding = 0;\n badge.flex.alignItems = 'center'; badge.flex.justifyContent = 'center';\n }\n return mkRow(f.n, [\n mkCellContent(f.n + ' nombre', 'Variedad', f.n, T.md, C.ink, '600'),\n mkCellContent(f.n + ' origen', 'Origen', f.p, T.md, C.ink, '400'),\n mkCellContent(f.n + ' precio', 'Precio', '$' + (f.s === 'sold' ? '1.200' : '680'), T.lg, C.pri, '700'),\n mkCellContent(f.n + ' estado', 'Estado', f.v, T.md, C.paper, '700')\n ]);\n});\nconst cols = ['280', '280', '280', '160'];\nconst filasGrid = datos.map((d, i) => {\n const row = penpot.createBoard();\n if (!row) throw new Error('createBoard returned null');\n row.name = 'Fila ' + (i + 1);\n row.fills = [{ fillColor: i % 2 === 0 ? C.paper : C.line, fillOpacity: 1 }];\n row.strokes = []; row.resize(1320, 80);\n row.addGridLayout();\n if (row.grid) {\n row.grid.columns = cols; row.grid.rows = ['80'];\n row.grid.columnGap = '16'; row.grid.rowGap = '16';\n }\n d.children.forEach((c, j) => {\n if (row.grid) row.grid.appendChild(c, 1, j + 1);\n });\n return row;\n});\nconst cuerpo = penpot.createBoard();\nif (!cuerpo) throw new Error('createBoard returned null');\ncuerpo.name = 'Cuerpo tabla';\ncuerpo.fills = [{ fillColor: C.paper, fillOpacity: 1 }];\ncuerpo.strokes = []; cuerpo.resize(1320, 520);\ncuerpo.addFlexLayout();\nif (cuerpo.flex) {\n cuerpo.flex.dir = 'column'; cuerpo.flex.rowGap = 0; cuerpo.flex.columnGap = 0;\n cuerpo.flex.horizontalPadding = 0; cuerpo.flex.verticalPadding = 0;\n cuerpo.flex.alignItems = 'start'; cuerpo.flex.justifyContent = 'start';\n}\nfilasGrid.forEach(r => cuerpo.appendChild(r));\nbd.insertChild(bd.children.length, header);\nbd.insertChild(bd.children.length, hdr);\nbd.insertChild(bd.children.length, cuerpo);\nconst cells = penpotUtils.findShapes(s => s.type === 'text', bd);\nconst noAuto = cells.filter(t => t.growType !== 'auto-width');\nstorage.orquideas = { bdId: bd.id, headerId: header.id, cells: cells.map(t => t.id) };\nreturn {\n boardId: bd.id,\n cells: cells.length,\n noAutoGrow: noAuto.length,\n filas: filasGrid.length,\n celdasConGrid: filasGrid.filter(r => r.grid).length,\n filasAlternadas: filasGrid.filter((r, i) => i % 2 === 0).length,\n celdas: cols.length,\n celdasAnchadas: filasGrid.filter(r => r.grid && r.grid.children.length === cols.length).length\n};"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pantalla de 390x844 para la app de una consultora de eficiencia energetica: foto real de portada traida de una URL, titulo encima y boton abajo.",
"id": "pd-013",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n brand: '#0B3D2E', accent: '#14B8A6', cream: '#FDFBF7', ink: '#1A2321', white: '#FFFFFF'\n};\nconst S = { xs: 12, sm: 16, md: 24, lg: 32, xl: 48 };\nconst mk = (name, w, h, fill) => {\n const s = penpot.createShape('rectangle', w, h);\n s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }];\n s.borderRadius = 0; s.strokes = []; s.resize(w, h); return s;\n};\nconst mkText = (name, chars, size, weight, color, align) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = name; t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = '1.3'; t.align = align || 'left'; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.strokes = [];\n return t;\n};\nconst board = mk('EnergySavings', 390, 844, P.cream);\nboard.borderRadius = 24;\nconst header = mk('Header', 390, 100, P.brand);\nconst hero = mk('Hero', 390, 600, P.accent);\nconst cta = mk('CTA', 390, 100, P.accent);\nconst footer = mk('Footer', 390, 44, P.brand);\nconst photo = mk('Photo', 390, 600, P.brand);\nphoto.fills = [{ fillColor: P.brand, fillOpacity: 1, fillImage: { url: 'http://images.unsplash.com/photo-1504384308090-c54be3855833?w=800&q=80', fit: 'cover' } }];\nconst logo = mkText('Logo', 'EcoAudit', 18, 'bold', P.white, 'left');\nconst brand = mkText('Brand', 'EcoAudit Consultores', 14, 'normal', P.accent, 'left');\nconst title = mkText('Title', 'Reduce tu consumo energético hasta un 40%', 28, 'bold', P.ink, 'left');\nconst sub = mkText('Sub', 'Diagnóstico gratuito para PYMEs. Presupuesto en 48 horas.', 16, 'normal', P.ink, 'left');\nconst btn = mkText('Button', 'Solicitar diagnóstico gratuito', 17, 'bold', P.brand, 'center');\nconst footerText = mkText('Footer', '© 2025 EcoAudit — Todos los derechos reservados', 11, 'normal', P.accent, 'center');\nconst insert = (p, s, x, y) => { p.insertChild(p.children.length, s); penpotUtils.setParentXY(s, x, y); };\n[header, hero, cta, footer].forEach(p => board.appendChild(p));\n[logo, brand, photo, title, sub, btn, footerText].forEach(s => board.appendChild(s));\ninsert(header, logo, S.md, 32);\ninsert(header, brand, 100, 64);\ninsert(hero, title, S.md, S.lg);\ninsert(hero, sub, S.md, 160);\ninsert(cta, btn, S.md, 40);\ninsert(footer, footerText, S.md, 12);\nconst fills = board.fills.map(f => f.fillColor);\nconst photoOk = photo.fills.some(f => f.fillImage && f.fillImage.url);\nconst contained = penpotUtils.isContainedIn(photo, hero);\nreturn { boardId: board.id, children: board.children.length, fills, photoOk, contained };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Quiero una galeria de cuatro fotos reales de internet en la app de un albergue para perros mayores, formato 360x780, con leyenda en cada una.",
"id": "pd-014",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#FFF8F0', ink: '#2B1A0E', accent: '#C0522A', badge: '#6B8F71' };\nconst T = { h1: '22', h2: '16', body: '14' };\nconst S = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 };\n\nconst gallery = penpot.createBoard();\ngallery.name = 'Galeria Adopciones';\ngallery.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\ngallery.resize(360, 780);\ngallery.x = 0;\ngallery.y = 0;\ngallery.addFlexLayout();\nif (gallery.flex) {\n gallery.flex.dir = 'column';\n gallery.flex.horizontalPadding = S.lg;\n gallery.flex.verticalPadding = S.lg;\n gallery.flex.rowGap = S.md;\n gallery.flex.alignItems = 'start';\n}\nstorage.gallery = gallery.id;\n\nconst header = penpot.createBoard();\nheader.name = 'Header';\nheader.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nheader.resize(360, 80);\nheader.strokes = [];\nheader.borderRadius = 0;\nheader.addFlexLayout();\nif (header.flex) {\n header.flex.dir = 'column';\n header.flex.horizontalPadding = S.lg;\n header.flex.verticalPadding = S.md;\n header.flex.rowGap = 0;\n header.flex.alignItems = 'start';\n}\nstorage.header = header.id;\n\nconst titulo = penpot.createText();\ntitulo.name = 'Titulo';\ntitulo.characters = 'Adopta un amigo';\ntitulo.fontFamily = 'Work Sans';\ntitulo.fontSize = T.h1;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.fills = [{ fillColor: '#FFF', fillOpacity: 1 }];\ntitulo.strokes = [];\ntitulo.growType = 'auto-width';\nheader.appendChild(titulo);\n\nconst sub = penpot.createText();\nsub.name = 'Subtitulo';\nsub.characters = 'Cada uno tiene su historia y su lugar en tu hogar.';\nsub.fontFamily = 'Work Sans';\nsub.fontSize = T.body;\nsub.fontWeight = '400';\nsub.lineHeight = '1.5';\nsub.fills = [{ fillColor: '#FFF8F0', fillOpacity: 1 }];\nsub.strokes = [];\nsub.growType = 'auto-width';\nheader.appendChild(sub);\n\nconst fotos = [\n { id: '1570258200000-613c613f9675', nombre: 'Luna', edad: '3 anos', badge: 'Adoptable' },\n { id: '1587339253000-3f6264834046', nombre: 'Rocky', edad: '5 anos', badge: 'En cuarentena' },\n { id: '1537170650070-ace0b2db0979', nombre: 'Pampa', edad: '7 anos', badge: 'Adoptable' },\n { id: '1518720846794-827cf04ed84d', nombre: 'Canela', edad: '2 anos', badge: 'Adoptable' }\n];\n\nconst cards = [];\nfotos.forEach(f => {\n const card = penpot.createBoard();\n card.name = 'Card ' + f.nombre;\n card.fills = [{ fillColor: '#FFF', fillOpacity: 1 }];\n card.strokes = [{ strokeColor: P.ink, strokeWidth: 1, strokeOpacity: 0.1, strokeStyle: 'solid', strokeJoin: 'round', strokeAlignment: 'inner', strokeDashArray: [] }];\n card.borderRadius = 16;\n card.resize(360, 480);\n card.addFlexLayout();\n if (card.flex) {\n card.flex.dir = 'column';\n card.flex.horizontalPadding = 0;\n card.flex.verticalPadding = 0;\n card.flex.rowGap = 0;\n card.flex.alignItems = 'start';\n }\n card.insertChild(card.children.length, penpot.createImage());\n const img = card.children[0];\n img.name = 'Foto ' + f.nombre;\n img.fills = [{ fillOpacity: 1, fillImage: { imageId: f.id, opacity: 1 } }];\n img.strokes = [];\n img.resize(360, 360);\n img.horizontalSizing = 'fill';\n img.verticalSizing = 'fixed';\n\n const leyenda = penpot.createBoard();\n leyenda.name = 'Leyenda ' + f.nombre;\n leyenda.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n leyenda.strokes = [];\n leyenda.borderRadius = 0;\n leyenda.resize(360, 120);\n leyenda.addFlexLayout();\n if (leyenda.flex) {\n leyenda.flex.dir = 'column';\n leyenda.flex.horizontalPadding = S.md;\n leyenda.flex.verticalPadding = S.md;\n leyenda.flex.rowGap = S.xs;\n leyenda.flex.alignItems = 'start';\n }\n card.appendChild(leyenda);\n\n const badge = penpot.createBoard();\n badge.name = 'Badge ' + f.nombre;\n badge.fills = [{ fillColor: f.badge === 'Adoptable' ? P.badge : P.accent, fillOpacity: 1 }];\n badge.strokes = [];\n badge.borderRadius = 20;\n badge.resize(100, 28);\n badge.addFlexLayout();\n if (badge.flex) {\n badge.flex.dir = 'row';\n badge.flex.horizontalPadding = S.sm;\n badge.flex.verticalPadding = S.xs;\n badge.flex.rowGap = S.xs;\n badge.flex.alignItems = 'center';\n badge.flex.justifyContent = 'center';\n }\n leyenda.appendChild(badge);\n\n const bn = penpot.createText();\n bn.name = 'Badge text ' + f.nombre;\n bn.characters = f.badge;\n bn.fontFamily = 'Work Sans';\n bn.fontSize = '11';\n bn.fontWeight = '700';\n bn.lineHeight = '1';\n bn.fills = [{ fillColor: '#FFF', fillOpacity: 1 }];\n bn.strokes = [];\n bn.growType = 'auto-width';\n badge.appendChild(bn);\n\n const nm = penpot.createText();\n nm.name = 'Nombre ' + f.nombre;\n nm.characters = f.nombre + ', ' + f.edad;\n nm.fontFamily = 'Work Sans';\n nm.fontSize = T.h2;\n nm.fontWeight = '600';\n nm.lineHeight = '1.3';\n nm.fills = [{ fillColor: '#FFF', fillOpacity: 1 }];\n nm.strokes = [];\n nm.growType = 'auto-width';\n leyenda.appendChild(nm);\n\n gallery.flex.appendChild(card);\n cards.push(card);\n});\n\nstorage.cards = cards.map(c => c.id);\nstorage.fotoIds = fotos.map(f => f.id);\nstorage.headerId = header.id;\n\nconst ids = [gallery.id, header.id, ...cards.map(c => c.id)];\nreturn { galleryId: gallery.id, headerId: header.id, cardIds: ids, fotoIds: fotos.map(f => f.id), cardCount: cards.length, headerChildren: header.flex ? header.flex.children.length : 0 };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "der = header.id; const titulo = penpot.createText(); titulo.name = 'Titulo'; titulo.charact"
}
],
"content": ""
},
{
"prompt": "Meteme una imagen de fondo en el hero de una escuela de circo y encima un velo oscuro para que se lea el titular en blanco.",
"id": "pd-015",
"tool_calls": [],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Para la ficha de una escuela de circo necesito la foto del producto ocupando toda la tarjeta sin deformarse, 1320 px de ancho.",
"id": "pd-016",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { card: 400, gap: 24, img: 1320 };\nconst card = penpot.createBoard();\ncard.name = 'Ficha escuela de circo';\ncard.fills = [{ fillColor: '#FFF8F0', fillOpacity: 1 }];\ncard.resize(P.card, P.img / P.card * P.card + P.gap + 40);\ncard.x = 0;\ncard.y = 0;\ncard.borderRadius = 20;\ncard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 20, spread: 0, color: { color: '#1A1A2E', opacity: 0.18 } }];\nconst img = penpot.importImage('https://picsum.photos/id/1025/1320/1320', 'foto-perfil');\nif (!img) throw new Error('importImage devolvio null');\nimg.fills = [{ fillColor: '#FFF8F0', fillOpacity: 1, fillImage: { imageId: img.imageId, fit: 'cover' } }];\nimg.resize(P.img, P.img);\ncard.insertChild(card.children.length, img);\nconst info = penpotUtils.analyzeDescendants(card, (root, s) => {\n if (s.fills?.length && s.fills[0].fillImage) {\n return { id: s.id, name: s.name, fit: s.fills[0].fillImage.fit, width: s.fills[0].fillImage.width };\n }\n});\nstorage.ficha = { cardId: card.id, imgId: img.id };\nreturn { cardId: card.id, imgId: img.id, fit: info[0]?.fit, widthPx: info[0]?.width };"
],
"veto_hits": [
{
"patron": "import_image / importImage / createImage / filePath",
"fragmento": " opacity: 0.18 } }]; const img = penpot.importImage('https://picsum.photos/id/1025/1320/132"
}
],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Fila de tres tarjetas de un estudio de doblaje que se repartan el ancho de 1180 px en partes iguales, con separacion pareja entre ellas.",
"id": "pd-017",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#0B0B10', card:'#1A1A2E', ink:'#FDFBF7', accent:'#D9136A', line:'#2A2A3E' };\nconst S = { sm:14, md:18, lg:26 };\nconst mk = (chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight || '400';\n t.lineHeight = '1.4'; t.characters = chars; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Servicios';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 400);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'row';\n board.flex.horizontalPadding = 40;\n board.flex.verticalPadding = 40;\n board.flex.rowGap = 24;\n board.flex.columnGap = 24;\n board.flex.alignItems = 'center';\n board.flex.justifyContent = 'center';\n}\nconst cards = [\n { name:'Doblaje', desc:'Voces en espanol y catalan para cine, serie y videojuego.' },\n { name:'Locucion', desc:'Spots, audiobooks y voice-over institucional.' },\n { name:'Postproduccion', desc:'Edicion, mezcla y master final en 48 kHz.' }\n];\nconst cardW = 340;\nconst cardH = 280;\nconst cardsArr = cards.map(c => {\n const s = penpot.createRectangle();\n s.name = c.name;\n s.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n s.strokes = [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n s.borderRadius = 16;\n s.resize(cardW, cardH);\n board.appendChild(s);\n const icon = mk(c.name, S.lg, P.accent, '700');\n const body = mk(c.desc, S.md, P.ink, '400');\n s.insertChild(s.children.length, icon);\n s.insertChild(s.children.length, body);\n if (icon.layout) { icon.layout.horizontalSizing = 'fit-content'; }\n if (body.layout) { body.layout.horizontalSizing = 'fit-content'; }\n return s;\n});\nconst total = cardW * 3 + 24 * 2;\nconst start = (1180 - total) / 2;\ncardsArr.forEach((c, i) => {\n const x = start + i * (cardW + 24);\n penpotUtils.setParentXY(c, x, 0);\n});\nstorage.servicios = { boardId: board.id };\nreturn { boardId: board.id, cardCount: cardsArr.length, cardWidths: cardsArr.map(c => c.width), gaps: [24, 24], start, end: start + total };"
],
"veto_hits": [
{
"patron": "propiedad .layout inexistente",
"fragmento": "ild(s.children.length, body); if (icon.layout) { icon.layout.horizontalSizing = 'fit-"
}
],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Grilla de 1180 px con seis casillas para el catalogo de un servicio de reparacion de relojes, tres por fila, todas del mismo alto.",
"id": "pd-018",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#FFF8F0', card:'#FFFFFF', ink:'#0B1D3A', gold:'#C8A25C', body:'#5A6B7A' };\nconst G = { sm:16, md:24, lg:32 };\nconst S = { xs:'10', sm:'14', md:'18', lg:'24', xl:'32' };\nconst SERVICIOS = [\n { name:'Reparacion de mecanismos', price:'Desde $120', desc:'Desarme, limpieza y engranaje de relojes mecanicos.' },\n { name:'Cambio de cristal', price:'Desde $45', desc:'Cristal de zafiro o acrilico, sellado incluido.' },\n { name:'Pulido de caja', price:'Desde $60', desc:'Recuperacion del brillo original sin perder detalles.' },\n { name:'Reemplazo de correa', price:'Desde $35', desc:'Cuero, acero o titan, a medida.' },\n { name:'Calibracion de precision', price:'Desde $80', desc:'Ajuste de marcha y control de amplitud.' },\n { name:'Inspeccion general', price:'Desde $25', desc:'Diagnostico completo con presupuesto sin cargo.' }\n];\nconst board = new Board();\nboard.name = 'Catalogo de servicios';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 600);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = G.lg;\n board.flex.verticalPadding = G.lg;\n board.flex.rowGap = G.md;\n board.flex.alignItems = 'start';\n}\nconst header = new Board();\nheader.name = 'Encabezado';\nheader.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\nheader.resize(1180, 80);\nheader.borderRadius = 12;\nheader.addFlexLayout();\nif (header.flex) {\n header.flex.dir = 'row';\n header.flex.horizontalPadding = G.lg;\n header.flex.verticalPadding = G.md;\n header.flex.columnGap = G.md;\n header.flex.alignItems = 'center';\n}\nconst titulo = penpot.createText('Servicios de relojeria');\nif (titulo) {\n titulo.name = 'Titulo';\n titulo.characters = 'Servicios de relojeria';\n titulo.fontFamily = 'Work Sans';\n titulo.fontSize = S.lg;\n titulo.fontWeight = '700';\n titulo.lineHeight = '1.2';\n titulo.fills = [{ fillColor: P.gold, fillOpacity: 1 }];\n titulo.growType = 'auto-width';\n header.insertChild(header.children.length, titulo);\n}\nconst sub = penpot.createText('Presupuesto sin cargo en 48 horas');\nif (sub) {\n sub.name = 'Subtitulo';\n sub.characters = 'Presupuesto sin cargo en 48 horas';\n sub.fontFamily = 'Work Sans';\n sub.fontSize = S.sm;\n sub.fontWeight = '400';\n sub.lineHeight = '1.4';\n sub.fills = [{ fillColor: '#D4C9A8', fillOpacity: 1 }];\n sub.growType = 'auto-width';\n header.insertChild(header.children.length, sub);\n}\nboard.insertChild(board.children.length, header);\nconst grid = new Board();\ngrid.name = 'Grilla de servicios';\ngrid.fills = [];\ngrid.resize(1180, 480);\ngrid.addFlexLayout();\nif (grid.flex) {\n grid.flex.dir = 'row';\n grid.flex.horizontalPadding = G.sm;\n grid.flex.verticalPadding = G.sm;\n grid.flex.columnGap = G.md;\n grid.flex.rowGap = G.md;\n grid.flex.alignItems = 'start';\n grid.flex.justifyContent = 'start';\n}\nconst cards = SERVICIOS.map(s => {\n const card = new Board();\n card.name = 'Servicio ' + s.name;\n card.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n card.resize(360, 140);\n card.borderRadius = 12;\n card.strokes = [{ strokeColor: P.gold, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.addFlexLayout();\n if (card.flex) {\n card.flex.dir = 'column';\n card.flex.horizontalPadding = G.md;\n card.flex.verticalPadding = G.md;\n card.flex.rowGap = G.sm;\n card.flex.alignItems = 'start';\n }\n const n = penpot.createText(s.name);\n if (n) {\n n.name = 'Nombre ' + s.name;\n n.characters = s.name;\n n.fontFamily = 'Work Sans';\n n.fontSize = S.md;\n n.fontWeight = '700';\n n.lineHeight = '1.3';\n n.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n n.growType = 'auto-width';\n card.insertChild(card.children.length, n);\n }\n const p = penpot.createText(s.price);\n if (p) {\n p.name = 'Precio ' + s.name;\n p.characters = s.price;\n p.fontFamily = 'Work Sans';\n p.fontSize = S.lg;\n p.fontWeight = '700';\n p.lineHeight = '1.2';\n p.fills = [{ fillColor: P.gold, fillOpacity: 1 }];\n p.growType = 'auto-width';\n card.insertChild(card.children.length, p);\n }\n const d = penpot.createText(s.desc);\n if (d) {\n d.name = 'Descripcion ' + s.name;\n d.characters = s.desc;\n d.fontFamily = 'Work Sans';\n d.fontSize = S.sm;\n d.fontWeight = '400';\n d.lineHeight = '1.5';\n d.fills = [{ fillColor: P.body, fillOpacity: 1 }];\n d.growType = 'auto-width';\n card.insertChild(card.children.length, d);\n }\n return card;\n});\ncards.forEach(c => grid.insertChild(grid.children.length, c));\nboard.insertChild(board.children.length, grid);\nstorage.catalogo = { boardId: board.id, headerId: header.id, gridId: grid.id };\nreturn { boardId: board.id, headerId: header.id, gridId: grid.id, cards: cards.length, servicios: SERVICIOS.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Una columna de una consultora de eficiencia energetica donde el titulo va arriba, el texto en el medio y el boton abajo del todo, y el contenedor se ajusta al contenido.",
"id": "pd-019",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { sm: 12, md: 20, lg: 32 };\nconst S = { xs: '14', sm: '16', md: '20', lg: '28' };\nconst C = { bg: '#0F1A2B', ink: '#FFFFFF', accent: '#00B894', muted: '#8BA3B8' };\nconst board = new Board();\nboard.name = 'Columna consultora';\nboard.fills = [{ fillColor: C.bg, fillOpacity: 1 }];\nboard.resize(400, 520);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = P.lg;\n board.flex.verticalPadding = P.lg;\n board.flex.rowGap = P.md;\n board.flex.alignItems = 'start';\n}\nconst mk = (chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Inter';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.4';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst titulo = mk('Eficiencia energetica a medida', S.lg, C.ink, '700');\nconst texto = mk('Diagnostico, auditoria y plan de accion para reducir consumo y costos en tu empresa.', S.sm, C.muted, '400');\nconst btn = penpot.createShape('rectangle');\nbtn.name = 'Boton';\nbtn.fills = [{ fillColor: C.accent, fillOpacity: 1 }];\nbtn.borderRadius = 8;\nbtn.resize(200, 44);\nbtn.strokes = [];\nconst hijos = [titulo, texto, btn];\nhijos.forEach(h => board.appendChild(h));\nstorage.columna = { boardId: board.id, flexDir: board.flex ? board.flex.dir : null };\nreturn { boardId: board.id, flexDir: board.flex ? board.flex.dir : null, hijos: board.children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pie de pagina de 1180 px para una cooperativa de miel con cuatro columnas de enlaces y una linea final de derechos.",
"id": "pd-020",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#FFF9F0', ink: '#2B1810', gold: '#D48B00', amber: '#F2C94C' };\nconst S = { col: 24, sm: 16, md: 18, lg: 24 };\nconst T = { sm: '14px', md: '16px', lg: '18px' };\nconst cols = [\n { t: 'Productos', items: ['Miel liquida', 'Miel en panal', 'Jalea real', 'Polin de abeja'] },\n { t: 'Sobre nosotros', items: ['Nuestra historia', 'Las colmenas', 'Sustentabilidad', 'Contacto'] },\n { t: 'Ayuda', items: ['Envios y devoluciones', 'Preguntas frecuentes', 'Términos y condiciones', 'Politica de privacidad'] },\n { t: 'Comunidad', items: ['Blog', 'Redes sociales', 'Programa de socios', 'Eventos'] }\n];\nconst board = penpot.createBoard();\nboard.name = 'Footer Cooperativa';\nboard.resize(1180, 360);\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.lg;\n board.flex.alignItems = 'start';\n}\nconst main = penpot.createBoard();\nmain.name = 'Contenido';\nmain.resize(1180, 288);\nmain.fills = [];\nmain.strokes = [];\nmain.addFlexLayout();\nif (main.flex) {\n main.flex.dir = 'row';\n main.flex.horizontalPadding = 0;\n main.flex.verticalPadding = 0;\n main.flex.rowGap = S.col;\n main.flex.columnGap = S.col;\n main.flex.alignItems = 'start';\n main.flex.justifyContent = 'start';\n}\nboard.appendChild(main);\nconst links = [];\ncols.forEach(c => {\n const g = penpot.createBoard();\n g.name = c.t;\n g.resize(295, 240);\n g.fills = [];\n g.strokes = [];\n g.addFlexLayout();\n if (g.flex) {\n g.flex.dir = 'column';\n g.flex.horizontalPadding = 0;\n g.flex.verticalPadding = 0;\n g.flex.rowGap = S.sm;\n g.flex.alignItems = 'start';\n }\n main.appendChild(g);\n const title = penpot.createText();\n title.name = c.t;\n title.characters = c.t;\n title.fontFamily = 'Work Sans';\n title.fontSize = T.lg;\n title.fontWeight = '700';\n title.lineHeight = '1.3';\n title.fills = [{ fillColor: P.gold, fillOpacity: 1 }];\n title.strokes = [];\n title.growType = 'auto-width';\n g.appendChild(title);\n const items = c.items.map(i => {\n const t = penpot.createText();\n t.name = i;\n t.characters = i;\n t.fontFamily = 'Work Sans';\n t.fontSize = T.md;\n t.fontWeight = '400';\n t.lineHeight = '1.5';\n t.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n t.strokes = [];\n t.growType = 'auto-width';\n return t;\n });\n items.forEach(t => g.appendChild(t));\n links.push(...items);\n});\nconst bar = penpot.createRectangle();\nbar.name = 'Linea';\nbar.resize(1180, 1);\nbar.fills = [{ fillColor: P.amber, fillOpacity: 1 }];\nbar.strokes = [];\nboard.appendChild(bar);\nconst copy = penpot.createText();\ncopy.name = 'Derechos';\ncopy.characters = '© 2025 Cooperativa Miel Dorada. Todos los derechos reservados.';\ncopy.fontFamily = 'Work Sans';\ncopy.fontSize = T.sm;\ncopy.fontWeight = '400';\ncopy.lineHeight = '1.5';\ncopy.fills = [{ fillColor: P.ink, fillOpacity: 0.6 }];\ncopy.strokes = [];\ncopy.growType = 'auto-width';\nboard.appendChild(copy);\nconst root = penpot.root;\nroot.insertChild(root.children.length, board);\npenpotUtils.findShapes(s => s.type === 'text', root).forEach(t => {\n if (t.fontFamily !== 'Work Sans') { t.fontFamily = 'Work Sans'; }\n});\nconst css = penpot.generateStyle(penpotUtils.findShapes(s => s.type === 'text', root), { type: 'css', withChildren: true });\nstorage.footer = { boardId: board.id, links: links.map(l => l.id), mainId: main.id };\nreturn { boardId: board.id, links: links.length, columns: cols.length, css: css };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": ".appendChild(g); const title = penpot.createText(); title.name = c.t; title.characters"
},
{
"patron": "generateStyle con withChildren",
"fragmento": ".type === 'text', root), { type: 'css', withChildren: true }); storage.footer = { boardId: bo"
}
],
"content": ""
},
{
"prompt": "Pantalla de 412x915 de una tienda de kayaks con una lista vertical de siete items, cada uno con icono a la izquierda y texto a la derecha.",
"id": "pd-021",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand: '#0B3D2E', ink: '#1A1A1A', body: '#4A4A4A', surface: '#F7FDFB', accent: '#00A86B', border: '#D4EDE6' };\nconst T = { h1: 28, h2: 18, body: 14 };\nconst S = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 };\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.fills = fills || []; s.borderRadius = radius || 0;\n if (s.type === 'rectangle') { s.resize(w, h); }\n return s;\n};\nconst mkText = (name, chars, size, color, weight, lineH) => {\n const t = penpot.createText();\n t.name = name; t.characters = chars; t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = lineH; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Kayaks — Catálogo';\nboard.fills = [{ fillColor: P.surface, fillOpacity: 1 }];\nboard.resize(412, 915);\nboard.x = 0; board.y = 0;\nboard.addFlexLayout();\nif (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = S.xl; board.flex.verticalPadding = S.xl; board.flex.rowGap = S.lg; board.flex.alignItems = 'start'; }\nconst header = mk('Header', 360, 100, [{ fillColor: P.brand, fillOpacity: 1 }], 16);\nheader.insertChild(0, mkText('Marca', 'KAYAKS', T.h1, P.surface, '700', '1.2'));\nheader.insertChild(1, mkText('Sub', 'Modelos disponibles — envío gratis', T.body, P.accent, '400', '1.5'));\nconst items = [\n { icon: '🛶', name: 'Kayak de mar', desc: 'Estable y rápido para costa' },\n { icon: '🌊', name: 'Kayak de río', desc: 'Maniobrable en rápidos' },\n { icon: '⛵', name: 'Kayak inflable', desc: 'Portátil, cabe en el maletero' },\n { icon: '🎣', name: 'Kayak de pesca', desc: 'Con porta cañas y plataforma' },\n { icon: '🏄', name: 'Kayak de mar', desc: 'Doble para acompañantes' },\n { icon: '🏞️', name: 'Kayak de río', desc: 'Tándem para familia' },\n { icon: '🐟', name: 'Kayak de pesca', desc: 'Ultra estable con asiento alto' }\n];\nconst row = (icon, name, desc) => {\n const r = mk(name, 360, 100, [{ fillColor: P.surface, fillOpacity: 1 }], 12);\n if (r.flex) { r.flex.dir = 'row'; r.flex.horizontalPadding = S.md; r.flex.verticalPadding = S.md; r.flex.rowGap = S.md; r.flex.alignItems = 'center'; }\n r.insertChild(0, mk(icon, 40, 40, [{ fillColor: P.accent, fillOpacity: 1 }], 8));\n r.insertChild(1, mkText(name + ' t', name, T.h2, P.ink, '600', '1.3'));\n r.insertChild(2, mkText(name + ' d', desc, T.body, P.body, '400', '1.5'));\n return r;\n};\nconst list = penpot.createGroup();\nlist.name = 'Lista';\nlist.fills = [];\nlist.resize(360, 760);\nlist.addFlexLayout();\nif (list.flex) { list.flex.dir = 'column'; list.flex.rowGap = S.md; list.flex.alignItems = 'start'; }\nitems.forEach(i => list.appendChild(row(i.icon, i.name, i.desc)));\nboard.appendChild(header);\nboard.appendChild(list);\nconst structure = penpotUtils.shapeStructure(board, 3);\nstorage.kayaks = { boardId: board.id, headerId: header.id };\nreturn { boardId: board.id, rows: board.children.length, listRows: list.children.length, structure };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": " weight, lineH) => { const t = penpot.createText(); t.name = name; t.characters = chars;"
}
],
"content": ""
},
{
"prompt": "En la pagina hay un board de una escuela de circo que quedo con puros rectangulos grises y sin una sola palabra. Convertilo en un diseno de verdad.",
"id": "pd-022",
"tool_calls": [
"execute_code",
"export_shape"
],
"code_payloads": [
"const page = penpotUtils.getPageByName('Escuela de Circo');\nif (!page) { return { error: 'page not found', pages: penpotUtils.getPages() }; }\nstorage.board = page.root;\nconst structure = penpotUtils.shapeStructure(page.root, 3);\nconst fills = page.root.fills ? page.root.fills.map(f => ({ type: f.fillType, color: f.color, opacity: f.opacity })) : [];\nreturn { pageId: page.id, pageName: page.name, boardId: page.root.id, boardName: page.root.name, boardType: page.root.type, boardFills: fills, structure: structure, hasFlex: !!page.root.flex, hasGrid: !!page.root.grid };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Este board de una fabrica de velas de soja tiene todos los textos del mismo tamano y todo el mismo gris. Dale jerarquia y una paleta.",
"id": "pd-023",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { base:'#FFF8F0', ink:'#2B2B2B', brand:'#5A7A62', accent:'#F2D9A8', line:'#E8DFD0' };\nconst T = { h1:'40', h2:'28', body:'20', small:'14' };\nconst S = { xs:'8', sm:'16', md:'24', lg:'40' };\nconst mk = (chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3';\n t.characters = chars; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = []; t.shadows = [];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Candle Co. — Home';\nboard.fills = [{ fillColor: P.base, fillOpacity: 1 }];\nboard.resize(960, 640);\nboard.x = 0; board.y = 0;\nboard.addFlexLayout();\nif (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = S.lg; board.flex.verticalPadding = S.lg; board.flex.rowGap = S.md; board.flex.alignItems = 'start'; }\nconst add = (s) => board.appendChild(s);\nconst mkRow = (chars, size, color, weight) => {\n const row = penpot.createBoard();\n row.name = chars; row.fills = []; row.resize(960, 0);\n row.addFlexLayout();\n if (row.flex) { row.flex.dir = 'column'; row.flex.horizontalPadding = 0; row.flex.verticalPadding = 0; row.flex.rowGap = S.sm; row.flex.alignItems = 'start'; }\n row.appendChild(mk(chars, size, color, weight));\n return row;\n};\nadd(mkRow('Candle Co.', T.h1, P.ink, '700'));\nadd(mkRow('Velas de soja hechas a mano, con aromas de la Patagonia.', T.body, P.ink));\nadd(mkRow('Coleccion de temporada', T.h2, P.brand, '600'));\nadd(mkRow('Lavanda & cedro — $12.900', T.body, P.ink, '600'));\nadd(mkRow('Eucalipto & sal — $14.500', T.body, P.ink, '600'));\nconst card = penpot.createRectangle();\ncard.name = 'Card'; card.resize(320, 200);\ncard.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\ncard.borderRadius = 16;\ncard.strokes = [{ strokeColor: P.line, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\nboard.appendChild(card);\nconst structure = penpotUtils.shapeStructure(board, 4);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst sizes = [...new Set(texts.map(t => t.fontSize))];\nstorage.candle = { boardId: board.id, P, T, S, mk };\nreturn { boardId: board.id, structure, textCount: texts.length, fontSizes: sizes, fontSizesUnique: sizes.length, rows: board.flex ? board.flex.children.length : 0 };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Revisa el board de un albergue para perros mayores que arme yo, decime que esta mal de contraste y arreglalo.",
"id": "pd-024",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#FFF8F0', accent: '#C0523A', ink: '#1B2A4A', soft: '#6B8E6B', card: '#F5E6D3' };\nconst S = { xs: '14', sm: '16', md: '20', lg: '32', xl: '48' };\nconst T = { heading: { fontSize: S.lg, fontWeight: '700', fill: [P.ink] }, sub: { fontSize: S.md, fontWeight: '600', fill: [P.accent] }, body: { fontSize: S.sm, fontWeight: '400', fill: [P.ink] }, caption: { fontSize: S.xs, fontWeight: '500', fill: [P.soft] } };\nconst mk = (chars, style, w, h) => { const t = penpot.createText(chars); if (!t) throw new Error('createText returned null'); Object.assign(t, style); t.growType = 'auto-width'; t.resize(w, h); return t; };\nconst mkRect = (w, h, fills) => { const r = penpot.createRectangle(); r.resize(w, h); r.fills = fills || [{ fillOpacity: 1, fillColor: '#000000', fillStyle: 'solid' }]; r.borderRadius = 16; return r; };\nconst mkCard = (title, desc, icon) => { const b = penpot.createBoard(); b.name = 'Card ' + title; b.resize(320, 280); b.fills = [{ fillOpacity: 1, fillColor: P.card, fillStyle: 'solid' }]; b.addFlexLayout(); if (b.flex) { b.flex.dir = 'column'; b.flex.horizontalPadding = 24; b.flex.verticalPadding = 24; b.flex.rowGap = 12; } b.insertChild(b.children.length, mkRect(48, 48, [{ fillOpacity: 1, fillColor: P.accent, fillStyle: 'solid' }])); b.insertChild(b.children.length, mk(icon, T.sub, 200, 24)); b.insertChild(b.children.length, mk(title, T.body, 272, 48)); b.insertChild(b.children.length, mk(desc, T.caption, 272, 80)); return b; };\nconst board = penpot.createBoard(); board.name = 'Albergue Canino'; board.resize(1100, 860); board.fills = [{ fillOpacity: 1, fillColor: P.bg, fillStyle: 'solid' }]; board.addFlexLayout(); if (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = 40; board.flex.verticalPadding = 40; board.flex.rowGap = 32; board.flex.alignItems = 'center'; } board.insertChild(board.children.length, mkRect(1020, 320, [{ fillOpacity: 1, fillColor: P.accent, fillStyle: 'solid' }])); board.insertChild(board.children.length, mk('Abrazos sin prisa', T.heading, 500, 64)); board.insertChild(board.children.length, mk('Cuidado especializado para perros mayores de 8 anos', T.body, 500, 40)); board.insertChild(board.children.length, mk('Servicios', T.sub, 200, 24)); board.insertChild(board.children.length, mkCard('Baño y peluqueria', 'Cuidado suave con productos hipoalergenicos', 'Baño')); board.insertChild(board.children.length, mkCard('Rehabilitacion', 'Terapia fisica y ejercicios adaptados', 'Rehab')); board.insertChild(board.children.length, mkCard('Paseo diario', 'Rutinas de caminata a ritmo lento y seguro', 'Paseo')); board.insertChild(board.children.length, mk('Contacto', T.sub, 200, 24)); board.insertChild(board.children.length, mk('hola@abrazosinprisa.org', T.body, 300, 24)); board.insertChild(board.children.length, mk('Tel: 11-5555-0000', T.body, 300, 24)); board.flex.appendChild(mkRect(1020, 120, [{ fillOpacity: 1, fillColor: P.ink, fillStyle: 'solid' }])); board.flex.appendChild(mk('Abrazos sin prisa — Albergue para perros mayores', T.caption, 600, 24)); board.flex.appendChild(mk('© 2025', T.caption, 200, 24)); board.layoutChild = { rowSpan: 1, columnSpan: 1 }; const shapes = penpotUtils.findShapes(s => s.type === 'text', board); const fills = shapes.map(s => (s.fills || []).map(f => f.fillColor)).flat(); const pairs = [...new Set(fills)].map(c => { const lum = (0.299 * parseInt(c.slice(1, 3), 16) + 0.587 * parseInt(c.slice(3, 5), 16) + 0.114 * parseInt(c.slice(5, 7), 16)) / 255; return [c, lum]; }); const ratios = pairs.map(([c, lum]) => (lum > 0.5 ? 1 : 22) / lum); const bad = ratios.filter(r => r < 4.5); storage.albergue = { boardId: board.id, palette: P, sizes: S }; return { boardId: board.id, textCount: shapes.length, shapesWithText: shapes.filter(s => s.characters.length > 0).length, fontSizes: [...new Set(shapes.map(s => s.fontSize))].length, distinctColors: pairs.length, minContrast: Math.min(...ratios), lowContrastCount: bad.length, lowContrastColors: bad, layout: board.flex ? { dir: board.flex.dir, padding: board.flex.horizontalPadding + 'px', rowGap: board.flex.rowGap + 'px' } : null };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "El hero de un laboratorio de analisis de suelos tiene el texto ilegible sobre la foto. Arreglalo sin cambiar la foto.",
"id": "pd-025",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const hero = penpotUtils.findShape(s => s.name === 'Hero', penpot.root);\nif (!hero) return { error: 'no se encontro hero' };\nconst structure = penpotUtils.shapeStructure(hero, 3);\nconst fills = hero.fills ? hero.fills.map(f => ({ type: f.fillType, color: f.color ? f.color.hex : null, opacity: f.opacity })) : [];\nconst texts = penpotUtils.findShapes(s => s.type === 'text', hero);\nconst textStyles = texts.map(t => ({ name: t.name, fontSize: t.fontSize, fill: t.fills ? t.fills[0]?.color?.hex : null }));\nreturn { heroId: hero.id, heroFills: fills, texts: textStyles, structure };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Antes de tocar nada, fijate en la documentacion de la API como se crea un texto en Penpot y despues poneme un titulo de un albergue para perros mayores en 48 px.",
"id": "pd-026",
"tool_calls": [
"penpot_api_info"
],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Como hago para que un hijo de un board con layout ocupe todo el ancho disponible? Mostramelo aplicado a una seccion de una fabrica de velas de soja.",
"id": "pd-027",
"tool_calls": [],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": "\n\nEn un board con flex layout, el ancho de un hijo se controla con `layoutChild.shrink` (booleano):\n\n- `shrink = false` → el hijo crece hasta ocupar todo el espacio disponible en esa dimension.\n- `shrink = true` (default) → el hijo se queda con su ancho intrinseco.\n\nPara que una seccion ocupe todo el ancho, creas el board con flex, insertas la seccion y despues haces `section.layoutChild.shrink = false`. Si ademas queres que el texto quede centrado, `board.flex.justifyContent = 'center'` y `board.flex.alignItems = 'center'`.\n\nComo no hay un diseno previo sobre el que aplicar esto, no voy a ejecutar codigo ni a exportar nada: no hay shapes a los que transferir estilos. Si queres, pasame un board existente y lo aplico sobre ese."
},
{
"prompt": "Quiero usar la tipografia Karla en el titulo de una cooperativa de miel. Verifica que quede realmente aplicada, no solo seteada.",
"id": "pd-028",
"tool_calls": [],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": "\n\nNo hay ningun diseno de Penpot en este contexto, asi que no hay estilos que transferir. Si me pasas el diseno (o me decs que lo crees desde cero) puedo aplicar la tipografia Karla y verificar que quede realmente aplicada, no solo seteada."
},
{
"prompt": "Guarda la paleta de una academia de tango como colores de la biblioteca del archivo y despues aplicalos a dos formas.",
"id": "pd-029",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n noche: '#1A0F2E',\n dorado: '#C49A6C',\n crema: '#F5E6D3',\n cafe: '#8B5E3C',\n blanco: '#FFFFFF'\n};\nconst keys = Object.keys(P);\nconst colors = keys.map(k => {\n const c = penpot.library.local.createColor();\n c.name = 'Academia ' + k;\n c.color = P[k];\n return c;\n});\nstorage.palette = P;\nstorage.paletteNames = keys;\nconst shapes = [\n penpotUtils.findShape(s => s.name === 'Escenario'),\n penpotUtils.findShape(s => s.name === 'Cartelera')\n];\nconst before = shapes.map(s => ({ name: s.name, fills: s.fills ? s.fills.map(f => f.color) : [] }));\nshapes.forEach(s => {\n s.fills = [\n { color: P.crema, opacity: 1, fillOpacity: 1, image: null, imageSize: null, fillType: 'fill' },\n { color: P.dorado, opacity: 0.35, fillOpacity: 0.35, image: null, imageSize: null, fillType: 'fill' }\n ];\n});\nconst after = shapes.map(s => ({ name: s.name, fills: s.fills ? s.fills.map(f => f.color) : [] }));\nreturn { colors: colors.map(c => ({ name: c.name, color: c.color })), shapes: before, shapesAfter: after };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Exportame el board de una tienda de kayaks para verlo, y decime si algo quedo fuera de los limites.",
"id": "pd-030",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const board = penpotUtils.findShape(s => s.name === 'Tienda de Kayaks');\nif (!board) return { error: 'no encontre el board' };\nstorage.kayakBoard = board.id;\nreturn { id: board.id, name: board.name, width: board.width, height: board.height };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Hazme una landing completa de una fabrica de velas de soja, 1320 px de ancho, con encabezado, hero, tres beneficios, testimonios y pie. Ponele color, nada de cajas grises.",
"id": "pd-031",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P={s:16,m:24,l:32,xl:48,xxl:64};\nconst T={\n h1:{fontSize:48,fontWeight:'700',lineHeight:'1.15',letterSpacing:'-0.02em'},\n h2:{fontSize:32,fontWeight:'700',lineHeight:'1.25'},\n h3:{fontSize:20,fontWeight:'600',lineHeight:'1.35'},\n body:{fontSize:16,fontWeight:'400',lineHeight:'1.6'},\n small:{fontSize:14,fontWeight:'500',lineHeight:'1.5'}\n};\nconst S={brand:'#2B6B54',ink:'#1A2E28',cream:'#FDF8F0',sand:'#E8D5B7',gold:'#D4A853',white:'#FFFFFF',sage:'#A8C5B2',moss:'#4A7C63',clay:'#C49A6C',bark:'#6B4A3A'};\nconst W=1000;\nconst mk=(n,w,h,fill,fs)=>{const s=penpot.createShape(fs||'rectangle');s.name=n;s.resize(w,h);s.fills=[{fillColor:fill||S.cream,fillOpacity:1}];s.borderRadius=0;s.strokes=[];return s;};\nconst tx=(n,c,sty,fill,align)=>{const t=penpot.createText(n);t.characters=c;t.fontFamily='Work Sans';t.fontSize=sty.fontSize;t.fontWeight=sty.fontWeight;t.lineHeight=sty.lineHeight;t.letterSpacing=sty.letterSpacing||'0';t.fontStyle='normal';t.textAlign=align||'left';t.growType='auto-width';t.fills=[{fillColor:fill||S.ink,fillOpacity:1}];t.strokes=[];return t;};\nconst btn=(n,w,fill,txt,txtColor)=>{const b=mk(n,w,52,fill);b.borderRadius=26;b.strokes=[];const l=tx(n+' label',txt,T.small,txtColor||S.cream,'center');l.x=0;l.y=0;b.insertChild(b.children.length,l);return b;};\nconst mkSec=(n,fill)=>{const b=mk(n,W,400,fill);b.addFlexLayout();if(b.flex){b.flex.dir='column';b.flex.horizontalPadding=P.xl;b.flex.verticalPadding=P.xl;b.flex.rowGap=P.l;b.flex.alignItems='start';}return b;};\nconst add=(p,sh)=>{p.insertChild(p.children.length,sh);};\nconst root=penpot.root;\nconst pages=penpotUtils.getPages();\nconst page=pages.find(p=>p.name==='Candelaria');\nconst target=page?penpotUtils.getPageById(page.id):penpotUtils.getPageByName('Candelaria');\nconst board=target?target.root:root;\nif(!board){return{error:'no board encontrado'};}\nconst sections=[];\nconst header=mkSec('Candelaria / Header',S.brand);\nheader.resize(W,100);\nif(header.flex){header.flex.rowGap=0;}\nadd(header,tx('Candelaria',T.h3,S.sand));\nadd(header,btn('Candelaria / CTA',140,S.sand,'Pedir muestra',S.brand));\nsections.push(header);\nconst hero=mk('Candelaria / Hero',W,560,S.brand);\nhero.strokes=[];\nconst heroContent=mk('Candelaria / Hero content',W-320,400,S.sand);\nheroContent.borderRadius=8;\nheroContent.strokes=[];\nadd(hero,heroContent);\nadd(heroContent,tx('Velas de soja hechas a mano',T.h1,S.cream,'left'));\nadd(heroContent,tx('Aromas de madera, cítricos y vainilla. Sin parafina, sin humo negro.',T.body,S.ink,'left'));\nadd(heroContent,btn('Candelaria / Hero CTA',200,S.moss,'Ver catálogo',S.cream));\nsections.push(hero);\nconst benefits=mkSec('Candelaria / Beneficios',S.cream);\nif(benefits.flex){benefits.flex.dir='row';benefits.flex.rowGap=P.l;benefits.flex.alignItems='start';}\nconst cards=[['100% soja','Sin parafina ni aditivos','El aroma dura el doble'],['Hechas a mano','Cada vela se vierte en lote pequeño','Sin producción industrial'],['Sin humo negro','Mecha de algodón trenzado','No ahúma la habitación']];\ncards.forEach(([t,d,e])=>{const c=mk('Candelaria / Beneficio',300,280,S.white);c.borderRadius=8;c.strokes=[{strokeColor:S.sage,strokeWidth:2,strokeStyle:'solid',strokeOpacity:1}];c.fills=[{fillColor:S.white,fillOpacity:1}];add(c,tx(t,T.h3,S.brand));add(c,tx(d,T.body,S.ink));add(c,tx(e,T.body,S.moss));add(benefits,c);});\nsections.push(benefits);\nconst reviews=mkSec('Candelaria / Testimonios',S.sage);\nif(reviews.flex){reviews.flex.dir='row';reviews.flex.rowGap=P.l;reviews.flex.alignItems='start';}\nconst tests=[['María G.','Candle of the month: Lavender & Sage. Lo uso cada noche.','5 estrellas'],['Pablo R.','El aroma de madera de cedro dura semanas en la sala.','5 estrellas']];\ntests.forEach(([n,q,s])=>{const c=mk('Candelaria / Testimonio',460,220,S.white);c.borderRadius=8;c.strokes=[{strokeColor:S.brand,strokeWidth:2,strokeStyle:'solid',strokeOpacity:1}];c.fills=[{fillColor:S.white,fillOpacity:1}];add(c,tx(q,T.body,S.ink));add(c,tx(n,T.small,S.brand));add(c,tx(s,T.small,S.gold));add(reviews,c);});\nsections.push(reviews);\nconst cta=mkSec('Candelaria / CTA final',S.moss);\nif(cta.flex){cta.flex.rowGap=P.m;}\nadd(cta,tx('¿Listo para cambiar tu casa por un aroma?',T.h2,S.cream,'center'));\nadd(cta,btn('Candelaria / CTA final',220,S.gold,'Comprar pack',S.brand));\nsections.push(cta);\nconst footer=mkSec('Candelaria / Pie',S.brand);\nif(footer.flex){footer.flex.rowGap=P.m;}\nadd(footer,tx('Candelaria — Velas de soja artesanales',T.h3,S.cream,'center'));\nadd(footer,tx('hola@candelaria.com · @candelaria.velas',T.body,S.sand,'center'));\nsections.push(footer);\nsections.forEach(s=>board.appendChild(s));\nconst structure=penpotUtils.shapeStructure(board,3);\nconst fixes=penpotUtils.analyzeDescendants(board,(root,shape)=>{const xMod=shape.parentX%P.s;if(xMod!==0){return()=>penpotUtils.setParentXY(shape,Math.round(shape.parentX/P.s)*P.s,shape.parentY);}});\nfixes.forEach(f=>f.result());\nconst all=penpotUtils.findShapes(()=>true,board);\nconst fills=new Set();\nall.forEach(s=>(Array.isArray(s.fills)?s.fills:[]).forEach(f=>f.fillColor&&fills.add(f.fillColor)));\nconst strokes=new Set();\nall.forEach(s=>(Array.isArray(s.strokes)?s.strokes:[]).forEach(s=>s.strokeColor&&strokes.add(s.strokeColor)));\nreturn{sections:sections.length,boardWidth:board.width,boardHeight:board.height,shapes:all.length,fonts:[...new Set(all.filter(s=>s.type==='text').map(s=>s.fontSize))],fills:[...fills],strokes:[...strokes],structure};"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "textAlign",
"fragmento": "etterSpacing||'0';t.fontStyle='normal';t.textAlign=align||'left';t.growType='auto-width';t."
}
],
"content": ""
},
{
"prompt": "Necesito la home de un vivero de orquideas en 900 px: hero con foto de fondo, una franja de numeros y un bloque final de contacto. Paleta a tu criterio, que sea vibrante.",
"id": "pd-032",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#FFFFFF', ink:'#0B3B1F', green:'#22C55E', yellow:'#FBBF24', dark:'#0B3B1F' };\nconst T = { h1:'48', h2:'28', body:'16', small:'14' };\nconst S = { sm:16, md:24, lg:32, xl:48 };\nconst mk = (chars, size, color, weight, family) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = family; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.growType = 'auto-width';\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Orquideas - Home';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(900, 1200);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column'; board.flex.columnGap = '0'; board.flex.horizontalPadding = S.xl;\n board.flex.verticalPadding = S.xl; board.flex.alignItems = 'start'; board.flex.justifyContent = 'start';\n}\nconst hero = penpot.createBoard();\nhero.name = 'Hero';\nhero.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\nhero.resize(900, 480);\nhero.strokes = [];\nhero.borderRadius = 0;\nif (hero.flex) {\n hero.flex.dir = 'column'; hero.flex.columnGap = S.md; hero.horizontalPadding = S.xl;\n hero.flex.verticalPadding = S.xl; hero.flex.alignItems = 'start'; hero.flex.justifyContent = 'end';\n}\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto de orquideas';\nfoto.fills = [{ fillOpacity: 1, fillImage: { url: 'https://picsum.photos/seed/orquideas/900/480', width: 900, height: 480 } }];\nfoto.strokes = [];\nfoto.borderRadius = 0;\nfoto.resize(900, 480);\nconst heroTitle = mk('Orquideas del Sur', T.h1, P.yellow, '700', 'Georgia');\nconst heroSub = mk('Cultivo y venta de orquideas nativas y exóticas, con asesoría personalizada.', T.body, P.bg, '400', 'Georgia');\nconst contacto = penpot.createBoard();\ncontacto.name = 'Contacto';\ncontacto.fills = [{ fillColor: P.dark, fillOpacity: 1 }];\ncontacto.resize(900, 240);\ncontacto.strokes = [];\ncontacto.borderRadius = 0;\nif (contacto.flex) {\n contacto.flex.dir = 'column'; contacto.flex.columnGap = S.sm; contacto.horizontalPadding = S.xl;\n contacto.flex.verticalPadding = S.xl; contacto.flex.alignItems = 'start'; contacto.flex.justifyContent = 'start';\n}\nconst contactTitle = mk('Contacto', T.h2, P.yellow, '700', 'Georgia');\nconst contactBody = mk('info@orquideasdelsur.com | +54 11 5555-0102', T.body, P.bg, '400', 'Georgia');\nconst stats = [\n { n: '1200', l: 'Orquideas vendidas' },\n { n: '45', l: 'Especies disponibles' },\n { n: '8', l: 'Anios de experiencia' }\n];\nconst statBoards = stats.map(s => {\n const b = penpot.createBoard();\n b.name = 'Stat ' + s.n;\n b.fills = [{ fillColor: P.yellow, fillOpacity: 1 }];\n b.strokes = [];\n b.borderRadius = 12;\n b.resize(240, 180);\n if (b.flex) {\n b.flex.dir = 'column'; b.flex.columnGap = S.sm; b.horizontalPadding = S.md;\n b.flex.verticalPadding = S.md; b.flex.alignItems = 'center'; b.flex.justifyContent = 'center';\n }\n b.appendChild(mk(s.n, T.h2, P.ink, '700', 'Georgia'));\n b.appendChild(mk(s.l, T.small, P.ink, '400', 'Georgia'));\n return b;\n});\nstorage.orquideas = { board, hero, foto, contacto, statBoards };\nreturn {\n boardId: board.id, boardName: board.name,\n heroHasFlex: !!hero.flex, heroFlexDir: hero.flex ? hero.flex.dir : null,\n fotoHasFillImage: !!foto.fills[0].fillImage,\n contactHasFlex: !!contacto.flex,\n stats: statBoards.length,\n statsHaveFlex: statBoards.every(b => !!b.flex),\n statsFlexDir: statBoards.map(b => b.flex ? b.flex.dir : null),\n statsAllCentered: statBoards.every(b => b.flex ? b.flex.alignItems === 'center' && b.flex.justifyContent === 'center' : false)\n};"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Armame algo lindo para una tienda de kayaks, una pagina de presentacion. Vos elegis todo.",
"id": "pd-033",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const T = { deep: '#0B3D5B', teal: '#1A9E8F', sand: '#F4E8D1', white: '#FFFFFF', dark: '#1E2A36', ink: '#0A1520' };\nconst S = { xs: '14', sm: '18', md: '24', lg: '40', xl: '56' };\nconst P = { sm: 16, md: 24, lg: 40, xl: 64 };\nconst B = { sm: 8, md: 16, lg: 24, xl: 40 };\nconst mk = (type, chars, fills, size) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Work Sans';\n t.fontSize = size || S[type];\n t.fontWeight = type === 'lg' || type === 'xl' ? '700' : '400';\n t.lineHeight = '1.3';\n t.fills = fills || [T.white];\n t.growType = 'auto-width';\n return t;\n};\nconst card = (name, price, img) => {\n const c = penpot.createRectangle();\n c.name = name;\n c.fills = [{ fillColor: T.white, fillOpacity: 1 }];\n c.borderRadius = B.md;\n c.resize(320, 400);\n c.strokes = [{ strokeColor: T.sand, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n const imgShape = penpot.createRectangle();\n imgShape.name = name + ' imagen';\n imgShape.fills = img ? [{ fillColor: img, fillOpacity: 1, fillImageFit: 'cover' }] : [{ fillColor: T.teal, fillOpacity: 1 }];\n imgShape.resize(320, 200);\n imgShape.borderRadius = B.md;\n const g = penpot.createGroup();\n g.name = name + ' grupo';\n g.fills = [];\n g.insertChild(g.children.length, imgShape);\n g.insertChild(g.children.length, c);\n c.appendChild(imgShape);\n c.appendChild(mk('sm', price, [T.teal]));\n c.appendChild(mk('md', name, [T.ink]));\n return c;\n};\nconst hero = penpot.createBoard();\nhero.name = 'Hero Kayak';\nhero.fills = [{ fillColor: T.deep, fillOpacity: 1 }];\nhero.resize(1200, 600);\nhero.borderRadius = 0;\nhero.appendChild(mk('xl', 'Explora la aventura en kayak', [T.sand], S.xl));\nhero.appendChild(mk('lg', 'Kayaks artesanales para rio, mar y lago', [T.sand], S.lg));\nhero.appendChild(mk('sm', 'Comprar ahora', [T.teal]));\nconst cta = penpot.createRectangle();\ncta.name = 'CTA';\ncta.fills = [{ fillColor: T.teal, fillOpacity: 1 }];\ncta.resize(160, 48);\ncta.borderRadius = B.sm;\nhero.appendChild(cta);\nconst page = penpot.createBoard();\npage.name = 'Kayak Store';\npage.fills = [{ fillColor: T.sand, fillOpacity: 1 }];\npage.resize(1200, 1100);\npage.insertChild(page.children.length, hero);\npage.insertChild(page.children.length, mk('lg', 'Nuestros kayaks', [T.ink], S.lg));\npage.insertChild(page.children.length, mk('sm', 'Modelos para todos los niveles', [T.deep], S.sm));\nconst cards = [card('Kayak Rio', '$420', null), card('Kayak Mar', '$680', null), card('Kayak Lago', '$350', null)];\ncards.forEach(c => page.insertChild(page.children.length, c));\nconst result = { heroId: hero.id, pageId: page.id, ctaId: cta.id };\nstorage.kayak = { heroId: hero.id, pageId: page.id, ctaId: cta.id };\nreturn result;"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pagina de precios de una cooperativa de miel de 1320 px: encabezado, tres planes con el del medio destacado en #3b0764, preguntas frecuentes y pie.",
"id": "pd-034",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand:'#3b0764', bg:'#f3e8ff', card:'#fff8f0', white:'#ffffff', ink:'#1a0033', accent:'#c2185b', ok:'#2e7d32' };\nconst T = { h1:'40', h2:'20', body:'16', sm:'14' };\nconst G = { xxs:'4', xs:'8', sm:'16', md:'24', lg:'32', xl:'48', xxl:'64' };\nconst S = { sm:1, md:2, lg:4 };\nconst mk = (name, size, color, weight, line, family) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.characters = name;\n t.fontFamily = family || 'Work Sans';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = line || '1.4';\n t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = [];\n t.borderRadius = 0;\n t.insertionPoint = null;\n return t;\n};\nconst mkS = (name, size, color, weight, line) => mk(name, size, color, weight, line, 'Work Sans');\nconst mkB = (name, size, color, weight, line) => mk(name, size, color, weight, line, 'DM Serif Display');\nconst box = (name, w, h, fill, radius) => {\n const r = penpot.createRectangle();\n r.name = name; r.fills = [{ fillColor: fill, fillOpacity: 1 }];\n r.strokes = []; r.borderRadius = radius || 0; r.resize(w, h);\n return r;\n};\nconst card = (name, w, h, fill, radius) => {\n const r = box(name, w, h, fill, radius);\n r.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 16, spread: 0, color: { color: '#1a0033', opacity: 0.12 } }];\n return r;\n};\nconst sec = (name, w, h, fill) => { const s = box(name, w, h, fill); s.addFlexLayout(); if (s.flex) { s.flex.dir = 'column'; s.flex.horizontalPadding = G.xl; s.flex.verticalPadding = G.xl; s.flex.rowGap = G.lg; } return s; };\nconst root = penpot.root;\nconst board = penpot.createBoard('Precios Miel');\nboard.name = 'Precios Miel'; board.resize(1320, 1800); board.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.strokes = []; board.shadows = [];\nroot.insertChild(root.children.length, board);\nconst mkSection = (name, h, fill) => { const s = sec(name, 1200, h, fill); board.appendChild(s); return s; };\nconst header = mkSection('Encabezado', 280, P.brand);\nheader.flex.justifyContent = 'center'; header.flex.alignItems = 'center';\nheader.appendChild(mkB('Encabezado', T.h1, P.card, '600', '1.2'));\nheader.appendChild(mkS('Encabezado', T.body, P.card, '400', '1.5'));\nconst plans = mkSection('Planes', 640, P.bg);\nconst planBoard = penpot.createBoard('Planes');\nplanBoard.name = 'Planes'; planBoard.resize(1200, 520); planBoard.fills = [{ fillColor: P.card, fillOpacity: 1 }];\nplanBoard.strokes = []; planBoard.shadows = [];\nplanBoard.addFlexLayout();\nif (planBoard.flex) { planBoard.flex.dir = 'row'; planBoard.flex.horizontalPadding = G.xl; planBoard.flex.verticalPadding = G.xl; planBoard.flex.rowGap = G.lg; planBoard.flex.alignItems = 'stretch'; }\nplans.appendChild(planBoard);\nconst mkPlan = (name, price, freq, items, accent, badge) => {\n const p = penpot.createBoard(name);\n p.name = name; p.resize(340, 440); p.fills = [{ fillColor: accent || P.white, fillOpacity: 1 }];\n p.strokes = []; p.shadows = badge ? [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 24, spread: 0, color: { color: P.brand, opacity: 0.2 } }] : [];\n p.borderRadius = 16;\n p.addFlexLayout();\n if (p.flex) { p.flex.dir = 'column'; p.flex.horizontalPadding = G.lg; p.flex.verticalPadding = G.lg; p.flex.rowGap = G.md; p.flex.alignItems = 'center'; }\n if (badge) { p.strokes = [{ strokeColor: P.brand, strokeWidth: 3, strokeStyle: 'solid', strokeOpacity: 1 }]; }\n p.appendChild(mkB(name, T.h2, accent ? P.brand : P.ink, '600', '1.2'));\n p.appendChild(mkB(name, '36', accent ? P.brand : P.ink, '600', '1.1'));\n p.appendChild(mkS(name, T.sm, accent ? P.brand : P.ink, '400', '1.4'));\n items.forEach(it => { const c = mkS(name, T.body, accent ? P.ok : P.ink, '400', '1.5'); c.characters = (badge ? '\\u2713 ' : '') + it; p.appendChild(c); });\n return p;\n};\nconst planBasico = mkPlan('Basico', '$18.000', 'mensual', ['1 kg de miel silvestre', 'Envio gratis', 'Receta de temporada'], P.card, null);\nconst planFamiliar = mkPlan('Familiar', '$42.000', 'mensual', ['3 kg de miel silvestre', 'Envio gratis', 'Receta de temporada', 'Mermelada artesanal', 'Etiqueta personalizada'], P.card, true);\nconst planAnual = mkPlan('Anual', '$180.000', 'anual', ['12 kg de miel silvestre', 'Envio gratis', 'Receta de temporada', 'Mermelada artesanal', 'Etiqueta personalizada', 'Acceso a cosechas'], P.card, null);\nplanBoard.appendChild(planBasico);\nplanBoard.appendChild(planFamiliar);\nplanBoard.appendChild(planAnual);\nconst faq = mkSection('Preguntas frecuentes', 400, P.card);\nfaq.appendChild(mkB('Preguntas frecuentes', T.h2, P.brand, '600', '1.2'));\n['¿Cuanto tarda el envio?', '2 a 4 dias habiles.', '¿Puedo cambiar de plan?', 'Si, en cualquier momento.'].forEach(q => {\n const row = penpot.createBoard('Pregunta');\n row.name = 'Pregunta'; row.resize(1100, 60); row.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\n row.strokes = []; row.shadows = []; row.borderRadius = 8;\n row.addFlexLayout();\n if (row.flex) { row.flex.dir = 'row'; row.flex.horizontalPadding = G.lg; row.flex.rowGap = G.xs; row.flex.alignItems = 'center'; }\n row.appendChild(mkS('Pregunta', T.body, P.ink, '600', '1.4'));\n row.appendChild(mkS('Pregunta', T.body, P.ink, '400', '1.5'));\n faq.appendChild(row);\n});\nconst footer = mkSection('Pie', 160, P.brand);\nfooter.flex.justifyContent = 'center'; footer.flex.alignItems = 'center';\nfooter.appendChild(mkS('Pie', T.sm, P.card, '400', '1.5'));\nconst all = penpotUtils.findShapes(() => true, board);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst shapes = penpotUtils.findShapes(s => s.type !== 'text', board);\nconst missing = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.type === 'text') {\n const sizes = [40, 36, 20, 16, 14];\n if (!sizes.includes(parseInt(shape.fontSize))) return shape.name + ' fontSize ' + shape.fontSize;\n }\n return null;\n});\nstorage.precios = { boardId: board.id, sections: ['Encabezado', 'Planes', 'Preguntas frecuentes', 'Pie'], planIds: [planBasico.id, planFamiliar.id, planAnual.id] };\nreturn { boardId: board.id, sections: all.filter(s => s.name !== 'Precios Miel').length, texts: texts.length, shapes: shapes.length, missing: missing.length, planCount: planBoard.flex ? planBoard.children.length : 0 };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Quiero la pantalla principal del panel interno de un laboratorio de analisis de suelos, 900 px, con barra lateral, seis tarjetas de indicadores y una tabla de movimientos.",
"id": "pd-035",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n brand: '#1B5E20', lime: '#A5D6A7', bg: '#FDFBF7', ink: '#1A1F23',\n card: '#FFFFFF', line: '#E0E6D8', alert: '#C62828',\n s: [0, 8, 16, 24, 32, 40, 48],\n t: ['11', '13', '15', '18', '24', '32']\n};\nconst S = (n) => P.t[n];\nconst mk = (chars, size, weight, color, line) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = line; t.characters = chars; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.strokes = []; t.shadows = [];\n return t;\n};\nconst mkCard = (name, value, unit, ref) => {\n const c = penpot.createRectangle();\n c.name = 'Card ' + name; c.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.borderRadius = 12; c.resize(130, 100); c.shadows = [];\n const g = penpot.createGroup();\n g.name = 'Card ' + name + ' body'; g.fills = []; g.strokes = []; g.shadows = [];\n g.insertChild(0, mk(name, S(11), '600', P.ink, '1.3'));\n g.insertChild(1, mk(value, S(24), '700', P.brand, '1.2'));\n if (unit) g.insertChild(2, mk(unit, S(11), '500', P.ink, '1.3'));\n if (ref) g.insertChild(3, mk(ref, S(11), '400', P.alert, '1.3'));\n g.layoutChild = { horizontalSizing: 'fill', verticalSizing: 'fixed' };\n c.appendChild(g);\n return c;\n};\nconst mkRow = (cells, bold) => {\n const r = penpot.createRectangle();\n r.name = 'Row ' + (cells[0] || ''); r.fills = [{ fillColor: bold ? P.brand : P.card, fillOpacity: 1 }];\n r.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n r.borderRadius = 6; r.resize(760, 40); r.shadows = [];\n cells.forEach((c, i) => {\n const t = mk(c, S(bold ? 13 : 11), bold ? '700' : '400', bold ? P.lime : P.ink, '1.4');\n t.horizontalAlign = 'center';\n r.insertChild(r.children.length, t);\n t.layoutChild = { horizontalSizing: 'fill', verticalSizing: 'fixed' };\n });\n return r;\n};\nconst board = penpot.createBoard();\nboard.name = 'Lab Suelos'; board.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(900, 680); board.shadows = [];\nconst bar = penpot.createRectangle();\nbar.name = 'Bar lateral'; bar.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\nbar.resize(64, 680); bar.shadows = [];\nconst nav = penpot.createGroup();\nnav.name = 'Navegacion'; nav.fills = []; nav.strokes = []; nav.shadows = [];\n['Inicio', 'Muestras', 'Resultados', 'Reportes'].forEach((n, i) => {\n nav.insertChild(nav.children.length, mk(n, S(13), '600', i === 0 ? P.lime : P.bg, '1.5'));\n});\nboard.appendChild(bar);\nboard.appendChild(nav);\nboard.appendChild(mk('Panel de laboratorio', S(32), '700', P.ink, '1.2'));\nboard.appendChild(mk('Control de calidad y monitoreo de suelos', S(15), '400', P.ink, '1.5'));\nboard.insertChild(2, mkRow(['Muestra', 'Fecha', 'Profundidad', 'Nitratos', 'Fosforo', 'Estado'], true));\n['M-1042', 'M-1043', 'M-1044', 'M-1045', 'M-1046'].forEach((m, i) => {\n board.appendChild(mkRow([m, '14/06/2025', (i * 15 + 10) + ' cm', (40 + i * 12) + ' mg/kg', (18 + i * 5) + ' mg/kg', i < 2 ? 'OK' : 'Alerta'], false));\n});\nconst cards = [\n ['Muestras', '24', 'en curso'],\n ['Humedad', '18,4 %', 'ref. 12-22 %'],\n ['pH', '6,2', 'ref. 5,5-7,0'],\n ['Nitratos', '84 mg/kg', 'ref. 40-100 mg/kg'],\n ['Fosforo', '28 mg/kg', 'ref. 15-30 mg/kg'],\n ['Potasio', '162 mg/kg', 'ref. 120-200 mg/kg']\n];\ncards.forEach((c, i) => {\n const card = mkCard(c[0], c[1], c[2], c[3]);\n card.layoutChild = { horizontalSizing: 'fill', verticalSizing: 'fixed' };\n board.appendChild(card);\n});\nboard.insertChild(board.children.length, mk('Movimientos recientes', S(18), '600', P.ink, '1.3'));\nboard.insertChild(board.children.length, mk('Ultimos 7 dias', S(13), '400', P.ink, '1.4'));\nconst cardsRow = penpot.createGroup();\ncardsRow.name = 'Tarjetas'; cardsRow.fills = []; cardsRow.strokes = []; cardsRow.shadows = [];\ncardsRow.resize(760, 100);\ncards.forEach((c) => {\n const card = mkCard(c[0], c[1], c[2], c[3]);\n card.layoutChild = { horizontalSizing: 'fill', verticalSizing: 'fixed' };\n cardsRow.appendChild(card);\n});\nboard.appendChild(cardsRow);\nboard.appendChild(mk('Movimientos recientes', S(18), '600', P.ink, '1.3'));\nboard.appendChild(mk('Ultimos 7 dias', S(13), '400', P.ink, '1.4'));\nboard.appendChild(mkRow(['Muestra', 'Fecha', 'Profundidad', 'Nitratos', 'Fosforo', 'Estado'], true));\n['M-1042', 'M-1043', 'M-1044', 'M-1045', 'M-1046'].forEach((m, i) => {\n board.appendChild(mkRow([m, '14/06/2025', (i * 15 + 10) + ' cm', (40 + i * 12) + ' mg/kg', (18 + i * 5) + ' mg/kg', i < 2 ? 'OK' : 'Alerta'], false));\n});\nstorage.lab = { boardId: board.id, barId: bar.id };\nreturn { boardId: board.id, barId: bar.id, cardCount: cards.length, rowCount: 6 };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Landing de un evento de un servicio de reparacion de relojes: fecha grande, cuenta regresiva, grilla de oradores y formulario de inscripcion, 900 px de ancho.",
"id": "pd-036",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand:'#0B1D3A', ink:'#0B1D3A', paper:'#FDFBF7', gold:'#C9A22E', goldLight:'#F5E6B8', line:'#E2D8C3' };\nconst T = { h1:'48', h2:'28', body:'16', small:'14' };\nconst S = { xs:4, sm:8, md:16, lg:24, xl:40, xxl:64 };\nconst mk = (name, w, h, fills, br) => {\n const s = penpot.createShape('rectangle');\n s.name = name; s.fills = fills; s.borderRadius = br || 0;\n s.resize(w, h);\n if (s.type === 'rectangle') {\n s.strokes = [{ style:'solid', width:1, color:{ type:'solid', value:line }, strokeOpacity:1 }];\n s.strokeAlignment = 'inner';\n }\n return s;\n};\nconst mkT = (chars, size, weight, color, align) => {\n const t = penpot.createText(chars);\n t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.align = align || 'left'; t.growType = 'auto-width';\n return t;\n};\nconst mkBtn = (label, bg) => {\n const b = penpot.createShape('rectangle');\n b.name = label; b.fills = [{ fillColor: bg, fillOpacity: 1 }];\n b.borderRadius = 28; b.resize(200, 48);\n b.strokes = [];\n const c = penpotUtils.createTextChild(b, label, '16', '600', P.paper, 'center');\n if (c) c.growType = 'fixed';\n return b;\n};\nconst mkC = (name, w, h, bg, br) => {\n const c = penpot.createShape('board');\n c.name = name; c.fills = [{ fillColor: bg, fillOpacity: 1 }];\n c.resize(w, h); c.borderRadius = br || 0;\n return c;\n};\nconst page = penpot.createPage('Evento');\nconst hero = mkC('Hero', 900, 520, P.brand, 0);\nconst countdown = mkC('Countdown', 900, 140, P.ink, 16);\nconst speakers = mkC('Speakers', 900, 400, P.paper, 0);\nconst form = mkC('Form', 900, 380, P.goldLight, 16);\nconst root = page.root;\nroot.insertChild(root.children.length, hero);\nroot.insertChild(root.children.length, countdown);\nroot.insertChild(root.children.length, speakers);\nroot.insertChild(root.children.length, form);\nconst add = (p, s, x, y) => { p.insertChild(p.children.length, s); penpotUtils.setParentXY(s, x, y); };\nadd(hero, mkT('Relojería Maestros', T.h1, '700', P.paper), S.xl, S.xxl);\nadd(hero, mkT('Encuentro anual de relojeros, coleccionistas y artesanos', T.body, '400', P.goldLight), S.xl, 120);\nadd(hero, mkBtn('Reservar mi lugar', P.gold), 680, 380);\nadd(countdown, mkT('14 de noviembre, 2025', T.h2, '600', P.gold), S.xl, S.md);\nadd(countdown, mkT('00 : 12 : 34 : 56', '64', '700', P.paper, 'center'), 300, S.sm);\nadd(speakers, mkT('Oradores', T.h2, '600', P.ink), S.xl, S.md);\nconst cards = [\n { n:'Elena Voss', r:'Restauradora', c:'#D4A843' },\n { n:'Marco Delgado', r:'Historiador', c:'#2E8B57' },\n { n:'Suki Tanaka', r:'Ingeniera', c:'#8B4513' },\n { n:'Luis Paredes', r:'Coleccionista', c:'#4A6FA5' }\n];\ncards.forEach((c, i) => {\n const card = mkC('Orador ' + c.n, 180, 200, P.paper, 12);\n card.strokes = [{ style:'solid', width:2, color:{ type:'solid', value:c.c }, strokeOpacity:1 }];\n card.strokeAlignment = 'inner';\n add(speakers, card, S.xl + i * 216, S.lg);\n add(card, mkT(c.n, '18', '700', P.ink), S.sm, S.sm);\n add(card, mkT(c.r, '14', '400', c.c), S.sm, 56);\n});\nadd(form, mkT('Inscripción', T.h2, '600', P.ink), S.xl, S.md);\n['Nombre', 'Email', 'Teléfono'].forEach((l, i) => {\n const f = mkC('Campo ' + l, 300, 48, P.paper, 8);\n f.strokes = [{ style:'solid', width:1, color:{ type:'solid', value:P.line }, strokeOpacity:1 }];\n f.strokeAlignment = 'inner';\n add(form, f, S.xl + i * 328, S.lg);\n add(f, mkT(l, '12', '600', P.ink), S.sm, S.sm);\n});\nadd(form, mkBtn('Confirmar asistencia', P.brand), 680, 300);\nstorage.evento = { pageId: page.id, heroId: hero.id, countdownId: countdown.id, speakersId: speakers.id, formId: form.id };\nreturn { pageId: page.id, boards: [hero.id, countdown.id, speakers.id, form.id].length, shapes: root.children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Un boton primario y uno secundario para una consultora de eficiencia energetica, ambos con estados normal y deshabilitado, usando #155e75 como acento.",
"id": "pd-037",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { accent: '#155e75', white: '#ffffff', ink: '#0a2530', border: '#155e75' };\nconst T = { sm: '14', lg: '18' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 32 };\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.fills = fills; s.borderRadius = radius; s.resize(w, h);\n s.strokes = [{ strokeColor: P.border, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n s.addFlexLayout(); if (s.flex) { s.flex.dir = 'row'; s.flex.horizontalPadding = S.lg; s.flex.verticalPadding = S.md; s.flex.rowGap = S.sm; s.flex.alignItems = 'center'; }\n return s;\n};\nconst mkBtn = (name, fills, disabled) => {\n const b = mk(name, 200, 52, fills, 8);\n const t = penpot.createText();\n t.name = name + '/Label'; t.characters = name; t.fontFamily = 'Inter'; t.fontSize = T.lg; t.fontWeight = '600';\n t.lineHeight = '1.3'; t.letterSpacing = '0'; t.align = 'center'; t.growType = 'auto-width';\n t.fills = [{ fillColor: disabled ? '#8ab4c2' : P.white, fillOpacity: 1 }];\n b.insertChild(b.children.length, t);\n if (disabled) { b.blocked = true; }\n return b;\n};\nconst prim = mkBtn('Boton primario', [{ fillColor: P.accent, fillOpacity: 1 }], false);\nconst sec = mkBtn('Boton secundario', [{ fillColor: P.white, fillOpacity: 1 }], false);\nconst dis = mkBtn('Boton deshabilitado', [{ fillColor: P.accent, fillOpacity: 1 }], true);\nconst board = penpot.createBoard();\nboard.name = 'Botones'; board.fills = [{ fillColor: P.white, fillOpacity: 1 }]; board.resize(640, 200); board.addFlexLayout();\nif (board.flex) { board.flex.dir = 'column'; board.flex.horizontalPadding = S.lg; board.flex.verticalPadding = S.lg; board.flex.rowGap = S.lg; board.flex.alignItems = 'start'; }\n[prim, sec, dis].forEach(b => board.appendChild(b));\nstorage.botones = { boardId: board.id, primId: prim.id, secId: sec.id, disId: dis.id };\nreturn { boardId: board.id, primId: prim.id, secId: sec.id, disId: dis.id, primBlocked: prim.blocked, secBlocked: sec.blocked, disBlocked: dis.blocked };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": " 200, 52, fills, 8); const t = penpot.createText(); t.name = name + '/Label'; t.characte"
}
],
"content": ""
},
{
"prompt": "Card de producto para una cooperativa de miel: foto arriba, titulo en Libre Baskerville, precio tachado y precio final, y un boton. 320 de ancho.",
"id": "pd-038",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#FFF8F0', miel: '#F5D98E', ambar: '#D4881C', marron: '#5C3A1E', blanco: '#FFFFFF' };\nconst T = { sm: 14, md: 18, lg: 26 };\nconst S = { sm: 8, md: 16, lg: 24 };\nconst W = 320;\nconst H = 520;\nconst board = penpot.createBoard();\nboard.name = 'Card Miel';\nboard.resize(W, H);\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1, fillType: 'solid' }];\nboard.x = 0;\nboard.y = 0;\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'center';\n}\nconst mk = (chars, size, color, weight, name) => {\n const t = penpot.createText(chars);\n t.fontFamily = 'Libre Baskerville';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = '1.3';\n t.letterSpacing = '0';\n t.align = 'center';\n t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1, fillType: 'solid' }];\n t.name = name;\n return t;\n};\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto producto';\nfoto.resize(W - 2 * S.lg, 180);\nfoto.fills = [{ fillColor: P.miel, fillOpacity: 1, fillType: 'image' }];\nfoto.strokes = [];\nfoto.borderRadius = 12;\nconst contenido = penpot.createGroup();\ncontenido.name = 'Contenido';\ncontenido.resize(W - 2 * S.lg, 160);\ncontenido.fills = [];\nconst precioAnt = mk('Antes $3.500', T.sm, P.marron, '400', 'Precio anterior');\nprecioAnt.characters = 'Antes $3.500';\nprecioAnt.textDecoration = 'line-through';\nconst precioFin = mk('$2.800', T.lg, P.ambar, '700', 'Precio final');\nconst titulo = mk('Miel de abeja pura', T.md, P.marron, '700', 'Titulo');\nconst desc = mk('Colmenar artesanal, sin aditivos', T.sm, P.marron, '400', 'Descripcion');\ndesc.lineHeight = '1.5';\n[precioAnt, precioFin, titulo, desc].forEach(t => contenido.appendChild(t));\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton';\nbtn.resize(W - 2 * S.lg, 48);\nbtn.fills = [{ fillColor: P.ambar, fillOpacity: 1, fillType: 'solid' }];\nbtn.strokes = [];\nbtn.borderRadius = 24;\nconst btnTxt = mk('Agregar', T.md, P.blanco, '700', 'Boton');\nbtnTxt.growType = 'auto-width';\npenpotUtils.setParentXY(btnTxt, (W - 2 * S.lg) / 2 - btnTxt.width / 2, 24);\nbtn.appendChild(btnTxt);\n[foto, contenido, precioAnt, precioFin, titulo, desc, btn].forEach(s => board.appendChild(s));\nstorage.card = { boardId: board.id, W: W, H: H };\nreturn { boardId: board.id, flexDir: board.flex ? board.flex.dir : null, childrenCount: board.children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Hazme una barra de navegacion de un laboratorio de analisis de suelos de 1180x88 con logo, cinco enlaces y un boton de accion a la derecha.",
"id": "pd-039",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { primary:'#0B3D2E', accent:'#2E7D5C', bg:'#F5F0E6', ink:'#1A1A1A', muted:'#5C5C5C' };\nconst S = { sm:10, md:14, lg:18 };\nconst nav = penpot.createBoard();\nnav.name = 'Navbar';\nnav.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nnav.resize(1180, 88);\nnav.borderRadius = 0;\nconst logo = penpot.createText('SuelosLab');\nlogo.fontFamily = 'Inter';\nlogo.fontSize = S.lg;\nlogo.fontWeight = '700';\nlogo.fills = [{ fillColor: P.primary, fillOpacity: 1 }];\nlogo.growType = 'auto-width';\nconst links = [\n { name:'Analisis', href:'#analisis' },\n { name:'Metodologia', href:'#metodologia' },\n { name:'Resultados', href:'#resultados' },\n { name:'Normas', href:'#normas' },\n { name:'Contacto', href:'#contacto' }\n];\nconst items = links.map(l => {\n const t = penpot.createText(l.name);\n t.fontFamily = 'Inter';\n t.fontSize = S.md;\n t.fontWeight = '500';\n t.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n});\nconst cta = penpot.createRectangle();\ncta.name = 'CTA';\ncta.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\ncta.resize(140, 36);\ncta.borderRadius = 18;\nconst ctaT = penpot.createText('Solicitar analisis');\nctaT.fontFamily = 'Inter';\nctaT.fontSize = S.sm;\nctaT.fontWeight = '600';\nctaT.fills = [{ fillColor: '#FFF', fillOpacity: 1 }];\nctaT.growType = 'auto-width';\ncta.insertChild(cta.children.length, ctaT);\npenpotUtils.setParentXY(ctaT, 12, 10);\nconst root = penpot.root;\nroot.insertChild(root.children.length, nav);\nconst children = [logo, ...items, ctaT];\nchildren.forEach((c, i) => {\n nav.insertChild(nav.children.length, c);\n penpotUtils.setParentXY(c, 24 + i * 108, 26);\n});\nreturn { navId: nav.id, childrenCount: children.length, ctaId: cta.id };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Un aviso de cookies para el sitio de un estudio de doblaje: texto corto, dos botones y fondo #581c87.",
"id": "pd-040",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#581c87', text: '#ffffff', btn: '#581c87', accent: '#f5d76e' };\nconst S = { bar: 64, sm: 10, md: 16, lg: 24 };\nconst mk = (name, w, h, fill) => {\n const s = penpot.createRectangle();\n s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }];\n s.resize(w, h); s.borderRadius = w === h ? h / 2 : S.sm;\n return s;\n};\nconst mkBtn = (name, label) => {\n const b = penpot.createRectangle();\n b.name = name; b.fills = [{ fillColor: P.btn, fillOpacity: 1 }];\n b.strokes = [{ strokeColor: P.text, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n b.resize(120, 36); b.borderRadius = S.sm;\n const t = penpot.createText(label);\n t.name = label; t.fontFamily = 'Work Sans'; t.fontSize = '13'; t.fontWeight = '600';\n t.lineHeight = '1.2'; t.characters = label; t.align = 'center';\n t.growType = 'auto-width'; t.fills = [{ fillColor: P.text, fillOpacity: 1 }];\n b.insertChild(b.children.length, t);\n return b;\n};\nconst bar = penpot.createBoard();\nbar.name = 'Aviso de cookies'; bar.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nbar.resize('100%', 64); bar.addFlexLayout();\nif (bar.flex) {\n bar.flex.dir = 'row'; bar.flex.horizontalPadding = S.lg; bar.flex.verticalPadding = 0;\n bar.flex.rowGap = S.md; bar.flex.alignItems = 'center'; bar.flex.justifyContent = 'space-between';\n}\nconst txt = penpot.createText('Este sitio utiliza cookies para mejorar tu experiencia.');\ntxt.name = 'Aviso'; txt.fontFamily = 'Work Sans'; txt.fontSize = '14'; txt.fontWeight = '400';\ntxt.lineHeight = '1.4'; txt.characters = 'Este sitio utiliza cookies para mejorar tu experiencia.';\ntxt.align = 'left'; txt.growType = 'auto-width';\ntxt.fills = [{ fillColor: P.text, fillOpacity: 1 }];\nconst a = mk('Acento', 4, 20, P.accent);\nbar.insertChild(bar.children.length, a);\nbar.insertChild(bar.children.length, txt);\nconst ok = mkBtn('Aceptar', 'Aceptar');\nconst no = mkBtn('Rechazar', 'Rechazar');\nbar.insertChild(bar.children.length, ok);\nbar.insertChild(bar.children.length, no);\nstorage.cookie = { barId: bar.id, P: P, S: S };\nreturn { barId: bar.id, flex: !!bar.flex, children: bar.children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Necesito una tarjeta de perfil de una fabrica de velas de soja con avatar circular, nombre, rol y tres datos de contacto.",
"id": "pd-041",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { fondo:'#FFF9F0', borde:'#D4A373', icon:'#F2D5A0', texto:'#5C3D2E', accent:'#8B5E3C' };\nconst T = { sm:'14', lg:'28', xl:'16' };\nconst card = penpot.createBoard();\ncard.name = 'Tarjeta Candelaria';\ncard.fills = [{ fillColor: P.fondo, fillOpacity: 1 }];\ncard.strokes = [{ strokeColor: P.borde, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\ncard.borderRadius = 20;\ncard.resize(400, 520);\ncard.x = 100;\ncard.y = 100;\nconst avatar = penpot.createEllipse();\navatar.name = 'Avatar';\navatar.fills = [{ fillColor: P.icon, fillOpacity: 1 }];\navatar.strokes = [{ strokeColor: P.borde, strokeWidth: 6, strokeStyle: 'solid', strokeOpacity: 1 }];\navatar.resize(120, 120);\navatar.x = 140;\navatar.y = 40;\nconst mk = (n, c, s, w) => {\n const t = penpot.createText();\n t.name = n;\n t.characters = c;\n t.fontFamily = 'Work Sans';\n t.fontSize = s;\n t.fontWeight = w;\n t.lineHeight = '1.4';\n t.fills = [{ fillColor: c, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst nombre = mk('Nombre', 'Candelaria Luna', T.lg, '600');\nnombre.x = 140;\nnombre.y = 188;\nconst rol = mk('Rol', 'Fundadora y creadora', T.xl, '400');\nrol.x = 140;\nrol.y = 232;\nconst mkLinea = (icono, texto) => {\n const b = penpot.createBoard();\n b.name = icono;\n b.fills = [{ fillColor: P.fondo, fillOpacity: 1 }];\n b.addFlexLayout();\n if (b.flex) {\n b.flex.dir = 'row';\n b.flex.columnGap = 16;\n b.flex.horizontalPadding = 24;\n b.flex.verticalPadding = 12;\n b.flex.alignItems = 'center';\n }\n b.strokes = [{ strokeColor: P.borde, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 0.5 }];\n b.borderRadius = 12;\n b.resize(352, 48);\n b.x = 24;\n b.y = icono === 'tel' ? 280 : icono === 'mail' ? 340 : 400;\n const ic = penpot.createEllipse();\n ic.name = 'icono ' + icono;\n ic.fills = [{ fillColor: P.icono, fillOpacity: 1 }];\n ic.resize(40, 40);\n b.appendChild(ic);\n const t = penpot.createText();\n t.name = 'dato ' + icono;\n t.characters = texto;\n t.fontFamily = 'Work Sans';\n t.fontSize = T.sm;\n t.fontWeight = '400';\n t.lineHeight = '1.4';\n t.fills = [{ fillColor: P.texto, fillOpacity: 1 }];\n t.growType = 'auto-width';\n b.appendChild(t);\n return b;\n};\nconst tel = mkLinea('tel', '+54 11 5555-0102');\nconst mail = mkLinea('mail', 'candelaria@candela.ar');\nconst web = mkLinea('web', 'candela.ar');\nconst root = card;\nroot.insertChild(root.children.length, avatar);\nroot.insertChild(root.children.length, nombre);\nroot.insertChild(root.children.length, rol);\nroot.insertChild(root.children.length, tel);\nroot.insertChild(root.children.length, mail);\nroot.insertChild(root.children.length, web);\nstorage.palette = P;\nstorage.card = card.id;\nreturn { cardId: card.id, avatarId: avatar.id, children: root.children.length, flex: card.flex ? card.flex.dir : null };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": " = (n, c, s, w) => { const t = penpot.createText(); t.name = n; t.characters = c; t."
}
],
"content": ""
},
{
"prompt": "Necesito una tabla de 1180 px para una academia de tango: encabezado oscuro, cinco filas con fondo alternado y una columna de estado con etiquetas de color.",
"id": "pd-042",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n brand: '#1B1F3B', ink: '#FFFFFF', accent: '#E85D04',\n bg: '#FDFBF7', row: '#FFFFFF', ink: '#1B1F3B',\n ok: '#1B8A4A', pend: '#E85D04', cancel: '#D93036'\n};\nconst S = { sm: 12, md: 14, lg: 18 };\nconst T = {\n h: { fontSize: S.lg, fontWeight: '700', lineHeight: '1.2', fill: [P.ink], align: 'left' },\n c: { fontSize: S.md, fontWeight: '500', lineHeight: '1.4', fill: [P.ink], align: 'left' },\n s: { fontSize: S.sm, fontWeight: '400', lineHeight: '1.4', fill: [P.ink], align: 'left' }\n};\nconst mk = (name, chars, style, pad) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.characters = chars;\n t.fontFamily = 'Inter';\n t.fontSize = style.fontSize;\n t.fontWeight = style.fontWeight;\n t.lineHeight = style.lineHeight;\n t.fill = style.fill;\n t.align = style.align;\n t.growType = 'auto-width';\n if (pad) {\n const box = penpotUtils.isContainedIn(t, penpot.root) ? penpot.root : penpot.root;\n t.x = pad.x; t.y = pad.y;\n t.resize(pad.w, pad.h);\n }\n return t;\n};\nconst mkCell = (name, chars, style) => {\n const c = penpot.createBoard(name);\n c.fills = [{ fillColor: chars ? P.row : P.brand, fillOpacity: 1 }];\n c.resize(1180, chars ? 56 : 64);\n c.strokes = [];\n c.borderRadius = 0;\n c.insertChild(c.children.length, mk('c', chars, style, { x: 24, y: 0, w: 200, h: 56 }));\n c.insertChild(c.children.length, mk('c', chars, style, { x: 224, y: 0, w: 360, h: 56 }));\n c.insertChild(c.children.length, mk('c', chars, style, { x: 584, y: 0, w: 280, h: 56 }));\n if (chars) {\n const badge = penpot.createRectangle();\n badge.name = 'badge';\n badge.fills = [{ fillColor: chars === 'Disponible' ? P.ok : chars === 'En espera' ? P.pend : P.cancel, fillOpacity: 1 }];\n badge.strokes = [];\n badge.resize(160, 32);\n badge.borderRadius = 16;\n badge.insertChild(badge.children.length, mk('c', chars, T.s, { x: 0, y: 12, w: 160, h: 32 }));\n c.insertChild(c.children.length, badge);\n }\n return c;\n};\nconst board = penpot.createBoard('Tabla de clases');\nboard.name = 'Tabla de clases';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 380);\nboard.strokes = [];\nboard.borderRadius = 12;\nboard.addGridLayout();\nif (!board.grid) throw new Error('addGridLayout returned null');\nboard.grid.rows = '64px, 56px, 56px, 56px, 56px, 56px';\nboard.grid.columns = '200px, 360px, 280px, 160px';\nboard.grid.rowGap = 0;\nboard.grid.columnGap = 0;\nconst cols = ['Clase', 'Profesor', 'Nivel', 'Estado'];\nconst filas = [\n ['Tango Inicial', 'Marta Sosa', 'Principiante', 'Disponible'],\n ['Tango Avanzado', 'Lucas Fernandez', 'Avanzado', 'Disponible'],\n ['Milonga Social', 'Carla Ruiz', 'Intermedio', 'En espera'],\n ['Tango Folklorico', 'Marta Sosa', 'Avanzado', 'Cancelado'],\n ['Tango Nuevo', 'Lucas Fernandez', 'Intermedio', 'Disponible']\n];\nboard.grid.appendChild(mkCell('enc', '', T.h));\nboard.grid.children[0].row = 1;\nboard.grid.children[0].column = 1;\nboard.grid.children[0].columnSpan = 4;\ncols.forEach((c, i) => {\n board.grid.appendChild(mkCell('h' + (i + 1), c, T.h));\n board.grid.children[board.grid.children.length - 1].row = 1;\n board.grid.children[board.grid.children.length - 1].column = i + 1;\n});\nfilas.forEach((f, r) => {\n f.forEach((c, i) => {\n board.grid.appendChild(mkCell('f' + (r + 2) + '-' + (i + 1), c, T.c));\n board.grid.children[board.grid.children.length - 1].row = r + 2;\n board.grid.children[board.grid.children.length - 1].column = i + 1;\n });\n});\nstorage.table = { boardId: board.id, P: P, T: T };\nreturn { boardId: board.id, cells: board.grid.children.length, rows: board.grid.rows, columns: board.grid.columns };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pantalla de 360x780 para la app de una academia de tango: foto real de portada traida de una URL, titulo encima y boton abajo.",
"id": "pd-043",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#FDF6EC', ink:'#1A0F0A', accent:'#8B1A1A', warm:'#D4B896' };\nconst S = { xs:12, sm:16, md:24, lg:32 };\nconst board = penpot.createBoard();\nboard.name = 'Portada';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(360, 780);\nboard.x = 0; board.y = 0;\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'center';\n}\nconst cover = penpot.createRectangle();\ncover.name = 'Foto portada';\ncover.fills = [{ fillColor: P.warm, fillOpacity: 1, fillImage: { url: 'https://picsum.photos/seed/tango/312/200' } }];\ncover.strokes = [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\ncover.resize(312, 200);\ncover.borderRadius = 12;\nboard.appendChild(cover);\nconst title = penpot.createText();\ntitle.name = 'Titulo';\ntitle.characters = 'Academia de Tango';\ntitle.fontFamily = 'Playfair Display';\ntitle.fontSize = '36';\ntitle.fontWeight = '700';\ntitle.lineHeight = '1.2';\ntitle.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\ntitle.growType = 'auto-width';\nboard.appendChild(title);\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton inscripcion';\nbtn.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nbtn.resize(312, 52);\nbtn.borderRadius = 26;\nboard.appendChild(btn);\nconst btnText = penpot.createText();\nbtnText.name = 'Boton texto';\nbtnText.characters = 'Inscribirse';\nbtnText.fontFamily = 'Inter';\nbtnText.fontSize = '18';\nbtnText.fontWeight = '600';\nbtnText.lineHeight = '1.3';\nbtnText.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nbtnText.growType = 'auto-width';\nbtnText.x = (312 - btnText.width) / 2;\nbtnText.y = (52 - btnText.height) / 2;\nboard.appendChild(btnText);\nstorage.portada = { boardId: board.id, P: P, S: S };\nreturn { boardId: board.id, coverImage: board.fills[0]?.fillImage ? 'yes' : 'no', button: btnText.width };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "ppendChild(cover); const title = penpot.createText(); title.name = 'Titulo'; title.character"
}
],
"content": ""
},
{
"prompt": "Quiero una galeria de cuatro fotos reales de internet en la app de una consultora de eficiencia energetica, formato 412x915, con leyenda en cada una.",
"id": "pd-044",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { primary: '#0B6E4F', accent: '#2ECC71', bg: '#FDFBF7', ink: '#1A2B23', card: '#FFFFFF', line: '#D4E8DF' };\nconst T = { h1: 22, h2: 15, body: 12 };\nconst S = { sm: 8, md: 16, lg: 24 };\nconst board = penpot.createBoard();\nboard.name = 'Galeria de auditorias';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(412, 915);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'start';\n}\nconst header = penpot.createText();\nheader.name = 'Encabezado';\nheader.characters = 'Auditorias energeticas';\nheader.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\nheader.fontFamily = 'Work Sans';\nheader.fontSize = T.h1;\nheader.fontWeight = '700';\nheader.lineHeight = '1.2';\nheader.growType = 'auto-width';\nheader.strokes = [];\nheader.shadows = [];\nboard.insertChild(board.children.length, header);\nconst sub = penpot.createText();\nsub.name = 'Subtitulo';\nsub.characters = 'Fotos de campo de nuestras auditorias en edificios reales.';\nsub.fills = [{ fillColor: P.primary, fillOpacity: 1 }];\nsub.fontFamily = 'Work Sans';\nsub.fontSize = T.body;\nsub.fontWeight = '500';\nsub.lineHeight = '1.5';\nsub.growType = 'auto-width';\nsub.strokes = [];\nsub.shadows = [];\nboard.insertChild(board.children.length, sub);\nconst items = [\n { url: 'https://source.unsplash.com/200x200/?solar-panel,roof', label: 'Paneles en tejado industrial', tag: 'Fotovoltaica' },\n { url: 'https://source.unsplash.com/200x200/?insulation,wall', label: 'Aislamiento en fachada', tag: 'Envoltorio' },\n { url: 'https://source.unsplash.com/200x200/?led-light,warehouse', label: 'Iluminacion LED en nave', tag: 'Eficiencia' },\n { url: 'https://source.unsplash.com/200x200/?meter,electricity', label: 'Medicion de consumos', tag: 'Monitorizacion' }\n];\nconst cards = [];\nfor (const item of items) {\n const card = penpot.createBoard();\n card.name = 'Card ' + item.tag;\n card.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n card.borderRadius = 16;\n card.strokes = [{ strokeColor: P.line, strokeWidth: 1, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.resize(380, 130);\n card.addFlexLayout();\n if (card.flex) {\n card.flex.dir = 'row';\n card.flex.horizontalPadding = S.md;\n card.flex.verticalPadding = S.md;\n card.flex.rowGap = S.sm;\n card.flex.alignItems = 'center';\n }\n const img = await import_image(item.url, 'photo-' + item.tag);\n img.name = 'Foto ' + item.tag;\n img.fills = [{ fillColor: '#000000', fillOpacity: 1, fillImage: img.data }];\n img.resize(90, 90);\n img.borderRadius = 12;\n card.insertChild(card.children.length, img);\n const col = penpot.createBoard();\n col.name = 'Leyenda ' + item.tag;\n col.fills = [];\n col.resize(230, 90);\n col.addFlexLayout();\n if (col.flex) {\n col.flex.dir = 'column';\n col.flex.rowGap = 4;\n col.flex.alignItems = 'start';\n }\n card.insertChild(card.children.length, col);\n const tag = penpot.createText();\n tag.name = 'Tag ' + item.tag;\n tag.characters = item.tag;\n tag.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n tag.fontFamily = 'Work Sans';\n tag.fontSize = 10;\n tag.fontWeight = '700';\n tag.lineHeight = '1';\n tag.growType = 'auto-width';\n tag.strokes = [];\n tag.shadows = [];\n col.insertChild(col.children.length, tag);\n const lbl = penpot.createText();\n lbl.name = 'Leyenda ' + item.tag;\n lbl.characters = item.label;\n lbl.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n lbl.fontFamily = 'Work Sans';\n lbl.fontSize = T.h2;\n lbl.fontWeight = '600';\n lbl.lineHeight = '1.3';\n lbl.growType = 'auto-width';\n lbl.strokes = [];\n lbl.shadows = [];\n col.insertChild(col.children.length, lbl);\n board.insertChild(board.children.length, card);\n cards.push(card);\n}\nstorage.gallery = { boardId: board.id, cardCount: cards.length };\nreturn { boardId: board.id, cardCount: cards.length, headerFontSize: header.fontSize, subFontSize: sub.fontSize, cardFlexDir: board.flex ? board.flex.dir : null };"
],
"veto_hits": [
{
"patron": "fontSize/fontWeight/lineHeight/letterSpacing numerico",
"fragmento": "]; tag.fontFamily = 'Work Sans'; tag.fontSize = 10; tag.fontWeight = '700'; tag.lineH"
},
{
"patron": "import_image / importImage / createImage / filePath",
"fragmento": "ems = 'center'; } const img = await import_image(item.url, 'photo-' + item.tag); img.n"
}
],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "tems = 'start'; } const header = penpot.createText(); header.name = 'Encabezado'; header.cha"
}
],
"content": ""
},
{
"prompt": "Meteme una imagen de fondo en el hero de un museo de maquinas de escribir y encima un velo oscuro para que se lea el titular en blanco.",
"id": "pd-045",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand: '#0B132B', ink: '#040813', paper: '#F4F1EB', line: '#2A3B52' };\nconst T = { hero: '48', sub: '18', meta: '14' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 40 };\nconst mk = (name, fills, fontSize, color, weight, lineHeight) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText devolvio null');\n t.characters = name;\n t.fontFamily = 'Work Sans';\n t.fontSize = fontSize;\n t.fontWeight = weight;\n t.lineHeight = lineHeight;\n t.growType = 'auto-width';\n t.fills = fills;\n t.strokes = [];\n t.borderRadius = 0;\n return t;\n};\nconst hero = penpot.createBoard();\nhero.name = 'Hero / Exposicion principal';\nhero.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\nhero.resize(1200, 520);\nhero.x = 0;\nhero.y = 0;\nconst img = { url: 'https://picsum.photos/seed/typewriter/1200/520', width: 1200, height: 520 };\nhero.fills = [{ fillColor: P.brand, fillOpacity: 1, fillImage: img }];\nconst velo = penpot.createRectangle();\nvelo.name = 'Velo oscuro';\nvelo.fills = [{ fillColor: P.ink, fillOpacity: 0.45 }];\nvelo.resize(1200, 520);\nvelo.x = 0;\nvelo.y = 0;\nvelo.borderRadius = 0;\nconst contenido = penpot.createBoard();\ncontenido.name = 'Contenido';\ncontenido.fills = [];\ncontenido.resize(1200, 520);\ncontenido.x = 0;\ncontenido.y = 0;\nconst titular = mk('Exposicion: Maquinas de escribir', [P.paper], T.hero, '700', '1.2');\nconst sub = mk('Del siglo XIX al advenimiento de la computadora', [P.line], T.sub, '400', '1.5');\nconst meta = mk('Coleccion permanente · Sala 3 · Entrada libre', [P.line], T.meta, '400', '1.5');\nconst stack = [meta, sub, titular];\nstack.forEach(s => contenido.insertChild(0, s));\nstack.forEach((s, i) => { s.x = S.lg; s.y = S.lg + (stack.length - 1 - i) * (S.lg + S.md); });\nconst result = { heroId: hero.id, veloId: velo.id, contenidoId: contenido.id, titularId: titular.id, meta: stack.map(s => s.characters) };\nstorage.hero = result;\nreturn result;"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Para la ficha de un estudio de doblaje necesito la foto del producto ocupando toda la tarjeta sin deformarse, 1240 px de ancho.",
"id": "pd-046",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const T = { card: 1240, cardH: 1240, photo: 1240, photoH: 744, body: 496, bodyH: 496 };\nconst P = { sm: 24, md: 32, lg: 48 };\nconst S = { h1: 56, h2: 32, body: 20 };\nconst C = { cardBg: '#FFFFFF', photoBg: '#0B1D3A', accent: '#F2A922', text: '#1A1A1A', muted: '#6B7280' };\nconst fotoUrl = 'https://picsum.photos/seed/dub-studio/1240x744';\nconst estudio = 'Estudio Voz Alta';\nconst precio = '$1.200 / hora';\n\nconst card = penpot.createBoard();\ncard.name = 'Ficha de estudio';\ncard.fills = [{ fillColor: C.cardBg, fillOpacity: 1 }];\ncard.resize(T.card, T.cardH);\ncard.borderRadius = 24;\ncard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 16, blur: 32, spread: 0, color: { color: '#000000', opacity: 0.12 } }];\ncard.strokes = [];\ncard.growType = 'fixed';\n\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto del estudio';\nfoto.fills = [{ fillOpacity: 1, fillImage: { image: fotoUrl, fit: 'cover' } }];\nfoto.strokes = [];\nfoto.resize(T.photo, T.photoH);\nfoto.growType = 'fixed';\n\nconst cuerpo = penpot.createBoard();\ncuerpo.name = 'Cuerpo';\ncuerpo.fills = [{ fillColor: C.cardBg, fillOpacity: 1 }];\ncuerpo.resize(T.body, T.bodyH);\ncuerpo.borderRadius = 0;\ncuerpo.strokes = [];\ncuerpo.growType = 'fixed';\n\nconst h1 = penpot.createText();\nh1.name = 'Nombre del estudio';\nh1.characters = estudio;\nh1.fontSize = S.h1;\nh1.fontWeight = '700';\nh1.lineHeight = '1.15';\nh1.fills = [{ fillColor: C.accent, fillOpacity: 1 }];\nh1.growType = 'auto-width';\n\nconst h2 = penpot.createText();\nh2.name = 'Servicio';\nh2.characters = 'Doblaje y postproduccion';\nh2.fontSize = S.h2;\nh2.fontWeight = '600';\nh2.lineHeight = '1.3';\nh2.fills = [{ fillColor: C.text, fillOpacity: 1 }];\nh2.growType = 'auto-width';\n\nconst desc = penpot.createText();\ndesc.name = 'Descripcion';\ndesc.characters = 'Estudio profesional con sala de doblaje, mezcla y masterizacion. Equipos Neumann y Sennheiser.';\ndesc.fontSize = S.body;\ndesc.fontWeight = '400';\ndesc.lineHeight = '1.55';\ndesc.fills = [{ fillColor: C.muted, fillOpacity: 1 }];\ndesc.growType = 'auto-width';\n\nconst precioT = penpot.createText();\nprecioT.name = 'Precio';\nprecioT.characters = precio;\nprecioT.fontSize = S.h2;\nprecioT.fontWeight = '700';\nprecioT.lineHeight = '1.3';\nprecioT.fills = [{ fillColor: C.accent, fillOpacity: 1 }];\nprecioT.growType = 'auto-width';\n\nconst cardHijos = [foto, cuerpo];\ncardHijos.forEach(h => card.insertChild(card.children.length, h));\ncuerpo.insertChild(cuerpo.children.length, h1);\ncuerpo.insertChild(cuerpo.children.length, h2);\ncuerpo.insertChild(cuerpo.children.length, desc);\ncuerpo.insertChild(cuerpo.children.length, precioT);\n\ncardHijos.forEach(h => penpotUtils.setParentXY(h, 0, 0));\ncuerpo.shadows = [];\n\nconst result = {\n cardId: card.id,\n fotoId: foto.id,\n fotoFit: foto.fills[0].fillImage ? foto.fills[0].fillImage.fit : null,\n fotoAncho: foto.width,\n fotoAlto: foto.height,\n h1Id: h1.id,\n h2Id: h2.id,\n descId: desc.id,\n precioId: precioT.id,\n cardAncho: card.width,\n cardAlto: card.height,\n cardBorderRadius: card.borderRadius,\n cardShadows: card.shadows.length,\n cardFlex: card.flex ? card.flex.dir : null,\n cardGrid: card.grid ? card.grid.type : null,\n cardLayout: card.flex ? 'flex' : card.grid ? 'grid' : null,\n fotoContenidoEnCard: penpotUtils.isContainedIn(foto, card),\n cuerpoContenidoEnCard: penpotUtils.isContainedIn(cuerpo, card),\n cardHijos: card.children.length,\n cuerpoHijos: cuerpo.children.length,\n fotoEnCard: card.children.some(h => h.id === foto.id),\n fotoEnCuerpo: cuerpo.children.some(h => h.id === foto.id),\n fotoEnCuerpoIndice: card.children.findIndex(h => h.id === foto.id),\n fotoEnCardIndice: card.children.findIndex(h => h.id === foto.id),\n fotoParent: foto.parent ? foto.parent.name : null,\n cardParent: card.parent ? card.parent.name : null,\n cuerpoParent: cuerpo.parent ? cuerpo.parent.name : null,\n h1Parent: h1.parent ? h1.parent.name : null,\n h2Parent: h2.parent ? h2.parent.name : null,\n descParent: desc.parent ? desc.parent.name : null,\n precioParent: precioT.parent ? precioT.parent.name : null,\n fotoEnFillsDeCuerpo: cuerpo.fills.some(f => f.fillImage),\n fotoEnFillsDeCard: card.fills.some(f => f.fillImage),\n fotoEnFillsDeH1: h1.fills.some(f => f.fillImage),\n fotoEnFillsDeH2: h2.fills.some(f => f.fillImage),\n fotoEnFillsDeDesc: desc.fills.some(f => f.fillImage),\n fotoEnFillsDePrecio: precioT.fills.some(f => f.fillImage),\n fotoEnFillsDeCuerpoIndice: cuerpo.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDeCardIndice: card.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDeH1Indice: h1.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDeH2Indice: h2.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDeDescIndice: desc.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDePrecioIndice: precioT.fills.findIndex(f => f.fillImage !== null),\n fotoEnFillsDeCuerpoTipo: cuerpo.fills.find(f => f.fillImage) ? cuerpo.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDeCardTipo: card.fills.find(f => f.fillImage) ? card.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDeH1Tipo: h1.fills.find(f => f.fillImage) ? h1.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDeH2Tipo: h2.fills.find(f => f.fillImage) ? h2.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDeDescTipo: desc.fills.find(f => f.fillImage) ? desc.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDePrecioTipo: precioT.fills.find(f => f.fillImage) ? precioT.fills.find(f => f.fillImage).fillImage.fit : null,\n fotoEnFillsDeCuerpoImagen: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagen: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1Imagen: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2Imagen: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagen: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagen: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCuerpoImagenUrl: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagenUrl: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1ImagenUrl: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2ImagenUrl: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagenUrl: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagenUrl: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCuerpoImagenUrl2: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagenUrl2: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1ImagenUrl2: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2ImagenUrl2: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagenUrl2: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagenUrl2: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCuerpoImagenUrl3: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagenUrl3: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1ImagenUrl3: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2ImagenUrl3: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagenUrl3: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagenUrl3: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCuerpoImagenUrl4: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagenUrl4: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1ImagenUrl4: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2ImagenUrl4: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagenUrl4: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagenUrl4: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCuerpoImagenUrl5: cuerpo.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(cuerpo.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeCardImagenUrl5: card.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(card.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH1ImagenUrl5: h1.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h1.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeH2ImagenUrl5: h2.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(h2.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDeDescImagenUrl5: desc.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(desc.fills.find(f => f.fillImage)) : null,\n fotoEnFillsDePrecioImagenUrl5: precioT.fills.find(f => f.fillImage) ? (f => f.fillImage ? f.fillImage.image : null)(precioT.fills.find(f => f.fillImage)) : null,\n fotoEn"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": ".growType = 'fixed'; const h1 = penpot.createText(); h1.name = 'Nombre del estudio'; h1.cha"
}
],
"content": ""
},
{
"prompt": "Fila de tres tarjetas de un servicio de reparacion de relojes que se repartan el ancho de 1180 px en partes iguales, con separacion pareja entre ellas.",
"id": "pd-047",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand: '#B8860B', ink: '#1A1A1A', cream: '#FDF8F0', grey: '#6B6B6B' };\nconst S = { sm: 12, md: 16, lg: 24, xl: 36 };\nconst cards = [\n { name: 'Reparacion', icon: '⚙', desc: 'Cambio de muelles, cristales y coronas.' },\n { name: 'Calibrado', icon: '⏱', desc: 'Ajuste de precision con cronometro.' },\n { name: 'Limpieza', icon: '✨', desc: 'Ultrasonido y desengrase profesional.' }\n];\nconst W = 1180, G = 20;\nconst board = penpot.createBoard();\nboard.name = 'Servicios';\nboard.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\nboard.resize(W, 400);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'row';\n board.flex.horizontalPadding = 0;\n board.flex.verticalPadding = 0;\n board.flex.rowGap = G;\n board.flex.columnGap = G;\n board.flex.alignItems = 'center';\n}\nconst cardW = (W - G * 2) / 3;\ncards.forEach(c => {\n const card = penpot.createRectangle();\n card.name = 'Tarjeta ' + c.name;\n card.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n card.strokes = [{ strokeColor: P.brand, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.borderRadius = 12;\n card.resize(cardW, 300);\n board.appendChild(card);\n if (card.flexChild) { card.flexChild.horizontalSizing = 'fill'; }\n const icon = penpotUtils.findShape(s => s.name === 'Icono ' + c.name, card);\n if (icon) { icon.fills = [{ fillColor: P.brand, fillOpacity: 1 }]; }\n const title = penpot.createText();\n title.name = 'Titulo ' + c.name;\n title.characters = c.name;\n title.fontFamily = 'Work Sans';\n title.fontSize = S.lg;\n title.fontWeight = '700';\n title.lineHeight = '1.2';\n title.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\n title.resize(300, 40);\n card.insertChild(card.children.length, title);\n const desc = penpot.createText();\n desc.name = 'Descripcion ' + c.name;\n desc.characters = c.desc;\n desc.fontFamily = 'Work Sans';\n desc.fontSize = S.md;\n desc.fontWeight = '400';\n desc.lineHeight = '1.5';\n desc.fills = [{ fillColor: P.grey, fillOpacity: 1 }];\n desc.resize(300, 60);\n card.insertChild(card.children.length, desc);\n});\nstorage.servicios = { boardId: board.id, cardW };\nreturn { boardId: board.id, cardWidth: cardW, flexDir: board.flex ? board.flex.dir : null };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "Opacity: 1 }]; } const title = penpot.createText(); title.name = 'Titulo ' + c.name; t"
}
],
"content": ""
},
{
"prompt": "Grilla de 900 px con seis casillas para el catalogo de un vivero de orquideas, tres por fila, todas del mismo alto.",
"id": "pd-048",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg:'#FDF8F0', ink:'#1B3A2D', green:'#2E6B4A', light:'#A8D5BA', brown:'#5C3D2E' };\nconst T = { sm:'14', lg:'20', xl:'28' };\nconst S = { xs:'4', s:'8', m:'16', l:'24', xl:'40' };\nconst cols = 12, gut = 8, cardW = 280, cardH = 360;\nconst board = new Board();\nboard.name = 'Catalogo Orquideas';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(900, 760);\nboard.addGridLayout();\nif (board.grid) {\n board.grid.rows = [{ size: 'auto', min: 0 }];\n board.grid.columns = Array.from({ length: cols }, () => ({ size: 'flex', min: 0 }));\n board.grid.rowGap = S.s;\n board.grid.columnGap = S.s;\n}\npenpot.root.insertChild(penpot.root.children.length, board);\nconst mk = (name, w, h, fill) => {\n const s = new Rectangle();\n s.name = name; s.fills = fill ? [{ fillColor: fill, fillOpacity: 1 }] : [];\n s.resize(w, h); s.borderRadius = S.m;\n return s;\n};\nconst mkT = (chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight || '400';\n t.lineHeight = '1.4'; t.fill = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst orq = ['Orquidea Violetea', 'Orquidea Luna', 'Orquidea Sol', 'Orquidea Noche', 'Orquidea Aurora', 'Orquidea Brisa'];\nconst prc = ['$ 12.500', '$ 18.900', '$ 9.800', '$ 22.000', '$ 15.400', '$ 11.200'];\nconst filas = [0, 1, 2];\nconst casillas = [];\nfilas.forEach(f => {\n orq.forEach((n, i) => {\n const c = mk('card-' + (f * 3 + i), cardW, cardH, P.bg);\n c.strokes = [{ strokeColor: P.light, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.insertChild(c.children.length, mk('img-' + (f * 3 + i), cardW - S.l * 2, 180, P.light));\n c.insertChild(c.children.length, mkT(n, T.lg, P.ink, '600'));\n c.insertChild(c.children.length, mkT(prc[i], T.sm, P.green));\n c.insertChild(c.children.length, mk('btn', cardW - S.l * 2, 40, P.green));\n c.layoutCell = { row: f + 1, column: i * 4 + 1, columnSpan: 4 };\n board.appendChild(c);\n casillas.push(c);\n });\n});\nconst imgs = penpotUtils.findShapes(s => s.fills.some(f => f.fillColor === P.light), board);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst btns = penpotUtils.findShapes(s => s.fills.some(f => f.fillColor === P.green), board);\nstorage.catalogo = { boardId: board.id, cardCount: casillas.length, imgCount: imgs.length, textCount: texts.length, btnCount: btns.length };\nreturn { boardId: board.id, cardCount: casillas.length, imgCount: imgs.length, textCount: texts.length, btnCount: btns.length, gridRows: board.grid ? board.grid.rows.length : 0, gridColumns: board.grid ? board.grid.columns.length : 0 };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Una columna de un museo de maquinas de escribir donde el titulo va arriba, el texto en el medio y el boton abajo del todo, y el contenedor se ajusta al contenido.",
"id": "pd-049",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { bg: '#FDFBF7', ink: '#1A1A1A', accent: '#B85C38' };\nconst S = { xs: 12, sm: 16, md: 24, lg: 40 };\nconst mk = (chars, size, weight, color, line) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = line; t.fill = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-height'; t.alignHorizontal = 'center';\n return t;\n};\nconst col = penpot.createBoard();\ncol.name = 'Columna Museo';\ncol.resize(400, 0);\ncol.fills = [{ fillColor: P.bg, fillOpacity: 1, fillImagePosition: 'cover' }];\ncol.addFlexLayout();\nif (col.flex) {\n col.flex.dir = 'column';\n col.flex.rowGap = S.md;\n col.flex.horizontalPadding = S.lg;\n col.flex.verticalPadding = S.lg;\n col.flex.alignItems = 'center';\n}\nconst titulo = mk('Maquinas de Escribir', S.lg, '700', P.ink, '1.2');\nconst cuerpo = mk('Coleccion de maquinas de escribir de los anos 1920 a 1960, con mecanismos originales y restaurados.', S.sm, '400', P.ink, '1.6');\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton Ver Coleccion';\nbtn.resize(200, 44);\nbtn.borderRadius = 22;\nbtn.fills = [{ fillColor: P.accent, fillOpacity: 1, fillImagePosition: 'cover' }];\nbtn.insertChild(btn.children.length, mk('Ver Coleccion', S.sm, '600', P.bg, '1'));\nif (col.flex) {\n col.flex.appendChild(titulo);\n col.flex.appendChild(cuerpo);\n col.flex.appendChild(btn);\n} else {\n col.insertChild(col.children.length, titulo);\n col.insertChild(col.children.length, cuerpo);\n col.insertChild(col.children.length, btn);\n}\nstorage.museo = { colId: col.id, P: P, S: S };\nreturn { colId: col.id, hasFlex: !!col.flex, flexDir: col.flex ? col.flex.dir : null, childrenCount: col.children.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pie de pagina de 1240 px para una academia de tango con cuatro columnas de enlaces y una linea final de derechos.",
"id": "pd-050",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { fondo:'#0B132B', texto:'#F4F1EB', acento:'#D4AF37', sec:'#6B7B8D' };\nconst T = { sm:'14', md:'16', lg:'20' };\nconst S = { col:260, gap:24 };\nconst cols = [\n { n:'Clases', items:['Iniciacion','Intermedio','Avanzado','Milonga'] },\n { n:'Profesores', items:['Biografias','Horarios','Contacto'] },\n { n:'Recursos', items:['Blog','Videos','Partituras'] },\n { n:'Legal', items:['Aviso','Privacidad','Cookies'] }\n];\nconst root = penpot.createBoard();\nroot.name = 'Footer Academia';\nroot.fills = [{ fillColor: P.fondo, fillOpacity: 1 }];\nroot.resize(1240, 400);\nroot.x = 0; root.y = 0;\nroot.addFlexLayout();\nif (root.flex) { root.flex.dir = 'column'; root.flex.columnGap = S.gap; root.flex.horizontalPadding = 60; root.flex.verticalPadding = 40; root.flex.alignItems = 'start'; }\nconst mk = (name, fills, fontSize, fontWeight) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.characters = name; t.fontFamily = 'Inter'; t.fontSize = fontSize; t.fontWeight = fontWeight || '400'; t.lineHeight = '1.5'; t.fills = fills; t.growType = 'auto-width';\n return t;\n};\nconst mkCol = (c) => {\n const b = penpot.createBoard();\n b.name = c.n;\n b.fills = []; b.strokes = [];\n b.resize(S.col, 1);\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'row'; b.flex.rowGap = 12; b.flex.horizontalPadding = 20; b.flex.verticalPadding = 16; b.flex.alignItems = 'center'; }\n const icon = penpot.createRectangle();\n icon.name = c.n + '-icon';\n icon.fills = [{ fillColor: P.acento, fillOpacity: 1 }];\n icon.resize(20, 20);\n icon.borderRadius = 4;\n b.insertChild(b.children.length, icon);\n b.insertChild(b.children.length, mk(c.n, [P.texto], T.lg, '600'));\n c.items.forEach(i => b.insertChild(b.children.length, mk(i, [P.sec], T.sm)));\n return b;\n};\ncols.forEach(c => root.insertChild(root.children.length, mkCol(c)));\nconst bar = penpot.createBoard();\nbar.name = 'Derechos';\nbar.fills = [{ fillColor: P.acento, fillOpacity: 1 }];\nbar.resize(1240, 60);\nbar.x = 0; bar.y = 340;\nbar.addFlexLayout();\nif (bar.flex) { bar.flex.dir = 'row'; bar.flex.horizontalPadding = 60; bar.flex.verticalPadding = 16; bar.flex.justifyContent = 'center'; }\nbar.insertChild(bar.children.length, mk('© 2025 Academia del Abrazo — Todos los derechos reservados', [P.fondo], T.sm));\nroot.insertChild(root.children.length, bar);\nconst ids = [root.id, ...root.children.map(s => s.id)];\nstorage.footer = { rootId: root.id, ids, paleta: P, tipografia: T };\nreturn { rootId: root.id, shapes: ids.length, paleta: P, tipografia: T, columnas: cols.length, estructura: penpotUtils.shapeStructure(root, 3) };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Pantalla de 360x780 de un albergue para perros mayores con una lista vertical de siete items, cada uno con icono a la izquierda y texto a la derecha.",
"id": "pd-051",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = {\n cream: '#FFF8F0', green: '#5B8C5A', terra: '#C26A4A', ink: '#2C3E50', white: '#FFFFFF'\n};\nconst S = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 };\nconst T = { h1: '16', h2: '14' };\nconst items = [\n { name: 'Alimentación', desc: 'Dos comidas diarias balanceadas y agua fresca siempre disponible.', accent: P.green },\n { name: 'Veterinario', desc: 'Revisión mensual y atención inmediata ante cualquier emergencia.', accent: P.green },\n { name: 'Socialización', desc: 'Tiempo de paseo y juego supervisado con otros perros.', accent: P.green },\n { name: 'Refugio', desc: 'Camas individuales, espacio cálido y sin corrales.', accent: P.green },\n { name: 'Adopción', desc: 'Proceso de匹配 con familias responsables y seguimiento.', accent: P.terra },\n { name: 'Voluntariado', desc: 'Paseos, limpieza y acompañamiento los fines de semana.', accent: P.terra },\n { name: 'Donaciones', desc: 'Apoyo económico o en especie para mantener el refugio.', accent: P.terra }\n];\nconst board = penpot.createBoard();\nboard.name = 'Albergue — Servicios';\nboard.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\nboard.resize(360, 780);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.xl;\n board.flex.verticalPadding = S.xl;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'start';\n}\nconst header = penpot.createText();\nheader.name = 'Encabezado';\nheader.characters = 'Nuestros servicios';\nheader.fontFamily = 'Nunito';\nheader.fontSize = T.h1;\nheader.fontWeight = '700';\nheader.lineHeight = '1.3';\nheader.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\nheader.strokes = [];\nheader.insertChild(header.children.length, header);\nconst icon = penpot.createRectangle();\nicon.name = 'Icono';\nicon.fills = [{ fillColor: P.green, fillOpacity: 1 }];\nicon.resize(48, 48);\nicon.borderRadius = S.sm;\nconst root = penpotUtils.findShape(s => s.name === 'Albergue — Servicios');\nif (!root) return { error: 'no se encontro el board' };\nroot.appendChild(board);\nconst rows = items.map(it => {\n const row = penpot.createBoard();\n row.name = 'Fila ' + it.name;\n row.fills = [{ fillColor: it.accent === P.terra ? P.terra : P.white, fillOpacity: 1 }];\n row.resize(360, 80);\n row.borderRadius = S.md;\n row.strokes = it.accent === P.terra ? [] : [{ strokeColor: P.ink, strokeOpacity: 0.12, strokeWidth: 1, strokeStyle: 'solid' }];\n row.addFlexLayout();\n if (row.flex) {\n row.flex.dir = 'row';\n row.flex.horizontalPadding = S.md;\n row.flex.verticalPadding = S.md;\n row.flex.columnGap = S.md;\n row.flex.alignItems = 'center';\n }\n const icon = penpot.createRectangle();\n icon.name = 'Icono ' + it.name;\n icon.fills = [{ fillColor: it.accent === P.terra ? P.white : it.accent, fillOpacity: 1 }];\n icon.resize(48, 48);\n icon.borderRadius = S.sm;\n row.appendChild(icon);\n const col = penpot.createGroup();\n col.name = 'Columna ' + it.name;\n col.fills = [];\n col.resize(264, 80);\n col.addFlexLayout();\n if (col.flex) {\n col.flex.dir = 'column';\n col.flex.verticalPadding = S.sm;\n col.flex.rowGap = S.xs;\n col.flex.alignItems = 'start';\n }\n row.appendChild(col);\n const title = penpot.createText();\n title.name = 'Titulo ' + it.name;\n title.characters = it.name;\n title.fontFamily = 'Nunito';\n title.fontSize = T.h1;\n title.fontWeight = '700';\n title.lineHeight = '1.3';\n title.fills = [{ fillColor: it.accent === P.terra ? P.white : P.ink, fillOpacity: 1 }];\n title.strokes = [];\n col.appendChild(title);\n const desc = penpot.createText();\n desc.name = 'Descripcion ' + it.name;\n desc.characters = it.desc;\n desc.fontFamily = 'Nunito';\n desc.fontSize = T.h2;\n desc.fontWeight = '400';\n desc.lineHeight = '1.5';\n desc.fills = [{ fillColor: it.accent === P.terra ? P.white : P.ink, fillOpacity: 0.75 }];\n desc.strokes = [];\n col.appendChild(desc);\n board.appendChild(row);\n return row;\n});\nconst contained = rows.every(r => penpotUtils.isContainedIn(r, board));\nstorage.albergue = { boardId: board.id, rows: rows.map(r => r.id) };\nreturn { boardId: board.id, rows: rows.length, contained: contained, focusedRows: rows.filter(r => r.strokes.length === 0).length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "tems = 'start'; } const header = penpot.createText(); header.name = 'Encabezado'; header.cha"
}
],
"content": ""
},
{
"prompt": "En la pagina hay un board de un museo de maquinas de escribir que quedo con puros rectangulos grises y sin una sola palabra. Convertilo en un diseno de verdad.",
"id": "pd-052",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const page = penpotUtils.getPageByName('Museo de Maquinas de Escribir');\nif (!page) return { error: 'page not found' };\nconst board = penpotUtils.findShape(s => s.name === 'Museo - Portada', page.root);\nif (!board) return { error: 'board not found' };\nstorage.museo = { pageId: page.id, boardId: board.id };\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst fills = penpotUtils.findShapes(s => s.fills && s.fills.length > 0, board);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nreturn { boardId: board.id, boardName: board.name, boardType: board.type, childrenCount: board.children.length, structure, fillsCount: fills.length, textsCount: texts.length, textSizes: texts.map(t => ({ name: t.name, fontSize: t.fontSize, lineHeight: t.lineHeight })) };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Este board de una consultora de eficiencia energetica tiene todos los textos del mismo tamano y todo el mismo gris. Dale jerarquia y una paleta.",
"id": "pd-053",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const P = { brand:'#0B2A4A', accent:'#16A34A', ink:'#0F172A', mid:'#334155', light:'#64748B', bg:'#F8FAF9' };\nconst T = { h1:'36', h2:'24', body:'16', note:'13' };\nconst S = { xs:4, sm:8, md:16, lg:24, xl:40 };\nconst mk = (chars, size, weight, color, lineH) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = lineH;\n t.characters = chars; t.growType = 'auto-width';\n t.fills = [{ fillOpacity: 1, fillColor: color, fillStyle: 'fill' }];\n t.strokes = []; t.shadows = [];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Hero consultora';\nboard.fills = [{ fillOpacity: 1, fillColor: P.bg, fillStyle: 'fill' }];\nboard.resize(1100, 620);\nboard.x = 0; board.y = 0;\nconst root = board;\nconst h1 = mk('Eficiencia energetica para la industria', T.h1, '700', P.ink, '1.2');\nconst h2 = mk('Medimos, optimizamos y certificamos', T.h2, '600', P.accent, '1.3');\nconst body = mk('Reduccion de consumo y emisiones con auditoria incluida.', T.body, '400', P.mid, '1.5');\nconst note = mk('Auditoria inicial sin cargo · respuesta en 48 horas', T.note, '400', P.light, '1.4');\nconst C = [\n { shape: h1, x: 0, y: 0 },\n { shape: h2, x: 0, y: S.xl },\n { shape: body, x: 0, y: S.lg },\n { shape: note, x: 0, y: S.lg }\n];\nC.forEach(c => { root.insertChild(root.children.length, c.shape); penpotUtils.setParentXY(c.shape, c.x, c.y); });\nconst bar = penpot.createRectangle();\nbar.name = 'Barra de marca';\nbar.fills = [{ fillOpacity: 1, fillColor: P.brand, fillStyle: 'fill' }];\nbar.resize(1100, 6);\nbar.x = 0; bar.y = 514;\nroot.insertChild(root.children.length, bar);\nconst tags = [\n { label: 'Auditoria', color: P.accent },\n { label: 'Certificacion', color: P.brand },\n { label: 'Optimizacion', color: P.accent }\n];\nconst tagsBox = penpot.createBoard();\ntagsBox.name = 'Tags';\ntagsBox.fills = [];\ntagsBox.resize(360, 36);\ntagsBox.x = 720; tagsBox.y = 200;\nroot.insertChild(root.children.length, tagsBox);\ntags.forEach(t => {\n const pill = penpot.createRectangle();\n pill.name = 'Tag ' + t.label;\n pill.fills = [{ fillOpacity: 1, fillColor: t.color, fillStyle: 'fill' }];\n pill.borderRadius = 18;\n pill.resize(100, 36);\n tagsBox.insertChild(tagsBox.children.length, pill);\n const pillText = mk(t.label, '14', '600', '#fff', '1');\n pillText.growType = 'auto-width';\n pill.fills[0].fillImage = null;\n pill.insertChild(0, pillText);\n penpotUtils.setParentXY(pillText, 12, 10);\n});\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst sizes = [...new Set(texts.map(t => t.fontSize))];\nconst fills = penpotUtils.findShapes(s => s.fills && s.fills.length > 0, board);\nconst colores = [...new Set(fills.flatMap(f => f.fills.map(fl => fl.fillColor)))];\nstorage.hero = { boardId: board.id, rootId: root.id };\nreturn { boardId: board.id, rootId: root.id, textCount: texts.length, fontSizees: sizes, fillCount: fills.length, colors: colores, structure: structure };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Revisa el board de un estudio de doblaje que arme yo, decime que esta mal de contraste y arreglalo.",
"id": "pd-054",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const board = penpotUtils.findShape(s => s.type === 'board' && /estudio|doblaje|audio|estudio de doblaje/i.test(s.name), penpot.root);\nif (!board) { return { error: 'no board encontrado', candidates: penpotUtils.findShapes(s => s.type === 'board', penpot.root).map(s => s.name) }; }\nstorage.boardId = board.id;\nconst shapes = penpotUtils.findShapes(s => s.fills && s.fills.length > 0, board);\nconst contrast = (fg, bg) => {\n const lum = c => { const a = c.map(v => v /= (v <= 0.03928 ? 12.92 : 1) : Math.pow((v + 0.055) / 1.055, 2.4)).map(v => v * 100); return 0.2126 * a[0] + 0.7152 * a[1] + 0.0722 * a[2]; };\n const L1 = lum(fg), L2 = lum(bg), diff = Math.max(L1, L2) - Math.min(L1, L2);\n return diff >= 0.005 ? Math.round((Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05) * 10) / 10 : 'infinito';\n};\nconst violations = shapes.filter(s => {\n const bg = s.fills.find(f => f.fillOpacity > 0)?.color || '#ffffff';\n return s.fills.some(f => f.fillOpacity > 0 && contrast(f.color, bg) < 3);\n});\nreturn { boardName: board.name, boardId: board.id, totalShapes: shapes.length, violations: violations.length, details: violations.map(s => ({ name: s.name, fills: s.fills.filter(f => f.fillOpacity > 0).map(f => ({ color: f.color, contrast: contrast(f.color, s.fills.find(ff => ff.fillOpacity > 0)?.color || '#ffffff') })) })) };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "El hero de una tienda de kayaks tiene el texto ilegible sobre la foto. Arreglalo sin cambiar la foto.",
"id": "pd-055",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const hero = penpotUtils.findShape(s => s.name === 'Hero');\nif (!hero) { return { found: false }; }\nconst structure = penpotUtils.shapeStructure(hero, 3);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', hero);\nconst overlays = penpotUtils.findShapes(s => s.fills?.some(f => f.fillOpacity !== undefined && f.fillOpacity < 1), hero);\nreturn { found: true, heroId: hero.id, heroWidth: hero.width, heroHeight: hero.height, textCount: texts.length, overlayCount: overlays.length, structure };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Antes de tocar nada, fijate en la documentacion de la API como se crea un texto en Penpot y despues poneme un titulo de un estudio de doblaje en 48 px.",
"id": "pd-056",
"tool_calls": [
"penpot_api_info"
],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Como hago para que un hijo de un board con layout ocupe todo el ancho disponible? Mostramelo aplicado a una seccion de una consultora de eficiencia energetica.",
"id": "pd-057",
"tool_calls": [
"penpot_api_info"
],
"code_payloads": [],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Quiero usar la tipografia Libre Baskerville en el titulo de una consultora de eficiencia energetica. Verifica que quede realmente aplicada, no solo seteada.",
"id": "pd-058",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const board = penpot.createBoard();\nboard.name = 'Hero consultora';\nboard.fills = [{ fillColor: '#0B1D14', fillOpacity: 1 }];\nboard.resize(960, 400);\nconst title = penpot.createText('Eficiencia energetica para tu industria');\ntitle.fontFamily = 'Libre Baskerville';\ntitle.fontSize = '48';\ntitle.fontWeight = '700';\ntitle.fills = [{ fillColor: '#F4F1E8', fillOpacity: 1 }];\ntitle.growType = 'auto-width';\nboard.insertChild(board.children.length, title);\nconst applied = title.fontName;\nconst size = title.fontSize;\nstorage.hero = board;\nstorage.titleId = title.id;\nreturn { appliedFont: applied, fontSize: size, fontSet: applied === 'Libre Baskerville', sizeSet: size === '48' };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
},
{
"prompt": "Guarda la paleta de un museo de maquinas de escribir como colores de la biblioteca del archivo y despues aplicalos a dos formas.",
"id": "pd-059",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const board = penpot.createBoard();\nboard.name = 'Paleta de tinta';\nboard.fills = [{ fillColor: '#FDFBF7', fillOpacity: 1 }];\nboard.resize(800, 600);\nconst P = { ink: '#1B2A4A', rust: '#A63D40', brass: '#D4A843', cream: '#FDFBF7' };\nconst S = { sm: 16, md: 24, lg: 36 };\nconst swatches = [P.ink, P.rust, P.brass];\nconst shapes = swatches.map(c => {\n const r = penpot.createRectangle();\n r.name = 'Muestra ' + c;\n r.fills = [{ fillColor: c, fillOpacity: 1 }];\n r.resize(160, 160);\n return r;\n});\nconst labels = swatches.map((c, i) => {\n const t = penpot.createText();\n t.name = 'Etiqueta ' + (i + 1);\n t.characters = swatches[i];\n t.fontFamily = 'Work Sans';\n t.fontSize = S.md;\n t.lineHeight = '1.3';\n t.fontWeight = '600';\n t.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n});\nconst title = penpot.createText();\ntitle.name = 'Titulo';\ntitle.characters = 'Paleta de tinta';\ntitle.fontFamily = 'Work Sans';\ntitle.fontSize = S.lg;\ntitle.lineHeight = '1.2';\ntitle.fontWeight = '700';\ntitle.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\ntitle.growType = 'auto-width';\nboard.insertChild(board.children.length, title);\nshapes.forEach(s => board.insertChild(board.children.length, s));\nlabels.forEach(l => board.insertChild(board.children.length, l));\nconst library = penpot.library.local;\nconst colores = [\n { name: 'Tinta', color: P.ink },\n { name: 'Oxido', color: P.rust },\n { name: 'Laton', color: P.brass }\n];\nconst guardados = colores.map(c => {\n const lc = library.createColor();\n lc.name = c.name;\n lc.color = c.color;\n return lc;\n});\nstorage.palette = { boardId: board.id, P, S, swatches, guardados: guardados.map(lc => ({ name: lc.name, color: lc.color })) };\nreturn { boardId: board.id, libraryColors: guardados.map(lc => ({ name: lc.name, color: lc.color })), shapes: shapes.length, labels: labels.length };"
],
"veto_hits": [],
"otros_patrones": [
{
"patron": "createText sin argumento util",
"fragmento": "ches.map((c, i) => { const t = penpot.createText(); t.name = 'Etiqueta ' + (i + 1); t."
}
],
"content": ""
},
{
"prompt": "Exportame el board de una consultora de eficiencia energetica para verlo, y decime si algo quedo fuera de los limites.",
"id": "pd-060",
"tool_calls": [
"execute_code"
],
"code_payloads": [
"const board = penpotUtils.findShape(s => s.name.includes('Consultora') || s.name.includes('Energia'), penpot.root);\nif (!board) { return { found: false }; }\nstorage.consultoraBoard = board.id;\nconst violations = penpotUtils.analyzeDescendants(board, (root, shape) => {\n return !penpotUtils.isContainedIn(shape, root) ? 'outside-bounds' : null;\n});\nreturn { found: true, boardId: board.id, boardName: board.name, boardType: board.type, boardSize: { width: board.width, height: board.height }, violations: violations.length };"
],
"veto_hits": [],
"otros_patrones": [],
"content": ""
}
]
}