Tasa de API prohibida: 8.3% (5/60), baja de 15% (9/60) en v2-bf16, pero
sigue por encima del umbral de 5% -- NO APRUEBA.
Desglose por patron (v2-bf16 -> v2b-bf16):
- import_image/importImage/createImage/filePath: 4 -> 1 (-75%, el
fix que apuntaban los seeds de uploadMediaUrl)
- fontSize/fontWeight/lineHeight/letterSpacing numerico: 3 -> 1 (-67%)
- flex.appendChild: 2 -> 3 (empeoro levemente; NO fue el foco de
esta ronda de seeds correctivos, es el patron ahora dominante:
3 de los 5 vetos restantes)
Se continua con el resto del runbook (verificacion de Penpot, puerta 5
full, sonda de generalizacion) para tener el cuadro completo antes de
reportar, tal como indico el usuario -- pero este resultado ya senala
que el disparador de fallback documentado en PLAN.md (<=5%) se activa
de nuevo, esta vez por un patron distinto y no cubierto por los seeds
de esta iteracion.
859 lines
173 KiB
JSON
859 lines
173 KiB
JSON
{
|
|
"modo": "holdout",
|
|
"tag": "v2b-bf16-holdout",
|
|
"modelo": "qwen3.6-35b-a3b-mcp-v2b-bf16",
|
|
"total": 60,
|
|
"prompts_en_archivo": 60,
|
|
"errores_de_request": 0,
|
|
"con_api_prohibida": 5,
|
|
"tasa": 0.08333333333333333,
|
|
"umbral": 0.05,
|
|
"aprueba": false,
|
|
"patrones": {
|
|
"flex.appendChild": 3,
|
|
"fontSize/fontWeight/lineHeight/letterSpacing numerico": 1,
|
|
"import_image / importImage / createImage / filePath": 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 = { tinta:'#0B0F19', vino:'#6B112A', dorado:'#D4A853', crema:'#FDF6EC', blanco:'#FFFFFF' };\nconst T = { h1:'48', h2:'32', h3:'20', body:'16', small:'13' };\nconst S = { xs:8, sm:16, md:24, lg:40, xl:64 };\nconst W = 1180;\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 || 'Playfair Display';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.35';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst mkBtn = (chars, bg, fg) => {\n const b = penpot.createRectangle();\n if (!b) throw new Error('createRectangle devolvio null');\n b.name = 'Boton ' + chars;\n b.fills = [{ fillColor: bg, fillOpacity: 1 }];\n b.borderRadius = 6;\n b.resize(220, 52);\n b.insertChild(0, mk(chars, '16', fg, '700', 'Montserrat'));\n return b;\n};\nconst mkCard = (chars, icon, desc) => {\n const c = penpot.createRectangle();\n if (!c) throw new Error('createRectangle devolvio null');\n c.name = 'Beneficio ' + chars;\n c.fills = [{ fillColor: P.crema, fillOpacity: 1 }];\n c.borderRadius = 12;\n c.resize(340, 260);\n c.strokes = [{ strokeColor: P.dorado, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.insertChild(0, mk(icon, '36', P.vino, '400'));\n c.insertChild(1, mk(chars, T.h3, P.tinta, '700'));\n c.insertChild(2, mk(desc, T.body, P.tinta));\n return c;\n};\nconst mkTestimonio = (quote, autor) => {\n const c = penpot.createRectangle();\n if (!c) throw new Error('createRectangle devolvio null');\n c.name = 'Testimonio ' + autor;\n c.fills = [{ fillColor: P.blanco, fillOpacity: 1 }];\n c.borderRadius = 12;\n c.resize(530, 200);\n c.strokes = [{ strokeColor: P.dorado, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.insertChild(0, mk(quote, T.body, P.tinta));\n c.insertChild(1, mk(autor, '14', P.vino, '700'));\n return c;\n};\nconst hero = penpot.createBoard();\nif (!hero) throw new Error('createBoard devolvio null');\nhero.name = 'Hero';\nhero.fills = [{ fillColor: P.vino, fillOpacity: 1 }];\nhero.resize(W, 520);\nhero.insertChild(0, mk('Tango en Buenos Aires', T.h1, P.dorado, '700'));\nhero.insertChild(1, mk('Clases para todos los niveles, en el corazon de San Telmo.', '20', P.crema, '400'));\nhero.insertChild(2, mkBtn('Reservar clase de prueba', P.dorado, P.vino));\nstorage.academia = { P, T, S, mk, mkBtn, mkCard, mkTestimonio, hero };\nreturn { heroId: hero.id, heroFills: hero.fills.length, heroChildren: hero.children.length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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:'#1B0A3C', accent:'#D9138A', ink:'#FFD600', white:'#FFFFFF' };\nconst T = { hero:'64', sub:'20', num:'56', label:'16', contact:'18' };\nconst S = { sm:16, md:24, lg:40, xl:60 };\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 || 'Work Sans';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst mkCard = (num, label) => {\n const c = penpot.createRectangle();\n c.name = 'Card ' + num;\n c.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n c.borderRadius = 16;\n c.resize(240, 220);\n c.addFlexLayout();\n if (c.flex) { c.flex.dir = 'column'; c.flex.alignItems = 'center'; c.flex.justifyContent = 'center'; c.flex.rowGap = S.sm; c.flex.horizontalPadding = S.md; c.flex.verticalPadding = S.md; }\n c.appendChild(mk(num, T.num, P.ink, '700'));\n c.appendChild(mk(label, T.label, P.white, '400'));\n return c;\n};\nconst mkContact = (icon, line) => {\n const c = penpot.createRectangle();\n c.name = 'Contact ' + icon;\n c.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n c.borderRadius = 12;\n c.resize(320, 80);\n c.addFlexLayout();\n if (c.flex) { c.flex.dir = 'row'; c.flex.alignItems = 'center'; c.flex.columnGap = S.md; c.flex.horizontalPadding = S.md; }\n c.appendChild(mk(icon, T.contact, P.ink, '700'));\n c.appendChild(mk(line, T.contact, P.white, '400'));\n return c;\n};\nconst mkSection = (name, w, h, bg, children) => {\n const b = penpot.createBoard();\n b.name = name;\n b.fills = [{ fillColor: bg, fillOpacity: 1 }];\n b.resize(w, h);\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'column'; b.flex.alignItems = 'start'; b.flex.rowGap = S.lg; b.flex.horizontalPadding = S.xl; b.flex.verticalPadding = S.xl; }\n children.forEach(ch => b.appendChild(ch));\n return b;\n};\nconst hero = mkSection('Hero', 1180, 520, P.bg, [\n mk('Estudio Voz Viva', T.hero, P.white, '700'),\n mk('Doblaje profesional en espanol para cine, series y videojuegos', T.sub, P.white, '400')\n]);\nconst numericos = mkSection('Numeros', 1180, 320, P.white, [\n mk('En cifras', T.sub, P.bg, '700'),\n ...['120+', '8', '350', '15'].map((n, i) => mkCard(n, ['Proyectos', 'Anios', 'Artistas', 'Paises'][i]))\n]);\nconst contacto = mkSection('Contacto', 1180, 360, P.bg, [\n mk('Hablemos', T.sub, P.ink, '700'),\n ...[['Email', 'hola@vozviva.studio'], ['Telefono', '+52 55 1234 5678'], ['Estudio', 'Ciudad de Mexico']].map(([i, l]) => mkContact(i, l))\n]);\nconst root = penpot.root;\nroot.insertChild(root.children.length, hero);\nroot.insertChild(root.children.length, numericos);\nroot.insertChild(root.children.length, contacto);\nconst sections = [hero, numericos, contacto];\nconst structure = penpotUtils.shapeStructure(root, 3);\nreturn {\n sections: sections.length,\n paleta: Object.keys(P).length,\n tamanios: Object.keys(T).length,\n hero: { width: hero.width, height: hero.height },\n numericos: { width: numericos.width, height: numericos.height },\n contacto: { width: contacto.width, height: contacto.height },\n cards: penpotUtils.findShapes(s => s.name.startsWith('Card'), root).length,\n contacts: penpotUtils.findShapes(s => s.name.startsWith('Contact'), root).length,\n structure: structure\n};"
|
|
],
|
|
"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 = { tinta: '#1B2A4A', papel: '#F4F0E6', metal: '#C2B280', oscuro: '#0D1117', claro: '#FFFFFF' };\nconst T = { hero: '64', sub: '22', card: '20', cuerpo: '16', pie: '13' };\nconst S = { sm: 12, md: 24, lg: 48 };\nconst page = penpot.createBoard();\npage.name = 'Museo Maquinas de Escribir';\npage.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\npage.resize(1200, 1100);\npage.addFlexLayout();\nif (page.flex) { page.flex.dir = 'column'; page.flex.horizontalPadding = S.lg; page.flex.verticalPadding = S.lg; page.flex.rowGap = S.lg; page.flex.alignItems = 'center'; }\nstorage.museo = { page, P, T, S };\nconst mk = (name, chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.name = name; t.fontFamily = 'Playfair Display'; t.fontSize = size; t.fontWeight = weight || '400'; t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.growType = 'auto-height';\n return t;\n};\nconst hero = penpot.createBoard();\nhero.name = 'Hero';\nhero.fills = [{ fillColor: P.tinta, fillOpacity: 1 }];\nhero.resize(1104, 420);\nhero.borderRadius = 16;\npage.insertChild(page.children.length, hero);\nif (hero.flex) { hero.flex.dir = 'column'; hero.flex.horizontalPadding = S.lg; hero.flex.verticalPadding = S.lg; hero.flex.rowGap = S.md; hero.flex.alignItems = 'center'; }\nhero.appendChild(mk('Hero / titulo', 'Museo de Maquinas de Escribir', T.hero, P.papel, '700'));\nhero.appendChild(mk('Hero / sub', 'Coleccion permanente de maquinas de 1874 a 1992', T.sub, P.metal, '400'));\nconst mkCard = (name, titulo, cuerpo) => {\n const c = penpot.createBoard();\n c.name = name;\n c.fills = [{ fillColor: P.claro, fillOpacity: 1 }];\n c.resize(340, 480);\n c.borderRadius = 12;\n c.strokes = [{ strokeColor: P.metal, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n c.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 20, spread: 0, color: { color: P.tinta, opacity: 0.12 } }];\n if (c.flex) { c.flex.dir = 'column'; c.flex.horizontalPadding = S.md; c.flex.verticalPadding = S.md; c.flex.rowGap = S.sm; c.flex.alignItems = 'start'; }\n return c;\n};\nconst exposiciones = [\n { titulo: 'La Remington No. 2', cuerpo: 'La primera maquina de escribir practica, 1874. Teclado QWERTY y mecanismo de palanca.' },\n { titulo: 'Olivetti Lettera 32', cuerpo: 'Portatil italiana de 1950, aluminio anodizado. 3,2 kg de precision mecanica.' },\n { titulo: 'Underwood No. 5', cuerpo: 'El estandar de la industria periodistica norteamericana durante 60 anos.' }\n];\nconst grilla = penpot.createBoard();\ngrilla.name = 'Exposiciones';\ngrilla.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\ngrilla.resize(1104, 560);\npage.insertChild(page.children.length, grilla);\nif (grilla.flex) { grilla.flex.dir = 'row'; grilla.flex.horizontalPadding = S.lg; grilla.flex.verticalPadding = S.lg; grilla.flex.rowGap = S.md; grilla.flex.alignItems = 'start'; }\nexposiciones.forEach(e => {\n const card = mkCard('Tarjeta / ' + e.titulo, e.titulo, e.cuerpo);\n card.appendChild(mk('Tarjeta / titulo', e.titulo, T.card, P.tinta, '700'));\n card.appendChild(mk('Tarjeta / cuerpo', e.cuerpo, T.cuerpo, P.tinta, '400'));\n grilla.appendChild(card);\n});\nconst footer = penpot.createBoard();\nfooter.name = 'Footer';\nfooter.fills = [{ fillColor: P.tinta, fillOpacity: 1 }];\nfooter.resize(1104, 80);\nfooter.borderRadius = 12;\npage.insertChild(page.children.length, footer);\nif (footer.flex) { footer.flex.dir = 'row'; footer.flex.horizontalPadding = S.lg; footer.flex.verticalPadding = S.md; footer.flex.rowGap = S.sm; footer.flex.alignItems = 'center'; footer.flex.justifyContent = 'center'; }\nfooter.appendChild(mk('Footer', 'Museo de Maquinas de Escribir — Abierto de martes a domingos, 10 a 18 hs.', T.pie, P.metal, '400'));\nconst shapes = penpotUtils.findShapes(() => true, page);\nconst fills = shapes.filter(s => s.fills && s.fills.length > 0);\nconst textos = penpotUtils.findShapes(s => s.type === 'text', page);\nreturn { pageId: page.id, shapes: shapes.length, fills: fills.length, textos: textos.length, flex: !!page.flex, grid: !!page.grid, roles: Object.keys(P).length, tamanios: Object.keys(T).length };"
|
|
],
|
|
"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 = { marca:'#312e81', acento:'#f59e0b', fondo:'#faf9f6', tinta:'#1e1b4b', linea:'#e7e5f4' };\nconst T = { xs:'16', sm:'24', md:'36', lg:'48' };\nconst S = { 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 || '400';\n t.lineHeight = '1.4'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width'; return t;\n};\nconst mkCard = (name, accent) => {\n const c = penpot.createRectangle();\n c.name = name; c.fills = [{ fillColor: '#fff', fillOpacity: 1 }];\n c.strokes = [{ strokeColor: accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n c.borderRadius = 20; c.resize(260, 420); c.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 24, spread: 0, color: { color: P.marca, opacity: 0.12 } }];\n return c;\n};\nconst mkPila = (items, gap) => {\n const p = penpot.createBoard();\n p.name = items[0].name; p.fills = []; p.resize(900, 1);\n p.addFlexLayout();\n if (p.flex) {\n p.flex.dir = 'column'; p.flex.rowGap = gap; p.flex.alignItems = 'center';\n p.flex.horizontalPadding = S.lg; p.flex.verticalPadding = S.lg;\n }\n items.forEach(i => p.appendChild(i));\n return p;\n};\nconst root = penpot.createBoard();\nroot.name = 'Precios'; root.fills = [{ fillColor: P.fondo, fillOpacity: 1 }]; root.resize(900, 1);\nconst enc = mkPila([\n mk('Vivero Orquideas', T.lg, P.marca, '700'),\n mk('El plan que elijas, las orquideas florecen igual.', T.sm, P.tinta),\n mk('Elige tu plan y recibe tus orquideas en 48 horas.', T.sm, P.acento)\n], S.md);\nenc.fills = [{ fillColor: P.fondo, fillOpacity: 1 }]; enc.resize(900, 1);\nconst planes = [\n { n:'Basico', p:'$12.000', d:'1 orquidea + sustrato', a:['Envio gratis','Guia de riego'] },\n { n:'Premium', p:'$24.000', d:'3 orquideas + maceta', a:['Envio gratis','Guia de riego','Reposicion gratis'] },\n { n:'Coleccion', p:'$42.000', d:'6 orquideas + macetas', a:['Envio gratis','Guia de riego','Reposicion gratis','Asesoramiento'] }\n];\nconst cards = planes.map(pl => {\n const c = mkCard('Plan ' + pl.n, pl.n === 'Premium' ? P.marca : P.linea);\n const p = mkPila([mk(pl.n, T.md, pl.n === 'Premium' ? '#fff' : P.marca, '700'), mk(pl.p, T.lg, pl.acento || P.acento, '700'), mk(pl.d, T.sm, P.tinta), ...pl.a.map(x => mk('\\u2022 ' + x, T.xs, P.tinta))], S.sm);\n p.fills = []; p.resize(260, 1);\n c.insertChild(c.children.length, p);\n return c;\n});\nconst pil = mkPila(cards, S.md);\npil.fills = [{ fillColor: P.fondo, fillOpacity: 1 }]; pil.resize(900, 1);\nconst faq = mkPila([mk('Preguntas frecuentes', T.md, P.marca, '700'), mk('Como riego?', T.sm, P.tinta), mk('Cada 7 dias, con agua sin cloro.', T.sm, P.tinta)], S.md);\nfaq.fills = [{ fillColor: P.fondo, fillOpacity: 1 }]; faq.resize(900, 1);\nconst pie = mkPila([mk('Vivero Orquideas', T.sm, P.marca, '700'), mk('hola@vivero.ar', T.xs, P.tinta)], S.sm);\npie.fills = [{ fillColor: P.fondo, fillOpacity: 1 }]; pie.resize(900, 1);\nconst todo = [enc, pil, faq, pie];\nconst H = [enc, pil, faq, pie].map(b => b.height);\nconst total = H.reduce((a, b) => a + b, 0) + S.lg * 3;\nroot.resize(900, total);\nroot.insertChild(root.children.length, enc);\nenc.y = S.lg;\nroot.insertChild(root.children.length, pil);\npil.y = enc.y + enc.height + S.lg;\nroot.insertChild(root.children.length, faq);\nfaq.y = pil.y + pil.height + S.lg;\nroot.insertChild(root.children.length, pie);\npie.y = faq.y + faq.height + S.lg;\nstorage.vivero = { rootId: root.id, P: P, T: T, S: S };\nreturn { rootId: root.id, pages: penpotUtils.getPages().length, boards: penpotUtils.findShapes(s => s.type === 'board', root).length, texts: penpotUtils.findShapes(s => s.type === 'text', root).length, fills: penpotUtils.findShapes(s => s.fills && s.fills.length > 0, root).length, shapes: penpotUtils.shapeStructure(root, 3) };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 = {\n primary: '#1B5E20', dark: '#0D3B12', bg: '#E8F5E9', card: '#FFFFFF',\n alert: '#FF6F00', info: '#1565C0', line: '#C8E6C9', white: '#FFFFFF'\n};\nconst T = { h1: '28', h2: '16', body: '14', small: '12' };\nconst S = { sm: 8, md: 16, lg: 24, xl: 32 };\n\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = (fills || []).map(c => ({ fillColor: c, fillOpacity: 1, fillStyle: null }));\n if (radius) s.borderRadius = radius;\n return s;\n};\nconst mkText = (chars, size, color, weight, align) => {\n const t = penpot.createText(chars);\n t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight || '400';\n t.lineHeight = '1.4'; t.letterSpacing = '0'; t.paragraphSpacing = '0';\n t.paragraphIndent = '0'; t.align = align || 'left';\n t.fills = [{ fillColor: color, fillOpacity: 1, fillStyle: null }];\n t.growType = 'auto-width';\n return t;\n};\nconst mkNav = (label, active) => {\n const b = penpot.createBoard();\n b.name = 'Nav ' + label; b.resize(216, 44);\n b.fills = [{ fillColor: active ? P.primary : 'transparent', fillOpacity: 1, fillStyle: null }];\n b.borderRadius = 8;\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'row'; b.flex.alignItems = 'center'; b.flex.rowGap = S.sm; b.flex.horizontalPadding = S.md; }\n b.appendChild(mkText(label, T.body, active ? P.white : P.dark, '500'));\n return b;\n};\nconst mkInd = (label, valor, color, icon) => {\n const b = penpot.createBoard();\n b.name = 'Ind ' + label; b.resize(340, 120);\n b.fills = [{ fillColor: P.card, fillOpacity: 1, fillStyle: null }];\n b.borderRadius = 12;\n b.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: '#000000', opacity: 0.08 } }];\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'column'; b.flex.alignItems = 'start'; b.flex.rowGap = S.sm; b.flex.horizontalPadding = S.md; b.flex.verticalPadding = S.md; }\n b.appendChild(mkText(icon + ' ' + label, T.small, color, '600'));\n b.appendChild(mkText(valor, T.h1, P.dark, '700'));\n return b;\n};\nconst mkRow = (celdas, isHeader) => {\n const b = penpot.createBoard();\n b.name = isHeader ? 'Tabla cabecera' : 'Tabla fila'; b.resize(1032, isHeader ? 40 : 48);\n b.fills = [{ fillColor: isHeader ? P.primary : P.card, fillOpacity: 1, fillStyle: null }];\n if (!isHeader) b.strokes = [{ strokeStyle: null, strokeWidth: 1, strokeColor: P.line, strokeOpacity: 1, strokeAlignment: 'inner', strokeDashes: [] }];\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'row'; b.flex.alignItems = 'center'; b.flex.columnGap = S.md; }\n celdas.forEach(c => b.appendChild(mkText(c, isHeader ? T.small : T.body, isHeader ? P.white : P.dark, isHeader ? '600' : '400')));\n return b;\n};\n\nconst root = penpot.createBoard();\nroot.name = 'Dashboard vivero'; root.resize(1320, 900);\nroot.fills = [{ fillColor: P.bg, fillOpacity: 1, fillStyle: null }];\nroot.addFlexLayout();\nif (root.flex) { root.flex.dir = 'row'; root.flex.alignItems = 'start'; root.flex.columnGap = 0; }\n\nconst side = penpot.createBoard();\nside.name = 'Sidebar'; side.resize(240, 900);\nside.fills = [{ fillColor: P.primary, fillOpacity: 1, fillStyle: null }];\nside.addFlexLayout();\nif (side.flex) { side.flex.dir = 'column'; side.flex.alignItems = 'start'; side.flex.rowGap = S.sm; side.flex.horizontalPadding = S.md; side.flex.verticalPadding = S.lg; }\nside.appendChild(mkText('Orquideas', T.h1, P.white, '700'));\n['Dashboard', 'Inventario', 'Movimientos', 'Reportes'].forEach((n, i) => side.appendChild(mkNav(n, i === 0)));\nside.appendChild(mkNav('Cerrar sesion', false));\nroot.appendChild(side);\n\nconst content = penpot.createBoard();\ncontent.name = 'Contenido'; content.resize(1080, 900);\ncontent.fills = [{ fillColor: P.bg, fillOpacity: 1, fillStyle: null }];\ncontent.addFlexLayout();\nif (content.flex) { content.flex.dir = 'column'; content.flex.alignItems = 'start'; content.flex.rowGap = S.lg; content.flex.horizontalPadding = S.xl; content.flex.verticalPadding = S.xl; }\nroot.appendChild(content);\n\ncontent.appendChild(mkText('Panel de control', T.h1, P.dark, '700'));\ncontent.appendChild(mkText('Actualizado hoy a las 09:00', T.body, P.info, '400'));\n\nconst indicadores = penpot.createBoard();\nindicadores.name = 'Indicadores'; indicadores.resize(1032, 160);\nindicadores.fills = [{ fillColor: P.bg, fillOpacity: 1, fillStyle: null }];\nindicadores.addFlexLayout();\nif (indicadores.flex) { indicadores.flex.dir = 'row'; indicadores.flex.alignItems = 'start'; indicadores.flex.columnGap = S.md; }\ncontent.appendChild(indicadores);\n[['Stock total', '1.240', P.primary, '🌿'], ['En flor', '87', P.info, '🌸'], ['En cuarentena', '23', P.alert, '⚠️'], ['Trasplantes', '156', P.primary, '🪴'], ['Ventas mes', '$48.300', P.info, '💰'], ['Alertas', '5', P.alert, '🔔']].forEach(d => indicadores.appendChild(mkInd(d[0], d[1], d[2], d[3])));\n\nconst tabla = penpot.createBoard();\ntabla.name = 'Movimientos'; tabla.resize(1032, 280);\ntabla.fills = [{ fillColor: P.card, fillOpacity: 1, fillStyle: null }];\ntabla.borderRadius = 12;\ntabla.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: '#000000', opacity: 0.08 } }];\ntabla.addGridLayout();\nif (tabla.grid) { tabla.grid.rows = 'auto'; tabla.grid.columns = '1fr 2fr'; tabla.grid.rowGap = '0'; tabla.grid.columnGap = '24'; }\ncontent.appendChild(tabla);\nconst cab = mkRow(['Fecha', 'Detalle'], true);\ntabla.grid.appendChild(cab, 1, 1);\n[['12/06', 'Entrada: 40 Phalaenopsis'], ['11/06', 'Trasplante: 12 Dendrobium'], ['10/06', 'Venta: 8 Cattleya'], ['09/06', 'Entrada: 25 Oncidium']].forEach((f, i) => {\n const fila = mkRow(f, false);\n tabla.grid.appendChild(fila, i + 2, 1);\n});\n\nstorage.dashboard = { rootId: root.id, sideId: side.id, contentId: content.id };\nreturn { rootId: root.id, sideId: side.id, contentId: content.id,\n navs: penpotUtils.findShapes(s => s.name.startsWith('Nav'), root).length,\n indicadores: penpotUtils.findShapes(s => s.name.startsWith('Ind'), root).length,\n filasTabla: penpotUtils.findShapes(s => s.name.startsWith('Tabla fila'), root).length,\n gridCells: penpotUtils.findShapes(s => s.layoutCell !== null, root).length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 P = {\n primary: '#1B4332', accent: '#52B788', sand: '#F4F1DE', clay: '#BC6C25', ink: '#2B2D42'\n};\nconst T = { xl: '48', lg: '32', md: '20', sm: '16', xs: '14' };\nconst S = { sm: 16, md: 24, lg: 40, xl: 64 };\nconst mk = (name, w, h, fills) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = fills ? fills.map(c => ({ fillColor: c, fillOpacity: 1, fillType: 'solid' })) : [];\n s.strokes = []; s.borderRadius = 0;\n return s;\n};\nconst txt = (chars, size, weight, color, align) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Montserrat'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1, fillType: 'solid' }];\n t.strokes = []; t.growType = 'auto-width'; t.align = align || 'center';\n return t;\n};\nconst page = penpot.createPage();\npage.name = 'Suelos 2025';\nconst board = mk('Landing', 1240, 1600, [P.sand]);\npage.root.appendChild(board);\nconst sec = (name, h, fills) => {\n const b = mk(name, 1240, h, fills);\n b.strokes = []; b.borderRadius = 0;\n b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'column'; b.flex.horizontalPadding = S.xl; b.flex.verticalPadding = S.lg; b.flex.rowGap = S.md; b.flex.alignItems = 'center'; b.flex.justifyContent = 'center'; }\n board.appendChild(b);\n return b;\n};\nconst header = sec('Header', 400, [P.primary]);\nheader.appendChild(txt('XIV Jornadas de Analisis de Suelos', T.xl, '700', P.sand));\nheader.appendChild(txt('CABA, 18 de Octubre de 2025', T.md, '400', P.accent));\nconst cd = sec('Countdown', 200, [P.clay]);\nconst cdBox = mk('Countdown Box', 600, 120, []);\ncdBox.addFlexLayout();\nif (cdBox.flex) { cdBox.flex.dir = 'row'; cdBox.flex.rowGap = 16; cdBox.flex.alignItems = 'center'; cdBox.flex.justifyContent = 'center'; }\ncd.appendChild(cdBox);\n['12', '08', '45', '30'].forEach((v, i) => {\n const n = mk('Num ' + (i + 1), 100, 100, [P.ink]);\n n.borderRadius = 12;\n cdBox.appendChild(txt(v, T.lg, '700', P.accent));\n});\ncd.appendChild(txt('Dias Horas Min Seg', T.sm, '400', P.sand));\nconst oradores = sec('Oradores', 520, [P.sand]);\nconst grid = mk('Grilla Oradores', 1112, 380, []);\ngrid.addFlexLayout();\nif (grid.flex) { grid.flex.dir = 'row'; grid.flex.rowGap = S.md; grid.flex.columnGap = S.md; grid.flex.alignItems = 'start'; }\noradores.appendChild(grid);\nconst ors = [\n { n: 'Dra. Lucia Fernandez', r: 'Microbiologia del suelo', c: P.primary },\n { n: 'Ing. Martin Ruiz', r: 'Quimica analitica', c: P.accent },\n { n: 'Dr. Pablo Sosa', r: 'Contaminacion y remediacion', c: P.clay },\n { n: 'Ing. Ana Torres', r: 'Pedologia aplicada', c: P.ink }\n];\nors.forEach(o => {\n const c = mk('Orador ' + o.n, 260, 340, [o.c]);\n c.borderRadius = 16;\n grid.appendChild(c);\n c.appendChild(txt(o.n, T.md, '700', P.sand));\n c.appendChild(txt(o.r, T.sm, '400', P.sand));\n});\nconst form = sec('Inscripcion', 360, [P.primary]);\nform.appendChild(txt('Inscripcion Gratuita', T.lg, '700', P.sand));\nform.appendChild(txt('Cupos limitados — registrate antes del 1 de octubre', T.sm, '400', P.accent));\nconst campos = ['Nombre completo', 'Email', 'Institucion', 'Motivo de interes'];\nconst camposB = mk('Campos', 800, 200, []);\ncamposB.addFlexLayout();\nif (camposB.flex) { camposB.flex.dir = 'column'; camposB.flex.rowGap = S.sm; camposB.flex.alignItems = 'start'; }\nform.appendChild(camposB);\ncampos.forEach(c => {\n const f = mk('Campo ' + c, 800, 44, [P.sand]);\n f.borderRadius = 8;\n camposB.appendChild(f);\n camposB.appendChild(txt(c, T.sm, '400', P.ink));\n});\nconst btn = mk('Boton', 280, 56, [P.accent]);\nbtn.borderRadius = 28;\nform.appendChild(btn);\nbtn.appendChild(txt('Reservar mi lugar', T.md, '700', P.ink));\nconst fixes = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.type === 'text') {\n if (shape.growType === 'fixed') return () => { shape.growType = 'auto-width'; };\n }\n return null;\n});\nstorage.landing = { pageId: page.id, boardId: board.id };\nreturn { pageId: page.id, boardId: board.id, sections: board.children.length, fields: campos.length, fixesApplied: fixes.filter(f => f.result !== null).length };"
|
|
],
|
|
"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 = {\n accent: '#581c87',\n ink: '#1a0a2e',\n white: '#ffffff',\n disabled: '#581c87'\n};\nconst T = { heading: '28', body: '16', small: '13' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 32 };\nconst mkBtn = (label, variant) => {\n const b = penpot.createBoard();\n b.name = 'Boton ' + label;\n b.fills = [{ fillColor: variant === 'primary' ? P.accent : P.white, fillOpacity: 1 }];\n b.borderRadius = 12;\n b.resize(220, 52);\n b.strokes = variant === 'secondary' ? [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }] : [];\n const txt = penpot.createText(label);\n txt.name = 'Texto ' + label;\n txt.fontFamily = 'Inter';\n txt.fontSize = T.body;\n txt.fontWeight = '600';\n txt.lineHeight = '1.5';\n txt.growType = 'auto-width';\n txt.fills = [{ fillColor: variant === 'primary' ? P.white : P.accent, fillOpacity: 1 }];\n txt.alignHorizontal = 'center';\n b.insertChild(b.children.length, txt);\n if (variant === 'disabled') {\n b.fills = [{ fillColor: P.disabled, fillOpacity: 0.3 }];\n b.strokes = variant === 'secondary' ? [{ strokeColor: P.disabled, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 0.3 }] : [];\n txt.fills = [{ fillColor: P.white, fillOpacity: 0.5 }];\n }\n return b;\n};\nconst page = penpotUtils.getPageByName('Landing');\nif (!page) return { error: 'page not found' };\nconst root = page.root;\nconst header = penpot.createBoard();\nheader.name = 'Header';\nheader.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nheader.resize(720, 180);\nheader.borderRadius = 16;\nconst h1 = penpot.createText('Relojeria Don Aurelio');\nh1.name = 'Titulo';\nh1.fontFamily = 'Inter';\nh1.fontSize = T.heading;\nh1.fontWeight = '700';\nh1.lineHeight = '1.2';\nh1.fills = [{ fillColor: P.white, fillOpacity: 1 }];\nh1.alignHorizontal = 'center';\nheader.insertChild(header.children.length, h1);\nconst sub = penpot.createText('Reparacion profesional de relojes mecanicos y de cuarzo');\nsub.name = 'Subtitulo';\nsub.fontFamily = 'Inter';\nsub.fontSize = T.small;\nsub.lineHeight = '1.5';\nsub.fills = [{ fillColor: P.white, fillOpacity: 0.85 }];\nsub.alignHorizontal = 'center';\nheader.insertChild(header.children.length, sub);\nconst btns = penpot.createBoard();\nbtns.name = 'Botones';\nbtns.fills = [];\nbtns.resize(720, 160);\nbtns.addFlexLayout();\nif (btns.flex) {\n btns.flex.dir = 'column';\n btns.flex.horizontalPadding = S.lg;\n btns.flex.verticalPadding = S.lg;\n btns.flex.rowGap = S.sm;\n btns.flex.alignItems = 'center';\n}\nconst prim = mkBtn('Agendar reparacion', 'primary');\nconst sec = mkBtn('Ver precios', 'secondary');\nconst disP = mkBtn('Agendar reparacion', 'primary');\ndisP.name = 'Boton Agendar deshabilitado';\nconst disS = mkBtn('Ver precios', 'secondary');\ndisS.name = 'Boton Ver precios deshabilitado';\n[prim, sec, disP, disS].forEach(b => btns.appendChild(b));\nroot.insertChild(root.children.length, header);\nroot.insertChild(root.children.length, btns);\nreturn {\n page: page.name,\n header: { fills: header.fills.length, borderRadius: header.borderRadius, width: header.width, height: header.height },\n btns: { dir: btns.flex ? btns.flex.dir : null, padding: btns.flex ? { top: btns.flex.topPadding, left: btns.flex.leftPadding } : null, gap: btns.flex ? btns.flex.rowGap : null, children: btns.children.length },\n disabled: { primary: disP.fills[0].fillOpacity, secondary: disS.fills[0].fillOpacity, text: disP.children[0].fills[0].fillOpacity },\n shapes: penpotUtils.shapeStructure(root, 2)\n};"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 = { bg: '#FFF8F0', ink: '#0B2545', muted: '#6B7280', accent: '#1E40AF', white: '#FFFFFF' };\nconst S = { sm: 14, md: 18, lg: 26 };\nconst T = { heading: 'Karla', body: 'Karla' };\nconst board = penpot.createBoard();\nboard.name = 'Card Servicio';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(320, 420);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.rowGap = 16;\n board.flex.horizontalPadding = 20;\n board.flex.verticalPadding = 20;\n board.flex.alignItems = 'center';\n}\nconst photo = penpot.createRectangle();\nphoto.name = 'Foto Servicio';\nphoto.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\nphoto.resize(280, 180);\nphoto.borderRadius = 12;\nconst titulo = penpot.createText('Relojeria Suiza');\ntitulo.name = 'Titulo';\ntitulo.fontFamily = T.heading;\ntitulo.fontSize = S.lg;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\ntitulo.growType = 'auto-width';\nconst precioTachado = penpot.createText('$ 120.000');\nprecioTachado.name = 'Precio Tachado';\nprecioTachado.fontFamily = T.body;\nprecioTachado.fontSize = S.md;\nprecioTachado.fontWeight = '400';\nprecioTachado.lineHeight = '1.4';\nprecioTachado.fills = [{ fillColor: P.muted, fillOpacity: 1 }];\nprecioTachado.textDecoration = 'line-through';\nprecioTachado.growType = 'auto-width';\nconst precioFinal = penpot.createText('$ 89.900');\nprecioFinal.name = 'Precio Final';\nprecioFinal.fontFamily = T.body;\nprecioFinal.fontSize = S.md;\nprecioFinal.fontWeight = '700';\nprecioFinal.lineHeight = '1.4';\nprecioFinal.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nprecioFinal.growType = 'auto-width';\nconst boton = penpot.createRectangle();\nboton.name = 'Boton Reservar';\nboton.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nboton.resize(280, 48);\nboton.borderRadius = 24;\nconst textoBoton = penpot.createText('Reservar Cita');\ntextoBoton.name = 'Texto Boton';\ntextoBoton.fontFamily = T.body;\ntextoBoton.fontSize = S.md;\ntextoBoton.fontWeight = '700';\ntextoBoton.lineHeight = '1.2';\ntextoBoton.fills = [{ fillColor: P.white, fillOpacity: 1 }];\ntextoBoton.align = 'center';\ntextoBoton.growType = 'auto-width';\nif (boton.flex) {\n boton.flex.dir = 'column';\n boton.flex.alignItems = 'center';\n boton.flex.justifyContent = 'center';\n boton.flex.verticalPadding = 12;\n}\nconst items = [board, photo, titulo, precioTachado, precioFinal, boton];\nitems.forEach(s => {\n if (s.parent) {\n s.parent.insertChild(s.parent.children.length, s);\n }\n});\nif (boton.flex) {\n boton.flex.appendChild(textoBoton);\n}\nreturn {\n boardId: board.id,\n boardWidth: board.width,\n boardHeight: board.height,\n flexDir: board.flex ? board.flex.dir : null,\n rowCount: board.flex ? board.flex.children.length : 0,\n shapes: penpotUtils.findShapes(() => true, board).length,\n texts: penpotUtils.findShapes(s => s.type === 'text', board).length,\n buttonText: textoBoton.characters\n};"
|
|
],
|
|
"veto_hits": [
|
|
{
|
|
"patron": "flex.appendChild",
|
|
"fragmento": "h, s); } }); if (boton.flex) { boton.flex.appendChild(textoBoton); } return { boardId: board"
|
|
}
|
|
],
|
|
"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:'#0B1220', ink:'#FDF6E3', brand:'#1E3A5F', gold:'#C8963C', white:'#FFFFFF' };\nconst T = { sm:'14', lg:'20', xl:'28' };\nconst S = { xs:4, sm:8, md:16, lg:24, xl:32 };\nconst mk = (name, size, color, weight, align) => {\n const t = penpot.createText(name);\n if (!t) return null;\n t.characters = name;\n t.fontFamily = 'Lora';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst nav = penpot.createBoard();\nnav.name = 'Navbar Academia';\nnav.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nnav.resize(900, 88);\nif (nav.flex) {\n nav.flex.dir = 'row';\n nav.flex.horizontalPadding = S.xl;\n nav.flex.rowGap = S.md;\n nav.flex.alignItems = 'center';\n nav.flex.justifyContent = 'space-between';\n}\nconst links = ['Clases', 'Pistas', 'Eventos', 'Profesores', 'Contacto'];\nconst logo = mk('Logo', T.lg, P.gold, '700', 'left');\nconst items = [logo];\nlinks.forEach(l => items.push(mk(l, T.sm, P.ink, '400', 'left')));\nconst cta = mk('Reservar clase', T.sm, P.white, '700', 'center');\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton CTA';\nbtn.fills = [{ fillColor: P.gold, fillOpacity: 1 }];\nbtn.borderRadius = 24;\nbtn.resize(140, 40);\nif (btn.flex) {\n btn.flex.dir = 'column';\n btn.flex.horizontalPadding = S.md;\n btn.flex.verticalPadding = S.sm;\n btn.flex.alignItems = 'center';\n btn.flex.justifyContent = 'center';\n}\nif (btn.flex) btn.flex.appendChild(cta);\nitems.push(btn);\nitems.forEach(s => nav.appendChild(s));\nstorage.navbar = { navId: nav.id, P: P, T: T, S: S, mk: mk };\nreturn { navId: nav.id, links: links.length, cta: btn.name, flexDir: nav.flex ? nav.flex.dir : null };"
|
|
],
|
|
"veto_hits": [
|
|
{
|
|
"patron": "flex.appendChild",
|
|
"fragmento": "yContent = 'center'; } if (btn.flex) btn.flex.appendChild(cta); items.push(btn); items.forEach(s ="
|
|
}
|
|
],
|
|
"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 = { fondo:'#3b0764', rosa:'#f72585', crema:'#fff4e6', blanco:'#ffffff' };\nconst T = { sm:'14', md:'16', lg:'22' };\nconst S = { xs:8, sm:16, md:24, lg:32 };\nconst bd = penpot.createBoard();\nbd.name = 'Aviso de cookies';\nbd.fills = [{ fillColor: P.fondo, fillOpacity: 1 }];\nbd.resize(640, 200);\nbd.addFlexLayout();\nif (bd.flex) { bd.flex.dir = 'column'; bd.flex.horizontalPadding = S.lg; bd.flex.verticalPadding = S.lg; bd.flex.rowGap = S.md; bd.flex.alignItems = 'center'; bd.flex.justifyContent = 'center'; }\nconst mk = (chars, size, color, weight) => { const t = penpot.createText(chars); if (!t) throw new Error('createText devolvio null'); t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight || '400'; t.lineHeight = '1.5'; t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.growType = 'auto-width'; return t; };\nconst btn = (label, fill) => { const b = penpot.createRectangle(); b.name = 'Boton ' + label; b.fills = [{ fillColor: fill, fillOpacity: 1 }]; b.resize(160, 40); b.borderRadius = 20; const t = mk(label, T.md, P.blanco, '600'); b.insertChild(b.children.length, t); penpotUtils.setParentXY(t, 0, 0); return b; };\nbd.insertChild(bd.children.length, mk('Usamos cookies para mejorar tu experiencia. Al continuar, aceptas nuestra politica.', T.sm, P.crema));\nconst fila = penpot.createBoard();\nfila.name = 'Botones';\nfila.fills = [];\nfila.resize(340, 40);\nfila.addFlexLayout();\nif (fila.flex) { fila.flex.dir = 'row'; fila.flex.rowGap = S.sm; fila.flex.alignItems = 'center'; fila.flex.justifyContent = 'center'; }\nfila.insertChild(fila.children.length, btn('Rechazar', P.fondo));\nfila.insertChild(fila.children.length, btn('Aceptar', P.rosa));\nbd.insertChild(bd.children.length, fila);\nstorage.aviso = { bdId: bd.id, P: P, T: T, S: S };\nreturn { bdId: bd.id, texto: bd.children.length, botones: fila.children.length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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:'#FF6B6B', text:'#2D2D2D', sub:'#6B6B6B', white:'#FFFFFF' };\nconst T = { sm:14, md:18, lg:26 };\nconst S = { card:360, gap:16, pad:32 };\nconst penpot = globalThis.penpot;\nconst root = penpot.root;\nconst board = penpot.createBoard();\nboard.name = 'Tarjeta Perfil';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(S.card, 520);\nboard.x = 100;\nboard.y = 100;\nroot.insertChild(root.children.length, board);\nconst flex = board.addFlexLayout();\nflex.dir = 'column';\nflex.horizontalPadding = S.pad;\nflex.verticalPadding = S.pad;\nflex.rowGap = S.gap;\nflex.alignItems = 'center';\nconst mk = (chars, size, color, weight) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = chars;\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-height';\n board.insertChild(board.children.length, t);\n return t;\n};\nconst avatar = penpot.createEllipse();\navatar.name = 'Avatar';\navatar.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\navatar.resize(120, 120);\navatar.strokes = [{ strokeColor: P.white, strokeWidth: 4, strokeStyle: 'solid', strokeOpacity: 1 }];\nboard.insertChild(board.children.length, avatar);\nmk('Circo Estelar', T.lg, P.text, '700');\nmk('Acrobata y equilibrista', T.md, P.accent, '600');\nmk('circo@estelar.art', T.sm, P.sub, '400');\nmk('+54 9 11 5555-0123', T.sm, P.sub, '400');\nmk('www.circoestelar.art', T.sm, P.sub, '400');\nreturn { boardId: board.id, avatarId: avatar.id, texts: 6, flexDir: flex.dir };"
|
|
],
|
|
"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 = { sm: 12, md: 20, lg: 28 };\nconst T = { h: '20', sm: '14', md: '13' };\nconst C = { bg: '#1B3A2D', ink: '#FFFFFF', rowA: '#F7FDF8', rowB: '#FFFFFF', line: '#D4E8D8', state: ['#006644','#D97706','#DC2626','#006644','#006644'] };\nconst S = [\n ['Orquidea Phalaenopsis','Phalaenopsis amabilis','120','180','Disponible'],\n ['Orquidea Dendrobium','Dendrobium nobile','85','150','Disponible'],\n ['Orquidea Cattleya','Cattleya labiata','210','0','Agotada'],\n ['Orquidea Oncidium','Oncidium flexuosum','95','60','En cuarentena'],\n ['Orquidea Vanda','Vanda coerulea','340','220','Disponible']\n];\nconst board = penpot.createBoard();\nboard.name = 'Tabla Orquideas';\nboard.fills = [{ fillColor: '#FFFFFF', fillOpacity: 1 }];\nboard.resize(1320, 400);\nboard.addGridLayout();\nif (!board.grid) return { error: 'grid no disponible' };\nboard.grid.rows = [{ size: 'auto' }, ...Array(6).fill({ size: '56' })];\nboard.grid.columns = [{ size: '200' }, { size: '280' }, { size: '100' }, { size: '100' }, { size: '120' }];\nboard.grid.rowGap = 0;\nboard.grid.columnGap = 0;\nconst mk = (chars, size, weight, color, align) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Inter'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }]; t.align = align; t.growType = 'auto-width';\n return t;\n};\nconst cab = penpot.createRectangle();\ncab.name = 'Encabezado'; cab.fills = [{ fillColor: C.bg, fillOpacity: 1 }]; cab.resize(1320, 60); cab.borderRadius = 8;\nboard.appendChild(cab);\nboard.grid.appendChild(cab, 1, 1);\nboard.appendChild(mk('Inventario de Orquideas', T.h, '700', C.ink, 'left'));\nconst cabRow = board.grid.rows[1];\nif (cabRow) cabRow.height = '60';\nconst cells = [];\nS.forEach((f, i) => {\n const row = i + 2;\n const bg = penpot.createRectangle();\n bg.name = 'Fila ' + (i + 1);\n bg.fills = [{ fillColor: i % 2 === 0 ? C.rowA : C.rowB, fillOpacity: 1 }];\n bg.resize(1320, 56);\n board.appendChild(bg);\n board.grid.appendChild(bg, row, 1);\n const c = penpotUtils.findShapes(s => s.name === 'Fila ' + (i + 1), board)[0];\n if (c) {\n c.layoutCell = { rowStart: row, columnStart: 1, rowSpan: 1, columnSpan: 5 };\n }\n cells.push(bg);\n f.forEach((v, j) => {\n const d = penpot.createRectangle();\n d.name = 'Celda ' + (i + 1) + '-' + (j + 1);\n d.fills = [{ fillColor: C.line, fillOpacity: 1 }];\n d.resize(1, 56);\n board.appendChild(d);\n board.grid.appendChild(d, row, j + 2);\n });\n f.forEach((v, j) => {\n const x = penpotUtils.findShapes(s => s.name === 'Celda ' + (i + 1) + '-' + (j + 1), board)[0];\n if (x) {\n const t = mk(v, j === 0 ? T.md : T.sm, j === 0 ? '600' : '400', j === 0 ? '#1B3A2D' : '#4A6B5A', 'left');\n board.appendChild(t);\n board.grid.appendChild(t, row, j + 2);\n const c = penpotUtils.findShape(s => s.type === 'text' && s.characters === v, board);\n if (c) {\n const cx = (j + 1) * 200 + P.md;\n penpotUtils.setParentXY(c, cx, 14);\n }\n }\n });\n});\nconst estados = ['Disponible', 'Agotada', 'En cuarentena'];\nconst badge = penpot.createRectangle();\nbadge.name = 'Badge estado';\nbadge.fills = [{ fillColor: C.state[2], fillOpacity: 1 }];\nbadge.resize(100, 28);\nbadge.borderRadius = 14;\nboard.appendChild(badge);\nboard.grid.appendChild(badge, 3, 5);\nconst c5 = penpotUtils.findShape(s => s.name === 'Badge estado', board);\nif (c5) {\n const cx = 4 * 200 + 280 + P.md;\n penpotUtils.setParentXY(c5, cx, 14);\n}\nconst bt = penpotUtils.findShape(s => s.type === 'text' && s.characters === 'En cuarentena', board);\nif (bt) {\n bt.fills = [{ fillColor: C.ink, fillOpacity: 1 }];\n bt.fontSize = T.sm;\n bt.fontWeight = '600';\n bt.align = 'center';\n penpotUtils.setParentXY(bt, cx + 10, 14);\n}\nconst cols = penpotUtils.findShapes(s => s.name.startsWith('Celda'), board);\nconst filas = penpotUtils.findShapes(s => s.name.startsWith('Fila'), board);\nreturn {\n boardId: board.id,\n cells: cells.length,\n celdas: cols.length,\n filas: filas.length,\n badge: c5 ? c5.name : null,\n badgeColor: c5 ? (c5.fills[0] ? c5.fills[0].fillColor : null) : null,\n textoEstado: bt ? bt.characters : null,\n textoEstadoColor: bt ? (bt.fills[0] ? bt.fills[0].fillColor : null) : null,\n celdasEnFila: cols.filter(c => {\n const lc = c.layoutCell;\n return lc && lc.rowStart === 3;\n }).length,\n celdasConParentX: cols.filter(c => c.parentX !== undefined).length\n};"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "asignacion a propiedad read-only",
|
|
"fragmento": "= board.grid.rows[1]; if (cabRow) cabRow.height = '60'; const cells = []; S.forEach((f, i"
|
|
}
|
|
],
|
|
"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 = { bg:'#FDFBF7', verde:'#0B6E4F', claro:'#D4EDDA', oscuro:'#1A2E22', blanco:'#FFFFFF' };\nconst T = { h1:32, h2:20, body:15 };\nconst S = { sm:8, md:16, lg:24, xl:40 };\nconst board = new Board();\nboard.name = 'Portada Energia';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(390, 844);\npenpot.root.insertChild(penpot.root.children.length, board);\nconst hero = new Rectangle();\nhero.name = 'Portada';\nhero.fills = [{ fillColor: P.verde, fillOpacity: 1 }];\nhero.resize(390, 420);\nhero.borderRadius = 0;\nboard.insertChild(board.children.length, hero);\nconst foto = await penpot.importImageUrl('https://picsum.photos/id/10/780/840', { width: 390, height: 420 });\nfoto.name = 'Foto portada';\nfoto.fills = [{ fillColor: P.verde, fillOpacity: 1 }];\nfoto.resize(390, 420);\nfoto.borderRadius = 0;\nboard.insertChild(board.children.length, foto);\nconst titulo = new Text();\ntitulo.name = 'Titulo';\ntitulo.characters = 'Consultora de eficiencia energetica';\ntitulo.fontFamily = 'Inter';\ntitulo.fontSize = T.h1;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.fills = [{ fillColor: P.blanco, fillOpacity: 1 }];\ntitulo.growType = 'auto-height';\nboard.insertChild(board.children.length, titulo);\nconst boton = new Rectangle();\nboton.name = 'Boton';\nboton.fills = [{ fillColor: P.blanco, fillOpacity: 1 }];\nboton.resize(220, 52);\nboton.borderRadius = 26;\nboton.strokes = [{ strokeColor: P.verde, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\nboard.insertChild(board.children.length, boton);\nconst label = new Text();\nlabel.name = 'Boton label';\nlabel.characters = 'Solicitar auditoria';\nlabel.fontFamily = 'Inter';\nlabel.fontSize = T.body;\nlabel.fontWeight = '600';\nlabel.lineHeight = '1.3';\nlabel.fills = [{ fillColor: P.verde, fillOpacity: 1 }];\nlabel.growType = 'auto-width';\nboard.insertChild(board.children.length, label);\nconst C = { hero: 0, titulo: 1, boton: 2 };\npenpotUtils.setParentXY(foto, 0, 0);\npenpotUtils.setParentXY(titulo, S.xl, 340);\npenpotUtils.setParentXY(boton, (390 - 220) / 2, 680);\npenpotUtils.setParentXY(label, (220 - label.width) / 2, (52 - label.fontSize) / 2 + 2);\nstorage.portada = { boardId: board.id, P: P, T: T, S: S, C: C };\nreturn { boardId: board.id, heroH: hero.height, fotoH: foto.height, tituloFontSize: titulo.fontSize, botonBorderRadius: boton.borderRadius, labelFontSize: label.fontSize, children: board.children.length };"
|
|
],
|
|
"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 = {\n primary: '#1B3A5C', cream: '#FFF8F0', accent: '#D96C4A',\n green: '#6B8E6B', brown: '#5C3D2E', white: '#FFFFFF'\n};\nconst S = { xs: 8, sm: 16, md: 24, lg: 32 };\nconst T = { heading: '28', body: '16' };\nconst photos = [\n { url: 'https://images.unsplash.com/photo-1587300003388-59208cc962cb?w=360&h=300&fit=crop', label: 'Max, 12 anos' },\n { url: 'https://images.unsplash.com/photo-1548199973-03cce0bbc87b?w=360&h=300&fit=crop', label: 'Luna, 10 anos' },\n { url: 'https://images.unsplash.com/photo-1583511655857-d19b40a7a54e?w=360&h=300&fit=crop', label: 'Rocky, 14 anos' },\n { url: 'https://images.unsplash.com/photo-1530281700549-e82e7bf110d6?w=360&h=300&fit=crop', label: 'Canela, 11 anos' }\n];\nconst board = penpot.createBoard();\nboard.name = 'Galeria - Albergue Patitas';\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.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.alignItems = 'start';\n}\nconst header = penpot.createBoard();\nheader.name = 'Encabezado';\nheader.fills = [{ fillColor: P.primary, fillOpacity: 1 }];\nheader.resize(360, 100);\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 = 4;\n header.flex.alignItems = 'start';\n}\nconst title = penpot.createText('Albergue Patitas');\ntitle.fontFamily = 'Nunito';\ntitle.fontSize = T.heading;\ntitle.fontWeight = '700';\ntitle.lineHeight = '1.2';\ntitle.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\ntitle.growType = 'auto-width';\nconst subtitle = penpot.createText('Nuestros perros senior esperando un hogar');\nsubtitle.fontFamily = 'Nunito';\nsubtitle.fontSize = T.body;\nsubtitle.fontWeight = '400';\nsubtitle.lineHeight = '1.4';\nsubtitle.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nsubtitle.growType = 'auto-width';\nheader.insertChild(header.children.length, title);\nheader.insertChild(header.children.length, subtitle);\nboard.insertChild(board.flex ? 0 : board.children.length, header);\nconst grid = penpot.createBoard();\ngrid.name = 'Grid de fotos';\ngrid.fills = [];\ngrid.resize(360, 560);\ngrid.addFlexLayout();\nif (grid.flex) {\n grid.flex.dir = 'column';\n grid.flex.rowGap = S.md;\n grid.flex.alignItems = 'start';\n}\nboard.insertChild(board.flex ? 1 : board.children.length, grid);\nconst row = (urls, labels) => {\n const r = penpot.createBoard();\n r.name = 'Fila ' + labels[0];\n r.fills = [];\n r.resize(360, 280);\n r.addFlexLayout();\n if (r.flex) {\n r.flex.dir = 'row';\n r.flex.columnGap = S.sm;\n r.flex.alignItems = 'start';\n }\n grid.insertChild(grid.flex ? 0 : grid.children.length, r);\n const cells = urls.map((u, i) => {\n const c = penpot.createBoard();\n c.name = 'Celda ' + labels[i];\n c.fills = [{ fillColor: P.white, fillOpacity: 1 }];\n c.borderRadius = 16;\n c.resize(168, 240);\n c.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: P.brown, opacity: 0.15 } }];\n c.strokes = [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n c.insertChild(c.children.length, penpot.createImage());\n const img = c.children[0];\n if (img) {\n img.name = 'Foto ' + labels[i];\n img.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\n img.resize(168, 180);\n img.borderRadius = 16;\n img.fills = [{ fillOpacity: 1, fillImage: { url: u, fit: 'cover' } }];\n }\n const ley = penpot.createBoard();\n ley.name = 'Leyenda ' + labels[i];\n ley.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n ley.borderRadius = 12;\n ley.resize(168, 44);\n ley.addFlexLayout();\n if (ley.flex) {\n ley.flex.dir = 'row';\n ley.flex.horizontalPadding = S.sm;\n ley.flex.verticalPadding = S.sm;\n ley.flex.rowGap = 0;\n ley.flex.alignItems = 'center';\n }\n ley.insertChild(ley.children.length, penpot.createText(labels[i]));\n const txt = ley.children[1];\n if (txt) {\n txt.fontFamily = 'Nunito';\n txt.fontSize = '14';\n txt.fontWeight = '700';\n txt.lineHeight = '1.3';\n txt.fills = [{ fillColor: P.white, fillOpacity: 1 }];\n txt.growType = 'auto-width';\n }\n c.insertChild(c.children.length, ley);\n return c;\n });\n cells.forEach(c => r.insertChild(r.children.length, c));\n return r;\n};\nrow([photos[0].url, photos[1].url], [photos[0].label, photos[1].label]);\nrow([photos[2].url, photos[3].url], [photos[2].label, photos[3].label]);\nconst footer = penpot.createBoard();\nfooter.name = 'Pie';\nfooter.fills = [{ fillColor: P.green, fillOpacity: 1 }];\nfooter.resize(360, 60);\nfooter.borderRadius = 0;\nfooter.addFlexLayout();\nif (footer.flex) {\n footer.flex.dir = 'row';\n footer.flex.horizontalPadding = S.lg;\n footer.flex.verticalPadding = S.sm;\n footer.flex.rowGap = S.sm;\n footer.flex.alignItems = 'center';\n footer.flex.justifyContent = 'center';\n}\nboard.insertChild(board.flex ? 2 : board.children.length, footer);\nconst badge = penpot.createText('Adopcion responsable');\nbadge.fontFamily = 'Nunito';\nbadge.fontSize = '14';\nbadge.fontWeight = '700';\nbadge.lineHeight = '1.3';\nbadge.fills = [{ fillColor: P.cream, fillOpacity: 1 }];\nbadge.growType = 'auto-width';\nfooter.insertChild(footer.children.length, badge);\nstorage.galeria = { boardId: board.id, headerId: header.id, gridId: grid.id, footerId: footer.id };\nreturn {\n boardId: board.id,\n headerId: header.id,\n gridId: grid.id,\n footerId: footer.id,\n shapes: penpotUtils.shapeStructure(board, 3),\n layout: board.flex ? { dir: board.flex.dir, padding: board.flex.horizontalPadding + 'px', rowGap: board.flex.rowGap + 'px', alignItems: board.flex.alignItems } : null,\n photos: photos.length,\n rows: 2,\n cellsPerRow: 2,\n leyendas: 4,\n fills: board.fills.map(f => f.fillColor),\n texts: penpotUtils.findShapes(s => s.type === 'text', board).length,\n images: penpotUtils.findShapes(s => s.type === 'image' || s.fills?.some(f => f.fillImage), board).length\n};"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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": [
|
|
"execute_code"
|
|
],
|
|
"code_payloads": [
|
|
"const P = { bg: '#0B132B', white: '#FFFFFF', gold: '#FFB703' };\nconst S = { xl: 56, lg: 32, md: 20, sm: 14 };\nconst T = { heading: '600 56px \"Playfair Display\", serif', body: '400 20px \"Inter\", sans-serif' };\nconst mk = (name, w, h, fills, fontSize, growType) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = fills || [{ fillColor: P.bg, fillOpacity: 1, fillImage: null }];\n if (fontSize) {\n const t = penpot.createText();\n t.characters = name; t.fontFamily = 'Playfair Display'; t.fontSize = fontSize;\n t.fontWeight = '600'; t.lineHeight = '1.2'; t.growType = growType || 'auto-width';\n t.fills = [{ fillColor: P.white, fillOpacity: 1, fillImage: null }];\n s.appendChild(t);\n }\n return s;\n};\nconst hero = mk('Hero Circo', 1200, 600, [{ fillColor: P.bg, fillOpacity: 1, fillImage: null }]);\nhero.fills = [{ fillColor: P.bg, fillOpacity: 1, fillImage: { url: 'https://picsum.photos/id/1025/1200/600', width: 1200, height: 600 } }];\nconst velo = mk('Velo oscuro', 1200, 600, [{ fillColor: P.bg, fillOpacity: 0.55 }]);\nconst titular = mk('Titular', 800, null, null, T.heading, 'auto-width');\ntitular.characters = 'Circo Nuevo — donde el arte se eleva';\nconst sub = mk('Sub', 800, null, null, T.body, 'auto-width');\nsub.characters = 'Clases para todas las edades. Primera clase gratis.';\nconst btn = mk('Boton', 220, 52, [{ fillColor: P.gold, fillOpacity: 1 }]);\nbtn.borderRadius = 26;\nconst btnText = penpot.createText();\nbtnText.characters = 'Reservar clase';\nbtnText.fontFamily = 'Inter'; btnText.fontSize = '18'; btnText.fontWeight = '700';\nbtnText.lineHeight = '1.2'; btnText.growType = 'auto-width';\nbtnText.fills = [{ fillColor: P.bg, fillOpacity: 1, fillImage: null }];\nbtn.appendChild(btnText);\nconst cols = [hero, velo, titular, sub, btn];\ncols.forEach(c => hero.insertChild(hero.children.length, c));\npenpotUtils.setParentXY(titular, 60, 220);\npenpotUtils.setParentXY(sub, 60, 310);\npenpotUtils.setParentXY(btn, 60, 390);\nstorage.hero = hero.id;\nreturn { heroId: hero.id, veloOpacity: velo.fills[0].fillOpacity, btnRadius: btn.borderRadius };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": " if (fontSize) { const t = penpot.createText(); t.characters = name; t.fontFamily "
|
|
}
|
|
],
|
|
"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 = { bg:'#FFF7ED', ink:'#1A0B00', accent:'#FF3B30', border:'#FFB347' };\nconst T = { xl:'48', lg:'20', md:'32' };\nconst W = { sm:'16', md:'24', lg:'40' };\nconst board = penpot.createBoard();\nboard.name = 'Ficha Escuela de Circo';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1320, 700);\nconst card = penpot.createRectangle();\ncard.name = 'Card';\ncard.fills = [{ fillColor: '#FFFFFF', fillOpacity: 1 }];\ncard.strokes = [{ strokeColor: P.border, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\ncard.borderRadius = 28;\ncard.resize(1240, 620);\ncard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 12, blur: 24, spread: 0, color: { color: '#1A0B00', opacity: 0.08 } }];\nboard.insertChild(board.children.length, card);\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto Escuela';\nfoto.fills = [{ fillColor: P.accent, fillOpacity: 1, fillImageFit: 'cover', fillImageOpacity: 1 }];\nfoto.resize(1240, 340);\ncard.insertChild(card.children.length, foto);\nconst titulo = penpot.createText('Escuela de Circo La Voltereta');\ntitulo.name = 'Titulo';\ntitulo.fontFamily = 'DM Serif Display';\ntitulo.fontSize = T.xl;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.15';\ntitulo.characters = 'Escuela de Circo La Voltereta';\ntitulo.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\ntitulo.growType = 'auto-width';\ncard.insertChild(card.children.length, titulo);\nconst sub = penpot.createText('Clases de trapecio, malabares y acrobacia para todas las edades.');\nsub.name = 'Subtitulo';\nsub.fontFamily = 'DM Sans';\nsub.fontSize = T.lg;\nsub.fontWeight = '400';\nsub.lineHeight = '1.5';\nsub.characters = 'Clases de trapecio, malabares y acrobacia para todas las edades.';\nsub.fills = [{ fillColor: P.ink, fillOpacity: 0.7 }];\nsub.growType = 'auto-width';\ncard.insertChild(card.children.length, sub);\nconst precio = penpot.createText('Desde $12.000 / mes');\nprecio.name = 'Precio';\nprecio.fontFamily = 'DM Serif Display';\nprecio.fontSize = T.md;\nprecio.fontWeight = '700';\nprecio.lineHeight = '1.3';\nprecio.characters = 'Desde $12.000 / mes';\nprecio.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nprecio.growType = 'auto-width';\ncard.insertChild(card.children.length, precio);\nconst btn = penpot.createRectangle();\nbtn.name = 'Boton Inscripcion';\nbtn.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nbtn.borderRadius = 40;\nbtn.resize(260, 56);\nbtn.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 6, blur: 16, spread: 0, color: { color: '#FF3B30', opacity: 0.3 } }];\ncard.insertChild(card.children.length, btn);\nconst cta = penpot.createText('Inscripcion abierta');\ncta.name = 'CTA';\ncta.fontFamily = 'DM Sans';\ncta.fontSize = '18';\ncta.fontWeight = '600';\ncta.lineHeight = '1';\ncta.characters = 'Inscripcion abierta';\ncta.fills = [{ fillColor: '#FFFFFF', fillOpacity: 1 }];\ncta.growType = 'auto-width';\ncard.insertChild(card.children.length, cta);\nconst S = penpotUtils.findShapes(s => s.type === 'text', board);\nconst fills = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.fills && shape.fills.length > 0) {\n return shape.fills.filter(f => f.fillOpacity !== undefined && f.fillOpacity < 1).length;\n }\n return null;\n});\nstorage.ficha = { boardId: board.id, cardId: card.id };\nreturn { boardId: board.id, cardId: card.id, shapes: board.children.length, texts: S.length, fontSizes: [...new Set(S.map(t => t.fontSize))], fillsWithOpacity: fills.filter(f => f.result > 0).length, fotoFit: foto.fills[0].fillImageFit };"
|
|
],
|
|
"veto_hits": [],
|
|
"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:'#0B0F19', card:'#151C2B', accent:'#00D4FF', white:'#F2F5FA', gray:'#8A95A8' };\nconst T = { sm:'14', md:'18', lg:'26' };\nconst S = { xs:8, sm:16, md:24, lg:40 };\n\nconst board = penpot.createBoard();\nboard.name = 'Servicios — Voz & Imagen';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 360);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'row';\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md;\n board.flex.columnGap = S.md;\n board.flex.alignItems = 'center';\n}\nstorage.viz = { boardId: board.id, P: P, T: T, S: S };\nreturn { boardId: board.id, layout: board.flex ? { dir: board.flex.dir, columnGap: board.flex.columnGap, horizontalPadding: board.flex.horizontalPadding } : null };"
|
|
],
|
|
"veto_hits": [],
|
|
"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:'#0B1D3A', card:'#1A3A6B', accent:'#C89B3C', text:'#FFFFFF' };\nconst T = { sm:14, md:18, lg:24 };\nconst S = { card:360, gap:20, pad:32 };\n\nconst page = penpot.createPage('Catalogo de servicios');\nconst board = penpot.createBoard();\nboard.name = 'Catalogo de servicios';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1180, 600);\npage.root.appendChild(board);\n\nconst addFlex = (b) => { const f = b.addFlexLayout(); f.dir = 'column'; f.rowGap = S.gap; f.horizontalPadding = S.pad; f.verticalPadding = S.pad; f.alignItems = 'start'; f.justifyContent = 'start'; return f; };\naddFlex(board);\n\nconst mkCard = (name, desc) => {\n const card = penpot.createBoard();\n card.name = name;\n card.fills = [{ fillColor: P.card, fillOpacity: 1 }];\n card.borderRadius = 12;\n card.strokes = [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n card.resize(S.card, 220);\n const t1 = penpot.createText(name);\n t1.name = 'Nombre';\n t1.fontFamily = 'Inter';\n t1.fontSize = T.lg;\n t1.fontWeight = '700';\n t1.lineHeight = '1.2';\n t1.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n t1.growType = 'auto-height';\n const t2 = penpot.createText(desc);\n t2.name = 'Descripcion';\n t2.fontFamily = 'Inter';\n t2.fontSize = T.md;\n t2.fontWeight = '400';\n t2.lineHeight = '1.5';\n t2.fills = [{ fillColor: P.text, fillOpacity: 1 }];\n t2.growType = 'auto-height';\n board.insertChild(board.children.length, card);\n card.insertChild(card.children.length, t1);\n card.insertChild(card.children.length, t2);\n return card;\n};\n\nconst filas = [\n [mkCard('Reparacion de mecanismos', 'Cambio de muelles, engranajes y ajustes de precision.'),\n mkCard('Cambio de cristal', 'Sustitucion de cristal de zafiro o mineral con sellado original.'),\n mkCard('Limpieza y engrase', 'Desmontaje completo, ultrasonido y engrase con aceites especificos.')],\n [mkCard('Cambio de bateria', 'Sustitucion de pilas y revision de estanqueidad.'),\n mkCard('Ajuste de correa', 'Adaptacion de longitud y cambio de hebillas.'),\n mkCard('Pulido de caja', 'Eliminacion de rayas superficiales y restauracion del brillo.')]\n];\n\nfilas.forEach(fila => {\n const row = penpot.createBoard();\n row.name = 'Fila ' + fila.length;\n row.fills = [];\n row.resize(S.card * 3 + S.gap * 2, 220);\n const rf = row.addFlexLayout();\n rf.dir = 'row';\n rf.columnGap = S.gap;\n rf.alignItems = 'start';\n rf.justifyContent = 'start';\n fila.forEach(c => row.appendChild(c));\n board.appendChild(row);\n});\n\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst cards = penpotUtils.findShapes(s => s.name === 'Reparacion de mecanismos' || s.name === 'Cambio de cristal' || s.name === 'Limpieza y engrase' || s.name === 'Cambio de bateria' || s.name === 'Ajuste de correa' || s.name === 'Pulido de caja', board);\nconst rows = penpotUtils.findShapes(s => s.name.startsWith('Fila'), board);\nconst flexOk = board.flex !== null && rows.every(r => r.flex !== null);\nconst allSame = cards.length === 6 && cards.every(c => c.type === 'board' && c.width === S.card && c.height === 220);\nreturn { boardId: board.id, pageId: page.id, cards: cards.length, rows: rows.length, flexLayout: flexOk, allSameSize: allSame, structure };"
|
|
],
|
|
"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 = { brand: '#0B3D2E', ink: '#111827', paper: '#F7FAF8', line: '#D1E7DD' };\nconst T = { xl: 32, lg: 18, sm: 14 };\nconst S = { xs: 8, sm: 16, md: 24, lg: 32, xl: 48 };\nconst mk = (name, fills, fontSize, fontWeight, lineHeight, color) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Inter';\n t.fontSize = fontSize;\n t.fontWeight = fontWeight;\n t.lineHeight = lineHeight;\n t.growType = 'auto-height';\n t.fills = fills;\n t.strokes = [];\n t.shadows = [];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Columna consultora';\nboard.fills = [{ fillColor: P.paper, fillOpacity: 1 }];\nboard.resize(400, 600);\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.lg;\n board.flex.alignItems = 'start';\n}\nconst titulo = mk('Titulo', [mk.fills ? { fillColor: P.brand, fillOpacity: 1 } : { fillColor: P.brand, fillOpacity: 1 }], T.xl, '700', '1.2', P.brand);\nconst texto = mk('Texto', [{ fillColor: P.ink, fillOpacity: 1 }], T.lg, '400', '1.6', P.ink);\nconst boton = penpot.createRectangle();\nboton.name = 'Boton';\nboton.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\nboton.resize(160, 48);\nboton.borderRadius = 24;\nconst btn = mk('Boton', [{ fillColor: P.paper, fillOpacity: 1 }], T.sm, '600', '1.2', P.paper);\nif (btn) {\n btn.alignHorizontal = 'center';\n btn.verticalAlign = 'center';\n boton.insertChild(0, btn);\n}\nconst hijos = [titulo, texto, boton];\nhijos.forEach(h => board.appendChild(h));\nstorage.columna = { boardId: board.id, P: P, T: T, S: S };\nreturn { boardId: board.id, flexDir: board.flex ? board.flex.dir : null, hijos: hijos.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 = {\n brand: '#F59E0B',\n bg: '#FEF3C7',\n ink: '#1C1917',\n surface: '#FFF7ED'\n};\nconst T = {\n sm: '14',\n md: '16',\n lg: '20'\n};\nconst S = { col: 260, gap: 24, pad: 48 };\nconst penpot = globalThis.penpot;\nconst lib = penpot.library.local;\nconst mk = (name, color) => {\n const c = lib.createColor();\n c.name = name;\n c.color = color;\n return c;\n};\nmk('Miel / Acento', P.brand);\nmk('Miel / Fondo', P.bg);\nmk('Miel / Tinta', P.ink);\nmk('Miel / Superficie', P.surface);\nconst root = penpot.root;\nconst board = penpot.createBoard();\nboard.name = 'Pie de pagina — Cooperativa';\nboard.fills = [{ fillColor: P.surface, fillOpacity: 1 }];\nboard.resize(1180, 360);\nroot.insertChild(root.children.length, board);\nconst flex = board.addFlexLayout();\nflex.dir = 'column';\nflex.horizontalPadding = S.pad;\nflex.verticalPadding = S.pad;\nflex.rowGap = S.gap;\nconst mkCol = (title, links) => {\n const col = penpot.createBoard();\n col.name = title;\n col.fills = [];\n col.resize(S.col, 1);\n const cf = col.addFlexLayout();\n cf.dir = 'column';\n cf.rowGap = 12;\n board.appendChild(col);\n const h = penpot.createText(title);\n h.name = title + ' / Titulo';\n h.characters = title;\n h.fontFamily = 'Inter';\n h.fontSize = T.lg;\n h.fontWeight = '700';\n h.lineHeight = '1.3';\n h.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\n h.strokes = [];\n h.growType = 'auto-width';\n col.appendChild(h);\n links.forEach(l => {\n const lnk = penpot.createText(l);\n lnk.name = title + ' / ' + l;\n lnk.characters = l;\n lnk.fontFamily = 'Inter';\n lnk.fontSize = T.md;\n lnk.fontWeight = '400';\n lnk.lineHeight = '1.5';\n lnk.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\n lnk.strokes = [];\n lnk.growType = 'auto-width';\n col.appendChild(lnk);\n });\n};\nmkCol('Productos', ['Miel cruda', 'Miel con nueces', 'Jalea real', 'Polen']);\nmkCol('Visitanos', ['Agroecologia', 'Talleres', 'Visitas guiadas', 'Tienda']);\nmkCol('Contacto', ['info@cooperativamiel.ar', 'WhatsApp', 'Instagram', 'Facebook']);\nmkCol('Legal', ['Socios', 'Transparencia', 'Pymes', 'Proveedores']);\nconst bar = penpot.createRectangle();\nbar.name = 'Linea de derechos';\nbar.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\nbar.resize(1180, 4);\nboard.appendChild(bar);\nconst copy = penpot.createText('© 2025 Cooperativa Miel — Todos los derechos reservados');\ncopy.name = 'Derechos';\ncopy.characters = copy.characters;\ncopy.fontFamily = 'Inter';\ncopy.fontSize = T.sm;\ncopy.fontWeight = '400';\ncopy.lineHeight = '1.5';\ncopy.fills = [{ fillColor: P.ink, fillOpacity: 1 }];\ncopy.strokes = [];\ncopy.growType = 'auto-width';\nboard.appendChild(copy);\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst cols = penpotUtils.findShapes(s => s.name.includes('Productos') || s.name.includes('Visitanos') || s.name.includes('Contacto') || s.name.includes('Legal'), board);\nconst links = penpotUtils.findShapes(s => s.type === 'text' && s.name !== 'Derechos', board);\nreturn {\n boardId: board.id,\n cols: cols.length,\n links: links.length,\n structure,\n flex: { dir: flex.dir, rowGap: flex.rowGap, horizontalPadding: flex.horizontalPadding, verticalPadding: flex.verticalPadding },\n barHeight: bar.height,\n copyFontSize: copy.fontSize,\n colorsInPalette: lib.colors.map(c => c.name)\n};"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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: '#0B6B4A', ink: '#1A2B22', sand: '#F5F0E6', white: '#FFFFFF', warm: '#E87A2A', light: '#D4E8DF' };\nconst T = { xl: 28, lg: 18, md: 14, sm: 12 };\nconst S = { sm: 8, md: 16, lg: 24 };\n\nconst file = penpot.createFile();\nfile.name = 'Kayaks - Lista de modelos';\nconst board = penpot.createBoard();\nboard.name = 'Pantalla principal';\nboard.fills = [{ fillColor: P.sand, 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}\nfile.pages[0].insertChild(file.pages[0].children.length, board);\n\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 = 12;\n return s;\n};\nconst mkIcon = (name, fill) => {\n const s = penpot.createEllipse();\n s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }];\n s.resize(48, 48);\n return s;\n};\nconst mkText = (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;\n t.lineHeight = lineH; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst mkIconText = (chars, size, color) => {\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 = '600';\n t.lineHeight = '1.3'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\n\nconst header = penpot.createBoard();\nheader.name = 'Header';\nheader.fills = [{ fillColor: P.brand, fillOpacity: 1 }];\nheader.resize(364, 120);\nheader.borderRadius = 16;\nheader.addFlexLayout();\nif (header.flex) {\n header.flex.dir = 'column';\n header.flex.horizontalPadding = S.lg;\n header.flex.verticalPadding = S.lg;\n header.flex.rowGap = 4;\n header.flex.alignItems = 'start';\n}\nboard.appendChild(header);\n\nheader.appendChild(mkIcon('Logo', P.warm));\nheader.appendChild(mkText('KayakStore', T.xl, '700', P.white, '1.2'));\nheader.appendChild(mkText('Modelos disponibles para alquiler', T.md, '400', P.light, '1.4'));\n\nconst items = [\n { icon: P.brand, title: 'Kayak de mar', desc: 'Doble, 4.2m, para 2 personas' },\n { icon: P.brand, title: 'Kayak de rio', desc: 'Individual, 3.5m, clasificacion III' },\n { icon: P.brand, title: 'Kayak de lago', desc: 'Estable, 3.8m, principiantes' },\n { icon: P.brand, title: 'Kayak hinchable', desc: 'Portatil, 3.2m, 2 plazas' },\n { icon: P.brand, title: 'Kayak de pesca', desc: 'Ancho, 4.0m, con porta-cañas' },\n { icon: P.brand, title: 'Kayak de tour', desc: 'Largo, 5.0m, para rutas largas' },\n { icon: P.warm, title: 'Kayak de surf', desc: 'Corto, 2.8m, para olas' }\n];\n\nconst list = penpot.createBoard();\nlist.name = 'Lista de modelos';\nlist.fills = [{ fillColor: P.white, fillOpacity: 1 }];\nlist.resize(364, 600);\nlist.borderRadius = 16;\nlist.strokes = [{ strokeColor: P.light, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\nlist.addFlexLayout();\nif (list.flex) {\n list.flex.dir = 'column';\n list.flex.horizontalPadding = S.md;\n list.flex.verticalPadding = S.md;\n list.flex.rowGap = S.sm;\n list.flex.alignItems = 'start';\n}\nboard.appendChild(list);\n\nitems.forEach(item => {\n const row = penpot.createBoard();\n row.name = item.title;\n row.fills = [{ fillColor: P.sand, fillOpacity: 1 }];\n row.resize(336, 72);\n row.borderRadius = 12;\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.rowGap = S.md;\n row.flex.alignItems = 'center';\n }\n list.appendChild(row);\n row.appendChild(mkIcon('Icono ' + item.title, item.icon));\n const grp = penpot.createGroup();\n grp.name = 'Texto ' + item.title;\n grp.fills = [];\n grp.resize(240, 40);\n row.appendChild(grp);\n grp.appendChild(mkIconText(item.title, T.lg, P.ink));\n grp.appendChild(mkText(item.desc, T.sm, '400', P.ink, '1.4'));\n});\n\nconst btn = mk('Reservar', 364, 52, P.warm);\nbtn.strokes = [{ strokeColor: P.warm, strokeWidth: 0, strokeStyle: 'solid', strokeAlignment: 'center', strokeOpacity: 1 }];\nbtn.appendChild(mkText('Reservar ahora', T.lg, '700', P.white, '1.3'));\nboard.appendChild(btn);\n\nconst result = penpotUtils.shapeStructure(board, 3);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst shapes = penpotUtils.findShapes(s => s.type !== 'board', board);\nreturn {\n boardId: board.id,\n pages: penpotUtils.getPages().length,\n boardCount: penpotUtils.findShapes(s => s.type === 'board', board).length,\n shapeCount: shapes.length,\n textCount: texts.length,\n fontSizes: [...new Set(texts.map(t => t.fontSize))].sort((a, b) => a - b),\n structure: result\n};"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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"
|
|
],
|
|
"code_payloads": [
|
|
"const board = penpotUtils.findShape(s => s.name === 'Escuela de Circo');\nif (!board) return { found: false };\nconst fills = board.fills ? board.fills.map(f => ({ type: f.fillOpacity, color: f.color })) : [];\nconst structure = penpotUtils.shapeStructure(board, 3);\nreturn { found: true, boardId: board.id, name: board.name, width: board.width, height: board.height, fills, hasFlex: !!board.flex, hasGrid: !!board.grid, childCount: board.children.length, structure };"
|
|
],
|
|
"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 board = penpotUtils.findShape(s => s.name === 'Caja de velas', penpot.root);\nif (!board) return { error: 'board no encontrado' };\nstorage.caja = board.id;\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst sizes = [...new Set(texts.map(t => t.fontSize))];\nconst fills = penpotUtils.analyzeDescendants(board, (r, s) => s.fills?.map(f => f.color) || []);\nconst colores = [...new Set(fills.flat())];\nreturn { boardId: board.id, texts: texts.length, sizes: sizes.length, colores: colores.length };"
|
|
],
|
|
"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 board = penpotUtils.findShape(s => s.name === 'Albergue Perros');\nif (!board) { return { error: 'board no encontrado' }; }\nstorage.albergue = { boardId: board.id };\nconst violations = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.type === 'text') {\n const fills = shape.fills || [];\n if (fills.length === 0) return { shapeId: shape.id, name: shape.name, missingFill: true };\n const fg = fills[0].color;\n if (!fg) return { shapeId: shape.id, name: shape.name, missingFgColor: true };\n const bg = root.fills ? root.fills[0].color : null;\n if (!bg) return { shapeId: shape.id, name: shape.name, missingBgColor: true };\n const lumFg = (0.299 * parseInt(fg.slice(1,3),16) + 0.587 * parseInt(fg.slice(3,5),16) + 0.114 * parseInt(fg.slice(5,7),16)) / 255;\n const lumBg = (0.299 * parseInt(bg.slice(1,3),16) + 0.587 * parseInt(bg.slice(3,5),16) + 0.114 * parseInt(bg.slice(5,7),16)) / 255;\n const ratio = (Math.max(lumFg, lumBg) + 0.05) / (Math.min(lumFg, lumBg) + 0.05);\n if (ratio < 4.5) return { shapeId: shape.id, name: shape.name, fgColor: fg, bgColor: bg, ratio: +ratio.toFixed(2) };\n }\n});\nconst bad = violations.filter(v => v.result && v.result.ratio);\nreturn { boardId: board.id, boardName: board.name, totalShapes: penpotUtils.analyzeDescendants(board, () => 1).length, textShapes: violations.filter(v => v.result && v.result.missingFill).length, lowContrast: bad.length, lowContrastDetails: bad.map(v => v.result) };"
|
|
],
|
|
"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: 'Hero no encontrado' };\nconst textos = penpotUtils.findShapes(s => s.type === 'text', hero);\nconst fills = textos.map(t => t.fills?.[0]?.color).filter(Boolean);\nreturn { heroId: hero.id, heroWidth: hero.width, heroHeight: hero.height, textos: textos.length, coloresTexto: [...new Set(fills)] };"
|
|
],
|
|
"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": [
|
|
"execute_code"
|
|
],
|
|
"code_payloads": [
|
|
"const P = { brand: '#2B6B3A', ink: '#1A1A1A', paper: '#FFF9F0', accent: '#D4A843', soft: '#F2E6D0' };\nconst T = { xl: 32, lg: 20, md: 16, sm: 13 };\nconst S = { xs: 4, sm: 8, md: 16, lg: 24, xl: 32 };\nconst mk = (name, w, h, fills, fontSize, growType) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = (fills || []).map(c => ({ fillColor: c, fillOpacity: 1, fillImageScaling: 'fit' }));\n if (s.fills.length) s.fills[0].fillOpacity = 1;\n if (s.strokes) s.strokes = [];\n return s;\n};\nconst txt = (name, chars, size, color, weight, lineH) => {\n const t = penpot.createText(chars);\n t.name = name; t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = lineH || '1.4'; t.growType = 'auto-height'; t.filled = true; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.stroked = false;\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Cuidado de velas';\nboard.resize(720, 520);\nboard.fills = [{ fillColor: P.paper, fillOpacity: 1, fillImageScaling: 'fit' }];\nboard.addGridLayout();\nif (!board.grid) throw new Error('grid no se creo');\nboard.grid.rows = [{ size: 'auto' }, { size: 'auto' }, { size: 'auto' }];\nboard.grid.columns = [{ size: '1fr' }, { size: '1fr' }, { size: '1fr' }];\nboard.grid.rowGap = S.lg;\nboard.grid.columnGap = S.lg;\nboard.horizontalPadding = S.xl;\nboard.verticalPadding = S.xl;\nconst cab = mk('Cabecera', 656, 100, [P.brand], null);\nif (board.grid) board.grid.appendChild(cab, 1, 1);\nconst col = board.grid.columns.length;\nconst card = mk('Cuidado', 656, 280, [P.soft], null);\nif (board.grid) board.grid.appendChild(card, 2, 1);\ncard.layoutCell.columnSpan = col;\nconst h2 = txt('Titulo', 'Cuidado de tus velas de soja', T.lg, P.ink, '700');\nconst p = txt('Texto', 'Recortame la mecha a 5 mm antes de encender, no quemes mas de 4 horas seguidas y mantene la vela lejos de corrientes de aire.', T.md, P.ink, '400');\nboard.insertChild(board.children.length, cab);\nboard.insertChild(board.children.length, card);\nboard.insertChild(board.children.length, h2);\nboard.insertChild(board.children.length, p);\nconst cols = board.grid ? board.grid.columns.length : 0;\nconst span = card.layoutCell ? card.layoutCell.columnSpan : 0;\nreturn { boardId: board.id, cols, span, cardContained: penpotUtils.isContainedIn(card, board), cardParentX: card.parentX, cardParentY: card.parentY };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"content": ""
|
|
},
|
|
{
|
|
"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": [
|
|
"execute_code"
|
|
],
|
|
"code_payloads": [
|
|
"const board = penpotUtils.findShape(s => s.name === 'Cooperativa Miel', penpot.root);\nif (!board) return { error: 'board no encontrado' };\nconst titulo = penpotUtils.findShape(s => s.type === 'text' && s.name === 'Titulo', board);\nif (!titulo) return { error: 'titulo no encontrado' };\nconst antes = { fontSize: titulo.fontSize, fontFamily: titulo.fontFamily, fontWeight: titulo.fontWeight };\ntitulo.fontFamily = 'Karla';\ntitulo.fontSize = 48;\ntitulo.fontWeight = '700';\nreturn { antes, despues: { fontFamily: titulo.fontFamily, fontSize: titulo.fontSize, fontWeight: titulo.fontWeight } };"
|
|
],
|
|
"veto_hits": [
|
|
{
|
|
"patron": "fontSize/fontWeight/lineHeight/letterSpacing numerico",
|
|
"fragmento": "t }; titulo.fontFamily = 'Karla'; titulo.fontSize = 48; titulo.fontWeight = '700'; return { a"
|
|
}
|
|
],
|
|
"otros_patrones": [],
|
|
"content": ""
|
|
},
|
|
{
|
|
"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 board = penpotUtils.findShape(s => s.name === 'Academia Tango', penpot.root);\nif (!board) { return { error: 'no encontre la forma de origen' }; }\nconst palette = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.fills && shape.fills.length > 0) {\n return shape.fills.map(f => ({ name: shape.name, color: f.color, role: f.fillType }));\n }\n return null;\n});\nconst fills = palette.flatMap(p => p.result);\nconst unique = [...new Set(fills.map(f => f.color))];\nstorage.academia = { boardId: board.id, palette: unique };\nconst library = penpot.library.local;\nconst colores = unique.map((c, i) => {\n const col = library.createColor();\n col.name = ['Negro Tango', 'Rojo', 'Crema', 'Dorado'][i] || 'Color ' + (i + 1);\n col.color = c;\n library.add(col);\n return { name: col.name, color: col.color };\n});\nstorage.colores = colores;\nconst rect1 = penpot.createRectangle();\nrect1.name = 'Fondo';\nrect1.fills = [{ fillColor: '#1A0A00', fillOpacity: 1 }];\nrect1.resize(300, 160);\nconst rect2 = penpot.createRectangle();\nrect2.name = 'Acento';\nrect2.fills = [{ fillColor: '#C41E3A', fillOpacity: 1 }];\nrect2.resize(120, 60);\nboard.insertChild(board.children.length, rect1);\nboard.insertChild(board.children.length, rect2);\nreturn { palette: unique, libraryColors: colores, shapes: [rect1.name, rect2.name] };"
|
|
],
|
|
"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 { found: false }; }\nstorage.tiendaBoard = 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, boardWidth: board.width, boardHeight: board.height, violations: violations.length, shapesChecked: violations.length + (violations.filter(v => v.result === null).length) };"
|
|
],
|
|
"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": [
|
|
"penpot_api_info"
|
|
],
|
|
"code_payloads": [],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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: '#0B2B1A', accent: '#BFFF00', white: '#FFFFFF', mid: '#2A7A4A' };\nconst T = { hero: '48', sub: '18', stat: '36', label: '14', cta: '16' };\nconst S = { sm: 12, md: 24, lg: 40 };\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 || 'Work Sans';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.3';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst bd = penpot.createBoard();\nbd.name = 'Vivero Orquideas - Home';\nbd.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nbd.resize(900, 1020);\npenpot.root.appendChild(bd);\nconst hero = penpot.createBoard();\nhero.name = 'Hero';\nhero.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nhero.resize(900, 480);\nbd.insertChild(bd.children.length, hero);\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto orquidea';\nfoto.fills = [{ fillOpacity: 1, fillImage: { id: 'img-orchid', opacity: 1 } }];\nfoto.resize(900, 480);\nhero.insertChild(hero.children.length, foto);\nconst overlay = penpot.createRectangle();\noverlay.name = 'Overlay';\noverlay.fills = [{ fillColor: P.bg, fillOpacity: 0.55 }];\noverlay.resize(900, 480);\nhero.insertChild(hero.children.length, overlay);\nconst heroTitle = mk('Orquideas a domicilio', T.hero, P.accent, '700');\nheroTitle.x = S.lg;\nheroTitle.y = 140;\nhero.insertChild(hero.children.length, heroTitle);\nconst heroSub = mk('Cultivo profesional de orquideas para tu hogar u oficina', T.sub, P.white);\nheroSub.x = S.lg;\nheroSub.y = 220;\nhero.insertChild(hero.children.length, heroSub);\nconst cta = penpot.createRectangle();\ncta.name = 'Boton CTA';\ncta.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\ncta.borderRadius = 28;\ncta.resize(220, 48);\ncta.x = S.lg;\ncta.y = 300;\nhero.insertChild(hero.children.length, cta);\nconst ctaText = mk('Ver catalogo', T.cta, P.bg, '700');\nctaText.x = cta.x + 40;\nctaText.y = cta.y + 12;\nhero.insertChild(hero.children.length, ctaText);\nconst stats = penpot.createBoard();\nstats.name = 'Franja de numeros';\nstats.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nstats.resize(900, 160);\nbd.insertChild(bd.children.length, stats);\nstats.addFlexLayout();\nif (stats.flex) {\n stats.flex.dir = 'row';\n stats.flex.rowGap = S.md;\n stats.flex.columnGap = S.md;\n stats.flex.horizontalPadding = S.lg;\n stats.flex.alignItems = 'center';\n stats.flex.justifyContent = 'center';\n}\nconst statsData = [\n { num: '1200', label: 'Orquideas vendidas' },\n { num: '8', label: 'Anos de experiencia' },\n { num: '45', label: 'Variedades' },\n { num: '98%', label: 'Clientes satisfechos' }\n];\nconst statShapes = statsData.map(d => {\n const col = penpot.createBoard();\n col.name = 'Stat ' + d.label;\n col.fills = [];\n col.resize(180, 120);\n stats.appendChild(col);\n const num = mk(d.num, T.stat, P.bg, '700');\n num.x = 0;\n num.y = 12;\n col.insertChild(col.children.length, num);\n const lbl = mk(d.label, T.label, P.bg);\n lbl.x = 0;\n lbl.y = 72;\n col.insertChild(col.children.length, lbl);\n return col;\n});\nconst contact = penpot.createBoard();\ncontact.name = 'Contacto';\ncontact.fills = [{ fillColor: P.mid, fillOpacity: 1 }];\ncontact.resize(900, 280);\nbd.insertChild(bd.children.length, contact);\ncontact.addFlexLayout();\nif (contact.flex) {\n contact.flex.dir = 'column';\n contact.flex.rowGap = S.md;\n contact.flex.horizontalPadding = S.lg;\n contact.flex.verticalPadding = S.lg;\n contact.flex.alignItems = 'center';\n}\nconst contactTitle = mk('Contactanos', T.hero, P.accent, '700');\ncontactTitle.x = 0;\ncontactTitle.y = 0;\ncontact.insertChild(contact.children.length, contactTitle);\nconst contactInfo = mk('info@vivero-orquideas.com | +54 11 5555-0123', T.sub, P.white);\ncontactInfo.x = 0;\ncontactInfo.y = 72;\ncontact.insertChild(contact.children.length, contactInfo);\nstorage.vivero = { bdId: bd.id, P: P, T: T };\nreturn { bdId: bd.id, heroId: hero.id, statsId: stats.id, contactId: contact.id, shapes: bd.children.length, fills: bd.fills.length };"
|
|
],
|
|
"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 P = { bg:'#0B2545', ink:'#F0F8FF', accent:'#00B4D8', card:'#113A5B', soft:'#0077B6', line:'#1A5276' };\nconst T = { h1:'48', h2:'28', body:'16', sm:'13' };\nconst S = { xs:8, s:16, m:24, l:40, xl:64 };\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.fills = fills; s.borderRadius = radius || 0;\n s.resize(w, h); return s;\n};\nconst tx = (name, chars, size, color, weight, lineH) => {\n const t = penpot.createText();\n t.name = name; t.characters = chars; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = lineH; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width'; return t;\n};\nconst root = penpot.createBoard();\nroot.name = 'Kayak Store'; root.fills = [{ fillColor: P.bg, fillOpacity: 1 }]; root.resize(1200, 1100); root.borderRadius = 0;\nroot.addFlexLayout();\nif (root.flex) {\n root.flex.dir = 'column'; root.flex.horizontalPadding = S.xl; root.flex.verticalPadding = S.xl;\n root.flex.rowGap = S.l; root.flex.alignItems = 'start'; root.flex.justifyContent = 'start';\n}\nconst add = (s) => { root.appendChild(s); };\nconst header = mk('Header', 1200, 64, [{ fillColor: P.ink, fillOpacity: 1 }], 0);\nheader.flex = { flexGrow: 0, flexShrink: 1 };\nadd(header);\nheader.insertChild(header.children.length, tx('Logo', 'KAYAKIA', '22', P.bg, '700', '1'));\nheader.insertChild(header.children.length, tx('Nav', 'Kayaks Alquiler Cursos Contacto', '14', P.ink, '500', '1'));\nconst hero = mk('Hero', 1200, 380, [{ fillColor: P.soft, fillOpacity: 1 }], 16);\nhero.flex = { flexGrow: 1, flexShrink: 1 };\nadd(hero);\nhero.insertChild(hero.children.length, tx('H1', 'Descubri el rio que llevas adentro', '48', P.ink, '700', '1.1'));\nhero.insertChild(hero.children.length, tx('Sub', 'Alquiler de kayaks por hora o dia, con equipamiento incluido.', '18', P.ink, '400', '1.5'));\nhero.insertChild(hero.children.length, mk('Boton', 200, 48, [{ fillColor: P.accent, fillOpacity: 1 }], 24));\nconst cards = mk('Cards', 1200, 420, [], 0);\ncards.flex = { flexGrow: 0, flexShrink: 0 };\nadd(cards);\nif (cards.flex) { cards.flex.dir = 'row'; cards.flex.horizontalPadding = S.m; cards.flex.verticalPadding = S.m; cards.flex.rowGap = S.m; cards.flex.columnGap = S.m; cards.flex.alignItems = 'start'; }\nconst prod = (name, precio, desc) => {\n const c = mk(name, 360, 340, [{ fillColor: P.card, fillOpacity: 1 }], 12);\n c.flex = { flexGrow: 1, flexShrink: 1 };\n cards.appendChild(c);\n c.insertChild(c.children.length, mk('Img', 360, 180, [{ fillColor: P.line, fillOpacity: 1 }], 8));\n c.insertChild(c.children.length, tx('Nombre', name, '20', P.ink, '600', '1.2'));\n c.insertChild(c.children.length, tx('Precio', precio, '24', P.accent, '700', '1'));\n c.insertChild(c.children.length, tx('Desc', desc, '14', P.ink, '400', '1.5'));\n return c;\n};\nprod('Kayak Individual', '$3.500 / hora', 'Para un explorador solitario.');\nprod('Kayak Doble', '$5.800 / hora', 'Para compartir la aventura.');\nprod('Curso Inicial', '$12.000 / dia', 'Aprende con instructores certificados.');\nconst footer = mk('Footer', 1200, 100, [{ fillColor: P.line, fillOpacity: 1 }], 0);\nfooter.flex = { flexGrow: 0, flexShrink: 0 };\nadd(footer);\nfooter.insertChild(footer.children.length, tx('Footer', 'KAYAKIA — Alquiler y cursos de kayak.', '13', P.ink, '400', '1.5'));\nconst fills = root.fills.filter(f => f.fillOpacity > 0);\nreturn { rootId: root.id, shapes: root.children.length, fills: fills.length,\n headerFlex: !!header.flex, heroFlex: !!hero.flex, cardsFlex: !!cards.flex,\n cardCount: cards.children.length,\n fonts: [...new Set(root.children.flatMap(b => penpotUtils.analyzeDescendants(b, (r, s) => s.type === 'text' ? s.fontSize : null).map(d => d.result)).filter(Boolean))],\n colors: [...new Set(fills.map(f => f.fillColor))].length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": " weight, lineH) => { const t = penpot.createText(); t.name = name; t.characters = chars;"
|
|
}
|
|
],
|
|
"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 = { bg: '#3b0764', ink: '#fff8e7', accent: '#f5c542', gold: '#e8a020', dark: '#1a0a2e', cream: '#ffffff', card: '#fff8e7', ink2: '#2d1b4e' };\nconst T = { h1: '48', h2: '32', h3: '20', body: '16', sm: '14' };\nconst S = { sm: 16, md: 24, lg: 40, xl: 56 };\nconst mk = (name, w, h, fills, fontSize, fontWeight, lineHeight, color, align) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Work Sans'; t.fontSize = fontSize; t.fontWeight = fontWeight; t.lineHeight = lineHeight; t.letterSpacing = '0';\n t.fills = fills; t.strokes = []; t.growType = 'auto-width';\n if (align) t.textAlign = align;\n if (t.textAlign) t.resize(w, h);\n return t;\n};\nconst mkBtn = (name, label, bg, fg) => {\n const b = penpot.createRectangle();\n b.name = name; b.fills = [{ fillColor: bg, fillOpacity: 1 }]; b.strokes = []; b.borderRadius = 28;\n b.resize(200, 44);\n const t = mk(name + '/btn', 200, 44, [{ fillColor: fg, fillOpacity: 1 }], T.h3, '700', '1', fg);\n t.characters = label; t.textAlign = 'center';\n b.insertChild(b.children.length, t);\n return b;\n};\nconst mkPlan = (name, plan, price, items, featured) => {\n const c = penpot.createBoard();\n c.name = name; c.fills = [{ fillColor: featured ? P.bg : P.cream, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: featured ? P.gold : P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n c.borderRadius = 16; c.resize(320, 460);\n c.insertChild(c.children.length, mk(name + '/plan', 280, 40, [{ fillColor: featured ? P.accent : P.ink, fillOpacity: 1 }], T.h2, '700', '1', featured ? P.accent : P.ink, 'center'));\n c.insertChild(c.children.length, mk(name + '/price', 280, 56, [{ fillColor: featured ? P.ink : P.ink2, fillOpacity: 1 }], '56', '700', '1', featured ? P.ink : P.ink2, 'center'));\n c.insertChild(c.children.length, mk(name + '/desc', 280, 32, [{ fillColor: featured ? P.accent : P.ink, fillOpacity: 1 }], T.body, '400', '1.4', featured ? P.accent : P.ink, 'center'));\n const ul = penpot.createBoard();\n ul.name = name + '/ul'; ul.fills = []; ul.strokes = []; ul.resize(280, 180);\n c.insertChild(c.children.length, ul);\n items.forEach((item, i) => {\n const row = penpot.createBoard();\n row.name = name + '/row-' + i; row.fills = []; row.strokes = []; row.resize(280, 36);\n row.insertChild(row.children.length, mk(row.name + '/dot', 8, 8, [{ fillColor: featured ? P.accent : P.accent, fillOpacity: 1 }], T.body, '700', '1', featured ? P.accent : P.accent));\n row.insertChild(row.children.length, mk(row.name + '/text', 240, 28, [{ fillColor: featured ? P.ink : P.ink2, fillOpacity: 1 }], T.body, '400', '1.4', featured ? P.ink : P.ink2));\n row.children[0].x = 0; row.children[0].y = 4;\n row.children[1].x = 20; row.children[1].y = 4;\n ul.insertChild(ul.children.length, row);\n });\n c.insertChild(c.children.length, mkBtn(name + '/btn', plan, featured ? P.accent : P.bg, featured ? P.ink : P.ink));\n return c;\n};\nconst mkFaq = (q, a) => {\n const c = penpot.createBoard();\n c.name = 'faq'; c.fills = []; c.strokes = []; c.resize(1200, 80);\n c.insertChild(c.children.length, mk(c.name + '/q', 1100, 32, [{ fillColor: P.ink, fillOpacity: 1 }], T.h3, '600', '1.2', P.ink));\n c.insertChild(c.children.length, mk(c.name + '/a', 1100, 32, [{ fillColor: P.ink2, fillOpacity: 1 }], T.body, '400', '1.4', P.ink2));\n return c;\n};\nconst root = penpot.createBoard();\nroot.name = 'Miel - Precios'; root.fills = [{ fillColor: P.cream, fillOpacity: 1 }]; root.resize(1320, 1600);\nroot.insertChild(root.children.length, mk('enc', 1200, 64, [{ fillColor: P.ink, fillOpacity: 1 }], T.h1, '700', '1.1', P.ink, 'center'));\nroot.insertChild(root.children.length, mk('sub', 1200, 36, [{ fillColor: P.accent, fillOpacity: 1 }], T.h3, '400', '1.4', P.accent, 'center'));\nconst plans = ['Básico', 'Familiar', 'Premium'];\nconst prices = ['S/ 45', 'S/ 89', 'S/ 149'];\nconst descs = ['1 litro de miel pura', '3 litros + 1 frasco de abeja', '6 litros + frasco + velas'];\nconst items = [['Polinización orgánica', 'Sin aditivos', 'Envase de vidrio'], ['Envío gratis', 'Miel de temporada', 'Etiqueta personalizada'], ['Visita a la colmena', 'Asesoría apícola', 'Kit de degustación']];\nplans.forEach((p, i) => root.insertChild(root.children.length, mkPlan('plan-' + i, p, prices[i], items[i], i === 1)));\nconst faqs = [\n ['¿La miel es 100% pura?', 'Sí, sin jarabes ni conservantes.'],\n ['¿Hacen envíos a todo el país?', 'Cubrimos Lima y provincias en 3-5 días.'],\n ['¿Tienen descuentos por volumen?', 'Sí, desde 10 frascos con 15% de descuento.']\n];\nconst faqBoard = penpot.createBoard();\nfaqBoard.name = 'FAQ'; faqBoard.fills = [{ fillColor: P.cream, fillOpacity: 1 }]; faqBoard.resize(1320, 400);\nfaqBoard.insertChild(faqBoard.children.length, mk('faq-t', 1200, 40, [{ fillColor: P.ink, fillOpacity: 1 }], T.h2, '700', '1.1', P.ink, 'center'));\nfaqBoard.insertChild(faqBoard.children.length, mk('faq-s', 1200, 32, [{ fillColor: P.ink2, fillOpacity: 1 }], T.body, '400', '1.4', P.ink2, 'center'));\nfaqs.forEach(f => faqBoard.insertChild(faqBoard.children.length, mkFaq(f[0], f[1])));\nroot.insertChild(root.children.length, faqBoard);\nconst footer = penpot.createBoard();\nfooter.name = 'Pie'; footer.fills = [{ fillColor: P.dark, fillOpacity: 1 }]; footer.resize(1320, 160);\nfooter.insertChild(footer.children.length, mk('foot', 1200, 32, [{ fillColor: P.accent, fillOpacity: 1 }], T.h3, '600', '1.2', P.accent, 'center'));\nroot.insertChild(root.children.length, footer);\nconst shapes = [root, mkBtn('enc/btn', 'Ver planes', P.accent, P.bg)];\nshapes.forEach((s, i) => {\n if (i === 0) { s.x = 60; s.y = 0; }\n else { s.x = 560; s.y = 240; }\n s.y += S.lg;\n});\nreturn { rootId: root.id, planCount: plans.length, faqCount: faqs.length, btnCount: shapes.filter(s => s.name.includes('btn')).length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "textAlign",
|
|
"fragmento": ".growType = 'auto-width'; if (align) t.textAlign = align; if (t.textAlign) t.resize(w, h"
|
|
}
|
|
],
|
|
"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 = { bg:'#F7F8FA', side:'#0F172A', accent:'#0D9488', card:'#F0FDF9', text:'#0F172A', border:'#E2E8F0', pos:'#059669', neg:'#DC2626' };\nconst T = { sm:'13', md:'16', lg:'22', xl:'30' };\nconst S = { xs:4, sm:8, md:16, lg:24 };\nconst mk = (name, w, h, fills, fontSize, color, weight) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = fills ? fills.map(c => ({ fillColor: c, fillOpacity: 1 })) : [];\n s.strokes = []; s.borderRadius = 0;\n if (s.fills) s.fills.forEach(f => { if (f.fillOpacity !== undefined) f.fillOpacity = 1; });\n return s;\n};\nconst mkText = (name, chars, size, color, weight, align) => {\n const t = penpot.createText();\n t.name = name; t.characters = chars; t.fontFamily = 'Inter'; t.fontSize = size;\n t.fontWeight = weight || '400'; t.lineHeight = '1.4'; t.letterSpacing = '0';\n t.align = align || 'left'; t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n return t;\n};\nconst mkIcon = (name, color) => {\n const c = penpot.createEllipse();\n c.name = name; c.resize(32, 32);\n c.fills = [{ fillColor: color, fillOpacity: 1 }];\n c.strokes = [];\n return c;\n};\nconst mkCard = (icon, num, label, color) => {\n const card = mk('Card ' + label, 200, 100, [P.card]);\n card.strokes = [{ strokeColor: P.border, strokeWidth: 1, strokeOpacity: 1, strokeStyle: 'solid', strokeAlignment: 'inner' }];\n card.borderRadius = 10;\n const row = mk('Row ' + label, 172, 72, []);\n row.borderRadius = 10;\n row.fills = [];\n card.insertChild(card.children.length, row);\n row.insertChild(row.children.length, mkIcon('Icon ' + label, color));\n row.insertChild(row.children.length, mkText('Num ' + label, num, T.lg, color, '700'));\n row.insertChild(row.children.length, mkText('Label ' + label, label, T.sm, P.text, '400'));\n row.flex = { dir: 'row', rowGap: S.sm, alignItems: 'center', justifyContent: 'start' };\n row.horizontalPadding = S.md;\n row.verticalPadding = S.md;\n return card;\n};\nconst mkCell = (chars, size, color, weight, bg) => {\n const c = mk('Cell', 150, 36, bg ? [bg] : []);\n c.strokes = [{ strokeColor: P.border, strokeWidth: 1, strokeOpacity: 1, strokeStyle: 'solid', strokeAlignment: 'inner' }];\n c.borderRadius = 4;\n c.insertChild(c.children.length, mkText('T', chars, size, color, weight));\n return c;\n};\nconst board = penpot.createBoard();\nboard.name = 'Dashboard Laboratorio';\nboard.resize(900, 640);\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.rowGap = S.md;\n board.flex.horizontalPadding = S.lg;\n board.flex.verticalPadding = S.lg;\n board.flex.alignItems = 'start';\n board.flex.justifyContent = 'start';\n}\nconst sidebar = mk('Sidebar', 200, 560, [P.side]);\nsidebar.strokes = [];\nconst contenido = mk('Contenido', 680, 560, [P.bg]);\ncontenido.strokes = [];\nboard.insertChild(board.children.length, sidebar);\nboard.insertChild(board.children.length, contenido);\nif (board.flex) {\n board.flex.appendChild(sidebar);\n board.flex.appendChild(contenido);\n}\nconst header = mk('Header', 680, 56, [P.accent]);\nheader.strokes = [];\nheader.borderRadius = 8;\nheader.insertChild(header.children.length, mkText('Titulo', 'Laboratorio de Analisis de Suelos', T.xl, '#FFFFFF', '700'));\ncontenido.insertChild(contenido.children.length, header);\nconst grid = penpot.createBoard();\ngrid.name = 'Indicadores';\ngrid.resize(680, 136);\ngrid.fills = [];\ngrid.strokes = [];\ngrid.addGridLayout();\nif (grid.grid) {\n grid.grid.rows = '136';\n grid.grid.columns = '200 200 200';\n grid.grid.rowGap = S.md;\n grid.grid.columnGap = S.md;\n}\nconst cards = [\n { icon: 'Muestras', num: '12', label: 'Muestras activas', color: P.accent },\n { icon: 'Pendientes', num: '5', label: 'Pendientes de analisis', color: '#F59E0B' },\n { icon: 'Promedio', num: '2.3', label: 'Promedio dias', color: P.text },\n { icon: 'Urgentes', num: '2', label: 'Urgentes', color: P.neg },\n { icon: 'Aprobadas', num: '87%', label: 'Aprobadas', color: P.pos },\n { icon: 'Facturacion', num: '$34k', label: 'Pendientes de pago', color: '#F59E0B' }\n];\ncards.forEach(c => {\n const card = mkCard(c.icon, c.num, c.label, c.color);\n grid.grid.appendChild(card, 1, grid.grid.columns.indexOf(c.icon) + 1);\n});\ncontenido.insertChild(contenido.children.length, grid);\nconst tabla = penpot.createBoard();\ntabla.name = 'Movimientos';\ntabla.resize(680, 360);\ntabla.fills = [P.bg];\ntabla.strokes = [];\ntabla.addGridLayout();\nif (tabla.grid) {\n tabla.grid.rows = '36 36 36 36 36 36 36 36 36 36';\n tabla.grid.columns = '180 120 120 120 130';\n tabla.grid.rowGap = 0;\n tabla.grid.columnGap = S.sm;\n}\nconst filas = [\n ['Fecha', 'Muestra', 'Tipo', 'Estado', 'Monto'],\n ['15/06', 'M-2024-089', 'Fertilidad', 'Completado', '$1.200'],\n ['15/06', 'M-2024-090', 'Microelementos', 'En proceso', '$850'],\n ['14/06', 'M-2024-088', 'pH y conductividad', 'Completado', '$600'],\n ['14/06', 'M-2024-087', 'Materia organica', 'Pendiente', '$950'],\n ['13/06', 'M-2024-086', 'NPK completo', 'Completado', '$1.400'],\n ['13/06', 'M-2024-085', 'Metales pesados', 'En proceso', '$2.100'],\n ['12/06', 'M-2024-084', 'Fertilidad', 'Completado', '$1.200'],\n ['12/06', 'M-2024-083', 'Microelementos', 'Pendiente', '$850'],\n ['11/06', 'M-2024-082', 'pH y conductividad', 'Completado', '$600']\n];\nfilas.forEach((f, i) => {\n f.forEach((c, j) => {\n const cell = mkCell(c, i === 0 ? T.md : T.sm, i === 0 ? '#FFFFFF' : (c.startsWith('$') ? (c.startsWith('-') ? P.neg : P.pos) : P.text), i === 0 ? '700' : '400', i === 0 ? P.accent : null);\n tabla.grid.appendChild(cell, i + 1, j + 1);\n });\n});\ncontenido.insertChild(contenido.children.length, tabla);\nconst sidebarItems = [\n { label: 'Dashboard', active: true },\n { label: 'Muestras' },\n { label: 'Analisis' },\n { label: 'Resultados' },\n { label: 'Reportes' },\n { label: 'Configuracion' }\n];\nsidebarItems.forEach(item => {\n const row = mk('Nav ' + item.label, 168, 40, item.active ? [P.accent] : []);\n row.borderRadius = 6;\n row.insertChild(row.children.length, mkText('Nav ' + item.label, item.label, T.md, item.active ? '#FFFFFF' : '#94A3B8', item.active ? '600' : '400'));\n row.flex = { dir: 'row', rowGap: S.sm, alignItems: 'center', justifyContent: 'start' };\n row.horizontalPadding = S.md;\n sidebar.insertChild(sidebar.children.length, row);\n});\nstorage.dashboard = { boardId: board.id, sidebarId: sidebar.id, contenidoId: contenido.id, gridId: grid.id, tablaId: tabla.id };\nreturn {\n boardId: board.id,\n sidebarId: sidebar.id,\n contenidoId: contenido.id,\n gridId: grid.id,\n tablaId: tabla.id,\n cards: cards.length,\n filasTabla: filas.length,\n sidebarItems: sidebarItems.length,\n layout: board.flex ? { dir: board.flex.dir, rowGap: board.flex.rowGap, horizontalPadding: board.flex.horizontalPadding, verticalPadding: board.flex.verticalPadding } : null,\n gridConfig: grid.grid ? { rows: grid.grid.rows, columns: grid.grid.columns, rowGap: grid.grid.rowGap, columnGap: grid.grid.columnGap } : null,\n tablaConfig: tabla.grid ? { rows: tabla.grid.rows, columns: tabla.grid.columns, rowGap: tabla.grid.rowGap, columnGap: tabla.grid.columnGap } : null\n};"
|
|
],
|
|
"veto_hits": [
|
|
{
|
|
"patron": "flex.appendChild",
|
|
"fragmento": "h, contenido); if (board.flex) { board.flex.appendChild(sidebar); board.flex.appendChild(conte"
|
|
}
|
|
],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": " weight, align) => { const t = penpot.createText(); t.name = name; t.characters = chars;"
|
|
}
|
|
],
|
|
"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 = { tinta:'#0B1A2C', papel:'#F7F3EE', acento:'#D4A017', linea:'#C9BFAE', sombra:'#0B1A2C' };\nconst T = { xs:'12', sm:'14', md:'16', lg:'20', xl:'28', xxl:'40', xxxl:'56' };\nconst S = { w:'600', h:'800', sm:'16', md:'24', lg:'40', xl:'56' };\nconst mk = (chars, size, weight, color, tracking, lineH) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.fontFamily = 'Lora'; t.fontSize = size; t.fontWeight = weight; t.lineHeight = lineH || '1.4';\n t.growType = 'auto-height'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n if (tracking) t.letterSpacing = tracking;\n return t;\n};\nconst btn = (label) => {\n const b = penpot.createRectangle();\n b.name = 'Boton ' + label; b.fills = [{ fillColor: P.acento, fillOpacity: 1 }];\n b.borderRadius = 4; b.resize(200, 48);\n const l = mk(label, '16', '700', P.tinta, null, '1');\n l.horizontalSizing = 'fit-content'; l.verticalSizing = 'fit-content';\n b.insertChild(b.children.length, l);\n return b;\n};\nconst mkInput = (placeholder) => {\n const c = penpot.createRectangle();\n c.name = 'Input ' + placeholder; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeWidth: 1, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n c.borderRadius = 4; c.resize(400, 48);\n c.insertChild(c.children.length, mk(placeholder, '14', '400', P.tinta, null, '1.4'));\n return c;\n};\nconst mkCard = (chars, size, weight, color) => {\n const c = penpot.createRectangle();\n c.name = 'Card ' + chars; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeOpacity: 1, strokeAlignment: 'inner', strokeWidth: 1, strokeStyle: 'solid' }];\n c.borderRadius = 8; c.resize(320, 200);\n c.insertChild(c.children.length, mk(chars, size, weight, color));\n return c;\n};\nconst mkSeccion = (name, w, h, bg) => {\n const b = penpot.createBoard();\n b.name = name; b.fills = [{ fillColor: bg, fillOpacity: 1 }];\n b.resize(w, h); b.addFlexLayout();\n if (b.flex) { b.flex.dir = 'column'; b.flex.horizontalPadding = S.xl; b.flex.verticalPadding = S.xl; b.flex.rowGap = S.md; }\n return b;\n};\nconst mkItem = (chars, size, weight, color) => {\n const c = penpot.createRectangle();\n c.name = 'Item ' + chars; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeOpacity: 1, strokeAlignment: 'inner', strokeWidth: 1, strokeStyle: 'solid' }];\n c.borderRadius = 8; c.resize(320, 200);\n c.insertChild(c.children.length, mk(chars, size, weight, color));\n return c;\n};\nconst mkCuenta = (num, label) => {\n const c = penpot.createRectangle();\n c.name = 'Cuenta ' + num; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeOpacity: 1, strokeAlignment: 'inner', strokeWidth: 1, strokeStyle: 'solid' }];\n c.borderRadius = 8; c.resize(120, 120);\n c.insertChild(c.children.length, mk(num, '40', '700', P.acento));\n c.insertChild(c.children.length, mk(label, '14', '600', P.tinta));\n return c;\n};\nconst mkOrador = (chars, rol) => {\n const c = penpot.createRectangle();\n c.name = 'Orador ' + chars; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeOpacity: 1, strokeAlignment: 'inner', strokeWidth: 1, strokeStyle: 'solid' }];\n c.borderRadius = 8; c.resize(320, 200);\n c.insertChild(c.children.length, mk(chars, '20', '700', P.tinta));\n c.insertChild(c.children.length, mk(rol, '14', '400', P.linea));\n return c;\n};\nconst mkLinea = (w, h, color) => {\n const l = penpot.createRectangle();\n l.name = 'Linea'; l.fills = [{ fillColor: color, fillOpacity: 1 }];\n l.resize(w, h);\n return l;\n};\nconst mkForm = (placeholder, label) => {\n const c = penpot.createRectangle();\n c.name = 'Form ' + label; c.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\n c.strokes = [{ strokeColor: P.linea, strokeOpacity: 1, strokeAlignment: 'inner', strokeWidth: 1, strokeStyle: 'solid' }];\n c.borderRadius = 8; c.resize(600, 200);\n c.insertChild(c.children.length, mk(label, '20', '700', P.tinta));\n const f = penpot.createBoard();\n f.name = 'Formulario ' + label; f.fills = []; f.resize(600, 80);\n f.addFlexLayout();\n if (f.flex) { f.flex.dir = 'row'; f.flex.rowGap = S.sm; f.flex.alignItems = 'center'; }\n f.insertChild(f.children.length, mkInput(placeholder));\n f.insertChild(f.children.length, btn('Reservar'));\n c.insertChild(c.children.length, f);\n c.insertChild(c.children.length, mk('Plazas limitadas. Confirmacion por email.', '12', '400', P.linea));\n return c;\n};\nconst root = penpot.createBoard();\nroot.name = 'Relojeria'; root.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\nroot.resize(900, 1600);\nconst hero = mkSeccion('Hero', 900, 400, P.tinta);\nhero.insertChild(hero.children.length, mk('Relojeria del Tiempo', '56', '700', P.papel));\nhero.insertChild(hero.children.length, mk('15 de noviembre, 19:00 hs', '20', '600', P.acento));\nhero.insertChild(hero.children.length, mkLinea(120, 2, P.acento));\nhero.insertChild(hero.children.length, mk('Charla abierta sobre restauracion de relojes de bolsillo del siglo XIX.', '16', '400', P.linea));\nroot.insertChild(root.children.length, hero);\nconst count = mkSeccion('Cuenta regresiva', 900, 200, P.papel);\ncount.insertChild(count.children.length, mk('Faltan', '28', '700', P.tinta));\nconst c = penpot.createBoard();\nc.name = 'Cuenta'; c.fills = []; c.resize(560, 120);\nc.addFlexLayout();\nif (c.flex) { c.flex.dir = 'row'; c.flex.rowGap = S.md; c.flex.alignItems = 'center'; c.flex.justifyContent = 'center'; }\nc.insertChild(c.children.length, mkCuenta('12', 'dias'));\nc.insertChild(c.children.length, mkCuenta('08', 'horas'));\nc.insertChild(c.children.length, mkCuenta('45', 'min'));\nc.insertChild(c.children.length, mkCuenta('30', 'seg'));\ncount.insertChild(count.children.length, c);\nroot.insertChild(root.children.length, count);\nconst oradores = mkSeccion('Oradores', 900, 480, P.tinta);\noradores.insertChild(oradores.children.length, mk('Oradores', '40', '700', P.papel));\noradores.insertChild(oradores.children.length, mkLinea(80, 2, P.acento));\nconst g = penpot.createBoard();\ng.name = 'Grilla oradores'; g.fills = []; g.resize(688, 320);\ng.addGridLayout();\nif (g.grid) { g.grid.rows = '160px 160px'; g.grid.columns = '320px 320px'; g.grid.rowGap = S.md; g.grid.columnGap = S.md; }\ng.insertChild(g.children.length, mkOrador('Elena Voss', 'Relojera independiente'));\ng.insertChild(g.children.length, mkOrador('Marcos Ruiz', 'Museo del Tiempo'));\ng.insertChild(g.children.length, mkOrador('Sofia Chen', 'Historiadora'));\ng.insertChild(g.children.length, mkOrador('Javier Moreno', 'Restaurador'));\noradores.insertChild(oradores.children.length, g);\nroot.insertChild(root.children.length, oradores);\nconst inscripcion = mkSeccion('Inscripcion', 900, 520, P.papel);\ninscripcion.insertChild(inscripcion.children.length, mk('Inscripcion', '40', '700', P.tinta));\ninscripcion.insertChild(inscripcion.children.length, mkLinea(80, 2, P.acento));\ninscripcion.insertChild(inscripcion.children.length, mkForm('tu@email.com', 'Reserva tu lugar'));\nroot.insertChild(root.children.length, inscripcion);\nstorage.relojeria = { rootId: root.id, heroId: hero.id, countId: count.id, oradoresId: oradores.id, inscripcionId: inscripcion.id };\nreturn { rootId: root.id, secciones: 4, hero: hero.children.length, cuenta: count.children.length, oradores: oradores.children.length, inscripcion: inscripcion.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', dark:'#0b3a4a', light:'#f0f7fa', white:'#ffffff', ink:'#0d2b38', muted:'#8bb5c2' };\nconst T = { sm:'14', md:'16', lg:'20' };\nconst S = { xs:4, sm:8, md:12, lg:20, xl:32 };\nconst mkBtn = (name, label, size, variant) => {\n const b = penpot.createBoard();\n b.name = name;\n b.fills = [{ fillColor: variant==='primary' ? P.accent : P.white, fillOpacity: 1 }];\n b.strokes = variant==='primary' ? [] : [{ strokeColor: P.accent, strokeWidth: 2, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n b.borderRadius = 10;\n b.resize(280, 52);\n const t = penpot.createText(label);\n if (!t) throw new Error('createText returned null');\n t.name = name + ' label';\n t.characters = label;\n t.fontFamily = 'Inter';\n t.fontSize = size;\n t.fontWeight = variant==='primary' ? '600' : '400';\n t.lineHeight = '1.5';\n t.fills = [{ fillColor: variant==='primary' ? P.white : P.accent, fillOpacity: 1 }];\n t.growType = 'auto-width';\n b.insertChild(b.children.length, t);\n b.addFlexLayout();\n if (b.flex) {\n b.flex.dir = 'row';\n b.flex.alignItems = 'center';\n b.flex.justifyContent = 'center';\n b.flex.horizontalPadding = S.lg;\n b.flex.verticalPadding = S.md;\n b.flex.rowGap = S.sm;\n }\n return b;\n};\nconst primary = mkBtn('Boton primario', 'Solicitar auditoria', T.md, 'primary');\nconst secondary = mkBtn('Boton secundario', 'Ver servicios', T.sm, 'secondary');\nconst disabled = mkBtn('Boton deshabilitado', 'Solicitar auditoria', T.md, 'primary');\ndisabled.fills = [{ fillColor: P.muted, fillOpacity: 1 }];\ndisabled.strokes = [];\ndisabled.borderRadius = 10;\ndisabled.resize(280, 52);\ndisabled.opacity = 0.5;\ndisabled.blocked = true;\nif (disabled.children.length) {\n disabled.children.forEach(c => { if (c.type === 'text') { c.fills = [{ fillColor: P.white, fillOpacity: 1 }]; c.opacity = 0.5; } });\n}\nconst board = penpot.createBoard();\nboard.name = 'Botones consultora';\nboard.fills = [{ fillColor: P.light, fillOpacity: 1 }];\nboard.resize(640, 280);\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.alignItems = 'start';\n board.flex.horizontalPadding = S.xl;\n board.flex.verticalPadding = S.xl;\n board.flex.rowGap = S.lg;\n}\nboard.insertChild(board.children.length, primary);\nboard.insertChild(board.children.length, secondary);\nboard.insertChild(board.children.length, disabled);\nstorage.botones = { primaryId: primary.id, secondaryId: secondary.id, disabledId: disabled.id, boardId: board.id };\nreturn { boardId: board.id, botones: [primary.id, secondary.id, disabled.id].length, flex: !!board.flex };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 = { fondo:'#F5E6C8', miel:'#D4A017', tierra:'#8B5E3C', carton:'#FFFDF5', tinta:'#2C1810', tachado:'#E8C547', final:'#B22222', boton:'#D4A017' };\nconst T = { micro:'11', cuerpo:'15', precio:'26', precioFinal:'32' };\nconst S = { sm:8, md:16, lg:24 };\nconst board = new Board();\nboard.name = 'Card Miel';\nboard.fills = [{ fillColor: P.fondo, fillOpacity: 1 }];\nboard.resize(320, 480);\nboard.borderRadius = 20;\nboard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 6, blur: 18, spread: 0, color: { color: P.tierra, opacity: 0.18 } }];\npenpot.root.appendChild(board);\nconst placeholder = new Rectangle();\nplaceholder.name = 'Foto placeholder';\nplaceholder.fills = [{ fillColor: '#F5E6C8', fillOpacity: 1, fillGradient: { type: 'radial', startX: 0.5, startY: 0.35, endX: 0.5, endY: 0.85, startRadius: 0, endRadius: 1, stops: [{ color: '#F5E6C8', offset: 0 }, { color: '#D4A017', offset: 0.55 }, { color: '#8B5E3C', offset: 1 }] } }];\nplaceholder.resize(280, 200);\nplaceholder.borderRadius = 14;\nplaceholder.x = 20;\nplaceholder.y = 20;\nboard.insertChild(board.children.length, placeholder);\nconst bloque = new Board();\nbloque.name = 'Bloque texto';\nbloque.fills = [];\nbloque.resize(280, 220);\nbloque.borderRadius = 14;\nbloque.x = 20;\nbloque.y = 240;\nbloque.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: P.tierra, opacity: 0.1 } }];\nboard.insertChild(board.children.length, bloque);\nconst titulo = new Text();\ntitulo.name = 'Titulo';\ntitulo.characters = 'Miel de abeja pura';\ntitulo.fontFamily = 'Libre Baskerville';\ntitulo.fontSize = T.precio;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.fills = [{ fillColor: P.tinta, fillOpacity: 1 }];\ntitulo.growType = 'auto-width';\nbloque.insertChild(bloque.children.length, titulo);\nconst precio = new Text();\nprecio.name = 'Precio';\nprecio.characters = '$ 1.800 $ 1.260';\nprecio.fontFamily = 'Libre Baskerville';\nprecio.fontSize = T.precioFinal;\nprecio.fontWeight = '700';\nprecio.lineHeight = '1.3';\nprecio.fills = [{ fillColor: P.final, fillOpacity: 1 }];\nprecio.growType = 'auto-width';\nbloque.insertChild(bloque.children.length, precio);\nconst boton = new Rectangle();\nboton.name = 'Boton Agregar';\nboton.fills = [{ fillColor: P.boton, fillOpacity: 1 }];\nboton.resize(280, 48);\nboton.borderRadius = 24;\nboton.x = 0;\nboton.y = 160;\nboton.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 10, spread: 0, color: { color: P.miel, opacity: 0.35 } }];\nbloque.insertChild(bloque.children.length, boton);\nconst btnText = new Text();\nbtnText.name = 'Texto boton';\nbtnText.characters = 'Agregar al carrito';\nbtnText.fontFamily = 'Libre Baskerville';\nbtnText.fontSize = T.cuerpo;\nbtnText.fontWeight = '700';\nbtnText.lineHeight = '1';\nbtnText.fills = [{ fillColor: P.carton, fillOpacity: 1 }];\nbtnText.growType = 'auto-width';\nbtnText.horizontalAlign = 'center';\nbtnText.verticalAlign = 'center';\nboton.insertChild(boton.children.length, btnText);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst fills = penpotUtils.analyzeDescendants(board, (root, shape) => {\n const c = shape.fills?.map(f => f.fillColor).filter(Boolean);\n return c ? c.length : 0;\n});\nreturn { boardId: board.id, placeholderId: placeholder.id, placeholderHasGradient: !!placeholder.fills?.[0]?.fillGradient, texts: texts.length, shapes: penpotUtils.shapeStructure(board, 2), colors: [...new Set(fills.flatMap(f => f.result))].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 = {\n primary: '#0B2545',\n accent: '#1B7A4B',\n bg: '#E8EDF2',\n white: '#FFFFFF'\n};\nconst S = { sm: 12, md: 16, lg: 22 };\nconst nav = penpot.createBoard();\nnav.name = 'Navbar Laboratorio';\nnav.fills = [{ fillColor: P.primary, fillOpacity: 1 }];\nnav.resize(1180, 88);\nnav.addFlexLayout();\nif (nav.flex) {\n nav.flex.dir = 'row';\n nav.flex.horizontalPadding = 40;\n nav.flex.verticalPadding = 0;\n nav.flex.alignItems = 'center';\n nav.flex.rowGap = 24;\n}\nconst logo = penpot.createBoard();\nlogo.name = 'Logo';\nlogo.fills = [{ fillColor: P.white, fillOpacity: 1 }];\nlogo.resize(180, 48);\nlogo.borderRadius = 8;\nnav.appendChild(logo);\nif (logo.flex) {\n logo.flex.dir = 'column';\n logo.flex.alignItems = 'center';\n logo.flex.justifyContent = 'center';\n logo.flex.verticalPadding = 8;\n}\nconst logoIcon = penpot.createRectangle();\nlogoIcon.name = 'Icono';\nlogoIcon.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\nlogoIcon.resize(28, 28);\nlogoIcon.borderRadius = 6;\nlogo.insertChild(0, logoIcon);\nconst logoText = penpot.createText('GeoAnalisis');\nlogoText.name = 'Logo texto';\nlogoText.fontFamily = 'Inter';\nlogoText.fontSize = S.lg;\nlogoText.fontWeight = '700';\nlogoText.lineHeight = '1.2';\nlogoText.fills = [{ fillColor: P.primary, fillOpacity: 1 }];\nlogoText.resize(140, 32);\nlogo.insertChild(1, logoText);\nconst links = ['Inicio', 'Servicios', 'Metodologia', 'Resultados', 'Contacto'];\nconst container = penpot.createBoard();\ncontainer.name = 'Enlaces';\ncontainer.fills = [];\ncontainer.resize(700, 40);\nnav.appendChild(container);\nif (container.flex) {\n container.flex.dir = 'row';\n container.flex.alignItems = 'center';\n container.flex.justifyContent = 'flex-end';\n container.flex.rowGap = 28;\n}\nconst linkShapes = links.map(name => {\n const t = penpot.createText(name);\n t.name = 'Link ' + name;\n t.fontFamily = 'Inter';\n t.fontSize = S.md;\n t.fontWeight = '500';\n t.lineHeight = '1.4';\n t.fills = [{ fillColor: P.white, fillOpacity: 1 }];\n t.resize(80, 24);\n container.appendChild(t);\n return t;\n});\nconst cta = penpot.createRectangle();\ncta.name = 'Boton Solicitar';\ncta.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\ncta.resize(180, 40);\ncta.borderRadius = 20;\nnav.appendChild(cta);\nif (cta.flex) {\n cta.flex.dir = 'row';\n cta.flex.alignItems = 'center';\n cta.flex.justifyContent = 'center';\n cta.flex.horizontalPadding = 16;\n}\nconst ctaText = penpot.createText('Solicitar analisis');\nctaText.name = 'CTA texto';\nctaText.fontFamily = 'Inter';\nctaText.fontSize = S.md;\nctaText.fontWeight = '600';\nctaText.lineHeight = '1.4';\nctaText.fills = [{ fillColor: P.white, fillOpacity: 1 }];\nctaText.resize(148, 24);\ncta.insertChild(0, ctaText);\nstorage.navbar = { boardId: nav.id, palette: P, links: linkShapes.length, ctaId: cta.id };\nreturn { boardId: nav.id, links: linkShapes.length, ctaId: cta.id, palette: P };"
|
|
],
|
|
"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', card: '#ffffff', btn: '#3b0f63', btnText: '#ffffff', text: '#1a0033' };\nconst T = { sm: '14', md: '16', lg: '22' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 32 };\nconst mk = (name, chars, size, weight, color, lineH) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = name;\n t.fontFamily = 'Work Sans';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = lineH || '1.5';\n t.growType = 'auto-width';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Aviso de cookies';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(640, 160);\nboard.x = 0;\nboard.y = 0;\nconst card = penpot.createRectangle();\ncard.name = 'Tarjeta';\ncard.fills = [{ fillColor: P.card, fillOpacity: 1 }];\ncard.resize(560, 120);\ncard.borderRadius = 12;\ncard.x = 40;\ncard.y = 20;\nconst btnOk = penpot.createRectangle();\nbtnOk.name = 'Boton Aceptar';\nbtnOk.fills = [{ fillColor: P.btn, fillOpacity: 1 }];\nbtnOk.resize(140, 40);\nbtnOk.borderRadius = 20;\nconst btnNo = penpot.createRectangle();\nbtnNo.name = 'Boton Rechazar';\nbtnNo.strokes = [{ strokeColor: P.btn, strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\nbtnNo.resize(140, 40);\nbtnNo.borderRadius = 20;\nconst t1 = mk('Texto', 'Este sitio utiliza cookies para mejorar tu experiencia. Al continuar navegando, aceptas su uso.', T.md, '400', P.text);\nconst t2 = mk('Boton Aceptar', 'Aceptar', T.sm, '700', P.btnText);\nconst t3 = mk('Boton Rechazar', 'Rechazar', T.sm, '700', P.btn);\nboard.insertChild(board.children.length, card);\nboard.insertChild(board.children.length, btnOk);\nboard.insertChild(board.children.length, btnNo);\nboard.insertChild(board.children.length, t1);\nboard.insertChild(board.children.length, t2);\nboard.insertChild(board.children.length, t3);\npenpotUtils.setParentXY(btnOk, 180, 76);\npenpotUtils.setParentXY(btnNo, 340, 76);\npenpotUtils.setParentXY(t1, 24, 24);\npenpotUtils.setParentXY(t2, 200, 76);\npenpotUtils.setParentXY(t3, 360, 76);\nstorage.avisos = storage.avisos || [];\nstorage.avisos.push({ boardId: board.id, cardId: card.id, palette: P });\nreturn { boardId: board.id, cardId: card.id, shapes: board.children.length, palette: P };"
|
|
],
|
|
"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 = { bg:'#FFF8F0', ink:'#1A1410', accent:'#D96A2B', sage:'#7A9E7E' };\nconst T = { sm:14, md:18, lg:28 };\nconst S = { card:320, gap:20, pad:24 };\nconst board = penpot.createBoard();\nboard.name = 'Tarjeta de perfil';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(S.card + S.pad * 2, 440);\nboard.x = 100;\nboard.y = 100;\nconst mk = (name, w, h, fontSize, color, weight, lineH) => {\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 = lineH;\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n return t;\n};\nconst avatar = penpot.createRectangle();\navatar.name = 'Avatar';\navatar.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\navatar.strokes = [{ strokeColor: P.ink, strokeWidth: 3, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\navatar.resize(120, 120);\navatar.borderRadius = 60;\nconst card = penpot.createBoard();\ncard.name = 'Card';\ncard.fills = [{ fillColor: '#FFFFFF', fillOpacity: 1 }];\ncard.resize(S.card, 400);\ncard.borderRadius = 20;\ncard.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 8, blur: 24, spread: 0, color: { color: P.ink, opacity: 0.12 } }];\nboard.appendChild(card);\nconst avatarShape = penpot.createRectangle();\navatarShape.name = 'Avatar placeholder';\navatarShape.fills = [{ fillColor: P.sage, fillOpacity: 1 }];\navatarShape.resize(120, 120);\navatarShape.borderRadius = 60;\ncard.insertChild(card.children.length, avatarShape);\npenpotUtils.setParentXY(avatarShape, S.pad, S.pad);\ncard.insertChild(card.children.length, mk('Cera del Sur', S.lg, 0, T.lg, P.ink, '700', '1.2'));\ncard.insertChild(card.children.length, mk('Sastrera de aromas', S.md, 0, T.md, P.accent, '500', '1.4'));\ncard.insertChild(card.children.length, mk('contacto@ceradelsur.com', S.sm, 0, T.sm, P.ink, '400', '1.5'));\ncard.insertChild(card.children.length, mk('+54 11 4567-8900', S.sm, 0, T.sm, P.ink, '400', '1.5'));\ncard.insertChild(card.children.length, mk('Buenos Aires, Argentina', S.sm, 0, T.sm, P.ink, '400', '1.5'));\ncard.children.forEach((c, i) => {\n if (c.type === 'text') penpotUtils.setParentXY(c, S.pad, S.pad + 160 + i * 44);\n});\nstorage.tarjeta = { boardId: board.id, cardId: card.id };\nreturn { boardId: board.id, cardId: card.id, avatarBorderRadius: avatar.borderRadius };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 header: '#1A1A2E',\n even: '#F8F9FA',\n odd: '#FFFFFF',\n border: '#E0E0E0',\n confirmed: '#E8F5E9',\n pending: '#FFF3E0',\n cancelled: '#FCE4EC',\n enrolled: '#E3F2FD',\n transfer: '#F3E5F5',\n textDark: '#1A1A2E',\n textLight: '#FFFFFF',\n textMuted: '#6B7280'\n};\nconst S = { sm: 12, md: 14, lg: 18, xl: 24 };\nconst T = {\n header: { fontSize: S.lg, fontWeight: '700', lineHeight: '1.3', color: P.textLight, letterSpacing: '0.5' },\n sub: { fontSize: S.md, fontWeight: '400', lineHeight: '1.4', color: P.textLight },\n cell: { fontSize: S.md, fontWeight: '500', lineHeight: '1.4', color: P.textDark },\n state: { fontSize: S.sm, fontWeight: '600', lineHeight: '1.4', color: P.textDark }\n};\nconst states = [\n { label: 'Confirmado', fill: P.confirmed },\n { label: 'Pendiente', fill: P.pending },\n { label: 'Cancelado', fill: P.cancelled },\n { label: 'Inscrito', fill: P.enrolled },\n { label: 'Transferencia', fill: P.transfer }\n];\nconst classes = [\n ['Tango milonga', 'Lunes', '19:00 - 20:30', 0],\n ['Tango básico', 'Miércoles', '18:00 - 19:30', 1],\n ['Tango intermedio', 'Viernes', '20:00 - 21:30', 2],\n ['Tango avanzado', 'Sábado', '10:00 - 12:00', 3],\n ['Tango folklórico', 'Domingo', '11:00 - 13:00', 4]\n];\nconst board = penpot.createBoard();\nboard.name = 'Tabla de clases';\nboard.fills = [{ fillColor: P.odd, fillOpacity: 1 }];\nboard.resize(1180, 420);\nboard.addGridLayout();\nif (!board.grid) throw new Error('No se pudo agregar grid layout');\nboard.grid.rows = 'auto, 70px, 70px, 70px, 70px, 70px';\nboard.grid.columns = '1fr, 1fr, 1fr, 1fr';\nboard.grid.rowGap = 0;\nboard.grid.columnGap = 0;\nboard.horizontalPadding = 0;\nboard.verticalPadding = 0;\nconst mk = (name, w, h, fill, fontSize, weight, color, letterSpacing) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText devolvio null');\n t.characters = name;\n t.fontFamily = 'Inter';\n t.fontSize = fontSize;\n t.fontWeight = weight;\n t.lineHeight = '1.4';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-width';\n if (letterSpacing) t.letterSpacing = letterSpacing;\n t.strokes = [];\n t.borderRadius = 0;\n t.resize(w, h);\n if (fill) {\n t.fills = [{ fillColor: fill, fillOpacity: 1 }];\n }\n return t;\n};\nconst mkCell = (name, row, col, w, h, fill) => {\n const c = penpot.createRectangle();\n c.name = name;\n c.fills = fill ? [{ fillColor: fill, fillOpacity: 1 }] : [];\n c.strokes = [{ strokeColor: P.border, strokeWidth: 1, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n c.borderRadius = 0;\n c.resize(w, h);\n board.insertChild(board.children.length, c);\n if (board.grid) {\n c.layoutCell = { row: row, column: col, rowSpan: 1, columnSpan: 1 };\n }\n return c;\n};\nconst mkState = (label, fill) => {\n const pill = penpot.createRectangle();\n pill.name = 'Estado ' + label;\n pill.fills = [{ fillColor: fill, fillOpacity: 1 }];\n pill.strokes = [];\n pill.borderRadius = 20;\n pill.resize(120, 32);\n board.insertChild(board.children.length, pill);\n if (board.grid) {\n pill.layoutCell = { row: 0, column: 4, rowSpan: 1, columnSpan: 1 };\n }\n const t = mk(label, 120, 32, null, S.sm, '600', P.textDark);\n t.alignHorizontal = 'center';\n board.insertChild(board.children.length, t);\n if (board.grid) {\n t.layoutCell = { row: 0, column: 4, rowSpan: 1, columnSpan: 1 };\n }\n return pill;\n};\nconst header = mkCell('Encabezado', 1, 1, 1180, 70, P.header);\nconst title = mk('Clases de tango', 300, 36, null, S.lg, '700', P.textLight, '0.5');\ntitle.alignHorizontal = 'left';\nconst sub = mk('Horarios y disponibilidad', 300, 30, null, S.md, '400', P.textLight);\nsub.alignHorizontal = 'left';\nheader.insertChild(header.children.length, title);\nheader.insertChild(header.children.length, sub);\nconst cols = ['Clase', 'Día', 'Hora', 'Estado'];\ncols.forEach((c, i) => {\n const cell = mkCell('Columna ' + c, 1, i + 1, 295, 70, P.header);\n const t = mk(c, 295, 70, null, S.md, '600', P.textLight);\n t.alignHorizontal = 'center';\n cell.insertChild(cell.children.length, t);\n});\nclasses.forEach((cls, i) => {\n const row = i + 2;\n const bg = i % 2 === 0 ? P.even : P.odd;\n cls.slice(0, 3).forEach((c, j) => {\n const cell = mkCell(cls[0] + ' ' + c, row, j + 1, 295, 70, bg);\n const t = mk(c, 295, 70, null, S.md, '500', P.textDark);\n t.alignHorizontal = 'center';\n cell.insertChild(cell.children.length, t);\n });\n mkState(states[i].label, states[i].fill);\n});\nconst cells = penpotUtils.findShapes(s => s.name.startsWith('Columna') || s.name.startsWith('Encabezado') || s.name.startsWith('Estado'), board);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst shapes = penpotUtils.findShapes(s => true, board);\nreturn {\n boardId: board.id,\n boardName: board.name,\n boardWidth: board.width,\n boardHeight: board.height,\n gridRows: board.grid ? board.grid.rows : null,\n gridColumns: board.grid ? board.grid.columns : null,\n gridRowGap: board.grid ? board.grid.rowGap : null,\n gridColumnGap: board.grid ? board.grid.columnGap : null,\n headerCells: cells.filter(c => c.name.startsWith('Columna') || c.name.startsWith('Encabezado')).length,\n statePills: cells.filter(c => c.name.startsWith('Estado')).length,\n texts: texts.length,\n totalShapes: shapes.length,\n headerFills: header.fills ? header.fills.map(f => f.fillColor) : [],\n headerStrokes: header.strokes ? header.strokes.map(s => ({ color: s.strokeColor, width: s.strokeWidth })) : [],\n cellStrokes: cells.filter(c => c.name.startsWith('Columna') || c.name.startsWith('Encabezado')).map(c => c.strokes ? c.strokes.map(s => ({ color: s.strokeColor, width: s.strokeWidth })) : []),\n stateFills: cells.filter(c => c.name.startsWith('Estado')).map(c => c.fills ? c.fills.map(f => f.fillColor) : []),\n stateTexts: texts.filter(t => t.fontSize === S.sm).length\n};"
|
|
],
|
|
"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 = { tinta:'#1A1118', papel:'#FDF8F4', vino:'#6B1D2A', dorado:'#C9963B' };\nconst T = { xl:'32', lg:'22', md:'16', sm:'13' };\nconst S = { xs:8, sm:16, md:24, lg:32 };\nconst board = penpot.createBoard();\nboard.name = 'Portada Academia';\nboard.resize(360, 780);\nboard.fills = [{ fillColor: P.papel, 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.md;\n}\nconst foto = penpot.createRectangle();\nfoto.name = 'Foto de portada';\nfoto.resize(360, 420);\nfoto.fills = [{ fillColor: P.vino, fillOpacity: 1, fillImageScaling: 'cover' }];\nfoto.borderRadius = 0;\nboard.appendChild(foto);\nconst img = await penpot.importImage('https://images.unsplash.com/photo-1504609813442-a8924e83f76e?auto=format&fit=crop&w=720&q=80');\nif (img) { foto.fills = [{ fillColor: P.vino, fillOpacity: 1, fillImage: img, fillImageScaling: 'cover' }]; }\nconst titulo = penpot.createText();\ntitulo.name = 'Titulo';\ntitulo.characters = 'Academia de Tango';\ntitulo.fontFamily = 'Playfair Display';\ntitulo.fontSize = T.xl;\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.fills = [{ fillColor: P.tinta, fillOpacity: 1 }];\ntitulo.growType = 'auto-height';\nboard.appendChild(titulo);\nconst sub = penpot.createText();\nsub.name = 'Subtitulo';\nsub.characters = 'Clases para todos los niveles en el centro de la ciudad';\nsub.fontFamily = 'Inter';\nsub.fontSize = T.md;\nsub.fontWeight = '400';\nsub.lineHeight = '1.5';\nsub.fills = [{ fillColor: P.tinta, fillOpacity: 0.7 }];\nsub.growType = 'auto-height';\nboard.appendChild(sub);\nconst boton = penpot.createRectangle();\nboton.name = 'Boton Inscripcion';\nboton.resize(200, 52);\nboton.fills = [{ fillColor: P.dorado, fillOpacity: 1 }];\nboton.borderRadius = 26;\nboton.addFlexLayout();\nif (boton.flex) {\n boton.flex.dir = 'row';\n boton.flex.horizontalPadding = S.md;\n boton.flex.verticalPadding = S.sm;\n boton.flex.alignItems = 'center';\n boton.flex.justifyContent = 'center';\n}\nboard.appendChild(boton);\nconst btnText = penpot.createText();\nbtnText.name = 'Texto boton';\nbtnText.characters = 'Inscribirse';\nbtnText.fontFamily = 'Inter';\nbtnText.fontSize = T.md;\nbtnText.fontWeight = '600';\nbtnText.lineHeight = '1.2';\nbtnText.fills = [{ fillColor: P.papel, fillOpacity: 1 }];\nbtnText.growType = 'auto-height';\nboton.appendChild(btnText);\nstorage.academia = { boardId: board.id, fotoId: foto.id };\nreturn { boardId: board.id, fotoId: foto.id, fotoConImagen: !!foto.fills[0].fillImage, botonConFlex: !!boton.flex };"
|
|
],
|
|
"veto_hits": [
|
|
{
|
|
"patron": "import_image / importImage / createImage / filePath",
|
|
"fragmento": "ndChild(foto); const img = await penpot.importImage('https://images.unsplash.com/photo-1504"
|
|
}
|
|
],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": "ng: 'cover' }]; } const titulo = penpot.createText(); titulo.name = 'Titulo'; titulo.charact"
|
|
}
|
|
],
|
|
"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 fotos = [\n { url: 'https://images.unsplash.com/photo-1509391366360-2e959784a276', titulo: 'Paneles solares en tejado', desc: 'Instalacion fotovoltaica en edificio residencial' },\n { url: 'https://images.unsplash.com/photo-1581092160562-40aa08e78837', titulo: 'Auditoria energetica', desc: 'Diagnostico de consumo en planta industrial' },\n { url: 'https://images.unsplash.com/photo-1508514177221-188b1cf16e9d', titulo: 'Campo fotovoltaico', desc: 'Parque solar de 2 MW en zona rural' },\n { url: 'https://images.unsplash.com/photo-1558618666-fcd25c85f82e', titulo: 'Termostato inteligente', desc: 'Control automatizado de climatizacion' }\n];\n\nconst page = penpotUtils.getPageByName('Galeria') || penpot.createPage('Galeria');\nconst board = penpot.createBoard();\nboard.name = 'Galeria principal';\nboard.fills = [{ fillColor: '#F7F9FC', fillOpacity: 1 }];\nboard.resize(412, 915);\npage.root.insertChild(page.root.children.length, board);\n\nconst cards = fotos.map(f => {\n const card = penpot.createBoard();\n card.name = 'Card: ' + f.titulo;\n card.fills = [{ fillColor: '#FFFFFF', fillOpacity: 1 }];\n card.strokes = [{ strokeColor: '#E2E8F0', strokeWidth: 1, strokeStyle: 'solid', strokeAlignment: 'inner', strokeOpacity: 1 }];\n card.borderRadius = 16;\n card.resize(360, 480);\n card.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: '#0F172A', opacity: 0.12 } }];\n board.insertChild(board.children.length, card);\n\n const img = penpot.createImage();\n img.name = 'Foto: ' + f.titulo;\n img.fills = [{ fillOpacity: 1, fillImage: f.imageData }];\n img.resize(360, 300);\n img.strokes = [];\n img.borderRadius = 16;\n img.clipContent = true;\n card.insertChild(card.children.length, img);\n\n const leyenda = penpot.createText();\n leyenda.name = 'Leyenda: ' + f.titulo;\n leyenda.characters = f.titulo;\n leyenda.fontFamily = 'Inter';\n leyenda.fontSize = '16';\n leyenda.fontWeight = '600';\n leyenda.lineHeight = '1.4';\n leyenda.growType = 'auto-height';\n leyenda.fills = [{ fillColor: '#0F172A', fillOpacity: 1 }];\n leyenda.align = 'center';\n card.insertChild(card.children.length, leyenda);\n\n const desc = penpot.createText();\n desc.name = 'Desc: ' + f.titulo;\n desc.characters = f.desc;\n desc.fontFamily = 'Inter';\n desc.fontSize = '13';\n desc.fontWeight = '400';\n desc.lineHeight = '1.5';\n desc.growType = 'auto-height';\n desc.fills = [{ fillColor: '#64748B', fillOpacity: 1 }];\n desc.align = 'center';\n card.insertChild(card.children.length, desc);\n\n return { card, img, leyenda, desc };\n});\n\ncards.forEach((c, i) => {\n penpotUtils.setParentXY(c.card, 26, 26 + i * 220);\n penpotUtils.setParentXY(c.leyenda, 20, 320);\n penpotUtils.setParentXY(c.desc, 20, 352);\n});\n\nstorage.galeria = { pageId: page.id, boardId: board.id, cards: cards.map(c => c.card.id) };\nreturn { pageId: page.id, boardId: board.id, cards: cards.length, fotos: fotos.map(f => f.imageData ? { id: f.imageData.id, url: f.imageData.url, width: f.imageData.width, height: f.imageData.height } : null) };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": "length, img); const leyenda = penpot.createText(); leyenda.name = 'Leyenda: ' + f.titul"
|
|
}
|
|
],
|
|
"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 = { bg:'#0B0F19', veil:'#000000', ink:'#FDFBF6', paper:'#C4BFAE', gold:'#D4A853', line:'#D4A85326' };\nconst T = { h1:48, h2:20, body:16 };\nconst S = { sm:12, md:24, lg:40, xl:64 };\nconst board = penpot.createBoard();\nboard.name = 'Museo de Maquinas de Escribir — Hero';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(1200, 600);\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.lg;\n board.flex.alignItems = 'start';\n}\nconst mk = (chars, size, color, weight, lineH) => {\n const t = penpot.createText(chars);\n if (!t) throw new Error('createText returned null');\n t.name = chars.split(' ')[0];\n t.fontFamily = 'Playfair Display';\n t.fontSize = size;\n t.fontWeight = weight;\n t.lineHeight = lineH;\n t.growType = 'auto-height';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.align = 'left';\n return t;\n};\nconst titular = mk('Maquinas que escribieron la historia', T.h1, P.ink, '700', '1.15');\nconst sub = mk('Coleccion de 1844 a 1980 — desde la Sholes hasta la Olivetti Lettera 32', T.h2, P.paper, '400', '1.5');\nconst cta = penpot.createRectangle();\ncta.name = 'CTA';\ncta.fills = [{ fillColor: P.gold, fillOpacity: 1 }];\ncta.resize(220, 48);\ncta.borderRadius = 6;\nconst line = penpot.createRectangle();\nline.name = 'Linea';\nline.fills = [{ fillColor: P.line, fillOpacity: 1 }];\nline.resize(120, 2);\nconst stack = [titular, sub, line, cta];\nstack.forEach(s => board.appendChild(s));\nconst veil = penpot.createRectangle();\nveil.name = 'Velo oscuro';\nveil.fills = [{ fillColor: P.veil, fillOpacity: 0.6 }];\nveil.resize(1200, 600);\nboard.insertChild(0, veil);\nconst img = penpotUtils.findShape(s => s.fills && s.fills.some(f => f.fillImage), board);\nstorage.hero = { boardId: board.id, veilId: veil.id };\nreturn { boardId: board.id, veilId: veil.id, veilOpacity: veil.fills[0].fillOpacity,\n titularFontSize: titular.fontSize, ctaWidth: cta.width,\n hasImage: !!img, veilOnTop: board.children[0] === veil };"
|
|
],
|
|
"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 card = penpot.createRectangle();\ncard.name = 'Ficha de estudio';\ncard.resize(1240, 700);\ncard.fills = [{ fillColor: '#0B0F19', fillOpacity: 1, fillImageFit: 'cover' }];\ncard.fills[0].fillImage = { url: 'https://picsum.photos/id/1025/1240/700', width: 1240, height: 700 };\ncard.borderRadius = 24;\nstorage.ficha = { cardId: card.id };\nreturn { cardId: card.id, ancho: card.width, fillImageFit: card.fills[0].fillImageFit };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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": [
|
|
"penpot_api_info"
|
|
],
|
|
"code_payloads": [],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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:'#E8F5E9', card:'#FAFAFA', ink:'#212121', mid:'#757575', brand:'#1B5E20', accent:'#F48FB1' };\nconst T = { sm:13, md:16, lg:22 };\nconst S = { xs:8, sm:16, md:20, lg:28 };\nconst board = penpot.createBoard();\nboard.name = 'Catalogo Orquideas';\nboard.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nboard.resize(900, 600);\nif (!board.flex) board.addFlexLayout();\nconst fl = board.flex;\nfl.dir = 'column';\nfl.horizontalPadding = S.lg;\nfl.verticalPadding = S.lg;\nfl.rowGap = S.md;\nfl.alignItems = 'start';\nconst cols = [280, 280, 280];\nconst gap = 20;\nconst cardH = 200;\nconst mk = (name, w, h, fills) => {\n const s = penpot.createRectangle();\n s.name = name;\n s.fills = fills || [{ fillColor: P.card, fillOpacity: 1 }];\n s.borderRadius = 12;\n s.resize(w, h);\n if (s.shadows) s.shadows = [];\n return s;\n};\nconst mkText = (chars, size, color, weight) => {\n const t = penpot.createText();\n t.characters = chars;\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 header = mk('Header', 900, 48, [{ fillColor: P.brand, fillOpacity: 1 }]);\nheader.insertChild(header.children.length, mkText('Catalogo de Orquideas', T.lg, '#FFFFFF', '600'));\nboard.appendChild(header);\nconst grid = penpot.createBoard();\ngrid.name = 'Grilla';\ngrid.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\ngrid.resize(844, 404);\nif (!grid.flex) grid.addFlexLayout();\nconst gf = grid.flex;\ngf.dir = 'column';\ngf.horizontalPadding = S.sm;\ngf.verticalPadding = S.sm;\ngf.rowGap = gap;\ngf.alignItems = 'start';\nconst names = ['Orquidea Phalaenopsis', 'Orquidea Dendrobium', 'Orquidea Cattleya'];\nconst prices = ['$ 45.000', '$ 38.000', '$ 52.000'];\nconst specs = ['Luz indirecta', 'Riego semanal', 'Luz filtrada'];\nconst rows = [\n [P.accent, P.brand, P.accent],\n [P.brand, P.accent, P.brand],\n [P.accent, P.brand, P.accent]\n];\nconst cards = [];\nfor (let r = 0; r < 3; r++) {\n const row = penpot.createBoard();\n row.name = 'Fila ' + (r + 1);\n row.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\n row.resize(844, cardH);\n if (!row.flex) row.addFlexLayout();\n const rf = row.flex;\n rf.dir = 'row';\n rf.horizontalPadding = S.sm;\n rf.rowGap = gap;\n rf.alignItems = 'start';\n for (let c = 0; c < 3; c++) {\n const idx = r * 3 + c;\n const card = mk('Card ' + (idx + 1), cols[c], cardH, [{ fillColor: P.card, fillOpacity: 1 }]);\n card.strokes = [{ strokeColor: rows[r][c], strokeWidth: 2, strokeStyle: 'solid', strokeOpacity: 1 }];\n card.shadows = [{ style: 'drop-shadow', offsetX: 0, offsetY: 4, blur: 12, spread: 0, color: { color: '#000000', opacity: 0.12 } }];\n card.insertChild(card.children.length, mkText(names[idx], T.md, P.ink, '600'));\n card.insertChild(card.children.length, mkText(specs[idx], T.sm, P.mid));\n card.insertChild(card.children.length, mkText(prices[idx], T.md, rows[r][c], '700'));\n rf.appendChild(card);\n cards.push(card);\n }\n gf.appendChild(row);\n}\nboard.appendChild(grid);\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst cardsChecked = penpotUtils.findShapes(s => s.name.startsWith('Card'), board);\nconst cardHeights = cardsChecked.map(c => c.height);\nreturn { boardId: board.id, boardSize: [board.width, board.height], cardCount: cards.length, cardHeights: cardHeights, uniqueHeights: new Set(cardHeights).size, headerFilled: header.fills.length > 0, structure: structure };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": " color, weight) => { const t = penpot.createText(); t.characters = chars; t.fontFamily"
|
|
}
|
|
],
|
|
"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 = { brand: '#1B3A5C', tinta: '#F4F1EB', acento: '#D94F3D', sombra: '#0E1F33' };\nconst T = { h1: 36, sub: 18, btn: 15 };\nconst S = { sm: 12, md: 24, lg: 40 };\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = fills.map(c => ({ fillColor: c, fillOpacity: 1, fillImageScaling: 'fill' }));\n if (radius) s.borderRadius = radius;\n return s;\n};\nconst txt = (name, chars, size, color, weight) => {\n const t = penpot.createText(chars);\n t.name = name; t.fontFamily = 'Work Sans'; t.fontSize = size; t.fontWeight = weight;\n t.lineHeight = '1.35'; t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-height';\n return t;\n};\nconst board = penpot.createBoard();\nboard.name = 'Columna Museo'; board.resize(480, 600); board.fills = [{ fillColor: P.tinta, fillOpacity: 1, fillImageScaling: 'fill' }];\nboard.addFlexLayout();\nif (board.flex) {\n board.flex.dir = 'column';\n board.flex.horizontalPadding = S.lg; board.flex.verticalPadding = S.lg;\n board.flex.rowGap = S.md; board.flex.alignItems = 'start';\n}\nconst cab = mk('Cabecera', 400, 100, [P.brand], 12);\nconst cuerpo = mk('Cuerpo', 400, 260, [P.tinta], 12);\nconst pie = mk('Pie', 400, 64, [P.acento], 8);\nboard.appendChild(cab); board.appendChild(cuerpo); board.appendChild(pie);\nconst orden = [cab, cuerpo, pie];\norden.forEach((s, i) => {\n if (s.layoutChild) { s.layoutChild.horizontalSizing = 'fill'; s.layoutChild.verticalSizing = i === 1 ? 'fill' : 'auto'; }\n});\ncab.appendChild(txt('Titulo', 'Maquinas de escribir', T.h1, P.tinta, '700'));\ncuerpo.appendChild(txt('Texto', 'Coleccion de maquinas de escribir de los anos 1920 a 1960, con piezas originales y restauradas.', T.sub, P.sombra, '400'));\npie.appendChild(txt('Boton', 'Ver coleccion', T.btn, P.tinta, '700'));\nconst hijos = board.children;\nconst sizing = hijos.map(s => s.layoutChild ? { name: s.name, h: s.layoutChild.horizontalSizing, v: s.layoutChild.verticalSizing } : null);\nreturn { boardId: board.id, flexDir: board.flex ? board.flex.dir : null, rowGap: board.flex ? board.flex.rowGap : null, padding: board.flex ? { top: board.flex.topPadding, right: board.flex.rightPadding, bottom: board.flex.bottomPadding, left: board.flex.leftPadding } : null, hijosCount: hijos.length, orden: orden.map(s => s.name), sizing: sizing.filter(Boolean) };"
|
|
],
|
|
"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 = { bg:'#0B0B0B', ink:'#F2EDE4', accent:'#C21807', line:'#2A1A1A' };\nconst T = { sm:'14', md:'16', lg:'22' };\nconst S = { col:30, row:16 };\nconst mk = (name, size, color, weight) => {\n const t = penpot.createText(name);\n if (!t) throw new Error('createText devolvio null');\n t.fontFamily = 'Playfair Display';\n t.fontSize = size;\n t.fontWeight = weight || '400';\n t.lineHeight = '1.5';\n t.fills = [{ fillColor: color, fillOpacity: 1 }];\n t.growType = 'auto-height';\n return t;\n};\nconst mkLink = (name, text) => {\n const t = mk(name, T.md, P.ink, '400');\n t.characters = text;\n t.fills = [{ fillColor: P.accent, fillOpacity: 1 }];\n return t;\n};\nconst mkCol = (name, titulo, links) => {\n const c = penpot.createBoard();\n c.name = name;\n c.fills = [];\n c.resize(260, 280);\n c.addFlexLayout();\n if (c.flex) {\n c.flex.dir = 'column';\n c.flex.horizontalPadding = 0;\n c.flex.verticalPadding = 0;\n c.flex.rowGap = S.row;\n c.flex.alignItems = 'start';\n }\n c.appendChild(mk(name + '/titulo', T.lg, P.accent, '700'));\n links.forEach(l => c.appendChild(mkLink(name + '/link/' + l, l)));\n return c;\n};\nconst root = penpot.createBoard();\nroot.name = 'Footer Academia';\nroot.fills = [{ fillColor: P.bg, fillOpacity: 1 }];\nroot.resize(1240, 400);\nroot.borderRadius = 0;\nroot.addFlexLayout();\nif (root.flex) {\n root.flex.dir = 'column';\n root.flex.horizontalPadding = 80;\n root.flex.verticalPadding = 48;\n root.flex.rowGap = 32;\n root.flex.alignItems = 'start';\n}\nconst cols = ['Clases', 'Eventos', 'Profesores', 'Contacto'];\nconst datos = [\n ['Clases de inicio', 'Nivel intermedio', 'Tango de salon', 'Clases online'],\n ['Milongas', 'Talleres', 'Festival anual', 'Clases abiertas'],\n ['Profesores titulares', 'Reservas', 'Horarios', 'Becas'],\n ['WhatsApp', 'Email', 'Instagram', 'Ubicacion']\n];\nconst columnas = cols.map((c, i) => mkCol('Columna ' + c, c, datos[i]));\ncolumnas.forEach(c => root.appendChild(c));\nconst legal = mk('Legal', '12', P.ink, '400');\nlegal.characters = 'Academia El Abrazo. Todos los derechos reservados. Buenos Aires, Argentina.';\nlegal.fills = [{ fillColor: P.line, fillOpacity: 1 }];\nroot.appendChild(legal);\nconst bar = penpot.createRectangle();\nbar.name = 'Barra';\nbar.fills = [{ fillColor: P.line, fillOpacity: 1 }];\nbar.resize(1080, 1);\nroot.appendChild(bar);\nstorage.footer = { rootId: root.id, cols: columnas.map(c => c.id) };\nreturn { rootId: root.id, cols: columnas.length, links: datos.flat().length };"
|
|
],
|
|
"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": [
|
|
"penpot_api_info"
|
|
],
|
|
"code_payloads": [],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 Maquinas de Escribir');\nif (!page) return { error: 'page not found' };\nconst board = penpotUtils.findShape(s => s.name === 'Museo', page.root);\nif (!board) return { error: 'board not found' };\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst fills = penpotUtils.analyzeDescendants(board, (root, shape) => {\n if (shape.fills && shape.fills.length > 0) {\n return shape.fills.map(f => f.fillOpacity != null ? { color: f.color, opacity: f.fillOpacity } : { color: f.color });\n }\n return null;\n});\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst colors = new Set();\nfills.forEach(f => f.result.forEach(c => colors.add(c.color)));\nreturn { boardId: board.id, boardName: board.name, boardWidth: board.width, boardHeight: board.height, structure, numDescendants: fills.length, numTexts: texts.length, uniqueFills: Array.from(colors) };"
|
|
],
|
|
"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 board = penpotUtils.findShape(s => s.name === 'Consultora Energia', penpot.root);\nif (!board) return { error: 'board no encontrado' };\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst fills = penpotUtils.findShapes(s => s.fills && s.fills.length > 0, board);\nconst structure = penpotUtils.shapeStructure(board, 3);\nconst hasLayout = board.flex ? 'flex' : (board.grid ? 'grid' : 'ninguno');\nreturn { boardId: board.id, boardSize: { width: board.width, height: board.height }, hasLayout, textCount: texts.length, shapesWithFills: fills.length, 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.name === 'Estudio de Doblaje', penpot.root);\nif (!board) { return { error: 'board no encontrado' }; }\nstorage.doblajeBoard = board.id;\nconst shapes = penpotUtils.findShapes(() => true, board);\nconst fills = shapes.filter(s => s.fills && s.fills.length > 0 && s.fills[0].fillOpacity > 0);\nconst texts = penpotUtils.findShapes(s => s.type === 'text', board);\nconst colores = new Set();\nfills.forEach(f => f.fills.forEach(fill => { if (fill.color) colores.add(fill.color); }));\nconst ratios = texts.map(t => {\n const bg = t.fills && t.fills.length > 0 ? t.fills[0].fillColor : null;\n if (!bg) return { characters: t.characters, fontSize: t.fontSize, ratio: null };\n const lumF = (c, a) => { const cs = c.split('').map(ch => { const v = parseInt(ch, 16); return v <= 9 ? v / 16 : (v - 9) / 10; }).map((c, i) => c <= 0.03928 ? c / 12.92 : Math.pow((c + 0.055) / 1.055, 2.4)); return 0.2126 * cs[0] + 0.7152 * cs[1] + 0.0722 * cs[2]; };\n const L1 = lumF(bg, 1), L2 = lumF('#F4F6FA', 1);\n const ratio = (Math.max(L1, L2) + 0.05) / (Math.min(L1, L2) + 0.05);\n return { characters: t.characters, fontSize: t.fontSize, ratio: Math.round(ratio * 10) / 10 };\n});\nreturn { boardId: board.id, boardName: board.name, shapesCount: shapes.length, fillsCount: fills.length, textsCount: texts.length, coloresUnicos: Array.from(colores), ratios: ratios.filter(r => r.ratio !== null), ratiosBajas: ratios.filter(r => r.ratio !== null && r.ratio < 4.5).length };"
|
|
],
|
|
"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 root = penpot.root;\nconst hero = penpotUtils.findShape(s => s.name.includes('Hero') || s.name.includes('hero'), root);\nif (!hero) return { error: 'no se encontro el hero', shapes: penpotUtils.shapeStructure(root, 2) };\nconst structure = penpotUtils.shapeStructure(hero, 3);\nconst fills = hero.fills ? hero.fills.map(f => ({ type: f.fillType, opacity: f.opacity })) : [];\nconst texts = penpotUtils.findShapes(s => s.type === 'text', hero);\nconst textStyles = texts.map(t => ({ name: t.name, fontSize: t.fontSize, color: t.fills?.[0]?.color }));\nreturn { heroId: hero.id, heroName: hero.name, heroWidth: hero.width, heroHeight: hero.height, 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 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": [
|
|
"execute_code"
|
|
],
|
|
"code_payloads": [
|
|
"const P = { bg: '#FFF8E7', ink: '#1B2A1C', brand: '#0B6E4F', mint: '#D8F3DC', white: '#FFFFFF' };\nconst S = { xs: 8, sm: 16, md: 24, lg: 40, xl: 64 };\nconst T = { h1: 36, h2: 22, body: 16 };\nconst mk = (name, w, h, fills, radius) => {\n const s = penpot.createRectangle();\n s.name = name; s.resize(w, h);\n s.fills = fills ? fills.map(c => ({ fillColor: c, fillOpacity: 1, fillImage: null })) : [];\n if (radius) s.borderRadius = radius;\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.fontSize = size; t.fontWeight = weight;\n t.lineHeight = lineH; t.fills = [{ fillColor: color, fillOpacity: 1, fillImage: null }];\n t.growType = 'auto-width';\n return t;\n};\nconst board = mk('Seccion CTA', 960, 320, [P.brand], 0);\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 = 'center';\n board.flex.justifyContent = 'center';\n}\nconst badge = mk('Badge', 200, 36, [P.mint], 18);\nbadge.layoutChild = { horizontalSizing: 'fit-content', verticalSizing: 'fit-content' };\nconst badgeText = mkText('Badge', 'Auditoria 2025', 13, P.brand, '600', '1.4');\nbadge.insertChild(badge.children.length, badgeText);\nconst titulo = mkText('Titulo', 'Reduzca su consumo energetico en un 40%', T.h1, P.white, '700', '1.2');\nconst desc = mkText('Desc', 'Analizamos su instalacion y proponemos un plan de accion con ROI medible en 18 meses.', T.body, P.mint, '400', '1.6');\nconst cta = mk('CTA', 220, 52, [P.ink], 26);\ncta.layoutChild = { horizontalSizing: 'fit-content', verticalSizing: 'fit-content' };\nconst ctaText = mkText('CTA', 'Solicitar auditoria', 16, P.white, '700', '1.4');\ncta.insertChild(cta.children.length, ctaText);\n[badge, titulo, desc, cta].forEach(c => board.appendChild(c));\nconst fill = board.fills ? board.fills[0] : null;\nif (fill) { fill.fillOpacity = 1; fill.fillImage = null; }\nstorage.cta = { boardId: board.id, P: P, S: S, T: T, mk: mk, mkText: mkText };\nreturn { boardId: board.id, flexDir: board.flex ? board.flex.dir : null,\n children: board.children.map(c => ({ name: c.name, sizing: c.layoutChild ? c.layoutChild.horizontalSizing : null })) };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [
|
|
{
|
|
"patron": "createText sin argumento util",
|
|
"fragmento": " weight, lineH) => { const t = penpot.createText(); t.name = name; t.characters = chars;"
|
|
}
|
|
],
|
|
"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 page = penpotUtils.getPageByName('Consultora');\nif (!page) return { error: 'page no encontrada' };\nconst board = penpotUtils.findShape(s => s.name === 'Hero', page.root);\nif (!board) return { error: 'board no encontrado' };\nconst titulo = penpotUtils.findShape(s => s.name === 'Titulo', board);\nif (!titulo) return { error: 'titulo no encontrado' };\nif (titulo.type !== 'text') return { error: 'no es texto' };\ntitulo.fontFamily = 'Libre Baskerville';\ntitulo.fontSize = '48';\ntitulo.fontWeight = '700';\ntitulo.lineHeight = '1.2';\ntitulo.characters = 'Eficiencia energetica para tu industria';\ntitulo.fills = [{ fillColor: '#0B3D2E', fillOpacity: 1 }];\ntitulo.growType = 'auto-width';\nconst verificacion = {\n fontFamily: titulo.fontFamily,\n fontSize: titulo.fontSize,\n fontWeight: titulo.fontWeight,\n lineHeight: titulo.lineHeight,\n characters: titulo.characters,\n growType: titulo.growType,\n fills: titulo.fills.map(f => f.fillColor)\n};\nreturn { verificado: verificacion };"
|
|
],
|
|
"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 P = { tinta:'#1B2A4A', papel:'#F4F1EB', marco:'#C29B6A', rueda:'#E85D3A', sombra:'#0D1526' };\nconst lib = penpot.library.local;\nconst colores = Object.entries(P).map(([n,c]) => { const col = lib.createColor(); col.name = n; col.color = c; return col; });\nconst rect = (name, w, h, fill) => { const s = penpot.createRectangle(); s.name = name; s.fills = [{ fillColor: fill, fillOpacity: 1 }]; s.resize(w, h); return s; };\nconst b = penpotUtils.findShape(s => s.name === 'Museo Maquinas', penpot.root);\nif (!b) throw new Error('no encontre el board Museo Maquinas');\nconst [c1, c2] = [rect('Tinta', 200, 120, P.tinta), rect('Rueda', 200, 120, P.rueda)];\nb.insertChild(b.children.length, c1);\nb.insertChild(b.children.length, c2);\nreturn { colores: colores.map(c => ({ name: c.name, color: c.color })), rectangulos: [c1.name, c2.name] };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"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 === 'Consultora Energia', penpot.root);\nif (!board) { return { error: 'board no encontrado' }; }\nstorage.consultora = { boardId: board.id };\nreturn { boardId: board.id, boardName: board.name, children: board.children.length };"
|
|
],
|
|
"veto_hits": [],
|
|
"otros_patrones": [],
|
|
"content": ""
|
|
}
|
|
]
|
|
} |