aiplang 2.11.9 → 2.11.11
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 +204 -8
- package/package.json +1 -1
- package/server/server.js +32 -2
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.
|
|
8
|
+
const VERSION = '2.11.11'
|
|
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.
|
|
689
|
+
lines.push(`export const AIPLANG_VERSION = '2.11.11'`)
|
|
690
690
|
lines.push(``)
|
|
691
691
|
return lines.join('\n')
|
|
692
692
|
}
|
|
@@ -936,7 +936,18 @@ console.error(`\n ✗ Unknown command: ${cmd}\n Run aiplang --help\n`)
|
|
|
936
936
|
process.exit(1)
|
|
937
937
|
|
|
938
938
|
function parsePages(src) {
|
|
939
|
-
|
|
939
|
+
// Extrair variáveis globais ~var name = value
|
|
940
|
+
const gVars = {}
|
|
941
|
+
src.split('\n').forEach(line => {
|
|
942
|
+
const vm = line.trim().match(/^~var\s+(\w+)\s*=\s*(.+)$/)
|
|
943
|
+
if (vm) gVars[vm[1].trim()] = vm[2].trim().replace(/^["']|["']$/g,'')
|
|
944
|
+
})
|
|
945
|
+
// Expandir $var em todo o src
|
|
946
|
+
function expand(s) {
|
|
947
|
+
if (!Object.keys(gVars).length) return s
|
|
948
|
+
return s.replace(/\$(\w+)/g, (m,k) => gVars[k]!==undefined ? gVars[k] : m)
|
|
949
|
+
}
|
|
950
|
+
return src.split(/\n---\n/).map(s=>parsePage(expand(s.trim()))).filter(Boolean)
|
|
940
951
|
}
|
|
941
952
|
|
|
942
953
|
function parsePage(src) {
|
|
@@ -944,6 +955,7 @@ function parsePage(src) {
|
|
|
944
955
|
if(!lines.length) return null
|
|
945
956
|
const p={id:'page',theme:'dark',route:'/',customTheme:null,themeVars:null,state:{},queries:[],blocks:[]}
|
|
946
957
|
for(const line of lines) {
|
|
958
|
+
if(line.startsWith('~var ')) continue // já processado globalmente
|
|
947
959
|
if(line.startsWith('%')) {
|
|
948
960
|
const pts=line.slice(1).trim().split(/\s+/)
|
|
949
961
|
p.id=pts[0]||'page'; p.route=pts[2]||'/'
|
|
@@ -1049,6 +1061,12 @@ function parseBlock(line) {
|
|
|
1049
1061
|
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
1062
|
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
1063
|
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()}} }
|
|
1064
|
+
if(line.startsWith('bento{')) {
|
|
1065
|
+
const m=line.match(/^bento\{([^}]*)\}/)
|
|
1066
|
+
if(m){const vm=line.match(/variant:(\S+)/);const am=line.match(/animate:(\S+)/)
|
|
1067
|
+
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()}})
|
|
1068
|
+
return{kind:'bento',items:items,variant:vm&&vm[1],animate:am&&am[1]}}
|
|
1069
|
+
}
|
|
1052
1070
|
if(line.startsWith('faq{')) {
|
|
1053
1071
|
const body=line.slice(4,line.lastIndexOf('}')).trim()
|
|
1054
1072
|
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)
|
|
@@ -1200,6 +1218,18 @@ function applyMods(html, b) {
|
|
|
1200
1218
|
|
|
1201
1219
|
function renderPage(page, allPages) {
|
|
1202
1220
|
const needsJS=page.queries.length>0||page.blocks.some(b=>['table','list','form','if','btn','select','faq'].includes(b.kind))
|
|
1221
|
+
// Expandir variáveis $var no conteúdo da página
|
|
1222
|
+
const _vars = (page.appVars||{})
|
|
1223
|
+
function expandVars(s) {
|
|
1224
|
+
if (!s || typeof s !== 'string' || !Object.keys(_vars).length) return s
|
|
1225
|
+
return s.replace(/\$(\w+)/g, (m, k) => _vars[k] !== undefined ? _vars[k] : m)
|
|
1226
|
+
}
|
|
1227
|
+
// Pré-processar rawLine de cada bloco
|
|
1228
|
+
page.blocks.forEach(b => {
|
|
1229
|
+
if (b.rawLine) b.rawLine = expandVars(b.rawLine)
|
|
1230
|
+
if (b.title) b.title = expandVars(b.title)
|
|
1231
|
+
if (b.sub) b.sub = expandVars(b.sub)
|
|
1232
|
+
})
|
|
1203
1233
|
const body=page.blocks.map(b=>{try{return applyMods(renderBlock(b,page),b)}catch(e){console.error('[aiplang] Block render error:',b.kind,e.message);return ''}}).join('')
|
|
1204
1234
|
|
|
1205
1235
|
const tableBlocks = page.blocks.filter(b => b.kind === 'table' && b.binding && b.cols && b.cols.length)
|
|
@@ -1254,8 +1284,12 @@ function renderPage(page, allPages) {
|
|
|
1254
1284
|
<meta name="description" content="${esc((()=>{const h=page.blocks.find(b=>b.kind==='hero');if(!h)return '';const m=h.rawLine&&h.rawLine.match(/\{([^}]+)\}/);if(!m)return '';const p=m[1].split('>');const s=p[0].split('|')[1];return s?s.trim():''})())}">
|
|
1255
1285
|
<meta property="og:title" content="${_title}">
|
|
1256
1286
|
<meta property="og:type" content="website">
|
|
1287
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
1288
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
1257
1289
|
<meta property="og:type" content="website">
|
|
1258
|
-
<
|
|
1290
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
1291
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
1292
|
+
<style>${minCSS(css(page.theme)+customVars+themeVarCSS)}</style>
|
|
1259
1293
|
</head>
|
|
1260
1294
|
<body>
|
|
1261
1295
|
${body}${hydrate}
|
|
@@ -1288,6 +1322,7 @@ function renderBlock(b, page) {
|
|
|
1288
1322
|
case 'compare': return rCompare(b)
|
|
1289
1323
|
case 'video': return rVideo(b)
|
|
1290
1324
|
case 'gallery': return rGallery(b)
|
|
1325
|
+
case 'bento': return rBento(b)
|
|
1291
1326
|
case 'raw': return (b.html||'')+'\n'
|
|
1292
1327
|
case 'html': return `<div class="fx-html">${b.content||''}</div>\n`
|
|
1293
1328
|
case 'spacer': return `<div class="fx-spacer" style="height:${esc(b.height||'2rem')}"></div>\n`
|
|
@@ -1417,8 +1452,11 @@ function rHero(b) {
|
|
|
1417
1452
|
else if(f.isLink) ctas+=`<a href="${esc(f.path)}" class="fx-cta">${esc(f.label)}</a>`
|
|
1418
1453
|
else if(!h1) {
|
|
1419
1454
|
// *texto* entre asteriscos = gradient text
|
|
1420
|
-
const gt = f.text.match(/^\*(.*?)
|
|
1421
|
-
if(gt)
|
|
1455
|
+
const gt = f.text.match(/^\*(.*?)\*(:warm|:ocean|:purple)?$/)
|
|
1456
|
+
if(gt) {
|
|
1457
|
+
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'
|
|
1458
|
+
h1=`<h1 class="fx-title"><span class="${gradClass}">${esc(gt[1])}</span></h1>`
|
|
1459
|
+
}
|
|
1422
1460
|
else h1=`<h1 class="fx-title">${esc(f.text)}</h1>`
|
|
1423
1461
|
}
|
|
1424
1462
|
else sub+=`<p class="fx-sub">${esc(f.text)}</p>`
|
|
@@ -1428,6 +1466,17 @@ function rHero(b) {
|
|
|
1428
1466
|
const inlineStyle = b.style && !b.bg ? ` style="${b.style.replace(/,/g,';')}"` : ''
|
|
1429
1467
|
if (v === 'landing') {
|
|
1430
1468
|
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>
|
|
1469
|
+
`
|
|
1470
|
+
}
|
|
1471
|
+
if (v === 'mesh') {
|
|
1472
|
+
// Lovable-style warm gradient blobs
|
|
1473
|
+
const meshStyle = b.style ? ` style="${b.style.replace(/,/g,';')}"` : ''
|
|
1474
|
+
return `<section class="fx-hero fx-hero-mesh"${bgStyle}${meshStyle}>
|
|
1475
|
+
<div class="fx-mesh-blob"></div>
|
|
1476
|
+
<div class="fx-mesh-blob"></div>
|
|
1477
|
+
<div class="fx-mesh-blob"></div>
|
|
1478
|
+
<div class="fx-hero-inner">${h1}${sub}${ctas}</div>
|
|
1479
|
+
</section>
|
|
1431
1480
|
`
|
|
1432
1481
|
}
|
|
1433
1482
|
if (h1) h1 = heroBadge + h1
|
|
@@ -1481,8 +1530,11 @@ function rRow(b) {
|
|
|
1481
1530
|
if(fi===1) return`<h3 class="fx-card-title">${esc(f.text)}</h3>`
|
|
1482
1531
|
return`<p class="fx-card-body">${esc(f.text)}</p>`
|
|
1483
1532
|
}).join('')
|
|
1533
|
+
let extraClass=''
|
|
1534
|
+
if(b.variant==='glass'||b.variant==='glassmorphism') extraClass=' fx-card-glass'
|
|
1535
|
+
if(b.variant==='shine') extraClass=' fx-card-shine'
|
|
1484
1536
|
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>`
|
|
1537
|
+
return`<div class="fx-card${extraClass}"${bgStyle}>${inner}</div>`
|
|
1486
1538
|
}).join('')
|
|
1487
1539
|
const v=b.variant||''
|
|
1488
1540
|
const wrapStyle=b.style?` style="${b.style.replace(/,/g,';')}"`:''
|
|
@@ -1585,6 +1637,28 @@ function rStatsUpgraded(b) {
|
|
|
1585
1637
|
|
|
1586
1638
|
|
|
1587
1639
|
|
|
1640
|
+
|
|
1641
|
+
// ── rBento: Lovable-style bento grid ─────────────────────────────
|
|
1642
|
+
function rBento(b) {
|
|
1643
|
+
const v = b.variant||'default'
|
|
1644
|
+
const glassClass = v==='glass' ? ' fx-bento-glass' : ''
|
|
1645
|
+
const cards = (b.items||[]).map(function(item, i) {
|
|
1646
|
+
const large = i===0 && (b.items||[]).length>=4
|
|
1647
|
+
const icon = item.icon ? '<div class="fx-bento-icon">'+esc(item.icon)+'</div>' : ''
|
|
1648
|
+
const title = item.title ? '<div class="fx-bento-title">'+esc(item.title)+'</div>' : ''
|
|
1649
|
+
const desc = item.desc ? '<div class="fx-bento-desc">'+esc(item.desc)+'</div>' : ''
|
|
1650
|
+
let link = ''
|
|
1651
|
+
if(item.link) {
|
|
1652
|
+
const lparts = item.link.split(':'); const lhref=lparts[0]; const llabel=lparts.slice(1).join(':')||item.link
|
|
1653
|
+
link = '<a href="'+esc(lhref)+'" class="fx-bento-link">'+esc(llabel)+' →</a>'
|
|
1654
|
+
}
|
|
1655
|
+
const largeClass = large ? ' fx-bento-large' : ''
|
|
1656
|
+
const shineClass = v==='shine' ? ' fx-card-shine' : ''
|
|
1657
|
+
return '<div class="fx-bento-card'+glassClass+largeClass+shineClass+'">'+icon+'<div class="fx-bento-body">'+title+desc+link+'</div></div>'
|
|
1658
|
+
}).join('')
|
|
1659
|
+
const animC = b.animate==='stagger'?' fx-animate-stagger':b.animate?' fx-animate':''
|
|
1660
|
+
return '<div class="fx-bento'+animC+'">'+cards+'</div>\n'
|
|
1661
|
+
}
|
|
1588
1662
|
// ── rMarquee: faixa de logos/texto em loop infinito ───────────────
|
|
1589
1663
|
function rMarquee(b) {
|
|
1590
1664
|
const speed = b.variant === 'fast' ? '15s' : b.variant === 'slow' ? '40s' : '25s'
|
|
@@ -1816,6 +1890,16 @@ function genThemeVarCSS(t) {
|
|
|
1816
1890
|
}
|
|
1817
1891
|
|
|
1818
1892
|
|
|
1893
|
+
function minCSS(s) {
|
|
1894
|
+
if(!s) return ''
|
|
1895
|
+
return s
|
|
1896
|
+
.replace(/\/\*[\s\S]*?\*\//g,'')
|
|
1897
|
+
.replace(/\s*([{};:,>~+|])\s*/g,'$1')
|
|
1898
|
+
.replace(/\s+/g,' ')
|
|
1899
|
+
.replace(/;}/g,'}')
|
|
1900
|
+
.trim()
|
|
1901
|
+
}
|
|
1902
|
+
|
|
1819
1903
|
function css(theme) {
|
|
1820
1904
|
// ════════════════════════════════════════════════════════════════
|
|
1821
1905
|
// GEIST DESIGN SYSTEM — Vercel's design language for aiplang
|
|
@@ -1823,7 +1907,7 @@ function css(theme) {
|
|
|
1823
1907
|
// Fonts: Geist Sans + Geist Mono (Google Fonts)
|
|
1824
1908
|
// ════════════════════════════════════════════════════════════════
|
|
1825
1909
|
const base = `
|
|
1826
|
-
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700;800;900&family=Geist+Mono:wght@400;500;700&display=swap');
|
|
1910
|
+
@import url('https://fonts.googleapis.com/css2?family=Geist:wght@300;400;500;600;700;800;900&family=Geist+Mono:wght@400;500;700&display=swap&display=swap');
|
|
1827
1911
|
|
|
1828
1912
|
:root {
|
|
1829
1913
|
--geist-font:'Geist','Geist Sans',-apple-system,BlinkMacSystemFont,system-ui,sans-serif;
|
|
@@ -2570,6 +2654,70 @@ textarea.fx-input{height:auto;padding:10px 12px;resize:vertical;min-height:80px;
|
|
|
2570
2654
|
.fx-footer-note{font-size:12px;color:var(--ds-gray-600);font-family:var(--geist-mono)}
|
|
2571
2655
|
.fx-footer-text{font-size:13px;color:var(--ds-gray-700)}
|
|
2572
2656
|
@media(max-width:640px){.fx-footer{padding:24px}.fx-footer-inner{flex-direction:column;align-items:flex-start;gap:12px}}
|
|
2657
|
+
|
|
2658
|
+
/* ══════════════════════════════════════════
|
|
2659
|
+
BENTO GRID — Lovable-style irregular grid
|
|
2660
|
+
══════════════════════════════════════════ */
|
|
2661
|
+
.fx-bento{display:grid;grid-template-columns:repeat(3,1fr);grid-auto-rows:min-content;gap:12px;padding:0 40px 80px}
|
|
2662
|
+
@media(max-width:900px){.fx-bento{grid-template-columns:repeat(2,1fr)}}
|
|
2663
|
+
@media(max-width:600px){.fx-bento{grid-template-columns:1fr;padding:0 24px 48px}}
|
|
2664
|
+
.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}
|
|
2665
|
+
.fx-bento-card:hover{border-color:var(--ds-gray-alpha-700);transform:translateY(-2px)}
|
|
2666
|
+
.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}
|
|
2667
|
+
.fx-bento-card:hover::before{opacity:1}
|
|
2668
|
+
.fx-bento-large{grid-column:span 2;min-height:220px}
|
|
2669
|
+
@media(max-width:600px){.fx-bento-large{grid-column:span 1}}
|
|
2670
|
+
.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)}
|
|
2671
|
+
.fx-bento-glass:hover{background:rgba(255,255,255,.07)!important;border-color:rgba(255,255,255,.2)!important}
|
|
2672
|
+
.fx-bento-icon{font-size:28px;line-height:1;margin-bottom:4px}
|
|
2673
|
+
.fx-bento-body{display:flex;flex-direction:column;gap:8px;flex:1}
|
|
2674
|
+
.fx-bento-title{font-size:15px;font-weight:700;letter-spacing:-.025em;color:var(--ds-gray-1000);line-height:1.3}
|
|
2675
|
+
.fx-bento-desc{font-size:13px;line-height:1.6;color:var(--ds-gray-900)}
|
|
2676
|
+
.fx-bento-link{font-size:12px;font-weight:600;color:var(--ds-blue-700);margin-top:auto;padding-top:8px}
|
|
2677
|
+
|
|
2678
|
+
/* ══════════════════════════════════════════
|
|
2679
|
+
GLASSMORPHISM — cards e hero
|
|
2680
|
+
══════════════════════════════════════════ */
|
|
2681
|
+
.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)}
|
|
2682
|
+
.fx-card-glass:hover{background:rgba(255,255,255,.07)!important;transform:translateY(-3px);box-shadow:0 12px 48px rgba(0,0,0,.35)}
|
|
2683
|
+
|
|
2684
|
+
/* ══════════════════════════════════════════
|
|
2685
|
+
MESH GRADIENT — Lovable-style blobs animados
|
|
2686
|
+
══════════════════════════════════════════ */
|
|
2687
|
+
.fx-hero-mesh{overflow:hidden}
|
|
2688
|
+
.fx-mesh-blob{position:absolute;pointer-events:none;border-radius:50%;filter:blur(80px);animation:fx-blob-float 12s ease-in-out infinite}
|
|
2689
|
+
.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%)}
|
|
2690
|
+
.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%)}
|
|
2691
|
+
.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%)}
|
|
2692
|
+
@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)}}
|
|
2693
|
+
|
|
2694
|
+
/* ══════════════════════════════════════════
|
|
2695
|
+
ANIMATED GRADIENT BUTTON — Vercel style
|
|
2696
|
+
══════════════════════════════════════════ */
|
|
2697
|
+
.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}
|
|
2698
|
+
@keyframes fx-grad-btn{50%{background-position:140% 50%}}
|
|
2699
|
+
.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}
|
|
2700
|
+
.fx-cta-glow-btn:hover .fx-cta-glow-btn-inner{background:var(--ds-background-200)}
|
|
2701
|
+
|
|
2702
|
+
/* ══════════════════════════════════════════
|
|
2703
|
+
GRADIENT TEXT VARIANTS — Lovable paletas
|
|
2704
|
+
══════════════════════════════════════════ */
|
|
2705
|
+
.fx-gradient-text-warm{background:linear-gradient(135deg,#6366f1,#ec4899,#f97316);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
2706
|
+
.fx-gradient-text-ocean{background:linear-gradient(135deg,#06b6d4,#3b82f6,#8b5cf6);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
2707
|
+
.fx-gradient-text-purple{background:linear-gradient(135deg,#a855f7,#ec4899);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
2708
|
+
|
|
2709
|
+
/* ══════════════════════════════════════════
|
|
2710
|
+
FLOATING ANIMATION — micro-interaction
|
|
2711
|
+
══════════════════════════════════════════ */
|
|
2712
|
+
.fx-float{animation:fx-floating 4s ease-in-out infinite}
|
|
2713
|
+
.fx-float-2{animation:fx-floating 5s ease-in-out infinite;animation-delay:-1.5s}
|
|
2714
|
+
.fx-float-3{animation:fx-floating 6s ease-in-out infinite;animation-delay:-3s}
|
|
2715
|
+
@keyframes fx-floating{0%,100%{transform:translateY(0)}50%{transform:translateY(-8px)}}
|
|
2716
|
+
|
|
2717
|
+
/* Shine sweep nos cards */
|
|
2718
|
+
.fx-card-shine{overflow:hidden}
|
|
2719
|
+
.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}
|
|
2720
|
+
.fx-card-shine:hover::after{transform:translateX(100%)}
|
|
2573
2721
|
`
|
|
2574
2722
|
|
|
2575
2723
|
// ════════════════════════════════════════════════════════════════
|
|
@@ -2613,6 +2761,54 @@ textarea.fx-input{height:auto;padding:10px 12px;resize:vertical;min-height:80px;
|
|
|
2613
2761
|
--ds-red-700:#ff0000;
|
|
2614
2762
|
--ds-green-100:rgba(0,188,112,.08);
|
|
2615
2763
|
--ds-green-700:#00bc70;
|
|
2764
|
+
}`,
|
|
2765
|
+
warm: `
|
|
2766
|
+
:root{
|
|
2767
|
+
--ds-background-100:#0a0010;--ds-background-100-rgb:10,0,16;--ds-background-200:#110018;
|
|
2768
|
+
--ds-gray-100:#110018;--ds-gray-200:#1a0025;--ds-gray-300:#250032;--ds-gray-400:#2e0040;
|
|
2769
|
+
--ds-gray-600:#9d6cb5;--ds-gray-700:#b87fd4;--ds-gray-800:#c896e8;--ds-gray-900:#d4aef0;--ds-gray-1000:#f0e0ff;
|
|
2770
|
+
--ds-gray-alpha-100:rgba(200,100,255,.04);--ds-gray-alpha-200:rgba(200,100,255,.07);
|
|
2771
|
+
--ds-gray-alpha-400:rgba(200,100,255,.12);--ds-gray-alpha-700:rgba(200,100,255,.22);
|
|
2772
|
+
--ds-blue-700:#a78bfa;--ds-blue-rgb:167,139,250;
|
|
2773
|
+
--ds-green-700:#4ade80;--ds-red-700:#f87171;
|
|
2774
|
+
--aip-accent:#ec4899;--aip-accent-rgb:236,72,153;
|
|
2775
|
+
--mesh-c1:99,102,241;--mesh-c2:236,72,153;--mesh-c3:251,146,60;
|
|
2776
|
+
}`,
|
|
2777
|
+
ocean: `
|
|
2778
|
+
:root{
|
|
2779
|
+
--ds-background-100:#00080f;--ds-background-100-rgb:0,8,15;--ds-background-200:#000d18;
|
|
2780
|
+
--ds-gray-100:#000d18;--ds-gray-200:#001524;--ds-gray-300:#001d30;--ds-gray-400:#00253e;
|
|
2781
|
+
--ds-gray-600:#4a8fab;--ds-gray-700:#5ba8c8;--ds-gray-800:#70bfe0;--ds-gray-900:#8dd0f0;--ds-gray-1000:#d0efff;
|
|
2782
|
+
--ds-gray-alpha-100:rgba(100,200,255,.04);--ds-gray-alpha-200:rgba(100,200,255,.07);
|
|
2783
|
+
--ds-gray-alpha-400:rgba(100,200,255,.12);--ds-gray-alpha-700:rgba(100,200,255,.22);
|
|
2784
|
+
--ds-blue-700:#38bdf8;--ds-blue-rgb:56,189,248;
|
|
2785
|
+
--ds-green-700:#34d399;--ds-red-700:#f87171;
|
|
2786
|
+
--aip-accent:#0ea5e9;--aip-accent-rgb:14,165,233;
|
|
2787
|
+
--mesh-c1:6,182,212;--mesh-c2:59,130,246;--mesh-c3:139,92,246;
|
|
2788
|
+
}`,
|
|
2789
|
+
purple: `
|
|
2790
|
+
:root{
|
|
2791
|
+
--ds-background-100:#05000f;--ds-background-100-rgb:5,0,15;--ds-background-200:#0a0018;
|
|
2792
|
+
--ds-gray-100:#0a0018;--ds-gray-200:#120022;--ds-gray-300:#1a002e;--ds-gray-400:#22003c;
|
|
2793
|
+
--ds-gray-600:#7c5caf;--ds-gray-700:#9b72d8;--ds-gray-800:#b38ef5;--ds-gray-900:#c9a8ff;--ds-gray-1000:#eddeff;
|
|
2794
|
+
--ds-gray-alpha-100:rgba(180,130,255,.04);--ds-gray-alpha-200:rgba(180,130,255,.07);
|
|
2795
|
+
--ds-gray-alpha-400:rgba(180,130,255,.12);--ds-gray-alpha-700:rgba(180,130,255,.22);
|
|
2796
|
+
--ds-blue-700:#a78bfa;--ds-blue-rgb:167,139,250;
|
|
2797
|
+
--ds-green-700:#4ade80;--ds-red-700:#f87171;
|
|
2798
|
+
--aip-accent:#8b5cf6;--aip-accent-rgb:139,92,246;
|
|
2799
|
+
--mesh-c1:139,92,246;--mesh-c2:236,72,153;--mesh-c3:99,102,241;
|
|
2800
|
+
}`,
|
|
2801
|
+
midnight: `
|
|
2802
|
+
:root{
|
|
2803
|
+
--ds-background-100:#010309;--ds-background-100-rgb:1,3,9;--ds-background-200:#030610;
|
|
2804
|
+
--ds-gray-100:#060c1a;--ds-gray-200:#0d1629;--ds-gray-300:#152033;--ds-gray-400:#1c2a3e;
|
|
2805
|
+
--ds-gray-600:#4c6480;--ds-gray-700:#6580a0;--ds-gray-800:#7d98bc;--ds-gray-900:#a0b4d0;--ds-gray-1000:#d8e8f8;
|
|
2806
|
+
--ds-gray-alpha-100:rgba(100,160,220,.04);--ds-gray-alpha-200:rgba(100,160,220,.07);
|
|
2807
|
+
--ds-gray-alpha-400:rgba(100,160,220,.12);--ds-gray-alpha-700:rgba(100,160,220,.22);
|
|
2808
|
+
--ds-blue-700:#60a5fa;--ds-blue-rgb:96,165,250;
|
|
2809
|
+
--ds-green-700:#4ade80;--ds-red-700:#f87171;
|
|
2810
|
+
--aip-accent:#3b82f6;--aip-accent-rgb:59,130,246;
|
|
2811
|
+
--mesh-c1:59,130,246;--mesh-c2:99,102,241;--mesh-c3:14,165,233;
|
|
2616
2812
|
}`,
|
|
2617
2813
|
light: `
|
|
2618
2814
|
:root {
|
package/package.json
CHANGED
package/server/server.js
CHANGED
|
@@ -1806,6 +1806,25 @@ class AiplangServer {
|
|
|
1806
1806
|
if (this._requestLogger) this._requestLogger(req, Date.now() - _start)
|
|
1807
1807
|
return
|
|
1808
1808
|
}
|
|
1809
|
+
// Servir arquivos estaticos /public/*
|
|
1810
|
+
const _sext = req.path && req.path.match(/\.([a-zA-Z0-9]+)$/)
|
|
1811
|
+
if (_sext && req.method === 'GET') {
|
|
1812
|
+
const _appDir = server && server._appDir ? server._appDir : process.cwd()
|
|
1813
|
+
const _cands = [
|
|
1814
|
+
require('path').join(_appDir,'public',req.path),
|
|
1815
|
+
require('path').join(process.cwd(),'public',req.path),
|
|
1816
|
+
require('path').join(_appDir,req.path.slice(1)),
|
|
1817
|
+
]
|
|
1818
|
+
for (const _fp of _cands) {
|
|
1819
|
+
if (require('fs').existsSync(_fp)) {
|
|
1820
|
+
const _mime = getMimeType(_fp)
|
|
1821
|
+
const _buf = require('fs').readFileSync(_fp)
|
|
1822
|
+
res.setHeader('Cache-Control','public,max-age=86400')
|
|
1823
|
+
res.writeHead(200,{'Content-Type':_mime,'Content-Length':_buf.length})
|
|
1824
|
+
return res.end(_buf)
|
|
1825
|
+
}
|
|
1826
|
+
}
|
|
1827
|
+
}
|
|
1809
1828
|
const _404b='{"error":"Not found"}'; res.writeHead(404,{'Content-Type':'application/json','Content-Length':18}); res.end(_404b)
|
|
1810
1829
|
}
|
|
1811
1830
|
|
|
@@ -2137,6 +2156,17 @@ function getMime(filename) {
|
|
|
2137
2156
|
return types[ext] || 'application/octet-stream'
|
|
2138
2157
|
}
|
|
2139
2158
|
|
|
2159
|
+
// Cache-Control helper
|
|
2160
|
+
function _cacheHeaders(res, type, maxAge=0) {
|
|
2161
|
+
if (type && (type.startsWith('image/') || type.startsWith('font/') || type.endsWith('javascript') || type.endsWith('css'))) {
|
|
2162
|
+
res.setHeader('Cache-Control', `public, max-age=${maxAge||86400}, immutable`)
|
|
2163
|
+
} else if (type && type.includes('html')) {
|
|
2164
|
+
res.setHeader('Cache-Control', 'no-cache')
|
|
2165
|
+
} else {
|
|
2166
|
+
res.setHeader('Cache-Control', 'public, max-age=3600')
|
|
2167
|
+
}
|
|
2168
|
+
}
|
|
2169
|
+
|
|
2140
2170
|
// ═══════════════════════════════════════════════════════════════════
|
|
2141
2171
|
// PLUGIN SYSTEM — ~plugin ./my-plugin.js | ~use rate-limit max=100
|
|
2142
2172
|
// ═══════════════════════════════════════════════════════════════════
|
|
@@ -2387,7 +2417,7 @@ async function startServer(aipFile, port = 3000) {
|
|
|
2387
2417
|
// Static assets
|
|
2388
2418
|
srv.addRoute('GET', '/aiplang-hydrate.js', (req, res) => {
|
|
2389
2419
|
const p = path.join(__dirname, '..', 'runtime', 'aiplang-hydrate.js')
|
|
2390
|
-
if (fs.existsSync(p)) { res.writeHead(200,{'Content-Type':'application/javascript'}); res.end(fs.readFileSync(p)) }
|
|
2420
|
+
if (fs.existsSync(p)) { res.setHeader('Cache-Control','public,max-age=86400'); res.writeHead(200,{'Content-Type':'application/javascript'}); res.end(fs.readFileSync(p)) }
|
|
2391
2421
|
else { res.writeHead(404); res.end('// not found') }
|
|
2392
2422
|
})
|
|
2393
2423
|
|
|
@@ -2432,7 +2462,7 @@ async function startServer(aipFile, port = 3000) {
|
|
|
2432
2462
|
})
|
|
2433
2463
|
|
|
2434
2464
|
srv.addRoute('GET', '/health', (req, res) => res.json(200, {
|
|
2435
|
-
status:'ok', version:'2.11.
|
|
2465
|
+
status:'ok', version:'2.11.11',
|
|
2436
2466
|
models: app.models.map(m=>m.name),
|
|
2437
2467
|
routes: app.apis.length, pages: app.pages.length,
|
|
2438
2468
|
admin: app.admin?.prefix || null,
|