prime8dev

prime8dev
git clone https://git.prime8.dev/prime8dev.git
Log | Files | Refs | README | LICENSE

commit a41387e654f47cb8b00b59bdfa37ba618b33147f
parent dac0e8a77972d881b726d731d411cc2bd6c84708
Author: Damian Myrda <damian@prime8.dev>
Date:   Wed, 21 Jan 2026 20:24:38 -0600

refactor: replace neofetch boot with glitch text reveal

- Replace slow neofetch-style boot (~2.5s) with fast scramble effect (~1.1s)
- Remove fake progress bar in favor of text decode animation
- Fix missing slash in git raw URL path for README images

Diffstat:
Mweb/src/layouts/Layout.astro | 163+++++++++----------------------------------------------------------------------
Mweb/src/lib/git.ts | 2+-
Mweb/src/scripts/ui/boot.ts | 198+++++++++++++++++++++----------------------------------------------------------
3 files changed, 71 insertions(+), 292 deletions(-)

diff --git a/web/src/layouts/Layout.astro b/web/src/layouts/Layout.astro @@ -377,165 +377,38 @@ const ogImage = image background: var(--bg); z-index: 9999; display: flex; - align-items: flex-start; - justify-content: flex-start; - padding: 20px; + align-items: center; + justify-content: center; overflow: hidden; } #boot-overlay.fade-out { - animation: bootFadeOut 0.6s ease-out forwards; - } - - .boot-scanline { - position: absolute; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: linear-gradient( - to bottom, - transparent 50%, - rgba(0, 0, 0, 0.1) 50% - ); - background-size: 100% 4px; - pointer-events: none; - opacity: 0.3; - z-index: 1; + animation: bootFadeOut 0.3s ease-out forwards; } .boot-terminal { - font-family: "JetBrains Mono", "SF Mono", monospace; - font-size: 14px; - color: var(--fg); - line-height: 1.6; - position: relative; - z-index: 2; - } - - .boot-content { - white-space: pre-wrap; - } - - .boot-line { - transition: - opacity 0.15s ease-out, - transform 0.15s ease-out; - } - - .boot-line-hidden { - opacity: 0; - transform: translateX(-10px); - } - - .boot-line-visible { - opacity: 1; - transform: translateX(0); - } - - .boot-cursor { - display: inline; - animation: blink 0.7s step-end infinite; - color: #ffffff; - } - - .boot-progress { - margin-top: 16px; - width: 200px; - height: 4px; - background: var(--border); - border-radius: 2px; - overflow: hidden; - } - - .boot-progress-bar { - height: 100%; - width: 0; - background: linear-gradient(90deg, var(--cyan), var(--accent)); - animation: progressFill 1s ease-out forwards; - } - - @keyframes progressFill { - 0% { - width: 0; - } - 100% { - width: 100%; - } - } - - /* ANSI colors */ - .ansi-bold-yellow { - color: #d7af5f; - font-weight: bold; - } - - .ansi-bold-cyan { - color: #5fafaf; - font-weight: bold; + text-align: center; } - .ansi-accent { - color: var(--accent); - font-weight: bold; - } - - .ansi-accent { - color: var(--accent); + .boot-text { + display: flex; + flex-direction: column; + gap: 8px; } - .ansi-bold-accent { + .boot-name { + font-family: "JetBrains Mono", "SF Mono", monospace; + font-size: 32px; + font-weight: 600; color: var(--accent); - font-weight: bold; - } - - .ansi-bold-green { - color: #5faf5f; - font-weight: bold; + letter-spacing: -0.5px; } - .ansi-white { - color: #e0e0e0; - } - - .ansi-dim { - color: #606060; - } - - /* Color blocks for neofetch */ - .ansi-block-black { - background: #0d0d0d; - } - .ansi-block-red { - background: #d75f5f; - } - .ansi-block-green { - background: #5faf5f; - } - .ansi-block-yellow { - background: #d7af5f; - } - .ansi-block-blue { - background: #5f87d7; - } - .ansi-block-magenta { - background: #af5faf; - } - .ansi-block-cyan { - background: #5fafaf; - } - .ansi-block-white { - background: #c0c0c0; - } - - @keyframes blink { - 0%, - 100% { - opacity: 1; - } - 50% { - opacity: 0; - } + .boot-title { + font-family: "JetBrains Mono", "SF Mono", monospace; + font-size: 14px; + color: var(--fg-dim); + letter-spacing: 0.5px; } @keyframes bootFadeOut { diff --git a/web/src/lib/git.ts b/web/src/lib/git.ts @@ -13,7 +13,7 @@ export async function fetchGitMetadataForProject( const data = await response.json(); - const baseUrl = `${data.web_url.replace("http://", "https://")}/-/raw${data.default_branch}`; + const baseUrl = `${data.web_url.replace("http://", "https://")}/-/raw/${data.default_branch}`; return { readme: await fetchReadme(baseUrl, toRawUrl(data.readme_url)), diff --git a/web/src/scripts/ui/boot.ts b/web/src/scripts/ui/boot.ts @@ -1,96 +1,28 @@ import env from "../../constants/env"; -interface SystemInfo { - platform: string; - browser: string; - screen: string; - locale: string; - uptime: string; - timezone: string; +const GLITCH_CHARS = "▓▒░█▄▀■□▪▫●○◆◇"; +const SCRAMBLE_DURATION = 400; +const SCRAMBLE_INTERVAL = 30; +const HOLD_DURATION = 200; +const FADE_DURATION = 300; + +function getRandomChar(): string { + return GLITCH_CHARS[Math.floor(Math.random() * GLITCH_CHARS.length)]; } -function getSystemInfo(): SystemInfo { - const ua = navigator.userAgent; - - let platform = "Unknown"; - if (ua.includes("Mac")) platform = "Darwin"; - else if (ua.includes("Win")) platform = "Windows"; - else if (ua.includes("Linux")) platform = "Linux"; - else if (ua.includes("Android")) platform = "Android"; - else if (ua.includes("iPhone") || ua.includes("iPad")) platform = "iOS"; - - let browser = "Unknown"; - if (ua.includes("Firefox")) browser = "Firefox"; - else if (ua.includes("Edg")) browser = "Edge"; - else if (ua.includes("Chrome")) browser = "Chrome"; - else if (ua.includes("Safari")) browser = "Safari"; - - const screen = `${window.screen.width}x${window.screen.height}`; - const locale = navigator.language || "en-US"; - - const now = new Date(); - const hours = now.getHours().toString().padStart(2, "0"); - const minutes = now.getMinutes().toString().padStart(2, "0"); - const seconds = now.getSeconds().toString().padStart(2, "0"); - const uptime = `${hours}:${minutes}:${seconds}`; - - const timezone = Intl.DateTimeFormat().resolvedOptions().timeZone; - - return { platform, browser, screen, locale, uptime, timezone }; -} - -function generateNeofetch(sys: SystemInfo): string[] { - const logo = [ - " .--. ", - " |o_o | ", - " |:_/ | ", - " // \\ \\ ", - " (| | ) ", - " /'\\_ _/`\\ ", - " \\___)=(___/ ", - ]; - - const info = [ - `<span class="ansi-bold-accent">prime8</span>@<span class="ansi-bold-accent">dev</span>`, - '<span class="ansi-dim">──────────────</span>', - `<span class="ansi-bold-accent">OS</span>: <span class="ansi-white">${sys.platform}</span>`, - `<span class="ansi-bold-accent">Browser</span>: <span class="ansi-white">${sys.browser}</span>`, - `<span class="ansi-bold-accent">Resolution</span>: <span class="ansi-white">${sys.screen}</span>`, - `<span class="ansi-bold-accent">Time</span>: <span class="ansi-white">${sys.uptime}</span>`, - `<span class="ansi-bold-accent">Timezone</span>: <span class="ansi-white">${sys.timezone}</span>`, - `<span class="ansi-bold-accent">Locale</span>: <span class="ansi-white">${sys.locale}</span>`, - ]; - - const lines: string[] = []; - const maxLen = Math.max(logo.length, info.length); - - for (let i = 0; i < maxLen; i++) { - const left = logo[i] || " "; - const right = info[i] || ""; - lines.push( - `<span class="ansi-bold-white">${left.replace(/ /g, "&nbsp;")}</span>&nbsp;&nbsp;${right}`, - ); - } - - return lines; -} - -function generateBootMessages(): string[] { - const sys = getSystemInfo(); - const neofetchLines = generateNeofetch(sys); - - return [...neofetchLines]; +function scrambleText(length: number): string { + return Array.from({ length }, () => getRandomChar()).join(""); } -const CURSOR_CHAR = "█"; - function createBootOverlay(): HTMLElement { const overlay = document.createElement("div"); overlay.id = "boot-overlay"; overlay.innerHTML = ` <div class="boot-terminal"> - <div class="boot-content"></div> - <div class="boot-cursor">${CURSOR_CHAR}</div> + <div class="boot-text"> + <span class="boot-name"></span> + <span class="boot-title"></span> + </div> </div> `; document.body.appendChild(overlay); @@ -101,75 +33,49 @@ function sleep(ms: number): Promise<void> { return new Promise((resolve) => setTimeout(resolve, ms)); } -async function runBootSequence(overlay: HTMLElement): Promise<void> { - const content = overlay.querySelector(".boot-content") as HTMLElement; - const cursor = overlay.querySelector(".boot-cursor") as HTMLElement; - const messages = generateBootMessages(); - - await sleep(150); - - for (let i = 0; i < messages.length; i++) { - const line = messages[i]; - const lineEl = document.createElement("div"); - lineEl.className = "boot-line boot-line-hidden"; - lineEl.innerHTML = line || "&nbsp;"; - content.appendChild(lineEl); - - await sleep(40); - lineEl.classList.remove("boot-line-hidden"); - lineEl.classList.add("boot-line-visible"); - } +async function scrambleReveal( + element: HTMLElement, + target: string, + duration: number, +): Promise<void> { + const steps = Math.floor(duration / SCRAMBLE_INTERVAL); + const length = target.length; + + for (let i = 0; i <= steps; i++) { + const progress = i / steps; + const revealedCount = Math.floor(progress * length); - await sleep(300); - - const startMsg = document.createElement("div"); - startMsg.className = "boot-line boot-line-hidden"; - startMsg.innerHTML = - '<br><span class="ansi-bold-white">Starting </span><span class="ansi-bold-accent">prime8dev</span><span class="ansi-bold-white">...</span>'; - content.appendChild(startMsg); - await sleep(60); - startMsg.classList.remove("boot-line-hidden"); - startMsg.classList.add("boot-line-visible"); - - await sleep(400); - - cursor.style.display = "none"; - - const progressBar = document.createElement("div"); - progressBar.className = "boot-line boot-line-visible"; - content.appendChild(progressBar); - - const progressWidth = 30; - const bar = "=".repeat(progressWidth); - - progressBar.innerHTML = `<span class="progress-wrapper">[<span class="progress-bar">${bar}</span>]</span> <span class="progress-percent">000</span><span class="progress-percent-sign">%</span>`; - progressBar.style.fontFamily = "monospace"; - - const barEl = progressBar.querySelector(".progress-bar") as HTMLElement; - const percentEl = progressBar.querySelector(".progress-percent") as HTMLElement; - - barEl.style.background = `linear-gradient(to right, var(--accent) 0%, var(--accent) 0%, #606060 0%, #606060 100%)`; - barEl.style.webkitBackgroundClip = "text"; - barEl.style.backgroundClip = "text"; - barEl.style.color = "transparent"; - - const percentSignEl = progressBar.querySelector(".progress-percent-sign") as HTMLElement; - percentSignEl.style.color = `var(--accent)`; - - for (let i = 0; i <= progressWidth; i++) { - const pct = (i / progressWidth) * 100; - barEl.style.background = `linear-gradient(to right, var(--accent) ${pct}%, #606060 ${pct}%)`; - barEl.style.webkitBackgroundClip = "text"; - barEl.style.backgroundClip = "text"; - percentEl.textContent = Math.round(pct).toString().padStart(3, " "); - percentEl.style.color = `var(--accent)`; - await sleep(20); + const revealed = target.slice(0, revealedCount); + const scrambled = scrambleText(length - revealedCount); + + element.textContent = revealed + scrambled; + await sleep(SCRAMBLE_INTERVAL); } - await sleep(200); + element.textContent = target; +} + +async function runBootSequence(overlay: HTMLElement): Promise<void> { + const nameEl = overlay.querySelector(".boot-name") as HTMLElement; + const titleEl = overlay.querySelector(".boot-title") as HTMLElement; + + const name = "prime8.dev"; + const title = "Damian Myrda"; + + // Initialize with scrambled text + nameEl.textContent = scrambleText(name.length); + titleEl.textContent = scrambleText(title.length); + + await sleep(100); + + // Reveal name first, then title + await scrambleReveal(nameEl, name, SCRAMBLE_DURATION); + await scrambleReveal(titleEl, title, SCRAMBLE_DURATION); + + await sleep(HOLD_DURATION); overlay.classList.add("fade-out"); - await sleep(400); + await sleep(FADE_DURATION); overlay.remove(); }