aiplang 2.11.9 → 2.11.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/bin/aiplang.js CHANGED
@@ -5,7 +5,7 @@ const fs = require('fs')
5
5
  const path = require('path')
6
6
  const http = require('http')
7
7
 
8
- const VERSION = '2.11.9'
8
+ const VERSION = '2.11.10'
9
9
  const RUNTIME_DIR = path.join(__dirname, '..', 'runtime')
10
10
  const cmd = process.argv[2]
11
11
  const args = process.argv.slice(3)
@@ -686,7 +686,7 @@ function generateTypes(app, srcFile) {
686
686
  }
687
687
 
688
688
  lines.push(`// ── aiplang version ──────────────────────────────────────────`)
689
- lines.push(`export const AIPLANG_VERSION = '2.11.9'`)
689
+ lines.push(`export const AIPLANG_VERSION = '2.11.10'`)
690
690
  lines.push(``)
691
691
  return lines.join('\n')
692
692
  }
@@ -1049,6 +1049,12 @@ function parseBlock(line) {
1049
1049
  if(line.startsWith('steps{')) { const m=line.match(/^steps\{([^}]*)\}/);if(m){const vm=line.match(/variant:(\S+)/);return{kind:'steps',items:m[1].split('|').map(it=>{const p=it.trim().split('>');return{num:p[0]?.trim(),title:p[1]?.trim(),desc:p[2]?.trim()}}),variant:vm?.[1]}} }
1050
1050
  if(line.startsWith('compare{')) { const m=line.match(/^compare\{([^}]*)\}/);if(m){const vm=line.match(/variant:(\S+)/);return{kind:'compare',rows:m[1].split('|').map(r=>r.trim().split(':').map(c=>c.trim())),variant:vm?.[1]}} }
1051
1051
  if(line.startsWith('video{')) { const m=line.match(/^video\{([^}]*)\}/);if(m){const pts=m[1].split('|');return{kind:'video',url:pts[0]?.trim(),poster:pts[1]?.trim()}} }
1052
+ if(line.startsWith('bento{')) {
1053
+ const m=line.match(/^bento\{([^}]*)\}/)
1054
+ if(m){const vm=line.match(/variant:(\S+)/);const am=line.match(/animate:(\S+)/)
1055
+ const items=m[1].split('|').map(function(it){const p=it.trim().split('>');return{title:p[0]&&p[0].trim(),desc:p[1]&&p[1].trim(),icon:p[2]&&p[2].trim(),link:p[3]&&p[3].trim()}})
1056
+ return{kind:'bento',items:items,variant:vm&&vm[1],animate:am&&am[1]}}
1057
+ }
1052
1058
  if(line.startsWith('faq{')) {
1053
1059
  const body=line.slice(4,line.lastIndexOf('}')).trim()
1054
1060
  const items=body.split('|').map(i=>{const idx=i.indexOf('>');return{q:i.slice(0,idx).trim(),a:i.slice(idx+1).trim()}}).filter(i=>i.q&&i.a)
@@ -1288,6 +1294,7 @@ function renderBlock(b, page) {
1288
1294
  case 'compare': return rCompare(b)
1289
1295
  case 'video': return rVideo(b)
1290
1296
  case 'gallery': return rGallery(b)
1297
+ case 'bento': return rBento(b)
1291
1298
  case 'raw': return (b.html||'')+'\n'
1292
1299
  case 'html': return `<div class="fx-html">${b.content||''}</div>\n`
1293
1300
  case 'spacer': return `<div class="fx-spacer" style="height:${esc(b.height||'2rem')}"></div>\n`
@@ -1417,8 +1424,11 @@ function rHero(b) {
1417
1424
  else if(f.isLink) ctas+=`<a href="${esc(f.path)}" class="fx-cta">${esc(f.label)}</a>`
1418
1425
  else if(!h1) {
1419
1426
  // *texto* entre asteriscos = gradient text
1420
- const gt = f.text.match(/^\*(.*?)\*$/)
1421
- if(gt) h1=`<h1 class="fx-title"><span class="fx-gradient-text">${esc(gt[1])}</span></h1>`
1427
+ const gt = f.text.match(/^\*(.*?)\*(:warm|:ocean|:purple)?$/)
1428
+ if(gt) {
1429
+ const gradClass = gt[2]=== ':warm' ? 'fx-gradient-text-warm' : gt[2]===':ocean' ? 'fx-gradient-text-ocean' : gt[2]===':purple' ? 'fx-gradient-text-purple' : 'fx-gradient-text'
1430
+ h1=`<h1 class="fx-title"><span class="${gradClass}">${esc(gt[1])}</span></h1>`
1431
+ }
1422
1432
  else h1=`<h1 class="fx-title">${esc(f.text)}</h1>`
1423
1433
  }
1424
1434
  else sub+=`<p class="fx-sub">${esc(f.text)}</p>`
@@ -1428,6 +1438,17 @@ function rHero(b) {
1428
1438
  const inlineStyle = b.style && !b.bg ? ` style="${b.style.replace(/,/g,';')}"` : ''
1429
1439
  if (v === 'landing') {
1430
1440
  return `<section class="fx-hero fx-hero-landing"${bgStyle}><div class="fx-hero-grid"></div><div class="fx-hero-inner">${h1}${sub}${ctas}</div></section>
1441
+ `
1442
+ }
1443
+ if (v === 'mesh') {
1444
+ // Lovable-style warm gradient blobs
1445
+ const meshStyle = b.style ? ` style="${b.style.replace(/,/g,';')}"` : ''
1446
+ return `<section class="fx-hero fx-hero-mesh"${bgStyle}${meshStyle}>
1447
+ <div class="fx-mesh-blob"></div>
1448
+ <div class="fx-mesh-blob"></div>
1449
+ <div class="fx-mesh-blob"></div>
1450
+ <div class="fx-hero-inner">${h1}${sub}${ctas}</div>
1451
+ </section>
1431
1452
  `
1432
1453
  }
1433
1454
  if (h1) h1 = heroBadge + h1
@@ -1481,8 +1502,11 @@ function rRow(b) {
1481
1502
  if(fi===1) return`<h3 class="fx-card-title">${esc(f.text)}</h3>`
1482
1503
  return`<p class="fx-card-body">${esc(f.text)}</p>`
1483
1504
  }).join('')
1505
+ let extraClass=''
1506
+ if(b.variant==='glass'||b.variant==='glassmorphism') extraClass=' fx-card-glass'
1507
+ if(b.variant==='shine') extraClass=' fx-card-shine'
1484
1508
  const bgStyle=b.bg?` style="background:${b.bg}"`:(b.variant==='bordered'?` style="border:1px solid var(--accent,#2563eb)22"`:colorStyle)
1485
- return`<div class="fx-card"${bgStyle}>${inner}</div>`
1509
+ return`<div class="fx-card${extraClass}"${bgStyle}>${inner}</div>`
1486
1510
  }).join('')
1487
1511
  const v=b.variant||''
1488
1512
  const wrapStyle=b.style?` style="${b.style.replace(/,/g,';')}"`:''
@@ -1585,6 +1609,28 @@ function rStatsUpgraded(b) {
1585
1609
 
1586
1610
 
1587
1611
 
1612
+
1613
+ // ── rBento: Lovable-style bento grid ─────────────────────────────
1614
+ function rBento(b) {
1615
+ const v = b.variant||'default'
1616
+ const glassClass = v==='glass' ? ' fx-bento-glass' : ''
1617
+ const cards = (b.items||[]).map(function(item, i) {
1618
+ const large = i===0 && (b.items||[]).length>=4
1619
+ const icon = item.icon ? '<div class="fx-bento-icon">'+esc(item.icon)+'</div>' : ''
1620
+ const title = item.title ? '<div class="fx-bento-title">'+esc(item.title)+'</div>' : ''
1621
+ const desc = item.desc ? '<div class="fx-bento-desc">'+esc(item.desc)+'</div>' : ''
1622
+ let link = ''
1623
+ if(item.link) {
1624
+ const lparts = item.link.split(':'); const lhref=lparts[0]; const llabel=lparts.slice(1).join(':')||item.link
1625
+ link = '<a href="'+esc(lhref)+'" class="fx-bento-link">'+esc(llabel)+' →</a>'
1626
+ }
1627
+ const largeClass = large ? ' fx-bento-large' : ''
1628
+ const shineClass = v==='shine' ? ' fx-card-shine' : ''
1629
+ return '<div class="fx-bento-card'+glassClass+largeClass+shineClass+'">'+icon+'<div class="fx-bento-body">'+title+desc+link+'</div></div>'
1630
+ }).join('')
1631
+ const animC = b.animate==='stagger'?' fx-animate-stagger':b.animate?' fx-animate':''
1632
+ return '<div class="fx-bento'+animC+'">'+cards+'</div>\n'
1633
+ }
1588
1634
  // ── rMarquee: faixa de logos/texto em loop infinito ───────────────
1589
1635
  function rMarquee(b) {
1590
1636
  const speed = b.variant === 'fast' ? '15s' : b.variant === 'slow' ? '40s' : '25s'
@@ -2570,6 +2616,70 @@ textarea.fx-input{height:auto;padding:10px 12px;resize:vertical;min-height:80px;
2570
2616
  .fx-footer-note{font-size:12px;color:var(--ds-gray-600);font-family:var(--geist-mono)}
2571
2617
  .fx-footer-text{font-size:13px;color:var(--ds-gray-700)}
2572
2618
  @media(max-width:640px){.fx-footer{padding:24px}.fx-footer-inner{flex-direction:column;align-items:flex-start;gap:12px}}
2619
+
2620
+ /* ══════════════════════════════════════════
2621
+ BENTO GRID — Lovable-style irregular grid
2622
+ ══════════════════════════════════════════ */
2623
+ .fx-bento{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:min-content;gap:12px;padding:0 40px 80px}
2624
+ @media(max-width:900px){.fx-bento{grid-template-columns:repeat(2,1fr)}}
2625
+ @media(max-width:600px){.fx-bento{grid-template-columns:1fr;padding:0 24px 48px}}
2626
+ .fx-bento-card{border-radius:var(--radius-lg);padding:28px;background:var(--ds-background-200);border:1px solid var(--ds-gray-alpha-400);display:flex;flex-direction:column;gap:12px;transition:border-color var(--duration-normal) var(--ease-in-out),transform var(--duration-slow) var(--ease-out);position:relative;overflow:hidden;min-height:160px}
2627
+ .fx-bento-card:hover{border-color:var(--ds-gray-alpha-700);transform:translateY(-2px)}
2628
+ .fx-bento-card::before{content:'';position:absolute;top:0;left:0;right:0;height:1px;background:linear-gradient(90deg,transparent,rgba(var(--aip-accent-rgb),.4),transparent);opacity:0;transition:opacity .3s}
2629
+ .fx-bento-card:hover::before{opacity:1}
2630
+ .fx-bento-large{grid-column:span 2;min-height:220px}
2631
+ @media(max-width:600px){.fx-bento-large{grid-column:span 1}}
2632
+ .fx-bento-glass{background:rgba(255,255,255,.04)!important;backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border:1px solid rgba(255,255,255,.1)!important;box-shadow:0 4px 24px rgba(0,0,0,.2)}
2633
+ .fx-bento-glass:hover{background:rgba(255,255,255,.07)!important;border-color:rgba(255,255,255,.2)!important}
2634
+ .fx-bento-icon{font-size:28px;line-height:1;margin-bottom:4px}
2635
+ .fx-bento-body{display:flex;flex-direction:column;gap:8px;flex:1}
2636
+ .fx-bento-title{font-size:15px;font-weight:700;letter-spacing:-.025em;color:var(--ds-gray-1000);line-height:1.3}
2637
+ .fx-bento-desc{font-size:13px;line-height:1.6;color:var(--ds-gray-900)}
2638
+ .fx-bento-link{font-size:12px;font-weight:600;color:var(--ds-blue-700);margin-top:auto;padding-top:8px}
2639
+
2640
+ /* ══════════════════════════════════════════
2641
+ GLASSMORPHISM — cards e hero
2642
+ ══════════════════════════════════════════ */
2643
+ .fx-card-glass{background:rgba(255,255,255,.04)!important;backdrop-filter:blur(20px) saturate(180%);-webkit-backdrop-filter:blur(20px) saturate(180%);border:1px solid rgba(255,255,255,.1)!important;box-shadow:0 8px 32px rgba(0,0,0,.25)}
2644
+ .fx-card-glass:hover{background:rgba(255,255,255,.07)!important;transform:translateY(-3px);box-shadow:0 12px 48px rgba(0,0,0,.35)}
2645
+
2646
+ /* ══════════════════════════════════════════
2647
+ MESH GRADIENT — Lovable-style blobs animados
2648
+ ══════════════════════════════════════════ */
2649
+ .fx-hero-mesh{overflow:hidden}
2650
+ .fx-mesh-blob{position:absolute;pointer-events:none;border-radius:50%;filter:blur(80px);animation:fx-blob-float 12s ease-in-out infinite}
2651
+ .fx-mesh-blob:nth-child(1){width:600px;height:500px;left:-100px;top:-100px;animation-delay:0s;background:radial-gradient(circle,rgba(var(--mesh-c1,99,102,241),.35),transparent 70%)}
2652
+ .fx-mesh-blob:nth-child(2){width:500px;height:400px;right:-100px;top:100px;animation-delay:-4s;background:radial-gradient(circle,rgba(var(--mesh-c2,236,72,153),.25),transparent 70%)}
2653
+ .fx-mesh-blob:nth-child(3){width:400px;height:400px;left:30%;bottom:-100px;animation-delay:-8s;background:radial-gradient(circle,rgba(var(--mesh-c3,251,146,60),.2),transparent 70%)}
2654
+ @keyframes fx-blob-float{0%,100%{transform:translate(0,0) scale(1)}33%{transform:translate(30px,-30px) scale(1.05)}66%{transform:translate(-20px,20px) scale(.97)}}
2655
+
2656
+ /* ══════════════════════════════════════════
2657
+ ANIMATED GRADIENT BUTTON — Vercel style
2658
+ ══════════════════════════════════════════ */
2659
+ .fx-cta-glow-btn{position:relative;border-radius:var(--radius-md);padding:2px;background:linear-gradient(-90deg,#007cf0,#00dfd8,rgba(var(--aip-accent-rgb),1),#007cf0);background-size:400% 100%;animation:fx-grad-btn 8s ease-in-out infinite;display:inline-block}
2660
+ @keyframes fx-grad-btn{50%{background-position:140% 50%}}
2661
+ .fx-cta-glow-btn-inner{display:block;height:38px;padding:0 20px;line-height:38px;background:var(--ds-background-100);border-radius:calc(var(--radius-md) - 2px);font-size:14px;font-weight:600;color:var(--ds-gray-1000);transition:background var(--duration-normal);white-space:nowrap}
2662
+ .fx-cta-glow-btn:hover .fx-cta-glow-btn-inner{background:var(--ds-background-200)}
2663
+
2664
+ /* ══════════════════════════════════════════
2665
+ GRADIENT TEXT VARIANTS — Lovable paletas
2666
+ ══════════════════════════════════════════ */
2667
+ .fx-gradient-text-warm{background:linear-gradient(135deg,#6366f1,#ec4899,#f97316);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
2668
+ .fx-gradient-text-ocean{background:linear-gradient(135deg,#06b6d4,#3b82f6,#8b5cf6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
2669
+ .fx-gradient-text-purple{background:linear-gradient(135deg,#a855f7,#ec4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
2670
+
2671
+ /* ══════════════════════════════════════════
2672
+ FLOATING ANIMATION — micro-interaction
2673
+ ══════════════════════════════════════════ */
2674
+ .fx-float{animation:fx-floating 4s ease-in-out infinite}
2675
+ .fx-float-2{animation:fx-floating 5s ease-in-out infinite;animation-delay:-1.5s}
2676
+ .fx-float-3{animation:fx-floating 6s ease-in-out infinite;animation-delay:-3s}
2677
+ @keyframes fx-floating{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
2678
+
2679
+ /* Shine sweep nos cards */
2680
+ .fx-card-shine{overflow:hidden}
2681
+ .fx-card-shine::after{content:'';position:absolute;inset:0;background:linear-gradient(135deg,transparent 30%,rgba(255,255,255,.04) 50%,transparent 70%);transform:translateX(-100%);transition:transform .6s var(--ease-out);pointer-events:none}
2682
+ .fx-card-shine:hover::after{transform:translateX(100%)}
2573
2683
  `
2574
2684
 
2575
2685
  // ════════════════════════════════════════════════════════════════
@@ -2613,6 +2723,54 @@ textarea.fx-input{height:auto;padding:10px 12px;resize:vertical;min-height:80px;
2613
2723
  --ds-red-700:#ff0000;
2614
2724
  --ds-green-100:rgba(0,188,112,.08);
2615
2725
  --ds-green-700:#00bc70;
2726
+ }`,
2727
+ warm: `
2728
+ :root{
2729
+ --ds-background-100:#0a0010;--ds-background-100-rgb:10,0,16;--ds-background-200:#110018;
2730
+ --ds-gray-100:#110018;--ds-gray-200:#1a0025;--ds-gray-300:#250032;--ds-gray-400:#2e0040;
2731
+ --ds-gray-600:#9d6cb5;--ds-gray-700:#b87fd4;--ds-gray-800:#c896e8;--ds-gray-900:#d4aef0;--ds-gray-1000:#f0e0ff;
2732
+ --ds-gray-alpha-100:rgba(200,100,255,.04);--ds-gray-alpha-200:rgba(200,100,255,.07);
2733
+ --ds-gray-alpha-400:rgba(200,100,255,.12);--ds-gray-alpha-700:rgba(200,100,255,.22);
2734
+ --ds-blue-700:#a78bfa;--ds-blue-rgb:167,139,250;
2735
+ --ds-green-700:#4ade80;--ds-red-700:#f87171;
2736
+ --aip-accent:#ec4899;--aip-accent-rgb:236,72,153;
2737
+ --mesh-c1:99,102,241;--mesh-c2:236,72,153;--mesh-c3:251,146,60;
2738
+ }`,
2739
+ ocean: `
2740
+ :root{
2741
+ --ds-background-100:#00080f;--ds-background-100-rgb:0,8,15;--ds-background-200:#000d18;
2742
+ --ds-gray-100:#000d18;--ds-gray-200:#001524;--ds-gray-300:#001d30;--ds-gray-400:#00253e;
2743
+ --ds-gray-600:#4a8fab;--ds-gray-700:#5ba8c8;--ds-gray-800:#70bfe0;--ds-gray-900:#8dd0f0;--ds-gray-1000:#d0efff;
2744
+ --ds-gray-alpha-100:rgba(100,200,255,.04);--ds-gray-alpha-200:rgba(100,200,255,.07);
2745
+ --ds-gray-alpha-400:rgba(100,200,255,.12);--ds-gray-alpha-700:rgba(100,200,255,.22);
2746
+ --ds-blue-700:#38bdf8;--ds-blue-rgb:56,189,248;
2747
+ --ds-green-700:#34d399;--ds-red-700:#f87171;
2748
+ --aip-accent:#0ea5e9;--aip-accent-rgb:14,165,233;
2749
+ --mesh-c1:6,182,212;--mesh-c2:59,130,246;--mesh-c3:139,92,246;
2750
+ }`,
2751
+ purple: `
2752
+ :root{
2753
+ --ds-background-100:#05000f;--ds-background-100-rgb:5,0,15;--ds-background-200:#0a0018;
2754
+ --ds-gray-100:#0a0018;--ds-gray-200:#120022;--ds-gray-300:#1a002e;--ds-gray-400:#22003c;
2755
+ --ds-gray-600:#7c5caf;--ds-gray-700:#9b72d8;--ds-gray-800:#b38ef5;--ds-gray-900:#c9a8ff;--ds-gray-1000:#eddeff;
2756
+ --ds-gray-alpha-100:rgba(180,130,255,.04);--ds-gray-alpha-200:rgba(180,130,255,.07);
2757
+ --ds-gray-alpha-400:rgba(180,130,255,.12);--ds-gray-alpha-700:rgba(180,130,255,.22);
2758
+ --ds-blue-700:#a78bfa;--ds-blue-rgb:167,139,250;
2759
+ --ds-green-700:#4ade80;--ds-red-700:#f87171;
2760
+ --aip-accent:#8b5cf6;--aip-accent-rgb:139,92,246;
2761
+ --mesh-c1:139,92,246;--mesh-c2:236,72,153;--mesh-c3:99,102,241;
2762
+ }`,
2763
+ midnight: `
2764
+ :root{
2765
+ --ds-background-100:#010309;--ds-background-100-rgb:1,3,9;--ds-background-200:#030610;
2766
+ --ds-gray-100:#060c1a;--ds-gray-200:#0d1629;--ds-gray-300:#152033;--ds-gray-400:#1c2a3e;
2767
+ --ds-gray-600:#4c6480;--ds-gray-700:#6580a0;--ds-gray-800:#7d98bc;--ds-gray-900:#a0b4d0;--ds-gray-1000:#d8e8f8;
2768
+ --ds-gray-alpha-100:rgba(100,160,220,.04);--ds-gray-alpha-200:rgba(100,160,220,.07);
2769
+ --ds-gray-alpha-400:rgba(100,160,220,.12);--ds-gray-alpha-700:rgba(100,160,220,.22);
2770
+ --ds-blue-700:#60a5fa;--ds-blue-rgb:96,165,250;
2771
+ --ds-green-700:#4ade80;--ds-red-700:#f87171;
2772
+ --aip-accent:#3b82f6;--aip-accent-rgb:59,130,246;
2773
+ --mesh-c1:59,130,246;--mesh-c2:99,102,241;--mesh-c3:14,165,233;
2616
2774
  }`,
2617
2775
  light: `
2618
2776
  :root {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "aiplang",
3
- "version": "2.11.9",
3
+ "version": "2.11.10",
4
4
  "description": "AI-first web language. One .aip file = complete app. Frontend + backend + database + auth.",
5
5
  "keywords": [
6
6
  "aiplang",
package/server/server.js CHANGED
@@ -2432,7 +2432,7 @@ async function startServer(aipFile, port = 3000) {
2432
2432
  })
2433
2433
 
2434
2434
  srv.addRoute('GET', '/health', (req, res) => res.json(200, {
2435
- status:'ok', version:'2.11.9',
2435
+ status:'ok', version:'2.11.10',
2436
2436
  models: app.models.map(m=>m.name),
2437
2437
  routes: app.apis.length, pages: app.pages.length,
2438
2438
  admin: app.admin?.prefix || null,