diff options
| author | Paul Buetow <paul@buetow.org> | 2026-04-25 22:13:32 +0300 |
|---|---|---|
| committer | Paul Buetow <paul@buetow.org> | 2026-04-25 22:13:32 +0300 |
| commit | a5cb9f7ad4b2bcb7ac367854147f661ab83c5ec1 (patch) | |
| tree | 96db490ed72dec49c4bc2aba8539fd57f3246f47 /internal/generator/templates/shared/shared.js | |
| parent | cc9c69ce748416ad1d9b28592ec463434023d63c (diff) | |
can swap themes dynamicallyv0.12.0
Diffstat (limited to 'internal/generator/templates/shared/shared.js')
| -rw-r--r-- | internal/generator/templates/shared/shared.js | 1343 |
1 files changed, 1343 insertions, 0 deletions
diff --git a/internal/generator/templates/shared/shared.js b/internal/generator/templates/shared/shared.js new file mode 100644 index 0000000..bf3cf35 --- /dev/null +++ b/internal/generator/templates/shared/shared.js @@ -0,0 +1,1343 @@ + var SNONUX_SOUNDS = (typeof window !== "undefined" && window.SNONUX_SOUNDS) || {}; + // Inject wild-mode badge used by all themes + (function() { var b=document.createElement('div'); b.id='sno-wild-badge'; b.textContent='WILD MODE'; document.body.appendChild(b); })(); + const SNONUX_WILD_PRESETS = { + aurora: { + banner: 'SOLAR STORM', + ticker: ['FIELD INTERFERENCE', 'PLASMA DRIFT', 'PARTICLE BOMBARDMENT', 'CHROMATIC SPIKE'], + scraps: ['MAGNETIC SHEAR', 'SOLAR WIND', 'AURORA NOISE', 'ION STORM', 'POLAR ARC'], + flash: 'rgba(220,255,240,0.72)', + emoji: ['\u2728','\u2604\uFE0F','\u{1F320}','\u{1F30C}','\u2B50'] + }, + brutalist: { + banner: 'STRUCTURAL COLLAPSE', + ticker: ['CONDEMNED', 'REBAR EXPOSED', 'FOUNDATION FAILURE', 'CRACK PROPAGATION'], + scraps: ['CONDEMNED', 'RUST BLEED', 'SHEAR WALL LOST', 'LOAD PATH BROKEN', 'SPALLING'], + flash: 'rgba(255,210,190,0.58)', + emoji: ['\u{1F9F1}','\u2692\uFE0F','\u26A0\uFE0F','\u{1F6A7}','\u{1F4A5}'] + }, + cosmos: { + banner: 'SUPERNOVA', + ticker: ['SINGULARITY LENSING', 'GAMMA BURST', 'SHOCKWAVE EXPANDING', 'SPACETIME TEAR'], + scraps: ['WHITEOUT', 'EVENT HORIZON', 'RADIATION FRONT', 'LENS LOCK', 'CORE BREACH'], + flash: 'rgba(255,255,255,0.8)', + emoji: ['\u{1F30C}','\u2604\uFE0F','\u{1F4AB}','\u2B50','\u{1FA90}'] + }, + dos: { + banner: 'KERNEL PANIC', + ticker: ['ABORT, RETRY, FAIL?', 'MEMORY CORRUPTION', 'STACK DUMP', 'SEGMENT FAULT'], + scraps: ['DEAD BEEF', 'C0FFEE', 'BAD SECTOR', 'IRQ STORM', 'NULL PTR', 'HEX DUMP'], + flash: 'rgba(255,255,255,0.88)', + emoji: ['\u{1F4BE}','\u{1F4BB}','\u26A1','\u2620\uFE0F','\u{1F41B}'] + }, + matrix: { + banner: 'CASCADE FAILURE', + ticker: ['SENTINEL TRACE', 'GLYPH SATURATION', 'PHOSPHOR BURN-IN', 'RAIN AT TERMINAL VELOCITY'], + scraps: ['SENTINEL', '0XDECODE', 'OVERRIDE', 'TRACE LOST', 'MACHINE DREAM'], + flash: 'rgba(180,255,190,0.68)', + emoji: ['\u{1F441}\uFE0F','\u{1F4A0}','\u{1F916}','\u{1F50D}','\u26D3\uFE0F'] + }, + neon: { + banner: 'GAS DISCHARGE', + ticker: ['TUBE ARC', 'ULTRAVIOLET BLEED', 'STROBE LOCK', 'SHORT CIRCUIT'], + scraps: ['ARC OVERLOAD', 'PLASMA SIGN', 'NOBLE GAS', 'HARD STROBE', 'OVERDRIVE'], + flash: 'rgba(255,245,170,0.72)', + emoji: ['\u26A1','\u{1F4A5}','\u{1F52E}','\u2728','\u{1F388}'] + }, + ocean: { + banner: 'HADAL DESCENT', + ticker: ['PRESSURE SPIKE', 'BIOLUMINESCENT SWARM', 'ABYSSAL DRAG', 'TSUNAMI FRONT'], + scraps: ['NO SURFACE', 'CRUSH DEPTH', 'TENTACLE DRIFT', 'SONAR LOST', 'DEEP CURRENT'], + flash: 'rgba(200,255,255,0.54)', + emoji: ['\u{1F419}','\u{1F420}','\u{1F30A}','\u{1F41A}','\u{1F9DC}'] + }, + plasma: { + banner: 'FUSION BREACH', + ticker: ['CONTAINMENT FAILURE', 'TOKAMAK DISTORTION', 'THERMAL RUNAWAY', 'WHITE-BLUE CORE'], + scraps: ['ION SPRAY', 'FIELD LOSS', 'HEAT HAZE', 'QUENCH', 'ARC SHELL'], + flash: 'rgba(230,250,255,0.78)', + emoji: ['\u{1F300}','\u26A1','\u{1F4A0}','\u2728','\u{1F52C}'] + }, + retro: { + banner: 'TAPE EAT', + ticker: ['TRACKING LOSS', 'CHROMA SPLIT', 'MAGNETIC SNOW', 'CLICK-EJECT'], + scraps: ['NO SIGNAL', 'HEAD DRAG', 'ROLL HOLD', 'SNOW PACK', 'EJECT CYCLE'], + flash: 'rgba(255,226,178,0.6)', + emoji: ['\u{1F4FC}','\u{1F4FA}','\u{1F3AE}','\u{1F579}\uFE0F','\u{1F4FB}'] + }, + retrofuture: { + banner: 'ATOMIC TWILIGHT', + ticker: ['GEIGER STATIC', 'FALLOUT DUST', 'RADIATION BURN', 'IRRADIATED SEPIA'], + scraps: ['FALLOUT', 'BETA LEAK', 'ASH DRIFT', 'HALF-LIFE', 'GLOW CLOUD'], + flash: 'rgba(255,240,180,0.62)', + emoji: ['\u2622\uFE0F','\u{1F4A3}','\u{1F3ED}','\u2623\uFE0F','\u{1F9EA}'] + }, + spaceage: { + banner: 'RE-ENTRY BURN', + ticker: ['HEAT SHIELD LOSS', 'PLASMA BLACKOUT', 'COMMS STATIC', 'G-FORCE COMPRESSION'], + scraps: ['BLACKOUT', 'SPARK SHOWER', 'PLASMA SHEATH', 'HULL GLOW', 'COMMS LOST'], + flash: 'rgba(255,220,190,0.68)', + emoji: ['\u{1F680}','\u{1F6F8}','\u{1FA90}','\u{1F30D}','\u2B50'] + }, + synthwave: { + banner: 'GRID COLLAPSE', + ticker: ['VOID PERSPECTIVE', 'MOLTEN SUN', 'CHROMA TEAR', 'OUT OF MEMORY'], + scraps: ['VOID GRID', 'SUN DRIP', 'NEON PANIC', 'FRAME DROP', 'MEMORY STARVE'], + flash: 'rgba(255,210,255,0.68)', + emoji: ['\u{1F305}','\u{1F3B6}','\u{1F3B9}','\u{1F338}','\u{1F52E}'] + }, + terminal: { + banner: 'FORK BOMB', + ticker: ['PROCESS STORM', 'STACK TRACE WATERFALL', 'MEMORY GARBAGE', 'BSOD CREEP'], + scraps: ['PID 65535', 'STACK OVERFLOW', 'OOM KILL', 'PANIC', '(:'], + flash: 'rgba(180,255,180,0.7)', + emoji: ['\u{1F4BB}','\u{1F41B}','\u2620\uFE0F','\u{1F5A5}\uFE0F','\u26A1'] + }, + tropicale: { + banner: 'CATEGORY 5', + ticker: ['HORIZONTAL RAIN', 'STORM SURGE', 'DEBRIS FIELD', 'WIND SHEAR'], + scraps: ['PALM SNAP', 'SURGE LINE', 'SPRAY WALL', 'FLYING ROOF', 'LANDFALL'], + flash: 'rgba(240,255,255,0.74)', + emoji: ['\u{1F334}','\u{1F3D6}\uFE0F','\u{1F940}','\u{1F965}','\u{1F30A}'] + }, + noir: { + banner: 'BLACKOUT DISTRICT', + ticker: ['BLINDS SLAMMED SHUT', 'SIREN SWEEP', 'PROJECTOR BURN', 'MIDNIGHT DOWNPOUR'], + scraps: ['NO WITNESSES', 'WET ASPHALT', 'RED CHANNEL', 'BLUE CHANNEL', 'SMOKE CURTAIN'], + flash: 'rgba(255,245,225,0.66)', + emoji: ['\u{1F576}\uFE0F','\u{1F52B}','\u{1F3A9}','\u{1F6AC}','\u{1F5DD}\uFE0F'] + }, + cathedral: { + banner: 'LAST JUDGMENT', + ticker: ['BELL SHOCKWAVE', 'INCENSE FIRESTORM', 'ROSE WINDOW FRACTURE', 'APSE IN FLAME'], + scraps: ['REQUIEM', 'SHARD RAIN', 'VESPER BURN', 'GLORIA STATIC', 'NAVE COLLAPSE'], + flash: 'rgba(255,239,202,0.72)', + emoji: ['\u{1F54E}','\u{1F56F}\uFE0F','\u271D\uFE0F','\u{1F54A}\uFE0F','\u{1F3F0}'] + }, + surveillance: { + banner: 'TOTAL COMPROMISE', + ticker: ['CAMERA MESH BREACH', 'TRACKING LOSS', 'MULTIPLEX PANIC', 'ALERT CASCADE'], + scraps: ['FLAGGED', 'OVERRIDDEN', 'TRACE LOOP', 'BOX LOST', 'ALERT 99'], + flash: 'rgba(210,255,225,0.72)', + emoji: ['\u{1F4F9}','\u{1F441}\uFE0F','\u{1F6A8}','\u{1F50D}','\u{1F4E1}'] + }, + biomech: { + banner: 'CONTAINMENT RUPTURE', + ticker: ['SYNAPSE STORM', 'TISSUE ARC', 'MEMBRANE TEAR', 'HYBRID OVERDRIVE'], + scraps: ['VENTRICLE', 'MYCELIUM', 'RUPTURE', 'BIOFILM', 'NERVE GRID'], + flash: 'rgba(255,205,220,0.7)', + emoji: ['\u{1F9EC}','\u{1F9E0}','\u{1F9A0}','\u{1F52C}','\u{1FAC0}'] + }, + paper: { + banner: 'PRESS JAM', + ticker: ['TONER BLIZZARD', 'INK BLEED', 'COPY LAMP WHITEOUT', 'PAGE STORM'], + scraps: ['MISPRINT', 'SKEWED FEED', 'RAG EDGE', 'CARBON DUST', 'REDACTION'], + flash: 'rgba(255,250,236,0.82)', + emoji: ['\u{1F4C4}','\u270F\uFE0F','\u{1F4CE}','\u2702\uFE0F','\u{1F5DE}\uFE0F'] + }, + volcano: { + banner: 'PYROCLASTIC SURGE', + ticker: ['ASH CASCADE', 'LAVA BOMB IMPACT', 'EARTHQUAKE SHAKE', 'SULFUR CLOUD'], + scraps: ['ASHFALL', 'VENT BLAST', 'PYROCLAST', 'SEISMIC HIT', 'MAGMA SPRAY'], + flash: 'rgba(255,220,150,0.72)', + emoji: ['\u{1F30B}','\u{1F525}','\u{1F4A5}','\u2668\uFE0F','\u{1FAA8}'] + } + }; + function snonuxDetectThemeName() { + // The shell sets window.SNONUX_CURRENT_THEME synchronously in <head>. + // Falls back to the splash class for resilience if something raced. + if (typeof window !== 'undefined' && window.SNONUX_CURRENT_THEME) { + return window.SNONUX_CURRENT_THEME; + } + var splash = document.getElementById('splash-overlay'); + if (splash) { + for (var i = 0; i < splash.classList.length; i++) { + var cls = splash.classList[i]; + if (cls.indexOf('splash-') === 0 && cls !== 'splash-overlay' && cls.indexOf('splash--') !== 0) { + return cls.slice(7); + } + } + } + return 'neon'; + } + + // snonuxSwitchTheme persists the user's choice and reloads. + // The shell's boot script picks it up on the next load. + function snonuxSwitchTheme(theme) { + var all = (typeof window !== 'undefined' && window.SNONUX_ALL_THEMES) || []; + if (all.indexOf(theme) < 0) return; + try { localStorage.setItem('snonuxTheme', theme); } catch (_) {} + location.reload(); + } + + function snonuxRandomTheme() { + var all = (typeof window !== 'undefined' && window.SNONUX_ALL_THEMES) || []; + if (all.length <= 1) return null; + var current = snonuxDetectThemeName(); + var pool = all.filter(function (t) { return t !== current; }); + return pool[Math.floor(Math.random() * pool.length)]; + } + function snonuxEnsureWildRoot() { + var root = document.getElementById('sno-wild-root'); + if (root) return root; + root = document.createElement('div'); + root.id = 'sno-wild-root'; + root.setAttribute('aria-hidden', 'true'); + root.innerHTML = + '<div id="sno-wild-colorwash" class="sno-wild-layer"></div>' + + '<div id="sno-wild-rain" class="sno-wild-layer"></div>' + + '<div id="sno-wild-wave" class="sno-wild-layer"></div>' + + '<div id="sno-wild-beacon" class="sno-wild-layer"></div>' + + '<div id="sno-wild-noise" class="sno-wild-layer"></div>' + + '<div id="sno-wild-banner"></div>' + + '<div id="sno-wild-ticker"><span></span></div>' + + '<div id="sno-wild-scraps"></div>'; + document.body.appendChild(root); + return root; + } + function snonuxApplyWildPreset(theme) { + var preset = SNONUX_WILD_PRESETS[theme] || SNONUX_WILD_PRESETS.neon; + var html = document.documentElement; + var body = document.body; + html.setAttribute('data-sno-theme', theme); + body.setAttribute('data-sno-theme', theme); + var root = snonuxEnsureWildRoot(); + var banner = root.querySelector('#sno-wild-banner'); + var ticker = root.querySelector('#sno-wild-ticker span'); + var scraps = root.querySelector('#sno-wild-scraps'); + banner.textContent = preset.banner; + ticker.textContent = ' ' + preset.ticker.join(' // ') + ' // ' + preset.ticker.join(' // ') + ' '; + scraps.innerHTML = ''; + var phrases = preset.scraps || []; + var count = theme === 'dos' || theme === 'terminal' || theme === 'matrix' ? 22 : 16; + for (var i = 0; i < count; i++) { + var span = document.createElement('span'); + span.textContent = phrases[i % phrases.length]; + span.style.setProperty('--x', (8 + Math.random() * 84).toFixed(2) + '%'); + span.style.setProperty('--y', (12 + Math.random() * 72).toFixed(2) + '%'); + span.style.setProperty('--rot', ((Math.random() * 36) - 18).toFixed(1) + 'deg'); + span.style.setProperty('--dx', ((Math.random() * 120) - 60).toFixed(1) + 'px'); + span.style.setProperty('--dy', ((Math.random() * 100) - 50).toFixed(1) + 'px'); + span.style.setProperty('--dur', (4.5 + Math.random() * 5.5).toFixed(2) + 's'); + span.style.setProperty('--delay', (-Math.random() * 6).toFixed(2) + 's'); + scraps.appendChild(span); + } + window._snonuxWildTheme = theme; + window._snonuxWildFlashColor = preset.flash || 'rgba(255,255,255,0.7)'; + } + function snonuxPulseFlash(color, duration) { + var ov = document.createElement('div'); + ov.style.cssText = 'position:fixed;inset:0;z-index:9998;pointer-events:none;opacity:0;'; + document.body.appendChild(ov); + var tone = color || window._snonuxWildFlashColor || 'rgba(255,255,255,0.7)'; + [0, 70, 150, 250].forEach(function(d, i) { + setTimeout(function() { + ov.style.transition = 'opacity 0.08s linear'; + ov.style.background = tone; + ov.style.opacity = (i % 2 === 0) ? '0.82' : '0'; + }, d); + }); + setTimeout(function() { + ov.style.transition = 'opacity 0.25s linear'; + ov.style.opacity = '0'; + }, Math.max(180, duration || 320)); + setTimeout(function() { ov.remove(); }, Math.max(520, duration || 320) + 260); + } + function snonuxScheduleWildBursts() { + clearTimeout(window._snonuxWildBurstTimer); + if (!window._snoWildActive) return; + var delay = 1400 + Math.random() * 3600; + window._snonuxWildBurstTimer = setTimeout(function() { + if (!window._snoWildActive) return; + snonuxPulseFlash(window._snonuxWildFlashColor, 260); + snonuxScheduleWildBursts(); + }, delay); + } + function snonuxSetWildState(on) { + var body = document.body; + var badge = document.getElementById('sno-wild-badge'); + body.classList.toggle('sno-wild-active', !!on); + if (badge) badge.classList.toggle('sno-wild-on', !!on); + if (on) { + snonuxApplyWildPreset(window._snonuxWildTheme || snonuxDetectThemeName()); + snonuxScheduleWildBursts(); + body.classList.add('sno-wild-hue'); + snonuxStartFlyingEmoji(); + snonuxStartRandomFlips(); + } else { + clearTimeout(window._snonuxWildBurstTimer); + body.classList.remove('sno-wild-hue'); + snonuxStopFlyingEmoji(); + snonuxStopRandomFlips(); + } + } + // === WILD FLYING EMOJI === + function snonuxStartFlyingEmoji() { + snonuxStopFlyingEmoji(); + var zone = document.getElementById('sno-flyzone'); + if (!zone) { + zone = document.createElement('div'); + zone.id = 'sno-flyzone'; + zone.setAttribute('aria-hidden', 'true'); + document.body.appendChild(zone); + } + function spawn() { + if (!window._snoWildActive) return; + var preset = SNONUX_WILD_PRESETS[window._snonuxWildTheme] || SNONUX_WILD_PRESETS.neon; + var emojis = preset.emoji || ['\u2B50']; + var s = document.createElement('span'); + s.textContent = emojis[Math.floor(Math.random() * emojis.length)]; + var top = (5 + Math.random() * 80).toFixed(1); + var dur = (3 + Math.random() * 4).toFixed(2); + var dir = Math.random() > 0.5 ? 'sno-fly-lr' : 'sno-fly-rl'; + var wobble = ((Math.random() - 0.5) * 60).toFixed(0); + var rot = (180 + Math.random() * 540).toFixed(0); + s.style.setProperty('--ftop', top + '%'); + s.style.setProperty('--fy', wobble + 'px'); + s.style.setProperty('--frot', rot + 'deg'); + s.style.animationName = dir; + s.style.animationDuration = dur + 's'; + zone.appendChild(s); + setTimeout(function() { s.remove(); }, parseFloat(dur) * 1000 + 200); + window._snoFlyTimer = setTimeout(spawn, 800 + Math.random() * 2200); + } + spawn(); + } + function snonuxStopFlyingEmoji() { + clearTimeout(window._snoFlyTimer); + var zone = document.getElementById('sno-flyzone'); + if (zone) zone.innerHTML = ''; + } + // === WILD RANDOM FLIPS === + function snonuxStartRandomFlips() { + snonuxStopRandomFlips(); + function flip() { + if (!window._snoWildActive) return; + var allPosts = document.querySelectorAll('.post:not(.post-active)'); + if (allPosts.length > 0) { + var p = allPosts[Math.floor(Math.random() * allPosts.length)]; + p.classList.add('sno-fx-flip'); + setTimeout(function() { p.classList.remove('sno-fx-flip'); }, 1500); + } + window._snoFlipTimer = setTimeout(flip, 2500 + Math.random() * 4000); + } + window._snoFlipTimer = setTimeout(flip, 1500 + Math.random() * 2000); + } + function snonuxStopRandomFlips() { + clearTimeout(window._snoFlipTimer); + var flipped = document.querySelectorAll('.sno-fx-flip'); + flipped.forEach(function(el) { el.classList.remove('sno-fx-flip'); }); + } + (function snonuxWildSetup() { + window._snoWildActive = !!window._snoWildActive; + snonuxApplyWildPreset(snonuxDetectThemeName()); + snonuxSetWildState(window._snoWildActive); + })(); + // Dramatic lightning flash on wild mode activation/deactivation + function snonuxWildFlash(on) { + var ov = document.createElement('div'); + ov.style.cssText = 'position:fixed;inset:0;z-index:9998;pointer-events:none;'; + document.body.appendChild(ov); + if (on) { + // Three rapid lightning flashes on activation + [0, 80, 180, 300, 440].forEach(function(d, i) { + setTimeout(function() { + ov.style.background = (i % 2 === 0) ? 'rgba(255,255,200,0.72)' : 'transparent'; + }, d); + }); + setTimeout(function() { ov.remove(); }, 550); + // Persistent storm overlay with intermittent flicker + var storm = document.createElement('div'); + storm.id = 'sno-wild-storm'; + storm.style.cssText = 'position:fixed;inset:0;z-index:4998;pointer-events:none;' + + 'background:radial-gradient(ellipse at 50% 0%,rgba(255,255,200,0.88) 0%,transparent 55%);' + + 'animation:sno-wild-flicker 3.7s ease-in-out infinite;'; + document.body.appendChild(storm); + } else { + // Brief dark veil on deactivation + ov.style.background = 'rgba(0,0,0,0.45)'; + ov.style.transition = 'opacity 0.45s'; + setTimeout(function() { ov.style.opacity = '0'; }, 60); + setTimeout(function() { ov.remove(); }, 550); + var storm = document.getElementById('sno-wild-storm'); + if (storm) { storm.style.transition = 'opacity 0.5s'; storm.style.opacity = '0'; setTimeout(function() { storm.remove(); }, 600); } + } + } + function snonuxWaveType(w) { + if (w === 'square') return 'square'; + if (w === 'triangle') return 'triangle'; + return 'sine'; + } + (function splashSetup() { + var el = document.getElementById('splash-overlay'); + if (!el) return; + var splashAudioCtx = null; + var splashChimePlayed = false; + function playSplashChime() { + if (splashChimePlayed) return; + try { + if (!splashAudioCtx) { + splashAudioCtx = new (window.AudioContext || window.webkitAudioContext)(); + } + var ctx = splashAudioCtx; + function ring() { + splashChimePlayed = true; + // Theme override: if defined, plays its own sound and returns true to skip the default chime. + if (window.snonuxSplashSound && window.snonuxSplashSound(ctx)) return; + var now = ctx.currentTime; + var sp = SNONUX_SOUNDS.splash; + var freqs = sp.freqs; + var spacing = sp.spacing != null ? sp.spacing : 0.075; + var gainAm = sp.gain != null ? sp.gain : 0.1; + var wave = snonuxWaveType(sp.wave); + var i, osc, g, t0; + for (i = 0; i < freqs.length; i++) { + osc = ctx.createOscillator(); + g = ctx.createGain(); + osc.connect(g); + g.connect(ctx.destination); + osc.type = wave; + osc.frequency.value = freqs[i]; + t0 = now + i * spacing; + g.gain.setValueAtTime(0, t0); + g.gain.linearRampToValueAtTime(gainAm, t0 + 0.028); + g.gain.exponentialRampToValueAtTime(0.001, t0 + 0.52); + osc.start(t0); + osc.stop(t0 + 0.55); + } + } + ctx.resume().then(ring).catch(function() {}); + } catch (_) {} + } + function dismiss() { + if (typeof splashDrift !== 'undefined') splashDrift.stop(); + if (el.classList.contains('splash--dismissed')) return; + el.classList.add('splash--dismissed'); + el.setAttribute('aria-hidden', 'true'); + } + function show() { + if (typeof splashDrift !== 'undefined') splashDrift.reset(); + document.documentElement.classList.remove('sno-splash-skip'); + el.classList.remove('splash--dismissed'); + el.removeAttribute('aria-hidden'); + el.focus({ preventScroll: true }); + } + function openSplashFromHeader(e) { + if (e.target.closest('a')) return; + e.preventDefault(); + if (typeof modalDrift !== 'undefined') modalDrift.stop(); + var modal = document.getElementById('post-modal'); + if (modal) modal.classList.remove('active'); + show(); + } + function bindHeaderTriggers() { + var triggers = document.querySelectorAll('.logo-mark, .logo-title h1, #sn-logo'); + triggers.forEach(function(trigger) { + trigger.addEventListener('click', openSplashFromHeader); + }); + } + bindHeaderTriggers(); + // Exposed so the runtime theme-meta apply can re-bind after replacing + // <header> innerHTML — the freshly-injected nodes have no listeners. + window._snonuxRebindHeader = bindHeaderTriggers; + window._snonuxDismissSplash = dismiss; + window._snonuxShowSplash = show; + if (document.documentElement.classList.contains('sno-splash-skip')) { + dismiss(); + return; + } + playSplashChime(); + el.addEventListener('pointerdown', function() { playSplashChime(); }, { passive: true }); + el.addEventListener('click', function(e) { e.preventDefault(); dismiss(); }); + el.focus({ preventScroll: true }); + })(); + + // === SPECIAL EFFECTS HELPERS === + function playBassDrop() { + try { + var ctx = new (window.AudioContext || window.webkitAudioContext)(); + var osc = ctx.createOscillator(); + var gain = ctx.createGain(); + osc.connect(gain); gain.connect(ctx.destination); + osc.type = 'sawtooth'; + osc.frequency.setValueAtTime(130, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(28, ctx.currentTime + 0.55); + gain.gain.setValueAtTime(0.22, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.9); + osc.start(); osc.stop(ctx.currentTime + 0.95); + } catch(_) {} + } + function snonuxParticleBurst(count, color) { + var burst = document.createElement('div'); + burst.id = 'sno-burst'; + burst.setAttribute('aria-hidden', 'true'); + document.body.appendChild(burst); + var cx = window.innerWidth / 2, cy = window.innerHeight / 2; + for (var i = 0; i < count; i++) { + var s = document.createElement('span'); + var angle = (i / count) * Math.PI * 2 + (Math.random() - 0.5) * 0.6; + var dist = 60 + Math.random() * 280; + s.style.left = cx + 'px'; + s.style.top = cy + 'px'; + s.style.setProperty('--px', (Math.cos(angle) * dist).toFixed(1) + 'px'); + s.style.setProperty('--py', (Math.sin(angle) * dist).toFixed(1) + 'px'); + s.style.setProperty('--pdur', (0.25 + Math.random() * 0.35).toFixed(2) + 's'); + s.style.setProperty('--pdel', (Math.random() * 0.08).toFixed(2) + 's'); + s.style.width = (3 + Math.random() * 6) + 'px'; + s.style.height = s.style.width; + s.style.background = color || 'currentColor'; + burst.appendChild(s); + } + setTimeout(function() { burst.remove(); }, 900); + } + function snonuxKonamiExplosion() { + document.querySelectorAll('.post').forEach(function(p) { + p.classList.add('sno-fx-flip'); + setTimeout(function() { p.classList.remove('sno-fx-flip'); }, 1500); + }); + var ov = document.createElement('div'); + ov.style.cssText = 'position:fixed;inset:0;z-index:9997;pointer-events:none;background:rgba(255,255,255,0.92);mix-blend-mode:difference;transition:opacity 0.25s'; + document.body.appendChild(ov); + setTimeout(function() { ov.style.opacity='0'; setTimeout(function(){ov.remove();}, 300); }, 220); + snonuxParticleBurst(72, '#fff'); + playBassDrop(); + var banner = document.createElement('div'); + banner.id = 'sno-konami-banner'; + banner.textContent = 'UNLOCKED'; + document.body.appendChild(banner); + requestAnimationFrame(function() { banner.style.opacity='1'; }); + setTimeout(function() { banner.style.opacity='0'; setTimeout(function(){banner.remove();}, 350); }, 2200); + } + function snonuxCriticalOverload() { + var ov = document.createElement('div'); + ov.style.cssText = 'position:fixed;inset:0;z-index:9997;pointer-events:none;background:#fff;mix-blend-mode:difference;'; + document.body.appendChild(ov); + [0, 70, 140, 210].forEach(function(d,i){ + setTimeout(function(){ ov.style.opacity = (i%2===0)?'1':'0.25'; }, d); + }); + setTimeout(function(){ ov.style.transition='opacity 0.3s'; ov.style.opacity='0'; setTimeout(function(){ov.remove();}, 350); }, 320); + document.querySelectorAll('.post').forEach(function(p){ + p.classList.add('sno-fx-flip'); + setTimeout(function(){ p.classList.remove('sno-fx-flip'); }, 1500); + }); + playBassDrop(); + snonuxParticleBurst(96, window._snonuxWildFlashColor || '#fff'); + } + // Konami code tracker + (function konamiSetup(){ + var seq = ['ArrowUp','ArrowUp','ArrowDown','ArrowDown','ArrowLeft','ArrowRight','ArrowLeft','ArrowRight','b','a']; + var idx = 0; + document.addEventListener('keydown', function(e){ + if (e.key === seq[idx]) { idx++; if (idx >= seq.length) { idx = 0; snonuxKonamiExplosion(); } } + else { idx = (e.key === seq[0]) ? 1 : 0; } + }); + })(); + // CRT overlay injection + (function crtSetup(){ + if (document.getElementById('sno-crt-overlay')) return; + var crt = document.createElement('div'); + crt.id = 'sno-crt-overlay'; + crt.innerHTML = '<div class="crt-scanlines"></div><div class="crt-flicker"></div>'; + crt.setAttribute('aria-hidden','true'); + document.body.appendChild(crt); + var svg = document.createElementNS('http://www.w3.org/2000/svg','svg'); + svg.id = 'sno-crt-svg'; + svg.setAttribute('style','position:absolute;width:0;height:0;'); + svg.innerHTML = '<defs><filter id="sno-crt-distort"><feTurbulence type="fractalNoise" baseFrequency="0.012 0.006" numOctaves="2" result="noise"/><feDisplacementMap in="SourceGraphic" in2="noise" scale="3" xChannelSelector="R" yChannelSelector="G"/></filter></defs>'; + document.body.appendChild(svg); + })(); + // Seasonal effects + (function seasonalEffects(){ + var month = new Date().getMonth(); + var season = (month >= 11 || month <= 1) ? 'winter' : (month <= 4) ? 'spring' : (month <= 7) ? 'summer' : 'autumn'; + document.body.classList.add('sno-season-' + season); + })(); + // === VAPORWAVE SUNSET (synthwave / retro only) === + (function vaporwaveSetup(){ + var theme = snonuxDetectThemeName(); + if (theme !== 'synthwave' && theme !== 'retro') return; + var sunset = document.createElement('div'); + sunset.id = 'sno-vaporwave-sunset'; + sunset.setAttribute('aria-hidden','true'); + sunset.innerHTML = '<svg style="width:100%;height:100%;" preserveAspectRatio="none" viewBox="0 0 100 100"><defs><linearGradient id="vwg" x1="0" y1="0" x2="0" y2="1"><stop offset="0%" stop-color="#ff00cc" stop-opacity="0.35"/><stop offset="40%" stop-color="#ff00cc" stop-opacity="0.1"/><stop offset="100%" stop-color="#0a0a1a" stop-opacity="0"/></linearGradient></defs><ellipse cx="50" cy="95" rx="55" ry="18" fill="url(#vwg)"/><rect x="0" y="0" width="100" height="100" fill="none"/><g opacity="0.35">' + Array.from({length:12},function(_,i){return '<rect x="0" y="' + (75 + i*2.1) + '" width="100" height="' + (1.2 + i*0.2) + '" fill="#ff00cc" opacity="' + (0.5 - i*0.04).toFixed(2) + '"/>';}).join('') + '</g></svg>'; + document.body.appendChild(sunset); + document.body.classList.add('sno-vaporwave-on'); + })(); + // === PARALLAX TILT === + (function parallaxSetup(){ + var enabled = true; // on by default + document.body.classList.add('sno-parallax-on'); + window.snonuxToggleParallax = function(on){ enabled = on; document.body.classList.toggle('sno-parallax-on', on); }; + document.addEventListener('mousemove', function(e){ + if (!enabled) return; + var x = (e.clientX / window.innerWidth - 0.5) * 2; + var y = (e.clientY / window.innerHeight - 0.5) * 2; + var ov = document.querySelector('.overlay'); + if (ov) { + var rotY = (x * 4.5).toFixed(2); + var rotX = (-y * 4.0).toFixed(2); + ov.style.transform = 'rotateY(' + rotY + 'deg) rotateX(' + rotX + 'deg) scale(1.04)'; + } + // Subtle depth per post + document.querySelectorAll('.post').forEach(function(p, i){ + var depth = ((i % 3) - 1) * 12; + p.style.transform = 'translateZ(' + depth + 'px)'; + }); + }, { passive: true }); + })(); + // === HACKER HOVER SCRAMBLE === + (function scrambleSetup(){ + var glyphs = '!<>-_\\/[]{}—=+*^?#________'; + function scramble(el){ + var original = el.getAttribute('data-scramble') || el.textContent; + if (!el.getAttribute('data-scramble')) el.setAttribute('data-scramble', original); + var len = original.length; + var iter = 0; + var interval = setInterval(function(){ + el.textContent = original.split('').map(function(ch, i){ + if (i < iter) return original[i]; + return glyphs[Math.floor(Math.random() * glyphs.length)]; + }).join(''); + iter += 1/3; + if (iter >= len) { clearInterval(interval); el.textContent = original; } + }, 30); + } + function attach(){ + document.querySelectorAll('.post-header strong, .logo-title h1, .splash-title').forEach(function(el){ + if (el._scrambleAttached) return; + el._scrambleAttached = true; + el.addEventListener('mouseenter', function(){ scramble(el); }); + }); + } + attach(); + // re-attach after any dynamic content changes (not common here but safe) + window.snonuxAttachScramble = attach; + })(); + // === SCREENSHOT FLASH === + function snonuxScreenshotFlash(){ + var flash = document.createElement('div'); + flash.id = 'sno-shutter-flash'; + document.body.appendChild(flash); + flash.style.transition = 'none'; + flash.style.opacity = '1'; + // Shutter sound + try { + var ctx = new (window.AudioContext || window.webkitAudioContext)(); + var osc = ctx.createOscillator(); + var gain = ctx.createGain(); + var noise = ctx.createScriptProcessor(4096, 1, 1); + noise.onaudioprocess = function(e){ + var out = e.outputBuffer.getChannelData(0); + for (var i = 0; i < out.length; i++) out[i] = (Math.random() * 2 - 1) * 0.15; + }; + noise.connect(gain); gain.connect(ctx.destination); + gain.gain.setValueAtTime(0.15, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + 0.12); + noise.connect(gain); + osc.connect(gain); + osc.type = 'square'; + osc.frequency.setValueAtTime(600, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(60, ctx.currentTime + 0.1); + osc.start(); osc.stop(ctx.currentTime + 0.12); + setTimeout(function(){ noise.disconnect(); }, 200); + } catch(_) {} + setTimeout(function(){ + flash.style.transition = 'opacity 0.35s ease'; + flash.style.opacity = '0'; + setTimeout(function(){ flash.remove(); }, 400); + }, 80); + } + // === POST SCATTER === + function snonuxPostScatter(){ + var all = document.querySelectorAll('.post'); + all.forEach(function(p){ + var sx = ((Math.random() - 0.5) * window.innerWidth * 0.6).toFixed(1) + 'px'; + var sy = ((Math.random() - 0.5) * window.innerHeight * 0.6).toFixed(1) + 'px'; + var sr = ((Math.random() - 0.5) * 25).toFixed(1) + 'deg'; + p.style.setProperty('--sx', sx); + p.style.setProperty('--sy', sy); + p.style.setProperty('--sr', sr); + p.classList.add('sno-scatter'); + setTimeout(function(){ p.classList.remove('sno-scatter'); }, 950); + }); + playBassDrop(); + } + // === RAINBOW SPARKLE TRAIL === + (function rainbowSparkle(){ + var throttle = 0; + var hue = 0; + document.addEventListener('pointermove', function(e){ + var now = Date.now(); + if (now - throttle < 45) return; + throttle = now; + var d = document.createElement('div'); + d.className = 'sno-sparkle'; + var size = 3 + Math.random() * 5; + d.style.width = size + 'px'; + d.style.height = size + 'px'; + d.style.left = (e.clientX - size / 2 + (Math.random() - 0.5) * 10) + 'px'; + d.style.top = (e.clientY - size / 2 + (Math.random() - 0.5) * 10) + 'px'; + d.style.background = 'hsl(' + hue + ',90%,65%)'; + d.style.opacity = '0.85'; + d.style.animation = 'sno-sparkle ' + (0.35 + Math.random() * 0.25).toFixed(2) + 's ease-out forwards'; + document.body.appendChild(d); + setTimeout(function() { d.remove(); }, 650); + hue = (hue + 22) % 360; + }, { passive: true }); + })(); + // === KEYBOARD NAVIGATION === + // j / ArrowDown → next post k / ArrowUp → previous post + // h / ArrowLeft → previous page l / ArrowRight → next page + // PageUp/PageDown → scroll the post list; re-highlight post at top of visible area + // Enter / click post → expand modal Esc → close modal + const posts = document.querySelectorAll('.post'); + let currentIndex = posts.length > 0 ? 0 : -1; + var prevPageURL = (typeof window !== "undefined") ? (window.snonuxPrevPageURL || null) : null; + var nextPageURL = (typeof window !== "undefined") ? (window.snonuxNextPageURL || null) : null; + + if (currentIndex >= 0) selectPost(0); + + function setActiveHighlight(index, playSound, scrollIntoView) { + if (posts.length === 0) return; + var prevIdx = currentIndex; + if (currentIndex >= 0) posts[currentIndex].classList.remove('post-active'); + currentIndex = Math.max(0, Math.min(index, posts.length - 1)); + posts[currentIndex].classList.add('post-active'); + if (prevIdx >= 0 && prevIdx !== currentIndex && posts[prevIdx]) { + var ghost = posts[prevIdx].querySelector('.sno-afterimage'); + if (!ghost) { + ghost = document.createElement('div'); + ghost.className = 'sno-afterimage'; + posts[prevIdx].appendChild(ghost); + } + ghost.classList.remove('sno-afterimage-active'); + void ghost.offsetWidth; + ghost.classList.add('sno-afterimage-active'); + } + if (scrollIntoView) { + posts[currentIndex].scrollIntoView({ behavior: 'smooth', block: 'nearest' }); + } + if (playSound) playNavSound(); + } + + function selectPost(index, direction) { + setActiveHighlight(index, true, true); + if (direction && posts[currentIndex]) { + var post = posts[currentIndex]; + post.classList.remove('sno-enter-down', 'sno-enter-up'); + void post.offsetWidth; + post.classList.add(direction === 'down' ? 'sno-enter-down' : 'sno-enter-up'); + setTimeout(function() { post.classList.remove('sno-enter-down', 'sno-enter-up'); }, 320); + } + if (window.snonuxNavEffect) window.snonuxNavEffect(); + } + + /** Pick the post that should be active for the current viewport (anchor near top of visible area). */ + function activeIndexForVisibleRegion(sc) { + if (posts.length === 0) return -1; + var scrTop, scrBot, anchorY; + if (sc) { + var scr = sc.getBoundingClientRect(); + scrTop = scr.top; + scrBot = scr.bottom; + anchorY = scr.top + Math.min(scr.height * 0.18, 100); + } else { + scrTop = 0; + scrBot = window.innerHeight; + anchorY = window.innerHeight * 0.15; + } + var i, pr; + for (i = 0; i < posts.length; i++) { + pr = posts[i].getBoundingClientRect(); + if (pr.top <= anchorY && anchorY < pr.bottom) return i; + } + for (i = 0; i < posts.length; i++) { + pr = posts[i].getBoundingClientRect(); + if (pr.bottom > scrTop && pr.top < scrBot) return i; + } + return posts.length - 1; + } + + function playNavSound() { + try { + var n = SNONUX_SOUNDS.nav; + const ctx = new (window.AudioContext || window.webkitAudioContext)(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.connect(gain); gain.connect(ctx.destination); + osc.frequency.value = n.freq; + osc.type = snonuxWaveType(n.wave); + var dur = n.dur != null ? n.dur : 0.08; + var g = n.gain != null ? n.gain : 0.12; + gain.gain.setValueAtTime(g, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + dur); + osc.start(ctx.currentTime); osc.stop(ctx.currentTime + dur + 0.02); + } catch (_) {} + } + + function playOpenSound() { + try { + var o = SNONUX_SOUNDS.open; + const ctx = new (window.AudioContext || window.webkitAudioContext)(); + const osc = ctx.createOscillator(); + const gain = ctx.createGain(); + osc.connect(gain); gain.connect(ctx.destination); + osc.type = snonuxWaveType(o.wave); + var dur = o.dur != null ? o.dur : 0.14; + var g = o.gain != null ? o.gain : 0.1; + osc.frequency.setValueAtTime(o.start, ctx.currentTime); + osc.frequency.exponentialRampToValueAtTime(o.end, ctx.currentTime + dur); + gain.gain.setValueAtTime(g, ctx.currentTime); + gain.gain.exponentialRampToValueAtTime(0.001, ctx.currentTime + dur + 0.06); + osc.start(ctx.currentTime); osc.stop(ctx.currentTime + dur + 0.07); + } catch (_) {} + } + + function playCloseSound() { + try { |
