diff --git a/README.css b/CSS/README.css similarity index 86% rename from README.css rename to CSS/README.css index d458b67..1fae20a 100644 --- a/README.css +++ b/CSS/README.css @@ -471,4 +471,107 @@ body[data-theme="day"] #dev-console li::before { margin: 0.5rem 0; font-family: monospace; border-left: 3px solid #ff4d4d; +} + +/* lower-case comment: stage 2 shake */ +@keyframes shake-anim { + + 0%, + 100% { + transform: translate(0, 0); + } + + 20% { + transform: translate(-5px, 5px); + } + + 40% { + transform: translate(5px, -5px); + } + + 60% { + transform: translate(-5px, -5px); + } + + 80% { + transform: translate(5px, 5px); + } +} + +body.shake { + animation: shake-anim 0.2s infinite; +} + +@keyframes flicker-anim { + + 0%, + 100% { + opacity: 1; + } + + 50% { + opacity: 0.5; + } +} + +body.flicker { + animation: flicker-anim 0.1s infinite; +} + +#virusHex { + color: #0f0; + background: black; +} + + +/* glitching text effect */ +@keyframes text-glitch { + 0% { + opacity: 1; + clip: rect(0, 9999px, 0, 0); + } + + 10% { + clip: rect(5px, 9999px, 10px, 0); + } + + 20% { + clip: rect(15px, 9999px, 20px, 0); + } + + 30% { + clip: rect(25px, 9999px, 30px, 0); + } + + 40% { + clip: rect(35px, 9999px, 40px, 0); + } + + 50% { + clip: rect(45px, 9999px, 50px, 0); + } + + 60% { + clip: rect(55px, 9999px, 60px, 0); + } + + 70% { + clip: rect(65px, 9999px, 70px, 0); + } + + 80% { + clip: rect(75px, 9999px, 80px, 0); + } + + 90% { + clip: rect(85px, 9999px, 90px, 0); + } + + 100% { + clip: rect(0, 9999px, 0, 0); + } +} + +#virusMsg { + text-align: center; } \ No newline at end of file diff --git a/links.css b/CSS/links.css similarity index 100% rename from links.css rename to CSS/links.css diff --git a/pageMenu.css b/CSS/pageMenu.css similarity index 100% rename from pageMenu.css rename to CSS/pageMenu.css diff --git a/projects.css b/CSS/projects.css similarity index 100% rename from projects.css rename to CSS/projects.css diff --git a/style.css b/CSS/style.css similarity index 100% rename from style.css rename to CSS/style.css diff --git a/README.js b/JS/README.js similarity index 82% rename from README.js rename to JS/README.js index 9e8ad9a..707750a 100644 --- a/README.js +++ b/JS/README.js @@ -169,38 +169,36 @@ const observer = new IntersectionObserver( { threshold: 0.1 } ); -document.addEventListener("DOMContentLoaded", () => { - document.querySelectorAll(".section").forEach((section, index) => { - observer.observe(section); - section.style.animationDelay = `${index * 0.2}s`; - }); - - createStarfield(); - typewriterEffect(); - particleEffectOnScroll(); - - let hovered = false; - const modTitle = document.querySelector("#moduleTitle"), - modules = modTitle.parentElement.querySelectorAll(".module"); - - modules.forEach((el) => { - el.addEventListener("mouseenter", () => el.classList.add("hovered")); - el.addEventListener("mouseleave", () => el.classList.remove("hovered")); - }); - - modTitle.addEventListener("mouseenter", async () => { - if (hovered) return; - hovered = true; - - for (const el of modules) { - el.classList.add("hovered"); - - // anim is .2 seconds - setTimeout(() => el.classList.remove("hovered"), 200); - - await new Promise((resolve) => setTimeout(resolve, 100)); - } - }); - - modTitle.addEventListener("mouseleave", () => (hovered = false)); +document.querySelectorAll(".section").forEach((section, index) => { + observer.observe(section); + section.style.animationDelay = `${index * 0.2}s`; }); + +createStarfield(); +typewriterEffect(); +particleEffectOnScroll(); + +let hovered = false; +const modTitle = document.querySelector("#moduleTitle"), + modules = modTitle.parentElement.querySelectorAll(".module"); + +modules.forEach((el) => { + el.addEventListener("mouseenter", () => el.classList.add("hovered")); + el.addEventListener("mouseleave", () => el.classList.remove("hovered")); +}); + +modTitle.addEventListener("mouseenter", async () => { + if (hovered) return; + hovered = true; + + for (const el of modules) { + el.classList.add("hovered"); + + // anim is .2 seconds + setTimeout(() => el.classList.remove("hovered"), 200); + + await new Promise((resolve) => setTimeout(resolve, 100)); + } +}); + +modTitle.addEventListener("mouseleave", () => (hovered = false)); diff --git a/JS/glitch.js b/JS/glitch.js new file mode 100644 index 0000000..5ea86a8 --- /dev/null +++ b/JS/glitch.js @@ -0,0 +1,134 @@ +async function triggerVirus() { + const stage1 = 3000; // 3 s red alert + const stage2 = 3000; // next 3 s data corruption + + const snapshot = await html2canvas(document.body); + const img = PIXI.Sprite.from(snapshot.toDataURL()); + + // wipe page & set black background + document.body.innerHTML = ""; + document.documentElement.style.cssText = + "background: black; margin: 0; padding: 0; overflow: hidden;"; + + // container for overlays/canvas + const container = document.createElement("div"); + container.id = "virusContainer"; + Object.assign(container.style, { + position: "fixed", top: 0, left: 0, + width: "100vw", height: "100vh", + display: "flex", alignItems: "center", + justifyContent: "center", flexDirection: "column", + color: "white", fontFamily: "monospace", + }); + document.body.appendChild(container); + + // -- stage 1: red alert + countdown -- + const alert = document.createElement("div"); + alert.id = "virusAlert"; + container.appendChild(alert); + + let countdown = 10; + alert.textContent = `⚠️ SYSTEM PURGE IN ${countdown} ⚠️`; + alert.style.cssText = "font-size: 3rem; color: #f00;"; + + const timerId = setInterval(() => { + countdown -= 1; + alert.textContent = `⚠️ SYSTEM PURGE IN ${countdown} ⚠️`; + // flash effect + alert.style.visibility = + alert.style.visibility === "hidden" ? "visible" : "hidden"; + }, 500); + + // after stage1 ms → stage 2 + setTimeout(() => { + clearInterval(timerId); + container.removeChild(alert); + + // -- stage 2: hex stream + shake/flicker -- + const hex = document.createElement("pre"); + hex.id = "virusHex"; + hex.style.cssText = "font-size:1rem; width:80vw; height:100vh; overflow:hidden;"; + container.appendChild(hex); + + // stream fake hex + const hexChars = "0123456789ABCDEF"; + const streamId = setInterval(() => { + let line = ""; + for (let i = 0; i < 64; i++) { + line += hexChars.charAt(Math.random() * 16 | 0); + } + hex.textContent += line + "\n"; + hex.scrollTop = hex.scrollHeight; + }, 50); + + document.body.classList.add("shake", "flicker"); + + setTimeout(() => { + console.debug("▶️ entering stage 3 (glitch filter)"); + + clearInterval(streamId); + container.removeChild(hex); + + // -- stage 3: pixi.js glitch filter on canvas -- + const canvas = document.createElement("canvas"); + canvas.id = "virusCanvas"; + + canvas.addEventListener('webglcontextlost', event => { + event.preventDefault(); // opt into manual recovery + console.warn('⚠️ my WebGL context was lost'); + }, false); + + canvas.width = window.innerWidth; + canvas.height = window.innerHeight; + + Object.assign(canvas.style, { + position: "fixed", top: 0, left: 0, + width: "100vw", height: "100vh", zIndex: 9999, + }); + + + container.appendChild(canvas); + + // initialize PixiJS application + const app = new PIXI.Application({ + view: canvas, + resizeTo: window, + resolution: 1, // force 1× device pixel ratio + autoDensity: true // keep CSS size but lower GPU size + }); + + app.renderer.view.addEventListener('webglcontextlost', (e) => { + e.preventDefault(); + console.warn('WebGL lost, falling back to CanvasRenderer'); + app.destroy(true, { children: true }); + }); + + + // Create a full-screen white rectangle as the sprite + img.width = window.innerWidth; + img.height = window.innerHeight; + app.stage.addChild(img); + + // apply realistic glitch filter + const filter = new PIXI.filters.GlitchFilter({ slices: 20, offset: 10 }); + app.stage.filters = [filter]; + + // animate the filter + app.ticker.add(() => { + filter.slices = 10 + Math.random() * 30; + filter.offset = Math.random() * 20; + }); + + console.log(app.renderer) + + // block all input + const block = (e) => { e.preventDefault(); e.stopImmediatePropagation(); }; + window.addEventListener("keydown", block, true); + window.addEventListener("mousedown", block, true); + window.addEventListener("touchstart", block, true); + + }, stage2); + }, stage1); + + return ""; +} diff --git a/pageMenu.js b/JS/pageMenu.js similarity index 100% rename from pageMenu.js rename to JS/pageMenu.js diff --git a/script.js b/JS/script.js similarity index 100% rename from script.js rename to JS/script.js diff --git a/terminal.js b/JS/terminal.js similarity index 59% rename from terminal.js rename to JS/terminal.js index 5b2b86d..bc0569e 100644 --- a/terminal.js +++ b/JS/terminal.js @@ -1,3 +1,27 @@ +const projectLinks = { + "bluesky-client": "https://github.com/ION606/bluesky-client", + "workout-app": "https://workout.ion606.com/", + "AI-overlord": "https://github.com/ION606/AI-overlord", + "black-hole-sim": "https://github.com/ION606/black-hole-sim", + "chatjs-main": "https://github.com/Proto-Chat/chatJS-main", + "custom_discordjs": "https://github.com/ION606/custom_discordjs", + "learn": "https://github.com/ION606/learn", + "ion-lang": "https://github.com/The-ION-Language/ION-Lang", + "vcs": "https://github.com/ION606/VCS", + "ml-pipeline": "https://github.com/ION606/ML-pipeline", + "browser-chromium": "https://github.com/ION606/browser-chromium", + "static-site-hosting": "https://github.com/ION606/static-site-hosting", + "procgen": "https://github.com/ION606/ProcGen", + "linkedin-api": "https://github.com/ION606/linkedin-api", + "github-to-fs": "https://github.com/ION606/github-to-fs", + "web-to-fish": "https://github.com/ION606/web-to-fish", + "commit_grabber": "https://github.com/ION606/commit_grabber", + "youtube-music-meta-extract": "https://github.com/ION606/youtube-music-meta-extract", + "mailpocket": "https://github.com/ION606/MailPocket", +}; + +const glitchText = 'T̵̙̻̭̤̺̱̥̖̤̭̗̜͓̓̈́̏͋̔̕̚͠h̴ͽ̳͎̳̱̘̎͛͆̓̐͑͛̈́̕̕͝͝é̷̛̮̥̲͇̊̅͋̏̊̅͊͝͝ ̵̡̛̪̮̦̘̘̼̼̺̪̪̋͋̀̌̇̉̋͌̾̿̓͝͝V̶̡̨̧̨̟̙̻͓̪͇̻̞̥͑̎͋͗̿́̓̌͒͊̈́́̚͠ơ̴̛̱̞̾̎̒̋̾̔̈́̓͑̋̉ȋ̴̡̛͔̙̘̝̙̬̠̹̙̻͖̽̿̓̑̈́͋́̐̕͠d̷̲̲̘̈́̑́̿̆̓̔͋́̓̋̅̏̚'; + // secret developer console toggle (using backtick key) const devConsoleToggle = () => { const devConsole = document.querySelector("#dev-console"); @@ -46,7 +70,7 @@ class TerminalFS { if (!cmdContent) { consoleOutput.innerHTML += `
+ .--. + |o_o | + |:_/ | + // \\ \\ + (| | ) + /'\\_ _/\\\` + \\___)=(___/ ++`, + ` +
+ (╯°□°)╯︵ ┻━┻ ++` + ]; + + const joke = jokes[Math.floor(Math.random() * jokes.length)], + art = asciiArts[Math.floor(Math.random() * asciiArts.length)]; + return `
${this.files[absPath].trim()}`;
@@ -284,6 +372,7 @@ class TerminalFS {