aiplang 2.11.6 → 2.11.7
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 +174 -7
- package/package.json +1 -1
- package/server/server.js +1 -1
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.7'
|
|
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.7'`)
|
|
690
690
|
lines.push(``)
|
|
691
691
|
return lines.join('\n')
|
|
692
692
|
}
|
|
@@ -1044,6 +1044,11 @@ function parseBlock(line) {
|
|
|
1044
1044
|
if(line.startsWith('benchmark{')) { const m=line.match(/^benchmark\{([^}]*)\}/);if(m){const vm=line.match(/variant:(\S+)/);const am=line.match(/animate:(\S+)/);return{kind:'benchmark',items:m[1].split('|').map(it=>{const p=it.trim().split(':');return{num:p[0]?.trim(),label:p[1]?.trim(),vs:p[2]?.trim(),pct:parseInt(p[3])||0}}),variant:vm?.[1],animate:am?.[1]}} }
|
|
1045
1045
|
if(line.startsWith('install{')) { const m=line.match(/^install\{([^}]*)\}/);if(m){const vm=line.match(/variant:(\S+)/);return{kind:'install',cmds:m[1].split('|').map(c=>c.trim()).filter(Boolean),variant:vm?.[1]}} }
|
|
1046
1046
|
if(line.startsWith('feature{')) { const b=parseBlock(line.replace(/^feature/,'row3'));if(b){b.variant='feature'};return b }
|
|
1047
|
+
if(line.startsWith('marquee{')) { const m=line.match(/^marquee\{([^}]*)\}/);if(m){const vm=line.match(/variant:(\S+)/);return{kind:'marquee',items:m[1].split('|').map(s=>s.trim()).filter(Boolean),variant:vm?.[1]}} }
|
|
1048
|
+
if(line.startsWith('cta{')) { const m=line.match(/^cta\{([^}]*)\}/);if(m){const pts=m[1].split('|');let t='',s='',links=[];pts.forEach(p=>{const lm=p.match(/^([^>]+)>([^>]+)$/);if(lm)links.push({label:lm[1].trim(),path:lm[2].trim()});else if(!t)t=p.trim();else s=p.trim()});const vm=line.match(/variant:(\S+)/);return{kind:'cta',title:t,sub:s,links,variant:vm?.[1]}} }
|
|
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
|
+
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
|
+
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()}} }
|
|
1047
1052
|
if(line.startsWith('faq{')) {
|
|
1048
1053
|
const body=line.slice(4,line.lastIndexOf('}')).trim()
|
|
1049
1054
|
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)
|
|
@@ -1277,6 +1282,11 @@ function renderBlock(b, page) {
|
|
|
1277
1282
|
case 'install': return rInstall(b)
|
|
1278
1283
|
case 'feature': return rRow(b)
|
|
1279
1284
|
case 'testimonial': return rTestimonial(b)
|
|
1285
|
+
case 'marquee': return rMarquee(b)
|
|
1286
|
+
case 'cta': return rCta(b)
|
|
1287
|
+
case 'steps': return rSteps(b)
|
|
1288
|
+
case 'compare': return rCompare(b)
|
|
1289
|
+
case 'video': return rVideo(b)
|
|
1280
1290
|
case 'gallery': return rGallery(b)
|
|
1281
1291
|
case 'raw': return (b.html||'')+'\n'
|
|
1282
1292
|
case 'html': return `<div class="fx-html">${b.content||''}</div>\n`
|
|
@@ -1400,10 +1410,17 @@ function parseFeature(line) {
|
|
|
1400
1410
|
|
|
1401
1411
|
function rHero(b) {
|
|
1402
1412
|
let h1='',sub='',img='',ctas=''
|
|
1413
|
+
let heroBadge = ''
|
|
1403
1414
|
for(const item of (b.items||[])) for(const f of item){
|
|
1415
|
+
if(f.text?.startsWith('badge:')) { heroBadge=`<div class="fx-hero-badge"><span class="fx-hero-badge-dot"></span>${esc(f.text.slice(6).trim())}</div>`; continue }
|
|
1404
1416
|
if(f.isImg) img=`<img src="${esc(f.src)}" class="fx-hero-img" alt="hero" loading="eager">`
|
|
1405
1417
|
else if(f.isLink) ctas+=`<a href="${esc(f.path)}" class="fx-cta">${esc(f.label)}</a>`
|
|
1406
|
-
else if(!h1)
|
|
1418
|
+
else if(!h1) {
|
|
1419
|
+
// *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>`
|
|
1422
|
+
else h1=`<h1 class="fx-title">${esc(f.text)}</h1>`
|
|
1423
|
+
}
|
|
1407
1424
|
else sub+=`<p class="fx-sub">${esc(f.text)}</p>`
|
|
1408
1425
|
}
|
|
1409
1426
|
const v = b.variant || (img ? 'split' : 'centered')
|
|
@@ -1413,6 +1430,7 @@ function rHero(b) {
|
|
|
1413
1430
|
return `<section class="fx-hero fx-hero-landing"${bgStyle}><div class="fx-hero-inner">${h1}${sub}${ctas}</div></section>
|
|
1414
1431
|
`
|
|
1415
1432
|
}
|
|
1433
|
+
if (h1) h1 = heroBadge + h1
|
|
1416
1434
|
if (v === 'minimal') {
|
|
1417
1435
|
return `<section class="fx-hero fx-hero-minimal"${bgStyle}><div class="fx-hero-inner">${h1}${sub}${ctas}</div></section>\n`
|
|
1418
1436
|
}
|
|
@@ -1468,7 +1486,7 @@ function rRow(b) {
|
|
|
1468
1486
|
}).join('')
|
|
1469
1487
|
const v=b.variant||''
|
|
1470
1488
|
const wrapStyle=b.style?` style="${b.style.replace(/,/g,';')}"`:''
|
|
1471
|
-
return `<div class="fx-grid fx-grid-${b.cols||3}${v?' fx-grid-'+v:''}"${wrapStyle}>${cards}</div>\n`
|
|
1489
|
+
return `<div class="fx-grid fx-grid-${b.cols||3}${v?' fx-grid-'+v:''} fx-animate-stagger"${wrapStyle}>${cards}</div>\n`
|
|
1472
1490
|
}
|
|
1473
1491
|
|
|
1474
1492
|
function rSect(b) {
|
|
@@ -1482,7 +1500,7 @@ function rSect(b) {
|
|
|
1482
1500
|
const bgStyle=b.bg?` style="background:${b.bg}"`:(b.style?` style="${b.style.replace(/,/g,';')}"`:'' )
|
|
1483
1501
|
const v = b.variant||''
|
|
1484
1502
|
const cls = v ? ` fx-sect-${v}` : ''
|
|
1485
|
-
return `<section class="fx-sect${cls}"${bgStyle}>${inner}</section>\n`
|
|
1503
|
+
return `<section class="fx-sect${cls} fx-animate"${bgStyle}>${inner}</section>\n`
|
|
1486
1504
|
}
|
|
1487
1505
|
|
|
1488
1506
|
|
|
@@ -1525,7 +1543,7 @@ function rBenchmark(b) {
|
|
|
1525
1543
|
<div class="fx-bench-bar"><div class="fx-bench-fill" style="width:${isLow?pct+'%':pct+'%'}"></div></div>
|
|
1526
1544
|
</div>`
|
|
1527
1545
|
}).join('')
|
|
1528
|
-
return `<div class="fx-benchmark">${cards}</div>\n`
|
|
1546
|
+
return `<div class="fx-benchmark fx-animate-stagger">${cards}</div>\n`
|
|
1529
1547
|
}
|
|
1530
1548
|
|
|
1531
1549
|
// ── rInstall: multi-step code box com botões ──────────────────────
|
|
@@ -1552,8 +1570,12 @@ function rStatsUpgraded(b) {
|
|
|
1552
1570
|
const lbl = parts[1]?.trim()
|
|
1553
1571
|
const vs = parts[2]?.trim()
|
|
1554
1572
|
const bind = isDyn(val) ? ` data-fx-bind="${esc(val)}"` : ''
|
|
1573
|
+
// Números → animados com counter
|
|
1574
|
+
const isNum = !isDyn(val) && /^[\d.,]+[KkMmBb%]?$/.test(val?.replace(/ms|KB|GB|px/,''))
|
|
1575
|
+
const numAttr = isNum && !isDyn(val) ? ` data-to="${val.replace(/[^\d.]/g,'')}" data-dec="${val.includes('.')?val.split('.')[1]?.replace(/[^\d]/g,'').length||0:0}"` : ''
|
|
1576
|
+
const countCls = isNum && !isDyn(val) ? ' fx-count' : ''
|
|
1555
1577
|
return `<div class="fx-stat">
|
|
1556
|
-
<div class="fx-stat-val"${bind}>${esc(val)}</div>
|
|
1578
|
+
<div class="fx-stat-val${countCls}"${bind}${numAttr}>${esc(val)}</div>
|
|
1557
1579
|
<div class="fx-stat-lbl">${esc(lbl||'')}</div>
|
|
1558
1580
|
${vs ? `<div class="fx-stat-vs">${esc(vs)}</div>` : ''}
|
|
1559
1581
|
</div>`
|
|
@@ -1562,6 +1584,88 @@ function rStatsUpgraded(b) {
|
|
|
1562
1584
|
}
|
|
1563
1585
|
|
|
1564
1586
|
|
|
1587
|
+
|
|
1588
|
+
// ── rMarquee: faixa de logos/texto em loop infinito ───────────────
|
|
1589
|
+
function rMarquee(b) {
|
|
1590
|
+
const speed = b.variant === 'fast' ? '15s' : b.variant === 'slow' ? '40s' : '25s'
|
|
1591
|
+
const items = (b.items||[]).map(item =>
|
|
1592
|
+
`<span class="fx-marquee-item">${esc(item)}</span><span class="fx-marquee-sep">·</span>`
|
|
1593
|
+
).join('')
|
|
1594
|
+
// Duplicar para loop contínuo
|
|
1595
|
+
return `<div class="fx-marquee"><div class="fx-marquee-track" style="animation-duration:${speed}">${items}${items}</div></div>\n`
|
|
1596
|
+
}
|
|
1597
|
+
|
|
1598
|
+
// ── rCta: seção call-to-action com glow ───────────────────────────
|
|
1599
|
+
function rCta(b) {
|
|
1600
|
+
const btns = (b.links||[]).map((l,i) =>
|
|
1601
|
+
`<a href="${esc(l.path)}" class="fx-cta${i===0?'':' fx-cta-outline'}">${esc(l.label)}</a>`
|
|
1602
|
+
).join('')
|
|
1603
|
+
const v = b.variant || 'default'
|
|
1604
|
+
return `<section class="fx-cta-section fx-cta-${v}">
|
|
1605
|
+
<div class="fx-cta-glow"></div>
|
|
1606
|
+
<div class="fx-cta-inner">
|
|
1607
|
+
${b.title?`<h2 class="fx-cta-title">${esc(b.title)}</h2>`:''}
|
|
1608
|
+
${b.sub?`<p class="fx-cta-sub">${esc(b.sub)}</p>`:''}
|
|
1609
|
+
<div class="fx-cta-actions">${btns}</div>
|
|
1610
|
+
</div>
|
|
1611
|
+
</section>\n`
|
|
1612
|
+
}
|
|
1613
|
+
|
|
1614
|
+
// ── rSteps: passos 1-2-3 com linha conectora ──────────────────────
|
|
1615
|
+
function rSteps(b) {
|
|
1616
|
+
const items = (b.items||[]).map((step, i) =>
|
|
1617
|
+
`<div class="fx-step">
|
|
1618
|
+
<div class="fx-step-num">${esc(step.num||String(i+1))}</div>
|
|
1619
|
+
<div class="fx-step-body">
|
|
1620
|
+
<div class="fx-step-title">${esc(step.title||'')}</div>
|
|
1621
|
+
<div class="fx-step-desc">${esc(step.desc||'')}</div>
|
|
1622
|
+
</div>
|
|
1623
|
+
</div>`
|
|
1624
|
+
).join('')
|
|
1625
|
+
const v = b.variant === 'vertical' ? ' fx-steps-vertical' : ''
|
|
1626
|
+
return `<div class="fx-steps${v} fx-animate-stagger">${items}</div>\n`
|
|
1627
|
+
}
|
|
1628
|
+
|
|
1629
|
+
// ── rCompare: tabela X vs Y ───────────────────────────────────────
|
|
1630
|
+
function rCompare(b) {
|
|
1631
|
+
const rows = b.rows || []
|
|
1632
|
+
if (!rows.length) return ''
|
|
1633
|
+
// Primeira linha = cabeçalho se tiver textos
|
|
1634
|
+
const header = rows[0]
|
|
1635
|
+
const isHeader = header.length > 1 && !header[0].startsWith('✅') && !header[0].startsWith('❌')
|
|
1636
|
+
const headerHtml = isHeader
|
|
1637
|
+
? `<div class="fx-compare-header">${header.map((h,i) => `<div class="fx-compare-cell${i===0?' fx-compare-feature':i===1?' fx-compare-col-a':' fx-compare-col-b'}">${esc(h)}</div>`).join('')}</div>`
|
|
1638
|
+
: ''
|
|
1639
|
+
const dataRows = isHeader ? rows.slice(1) : rows
|
|
1640
|
+
const bodyHtml = dataRows.map(row => {
|
|
1641
|
+
const feature = row[0] || ''
|
|
1642
|
+
const a = row[1] || ''
|
|
1643
|
+
const b2 = row[2] || ''
|
|
1644
|
+
const checkA = a === '✅' || a === 'sim' || a === 'yes' ? '✅' : a === '❌' || a === 'nao' || a === 'no' ? '❌' : esc(a)
|
|
1645
|
+
const checkB = b2 === '✅' || b2 === 'sim' || b2 === 'yes' ? '✅' : b2 === '❌' || b2 === 'nao' || b2 === 'no' ? '❌' : esc(b2)
|
|
1646
|
+
return `<div class="fx-compare-row">
|
|
1647
|
+
<div class="fx-compare-cell fx-compare-feature">${esc(feature)}</div>
|
|
1648
|
+
<div class="fx-compare-cell fx-compare-col-a">${checkA}</div>
|
|
1649
|
+
<div class="fx-compare-cell fx-compare-col-b">${checkB}</div>
|
|
1650
|
+
</div>`
|
|
1651
|
+
}).join('')
|
|
1652
|
+
return `<div class="fx-compare">${headerHtml}${bodyHtml}</div>\n`
|
|
1653
|
+
}
|
|
1654
|
+
|
|
1655
|
+
// ── rVideo: embed de vídeo ou youtube ─────────────────────────────
|
|
1656
|
+
function rVideo(b) {
|
|
1657
|
+
const url = b.url || ''
|
|
1658
|
+
const poster = b.poster || ''
|
|
1659
|
+
// Detectar YouTube
|
|
1660
|
+
const ytMatch = url.match(/(?:youtube\.com\/watch\?v=|youtu\.be\/)([a-zA-Z0-9_-]{11})/)
|
|
1661
|
+
if (ytMatch) {
|
|
1662
|
+
const id = ytMatch[1]
|
|
1663
|
+
return `<div class="fx-video-wrap"><div class="fx-video-yt"><iframe src="https://www.youtube-nocookie.com/embed/${esc(id)}?rel=0" frameborder="0" allowfullscreen loading="lazy" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture"></iframe></div></div>\n`
|
|
1664
|
+
}
|
|
1665
|
+
// Vídeo HTML5
|
|
1666
|
+
return `<div class="fx-video-wrap"><video class="fx-video" controls${poster?` poster="${esc(poster)}"`:''} preload="metadata"><source src="${esc(url)}"></video></div>\n`
|
|
1667
|
+
}
|
|
1668
|
+
|
|
1565
1669
|
// ── autoYear: substitui © YYYY por © <span data-fx-year></span> ──
|
|
1566
1670
|
function autoYear(text) {
|
|
1567
1671
|
// Substitui padrões como "© 2024", "© 2025", "© 2026" ou só "©" pelo ano dinâmico
|
|
@@ -1757,6 +1861,69 @@ function css(theme) {
|
|
|
1757
1861
|
.fx-footer-brand{font-size:1rem;font-weight:800;letter-spacing:-.02em}
|
|
1758
1862
|
.fx-footer-links{display:flex;gap:1.5rem;flex-wrap:wrap}
|
|
1759
1863
|
.fx-footer-note{font-size:.72rem;opacity:.3;font-family:monospace}
|
|
1864
|
+
/* ── marquee ── */
|
|
1865
|
+
.fx-marquee{overflow:hidden;padding:1.5rem 0;border-top:1px solid rgba(255,255,255,.06);border-bottom:1px solid rgba(255,255,255,.06);-webkit-mask:linear-gradient(90deg,transparent,black 10%,black 90%,transparent);mask:linear-gradient(90deg,transparent,black 10%,black 90%,transparent)}
|
|
1866
|
+
.fx-marquee-track{display:flex;width:max-content;animation:fx-marquee 25s linear infinite}
|
|
1867
|
+
.fx-marquee:hover .fx-marquee-track{animation-play-state:paused}
|
|
1868
|
+
@keyframes fx-marquee{0%{transform:translateX(0)}100%{transform:translateX(-50%)}}
|
|
1869
|
+
.fx-marquee-item{font-size:.9375rem;font-weight:600;opacity:.35;white-space:nowrap;padding:0 1.5rem;transition:opacity .2s}
|
|
1870
|
+
.fx-marquee-item:hover{opacity:.7}
|
|
1871
|
+
.fx-marquee-sep{opacity:.15;padding:0 .25rem}
|
|
1872
|
+
/* ── cta section ── */
|
|
1873
|
+
.fx-cta-section{position:relative;padding:6rem 2.5rem;text-align:center;overflow:hidden}
|
|
1874
|
+
.fx-cta-glow{position:absolute;width:600px;height:400px;border-radius:50%;background:radial-gradient(ellipse,rgba(var(--accent-rgb,255,87,34),.12),transparent 70%);left:50%;top:50%;transform:translate(-50%,-50%);pointer-events:none}
|
|
1875
|
+
.fx-cta-inner{position:relative;z-index:1;max-width:44rem;margin:0 auto;display:flex;flex-direction:column;align-items:center;gap:1.5rem}
|
|
1876
|
+
.fx-cta-title{font-size:clamp(2rem,5vw,4rem);font-weight:900;letter-spacing:-.04em;line-height:1.05}
|
|
1877
|
+
.fx-cta-sub{font-size:1.0625rem;line-height:1.75;opacity:.65;max-width:36rem}
|
|
1878
|
+
.fx-cta-actions{display:flex;gap:.875rem;flex-wrap:wrap;justify-content:center}
|
|
1879
|
+
.fx-cta-outline{background:transparent!important;border:1px solid rgba(255,255,255,.2)!important;color:inherit!important}
|
|
1880
|
+
.fx-cta-outline:hover{background:rgba(255,255,255,.05)!important}
|
|
1881
|
+
/* ── steps ── */
|
|
1882
|
+
.fx-steps{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:2rem;padding:2rem 2.5rem 4rem;position:relative}
|
|
1883
|
+
.fx-steps::before{content:"";position:absolute;top:3.5rem;left:calc(2.5rem + 1.5rem);right:calc(2.5rem + 1.5rem);height:1px;background:linear-gradient(90deg,transparent,rgba(255,255,255,.1),transparent);pointer-events:none}
|
|
1884
|
+
.fx-step{display:flex;flex-direction:column;align-items:flex-start;gap:1rem}
|
|
1885
|
+
.fx-step-num{width:48px;height:48px;border-radius:50%;background:var(--accent,#ff5722);color:#fff;font-weight:900;font-size:1.125rem;display:flex;align-items:center;justify-content:center;flex-shrink:0;position:relative;z-index:1}
|
|
1886
|
+
.fx-step-title{font-size:1rem;font-weight:700;margin-bottom:.375rem;letter-spacing:-.02em}
|
|
1887
|
+
.fx-step-desc{font-size:.875rem;line-height:1.65;opacity:.6}
|
|
1888
|
+
.fx-steps-vertical{grid-template-columns:1fr}
|
|
1889
|
+
.fx-steps-vertical::before{display:none}
|
|
1890
|
+
.fx-steps-vertical .fx-step{flex-direction:row}
|
|
1891
|
+
/* ── compare ── */
|
|
1892
|
+
.fx-compare{max-width:640px;margin:0 auto 4rem;padding:0 2.5rem}
|
|
1893
|
+
.fx-compare-header{display:grid;grid-template-columns:1fr 1fr 1fr;padding:.75rem 1rem;background:rgba(255,255,255,.04);border-radius:.75rem .75rem 0 0;font-size:.72rem;font-weight:700;text-transform:uppercase;letter-spacing:.1em;opacity:.6}
|
|
1894
|
+
.fx-compare-row{display:grid;grid-template-columns:1fr 1fr 1fr;padding:.75rem 1rem;border-bottom:1px solid rgba(255,255,255,.05);transition:background .15s}
|
|
1895
|
+
.fx-compare-row:hover{background:rgba(255,255,255,.02)}
|
|
1896
|
+
.fx-compare-row:last-child{border-bottom:none;border-radius:0 0 .75rem .75rem}
|
|
1897
|
+
.fx-compare-cell{font-size:.875rem}
|
|
1898
|
+
.fx-compare-feature{opacity:.7}
|
|
1899
|
+
.fx-compare-col-a{text-align:center;color:#4ade80}
|
|
1900
|
+
.fx-compare-col-b{text-align:center;opacity:.35}
|
|
1901
|
+
/* ── video ── */
|
|
1902
|
+
.fx-video-wrap{padding:0 2.5rem 3rem}
|
|
1903
|
+
.fx-video-yt{position:relative;padding-bottom:56.25%;height:0;overflow:hidden;border-radius:1rem;border:1px solid rgba(255,255,255,.06)}
|
|
1904
|
+
.fx-video-yt iframe{position:absolute;top:0;left:0;width:100%;height:100%}
|
|
1905
|
+
.fx-video{width:100%;border-radius:1rem;border:1px solid rgba(255,255,255,.06)}
|
|
1906
|
+
/* ── gradient text ── */
|
|
1907
|
+
.fx-gradient-text{background:linear-gradient(135deg,var(--accent,#ff5722),#ff8a50,#ffd4c4);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text}
|
|
1908
|
+
/* ── scroll animations (IntersectionObserver) ── */
|
|
1909
|
+
.fx-animate{opacity:0;transform:translateY(20px);transition:opacity .6s cubic-bezier(.4,0,.2,1),transform .6s cubic-bezier(.4,0,.2,1)}
|
|
1910
|
+
.fx-animate.fx-visible{opacity:1;transform:none}
|
|
1911
|
+
.fx-animate-delay-1{transition-delay:.1s}
|
|
1912
|
+
.fx-animate-delay-2{transition-delay:.2s}
|
|
1913
|
+
.fx-animate-delay-3{transition-delay:.3s}
|
|
1914
|
+
.fx-animate-stagger>*{opacity:0;transform:translateY(16px);transition:opacity .5s cubic-bezier(.4,0,.2,1),transform .5s cubic-bezier(.4,0,.2,1)}
|
|
1915
|
+
.fx-animate-stagger.fx-visible>*:nth-child(1){opacity:1;transform:none;transition-delay:.05s}
|
|
1916
|
+
.fx-animate-stagger.fx-visible>*:nth-child(2){opacity:1;transform:none;transition-delay:.15s}
|
|
1917
|
+
.fx-animate-stagger.fx-visible>*:nth-child(3){opacity:1;transform:none;transition-delay:.25s}
|
|
1918
|
+
.fx-animate-stagger.fx-visible>*:nth-child(4){opacity:1;transform:none;transition-delay:.35s}
|
|
1919
|
+
.fx-animate-stagger.fx-visible>*:nth-child(5){opacity:1;transform:none;transition-delay:.45s}
|
|
1920
|
+
.fx-animate-stagger.fx-visible>*:nth-child(6){opacity:1;transform:none;transition-delay:.55s}
|
|
1921
|
+
/* ── number counter ── */
|
|
1922
|
+
.fx-count{display:inline-block}
|
|
1923
|
+
/* ── hero badge ── */
|
|
1924
|
+
.fx-hero-badge{display:inline-flex;align-items:center;gap:.5rem;font-size:.7rem;letter-spacing:.12em;text-transform:uppercase;padding:.3rem 1rem;border-radius:999px;border:1px solid rgba(255,255,255,.12);background:rgba(255,255,255,.04);margin-bottom:1.25rem}
|
|
1925
|
+
.fx-hero-badge-dot{width:6px;height:6px;border-radius:50%;background:var(--accent,#ff5722);animation:fx-blink 2s ease infinite}
|
|
1926
|
+
@keyframes fx-blink{0%,100%{opacity:1}50%{opacity:.2}}
|
|
1760
1927
|
.fx-year{font-variant-numeric:tabular-nums}
|
|
1761
1928
|
.fx-hero-minimal{min-height:50vh!important}
|
|
1762
1929
|
.fx-hero-minimal .fx-hero-inner{gap:1rem}
|
package/package.json
CHANGED
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.
|
|
2435
|
+
status:'ok', version:'2.11.7',
|
|
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,
|