diff --git a/src/pages/index.astro b/src/pages/index.astro index c578204..ed19f17 100644 --- a/src/pages/index.astro +++ b/src/pages/index.astro @@ -364,6 +364,81 @@ const skillsMap = [...SKILLS_MAP].sort((a: any, b: any) => { await navigator.clipboard.writeText('npx autoskills') }) + function PrintAsciiWithAnimation (ascii: string[], id: string) { + const START_COLOR = '#3f3f46' // zinc-700 (gris clarito) + const END_COLOR = '#f4f4f5' // zinc-100 (blanco) + + const logo = document.getElementById(id) + if (!logo) { + // Element not in DOM yet, wait for it + const observer = new MutationObserver(() => { + const el = document.getElementById(id) + if (el) { + observer.disconnect() + runAnimation(el) + } + }) + observer.observe(document.body, { childList: true, subtree: true }) + return + } + runAnimation(logo) + + function runAnimation(logo: HTMLElement) { + const cols = Math.max(...ascii.map(l => l.length)) + const rows = ascii.length + + // Build grid + const grid: HTMLSpanElement[][] = [] + for (const line of ascii) { + const row: HTMLSpanElement[] = [] + const lineEl = document.createElement('div') + lineEl.style.height = '1lh' + lineEl.style.lineHeight = '1' + for (let c = 0; c < cols; c++) { + const ch = line[c] ?? ' ' + const span = document.createElement('span') + span.textContent = ch + span.style.color = START_COLOR + lineEl.appendChild(span) + row.push(span) + } + logo.appendChild(lineEl) + grid.push(row) + } + + // Diagonal sweep: top-left to bottom-right, gray → white + const maxDist = cols + rows + const SPEED = 0.9 + let frame = 0 + const totalFrames = Math.ceil((maxDist + 10) / SPEED) + + function animate() { + const waveFront = frame * SPEED + + for (let r = 0; r < rows; r++) { + for (let c = 0; c < grid[r].length; c++) { + const dist = c + r * 3 + const progress = Math.max(0, Math.min(1, (waveFront - dist) / 10)) + + const gray = Math.round(63 + progress * (244 - 63)) + grid[r][c].style.color = `rgb(${gray},${gray},${gray})` + } + } + + frame++ + if (frame <= totalFrames) { + requestAnimationFrame(animate) + } else { + for (let r = 0; r < rows; r++) + for (let c = 0; c < grid[r].length; c++) + grid[r][c].style.color = END_COLOR + } + } + + requestAnimationFrame(animate) + } + } + // ── ASCII logo animation ───────────────────────────────── { const LOGO_LINES = [ @@ -375,61 +450,7 @@ const skillsMap = [...SKILLS_MAP].sort((a: any, b: any) => { '╚═╝ ╚═╝ ╚═════╝ ╚═╝ ╚═════╝ ╚══════╝╚═╝ ╚═╝╚═╝╚══════╝╚══════╝╚══════╝', ] - const START_COLOR = '#3f3f46' // zinc-700 (gris clarito) - const END_COLOR = '#f4f4f5' // zinc-100 (blanco) - - const logo = document.getElementById('ascii-logo')! - const cols = Math.max(...LOGO_LINES.map(l => l.length)) - const rows = LOGO_LINES.length - - // Build grid - const grid: HTMLSpanElement[][] = [] - for (const line of LOGO_LINES) { - const row: HTMLSpanElement[] = [] - const lineEl = document.createElement('div') - lineEl.style.height = '1lh' - for (let c = 0; c < cols; c++) { - const ch = line[c] ?? ' ' - const span = document.createElement('span') - span.textContent = ch - span.style.color = START_COLOR - lineEl.appendChild(span) - row.push(span) - } - logo.appendChild(lineEl) - grid.push(row) - } - - // Diagonal sweep: top-left to bottom-right, gray → white - const maxDist = cols + rows - const SPEED = 0.9 - let frame = 0 - const totalFrames = Math.ceil((maxDist + 10) / SPEED) - - function animate() { - const waveFront = frame * SPEED - - for (let r = 0; r < rows; r++) { - for (let c = 0; c < grid[r].length; c++) { - const dist = c + r * 3 - const progress = Math.max(0, Math.min(1, (waveFront - dist) / 10)) - - const gray = Math.round(63 + progress * (244 - 63)) - grid[r][c].style.color = `rgb(${gray},${gray},${gray})` - } - } - - frame++ - if (frame <= totalFrames) { - requestAnimationFrame(animate) - } else { - for (let r = 0; r < rows; r++) - for (let c = 0; c < grid[r].length; c++) - grid[r][c].style.color = END_COLOR - } - } - - requestAnimationFrame(animate) + PrintAsciiWithAnimation(LOGO_LINES, 'ascii-logo') } // ── Terminal animation ──────────────────────────────────── @@ -459,11 +480,9 @@ const skillsMap = [...SKILLS_MAP].sort((a: any, b: any) => { const bannerGap = ' '.repeat(Math.max(1, 39 - bannerTitle.length - ver.length - 3)) const BANNER = [ - s(C.cy, ' ╔═══════════════════════════════════════╗'), - s(C.cy, ' ║') + s(C.yw, bannerTitle) + bannerGap + s(C.z6, ver) + ' ' + s(C.cy, '║'), - s(C.cy, ' ║') + s(C.z5, ' Auto-install the best AI skills ') + s(C.cy, '║'), - s(C.cy, ' ║') + s(C.z5, ' for your project ') + s(C.cy, '║'), - s(C.cy, ' ╚═══════════════════════════════════════╝'), + " ┌─┐┬ ┬┌┬┐┌─┐┌─┐┬┌─┬┬ ┬ ┌─┐", + " ├─┤│ │ │ │ │└─┐├┴┐││ │ └─┐", + " ┴ ┴└─┘ ┴ └─┘└─┘┴ ┴┴┴─┘┴─┘└─┘", ] const TECH_NAMES = ['Next.js', 'React', 'Tailwind CSS', 'TypeScript', 'Supabase', 'Astro'] @@ -506,7 +525,8 @@ const skillsMap = [...SKILLS_MAP].sort((a: any, b: any) => { // Banner d(500, '') - for (const line of BANNER) d(30, line) + d(30, '
')
+      PrintAsciiWithAnimation(BANNER, 'term-ascii')
       d(0, '')
 
       // Detected technologies