docs(MEMORY): @pulse-libs/ui — design system compartilhado

This commit is contained in:
Pulse Agent
2026-05-20 20:04:10 -03:00
parent 9d52098b57
commit 0303870669
6 changed files with 204 additions and 2 deletions
+7
View File
@@ -0,0 +1,7 @@
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
RUN apk add --no-cache curl && \
ln -sf /dev/stdout /usr/share/nginx/html/health
EXPOSE 80
HEALTHCHECK --interval=10s --timeout=3s --retries=3 CMD curl -sf /health || exit 1
CMD ["nginx", "-g", "daemon off;"]
+12
View File
@@ -109,3 +109,15 @@ Stack Swarm `dev` com ambiente completo de desenvolvimento:
- Stack: Vite + React 18 + TS + @react-three/fiber + drei + framer-motion - Stack: Vite + React 18 + TS + @react-three/fiber + drei + framer-motion
- npm install + build OK - npm install + build OK
- Dev: `cd pulse-3d-landing && npm run dev` - Dev: `cd pulse-3d-landing && npm run dev`
## 📦 @pulse-libs/ui — Design System Compartilhado (2026-05-20)
- **Repo**: https://git.octal.tec.br/Roberto/pulse-libs
- **30 arquivos TS** — Atoms(10) · Molecules(3) · Organisms(4) · Templates(3) · Lib(1) + indices
- Atoms 2D: Button, Badge, Card, GradientText, Divider, ThemeToggle
- Atoms 3D stubs: FloatingMesh3d, ParticleField3d, LightGlow3d, FloatingText3d (deprecated)
- Molecules: FeatureCard, Navbar, Footer
- Organisms: HeroSection, FeaturesGrid, CtaBlock, StatsGrid
- Templates: MainLayout, MinimalLayout, PageWithSidebar
- TOKENS export: color / space / radius constants em TypeScript
- Uso: projetos importam do path `../libs/pulse-libs/src/` ou como submodule
- Ligado aos projetos: pulse-3d-landing, test-octal (landing page)
Submodule pulse-docs updated: 8208a6783d...e1924ad4f2
Submodule
+1
Submodule pulse-libs added at 29a7a5adb9
Submodule pulse-memory updated: de88a83ca3...73c50dccd5
+182
View File
@@ -0,0 +1,182 @@
import pathlib
lib = pathlib.Path("/root/.openclaw/workspace/pulse-libs/src")
mol = {
"FeatureCard.ts": """\
import { Card } from '../atoms'
interface FeatureCardProps { title:string, description:string, icon?:string, delay?:number, variant?:'default'|'accent', style?:React.CSSProperties }
export function FeatureCard({title,description,icon,delay=0,variant='default',style}:FeatureCardProps){
const bg = variant==='accent'?'linear-gradient(135deg,rgba(37,99,235,.06),rgba(124,58,237,.04))':'transparent'
return (
<Card hover style={{cursor:'default',position:'relative',overflow:'hidden',...style}}>
{variant==='accent' && <div style={{position:'absolute',inset:0,background:bg,pointerEvents:'none'}}/>}
{icon && <div style={{fontSize:'2rem',marginBottom:'.7rem',display:'block'}}>{icon}</div>}
<h3 style={{fontSize:'1.05rem',fontWeight:700,marginBottom:'.4rem',color:'#e4e4e7'}}>{title}</h3>
<p style={{fontSize:'.85rem',color:'#94a3b8',lineHeight:1.65}}>{description}</p>
<div style={{marginTop:'1rem',display:'flex',gap:'.45rem',flexWrap:'wrap'}}>
{variant==='accent' && <span style={{padding:'1px 8px',borderRadius:99,background:'rgba(37,99,235,.12)',border:'1px solid rgba(37,99,235,.3)',color:'#60a5fa',fontSize:'.68rem',fontFamily:"'JetBrains Mono',monospace",fontWeight:600}}>core</span>}
</div>
</Card>
)
}
""",
"Navbar.ts": """\
import { GradientText, Button } from '../atoms'
interface NavProps { logo:string, links:{label:string,href:string}[], ctaLabel?:string, ctaHref?:string }
export function Navbar({logo,links,ctaLabel,ctaHref='#'}:NavProps){
return (
<nav role="navigation" aria-label="Main" style={{position:'sticky',top:0,zIndex:100,display:'flex',alignItems:'center',justifyContent:'space-between',padding:'10px 2rem',background:'rgba(5,5,16,.82)',backdropFilter:'blur(16px)',borderBottom:'1px solid rgba(51,65,85,.3)'}}>
<a href="/" aria-label="Home" style={{fontSize:'1rem',fontWeight:900,color:'#e4e4e7',letterSpacing:'-.03em',textDecoration:'none'}}>
<GradientText from="#60a5fa" to="#a78bfa">{logo}</GradientText>
</a>
<ul style={{display:'flex',alignItems:'center',gap:'1.5rem',listStyle:'none',margin:0,padding:0}}>
{links.map(l=> <li key={l.href}><a href={l.href} style={{color:'#94a3b8',fontSize:'.88rem',textDecoration:'none',transition:'color .15s'}} onMouseEnter={e=>{e.currentTarget.style.color='#60a5fa'}} onMouseLeave={e=>{e.currentTarget.style.color='#94a3b8'}}>{l.label}</a></li>)}
{ctaLabel && <li><Button onClick={()=>{window.location.href=ctaHref}} variant="primary" style={{padding:'6px 16px',fontSize:'.83rem'}}>{ctaLabel}</Button></li>}
</ul>
</nav>
)
}
""",
"Footer.ts": """\
interface FooterProps { brand?:string, year?:number, links:{label:string,href:string}[] }
export function Footer({brand='Pulse 3D',year=__DATE__,links=[]}:FooterProps){
return (
<footer style={{padding:'1.8rem 2rem',borderTop:'1px solid rgba(51,65,85,.4)',display:'flex',justifyContent:'space-between',alignItems:'center',flexWrap:'wrap',gap:'1rem'}}>
<span style={{color:'#64748b',fontSize:'.75rem'}}>{'\\u2699'} {brand} \\u00b7 MIT \\u00b7 {new Date().getFullYear()}</span>
<ul style={{display:'flex',gap:'1.2rem',listStyle:'none',margin:0,padding:0}}>
{links.map(l=><li key={l.href}><a href={l.href} style={{color:'#60a5fa',fontSize:'.75rem',textDecoration:'none'}} onMouseEnter={e=>{e.currentTarget.style.textDecoration='underline'}} onMouseLeave={e=>{e.currentTarget.style.textDecoration='none'}}>{l.label}</a></li>)}
</ul>
</footer>
)
}
""",
"index.ts": """\
export { FeatureCard } from './FeatureCard'
export { Navbar } from './Navbar'
export { Footer } from './Footer'
""",
}
for name, code in mol.items():
(lib / "molecules" / name).write_text(code)
print("✅ Molecules: FeatureCard, Navbar, Footer + index")
# ── ORGANISMS ──────────────────────────────────────────────────────
org = {
"HeroSection.ts": """\
import { Card, Badge, GradientText, Button } from '../atoms'
import type { JSX } from 'react'
interface HeroProps { badge?:string, title:string, description:string, cta:{label:string,onClick():void}[], showScrollHint?:boolean, style?:React.CSSProperties }
export function HeroSection({badge,title,description,cta=[],showScrollHint=true,style}:HeroProps){
return (
<section style={{minHeight:'100vh',display:'flex',flexDirection:'column',alignItems:'center',justifyContent:'center',textAlign:'center',padding:'2rem',background:'radial-gradient(ellipse at 50% 20%,rgba(37,99,235,.10) 0%,transparent 65%),radial-gradient(ellipse at 80% 75%,rgba(124,58,237,.07) 0%,transparent 60%)',...style}} role="banner">
{badge && <Badge variant="accent">{badge}</Badge>}
<h1 style={{fontSize:'clamp(2.2rem,7vw,5.2rem)',fontWeight:900,lineHeight:1.04,letterSpacing:'-.03em',marginBottom:'1rem'}}>
<GradientText from="#60a5fa" to="#a78bfa">{title}</GradientText>
</h1>
<p style={{fontSize:'clamp(.95rem,2.2vw,1.2rem)',color:'#94a3b8',maxWidth:620,marginBottom:'2.5rem',lineHeight:1.6}}>{description}</p>
<div style={{display:'flex',gap:'1rem',flexWrap:'wrap',justifyContent:'center'}}>
{cta.map((b,i)=><Button key={i} variant={i===0?'primary':'ghost'} onClick={b.onClick}>{b.label}</Button>)}
</div>
{showScrollHint && <div style={{position:'absolute',bottom:'2.5rem',left:'50%',transform:'translateX(-50%)',color:'#64748b',fontSize:'.72rem',letterSpacing:'.18em',textTransform:'uppercase',display:'flex',flexDirection:'column',alignItems:'center',gap:'.4rem'}}><span>scroll para explorar</span><div style={{animation:'bounce 1.5s infinite'}}>↓</div></div>}
</section>
)
}
""",
"FeaturesGrid.ts": """\
import { Card } from '../atoms'
interface Feature { icon:string, title:string, description:string }
interface FeaturesProps { title?:string, features:Feature[], style?:React.CSSProperties }
export function FeaturesGrid({title,features,style}:FeaturesProps){
return (
<section style={{padding:'3.5rem 2rem',maxWidth:1100,margin:'0 auto',...style}}>
{title && <h2 style={{fontSize:'clamp(1.4rem,4vw,2.6rem)',fontWeight:800,lineHeight:1.15,color:'#e4e4e7',marginBottom:'.5rem'}}><span style={{background:'linear-gradient(135deg,#60a5fa,#a78bfa)',WebkitBackgroundClip:'text',WebkitTextFillColor:'transparent'}}>{title}</span></h2>}
<p style={{color:'#94a3b8',maxWidth:580,marginBottom:'2rem',fontSize:'.95rem',lineHeight:1.6}}>Componentes que compõem o sistema — do átomo ao organismo.</p>
<div style={{display:'grid',gridTemplateColumns:'repeat(auto-fill,minmax(260px,1fr))',gap:'1.2rem'}}>
{features.map((f,i)=><Card key={i} hover style={{cursor:'default'}}><div style={{fontSize:'2.2rem',marginBottom:'.8rem'}}>{f.icon}</div><h3 style={{fontSize:'1.04rem',fontWeight:700,marginBottom:'.4rem',color:'#e4e4e7'}}>{f.title}</h3><p style={{fontSize:'.86rem',color:'#94a3b8',lineHeight:1.62}}>{f.description}</p></Card>) }
</div>
</section>
)
}
""",
"CtaBlock.ts": """\
import { Button, GradientText } from '../atoms'
interface CtaProps { title:string, description:string, primary:{label:string,onClick():void}, secondary?:{label:string,onClick():void}, dark?:boolean }
export function CtaBlock({title,description,primary,secondary,dark=false}:CtaProps){
const bg = dark ? 'rgba(5,5,16,.96)' : 'rgba(15,17,23,.5)'
return (
<section style={{padding:'6rem 2rem',textAlign:'center',background:'linear-gradient(135deg,rgba(37,99,235,.05),rgba(124,58,237,.05))',borderTop:'1px solid rgba(51,65,85,.3)'}}>
<h2 style={{fontSize:'clamp(1.6rem,5vw,3rem)',fontWeight:900,marginBottom:'.8rem',lineHeight:1.15}}><GradientText from="#60a5fa" to="#a78bfa">{title}</GradientText></h2>
<p style={{color:'#94a3b8',maxWidth:520,margin:'0 auto 2rem',fontSize:'1.05rem',lineHeight:1.65}}>{description}</p>
<div style={{display:'flex',gap:'1rem',flexWrap:'wrap',justifyContent:'center'}}>
<Button variant="primary" onClick={primary.onClick} style={{boxShadow:'0 0 40px rgba(37,99,235,.3)'}}>{primary.label}</Button>
{secondary && <Button variant="ghost" onClick={()=>window.location.href='/'} style={{border:'1px solid #334155',color:'#e4e4e7'}}>{secondary.label}</Button>}
</div>
<p style={{marginTop:'2rem',fontSize:'.72rem',color:'#475569'}}>{'\\u2699'} @pulse-libs/ui — MIT — {new Date().getFullYear()}</p>
</section>
)
}
""",
"StatsGrid.ts": """\
interface Stat { label:string, value:string|number, color?:string }
export function StatsGrid({stats}:{stats:Stat[]}){
return (
<div style={{padding:'3rem 2rem',maxWidth:900,margin:'0 auto',display:'grid',gridTemplateColumns:'repeat(auto-fit,minmax(160px,1fr))',gap:'1rem',textAlign:'center'}}>
{stats.map((s,i)=><div key={i} style={{padding:'1.5rem',background:'rgba(15,17,23,.6)',border:'1px solid rgba(51,65,85,.4)',borderRadius:16,backdropFilter:'blur(12px)'}}>
<div style={{fontSize:'2rem',fontWeight:900,color:s.color||'#60a5fa'}}>{s.value}</div>
<div style={{fontSize:'.8rem',color:'#94a3b8',marginTop:'.2rem'}}>{s.label}</div>
</div>)}
</div>
)
}
""",
"index.ts": """\
export { HeroSection } from './HeroSection'
export { FeaturesGrid } from './FeaturesGrid'
export { CtaBlock } from './CtaBlock'
export { StatsGrid } from './StatsGrid'
""",
}
for name, code in org.items():
if name != "index.ts":
(lib / "organisms" / name).write_text(code)
else:
(lib / "organisms" / name).write_text(code)
print("✅ Organisms: HeroSection, FeaturesGrid, CtaBlock, StatsGrid + index")
# ── TEMPLATES ──────────────────────────────────────────────────────
templates = {
"MainLayout.ts": """\
import { ReactNode } from 'react'
import { Navbar, Footer, Divider } from './..'
interface LayoutProps { children:ReactNode, nav:{logo:string,links:{label:string,href:string}[],cta?:{label:string,href:string}}, footer?:{brand:string,links:{label:string,href:string}[]} }
export function MainLayout({children,nav,footer}:LayoutProps){
return (
<div style={{minHeight:'100vh',display:'flex',flexDirection:'column',background:'#050510'}}>
<Navbar logo={nav.logo} links={nav.links} ctaLabel={nav.cta?.label} ctaHref={nav.cta?.href}/>
<main style={{flex:1}}>{children}</main>
{footer && <Footer brand={footer.brand} links={footer.links}/>}
</div>
)
}
""",
"MinimalLayout.ts": """\
interface MinimalProps { children:React.ReactNode, centered?:boolean }
export function MinimalLayout({children,centered=true}:MinimalProps){
return <div style={{minHeight:'100vh',display:'flex',alignItems:centered?'center':'flex-start',justifyContent:'center',padding:'4rem 2rem',background:'#050510',color:'#e4e4e7'}}>{children}</div>
}
""",
"index.ts": """\
export { MainLayout } from './MainLayout'
export { MinimalLayout } from './MinimalLayout'
""",
}
for name, code in templates.items():
(lib / "templates" / name).write_text(code)
print("✅ Templates: MainLayout, MinimalLayout + index")
PYEOF