/* ============================================================ SORTARE POSTARI ============================================================ */ const sortedPosts = () => Object.entries(POSTS).sort((a, b) => Number(b[0]) - Number(a[0])); /* ============================================================ COMENZILE DISPONIBILE Fiecare cheie (ex: 'help', 'about') e o comanda Valoarea e o functie care intoarce un array de obiecte: { text: 'textul de afisat', cls: 'clasa-css' } Clasele CSS determina culoarea (vezi style.css -> .line.*) ============================================================ */ const COMMANDS_WITH_ARGS = { blog: 1, // "post 1" ok, "post 2 2" eroare // lore: 1, theme: 1, }; const birthday = new Date('2022-10-07'); const age = Math.floor((new Date() - birthday) / (1000 * 60 * 60 * 24)); const COMMANDS = { /* ── help ── lista cu toate comenzile disponibile */ help: () => [ { text: '', cls: 'blank' }, { text: 'Available commands:', cls: 'info' }, { text: ' about who am i?', cls: 'output' }, { text: ' banner displays ASCII art', cls: 'output' }, { text: ' blog thoughts and updates', cls: 'output' }, { text: ' clear clears the terminal', cls: 'output' }, { text: ' help shows available commands', cls: 'output' }, { text: ' history how did i get here', cls: 'output' }, // { text: ' lore the nemex lore (ROOT ACCESS REQUIRED)', cls: 'output' }, TODO { text: ' pipeline programs i use', cls: 'output' }, // { text: ' portfolio my projects (ROOT ACCESS REQUIRED)', cls: 'output' }, TODO { text: ' rodate current time in romania', cls: 'output' }, { text: ' skills what i can do', cls: 'output' }, { text: ' socials find me', cls: 'output' }, { text: ' specs current machine setup', cls: 'output' }, { text: ' thanks people who made this worthwhile', cls: 'output' }, { text: ' theme switch visual theme', cls: 'output' }, { text: '', cls: 'blank' }, ], /* ── about ── sectiunea de prezentare */ about: () => [ { text: '', cls: 'blank' }, { text: '┌─ Hello ──────────────────────────────────────┐', cls: 'info' }, { text: '│ │', cls: 'info' }, { text: '│ @nemesiseanu — romanian 3D animator │', cls: 'info' }, { text: '│ — mischievous af │', cls: 'info' }, { text: '│ — into electronics and gaming │', cls: 'info' }, { text: '│ — birthed on 20 jun 2003 │', cls: 'info' }, { text: '│ — hates hazbin hotel │', cls: 'info' }, { text: '│ │', cls: 'info' }, { text: '│ teal\'s the deal >:] │', cls: 'info' }, { text: '└──────────────────────────────────────────────┘', cls: 'info' }, { text: '', cls: 'blank' }, ], /* ── history ── sectiunea de prezentare */ history: () => [ { text: '', cls: 'blank' }, { text: ' NOV 2019 during CS class, a classmate showed me krunker.io', cls: 'output' }, { text: ' i was immediately hooked and decided instantly:', cls: 'dim' }, { text: ' i wanted to be a gaming youtuber!', cls: 'dim' }, { text: ' (earliest krunker videos i could find date back to around that time)', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' MAR 2020 created my first youtube channel (referred here as the legacy one)', cls: 'output' }, { text: ' first video uploaded 4 may 2020. ', cls: 'dim' }, { text: ' basically krunker gameplay with terrible editing skills.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' APR 2021 i wanted to animate krunker. found a blender tutorial by @giantninja5298', cls: 'output' }, { text: ' 20 apr 2021 — first blender render ever made.', cls: 'dim' }, { text: ' as i focused more on blender and highschool, krunker content faded.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' JUL 2022 got a lenovo legion laptop. still using it today', cls: 'output' }, { text: ' with proper hardware, i started to dip my toes into animation.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' JUL 2023 20 jul 2023 — the nemex mascot appeared for the first time', cls: 'output' }, { text: ' teal. mischievous. can manipulate black holes.', cls: 'dim' }, { text: ' GOD\'S PERFECT CREATURE!', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' OCT 2023 legacy channel hit 100 subscribers', cls: 'output' }, { text: ' launched my discord server around this time.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' JAN 2024 hazbin hotel s1 dropped', cls: 'output' }, { text: ' i declared myself its #1 hater.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' APR 2024 8 apr — created @nemesiseanu. retired the legacy channel', cls: 'output' }, { text: ' 15 apr — a hazbin hate reel went viral on instagram.', cls: 'dim' }, { text: ' ~3000 followers overnight (absolute ragebait).', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' AUG 2024 recovered 304 subs from the legacy channel', cls: 'output' }, { text: ' busiest year as an animator by far, with 54 videos total.', cls: 'dim' }, { text: ' also joined the blender goobers — a small 3D animator crew.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' OCT 2024 new channel hit 500 subscribers', cls: 'output' }, { text: ' discovered ultrakill. began livestreaming it.', cls: 'dim' }, { text: ' punching demons became a proper outlet for the hazbin hatred.', cls: 'dim' }, { text: ' also my immaturity got me banned from a few discord servers.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' APR 2025 1000 subscribers. 1 year on the new channel', cls: 'output' }, { text: ' 5 years since the legacy channel started.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' MAY 2025 significant physical changes', cls: 'output' }, { text: ' the kid voice was finally gone.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' SEP 2025 launched a secondary channel to separate content niches', cls: 'output' }, { text: ' tbf, it\'s a literal video dump, still nice to have.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' OCT 2025 i watched the hazbin hotel pilot', cls: 'output' }, { text: ' didn\'t like it.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' MAR 2026 ultrakill x jjk — first video to hit 100k views', cls: 'output' }, { text: ' also crossed 2000 subscribers.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' APR 2026 got monetized. year two on this channel', cls: 'output' }, { text: ' six years since it all started in a CS classroom.', cls: 'dim' }, { text: '', cls: 'blank' }, { text: ' still obscenely teal >:]', cls: 'info' }, { text: '', cls: 'blank' }, ], /* ── thanks ── sectiunea de prezentare */ thanks: () => [ { text: '', cls: 'blank' }, { text: ' my gratitude upon thee:', cls: 'dim' }, { text: '', cls: 'blank' }, //{ text: '', cls: 'blank' }, // { text: ' TOP DONORS ─────────────────────────────────', cls: 'warn' }, // { text: ' Nume3 ko-fi', cls: 'output' }, // { text: ' Nume4 youtube · member', cls: 'output' }, //{ text: '', cls: 'blank' }, // { text: ' SPONSORS ─────────────────────────────────', cls: 'warn' }, // { text: ' Nume3 ko-fi', cls: 'output' }, // { text: ' Nume4 youtube · member', cls: 'output' }, { text: ' NOT VIVZIEPOP for making that abominable hotel', cls: 'error' }, { text: '', cls: 'blank' }, { text: ' SPECIAL THANKS ─────────────────────────', cls: 'warn' }, { text: ' My family for birthing and encouraging me', cls: 'output' }, { text: ' Nomis and her incredible bees', cls: 'output' }, { text: ' TheCubezian for putting up with me', cls: 'output' }, { text: ' Executie & Linnea the sweetest (and wackiest) people i could find', cls: 'output' }, { text: ' PixelzwithaZ my greatest source of inspiration (and impostor syndrome)', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' SUPPORTERS ─────────────────────────────', cls: 'success' }, { text: ' vid163 MichaelBreath Mr.Bear', cls: 'output' }, { text: ' NULLBREAKER The Bagimator Toppin', cls: 'output' }, { text: ' Soofloo Inusitatus ace_of_cinder', cls: 'output' }, { text: ' GiorgioMaXv Ampixyy Copperon', cls: 'output' }, { text: ' Coblasteus TheSturmerer toinkity', cls: 'output' }, { text: ' Dirtyfrog51 Espark Bardur Giant Benford Adams', cls: 'output' }, { text: ' girlfailurebrain Frogger Wobbo', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' and to you, for being here!', cls: 'dim' }, { text: '', cls: 'blank' }, ], specs: () => [ { text: '', cls: 'blank' }, { text: ' SYSTEM ──────────────────', cls: 'warn' }, { text: ' OS Linux Mint 22.3 x86_64', cls: 'output' }, { text: ' Host Legion 5 15IMH6', cls: 'output' }, { text: ' Kernel Linux 6.8.0-111-generic', cls: 'output' }, { text: ` OS Age ${age} days`, cls: 'output' }, { text: ' OS Birthday 07 Oct 2022', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' HARDWARE ───────────────', cls: 'warn' }, { text: ' CPU Intel(R) Core(TM) i7-10750H (12) @ 5.00 GHz', cls: 'output' }, { text: ' GPU NVIDIA GeForce RTX 3050 Mobile', cls: 'output' }, { text: ' Memory 15.53 GiB', cls: 'output' }, { text: ' Storage 476.94 GiB SSD', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' DEVICES ───────────────', cls: 'warn' }, { text: ' Display 1920x1080 in 16", 120 Hz', cls: 'output' }, { text: ' Mouse Logitech G203 LIGHTSYNC Gaming Mouse', cls: 'output' }, { text: ' Mic HyperX SoloCast', cls: 'output' }, { text: '', cls: 'blank' } ], pipeline: () => [ { text: '', cls: 'blank' }, { text: ' 3D / Animation', cls: 'info' }, { text: ' Blender', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' Video / Streaming', cls: 'info' }, { text: ' Kdenlive', cls: 'output' }, { text: ' OBS Studio', cls: 'output' }, { text: ' Fade In', cls: 'output' }, { text: ' ntsc-rs', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' Audio', cls: 'info' }, { text: ' Audacity', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' Graphics / Design', cls: 'info' }, { text: ' Inkscape', cls: 'output' }, { text: ' Krita', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' Pixel Art', cls: 'info' }, { text: ' Aseprite', cls: 'output' }, { text: ' SLK_img2pixel', cls: 'output' }, { text: '', cls: 'blank' } ], /* ── skills ── skill-urile tehnice */ skills: () => [ { text: '', cls: 'blank' }, { text: ' 3D Graphics ██████████ experienced (5 years)', cls: 'output' }, { text: ' HDL ███████░░░ advanced', cls: 'output' }, { text: ' PCB Design ██████░░░░ intermediate', cls: 'output' }, { text: ' Vector Graphics █████░░░░░ intermediate', cls: 'output' }, { text: ' Video Editing ████████░░ advanced', cls: 'output' }, { text: ' Pixel Art ████░░░░░░ novice', cls: 'output' }, { text: '', cls: 'blank' } ], /* ── projects ── proiectele tale */ portfolio: () => [ { text: '', cls: 'blank' }, { text: 'ELEVATED PRIVILEGES REQUIRED', cls: 'error' }, { text: '', cls: 'blank' }, ], /* ── socials ── linkuri sociale */ socials: () => [ { text: '', cls: 'blank' }, { text: ' CONTACT ME ', link: 'planetnemex@gmail.com', cls: 'info', url: 'mailto:planetnemex@gmail.com' }, { text: ' YOUTUBE ', link: 'youtube.com/@nemesiseanu', cls: 'info', url: 'https://youtube.com/@nemesiseanu' }, { text: ' YOUTUBE 2 ', link: 'youtube.com/@unaesisemen', cls: 'info', url: 'https://youtube.com/@unaesisemen' }, { text: ' INSTAGRAM ', link: 'instagram.com/nemesiseanu', cls: 'info', url: 'https://instagram.com/nemesiseanu' }, { text: ' DISCORD ', link: 'discord.com/invite/gJR2MxKX5F', cls: 'info', url: 'https://discord.com/invite/gJR2MxKX5F' }, { text: ' KO-FI ', link: 'ko-fi.com/nemesiseanu', cls: 'info', url: 'https://ko-fi.com/nemesiseanu' }, { text: '', cls: 'blank' }, ], /* ── lore ── cu update-uri */ lore: () => [ { text: '', cls: 'blank' }, { text: 'ELEVATED PRIVILEGES REQUIRED', cls: 'error' }, { text: '', cls: 'blank' }, ], /* ── blog ── cu update-uri */ blog: (args) => { // cu argument → deschide postul if (args[0]) { const resolvedId = args[0] === 'latest' ? sortedPosts()[0][0] : args[0]; const p = POSTS[resolvedId]; if (!p) return [ { text: `post not found: ${args[0]}`, cls: 'error' }, { text: ` available: ${Object.keys(POSTS).join(', ')}`, cls: 'dim' }, ]; return [ { text: '', cls: 'blank' }, { text: `┌─ ${p.title}`, cls: 'dim' }, { text: `│ ${p.date}`, cls: 'dim' }, { text: '│', cls: 'dim' }, ...p.body.map(line => ({ text: `│ ${line}`, cls: line === '' ? 'dim' : 'output' })), { text: '│', cls: 'dim' }, { text: '└────────────────────────────────────────────', cls: 'dim' }, { text: '', cls: 'blank' }, ]; } // fara argument → lista posturilor return [ { text: '', cls: 'blank' }, { text: 'Recent posts:', cls: 'info' }, ...sortedPosts().map(([id, post]) => ({ text: ` [${id}] ${post.title} · ${post.date}`, cls: 'output' }) ), { text: '', cls: 'blank' }, { text: ' use: blog ex: blog 1', cls: 'dim' }, { text: ' use: blog latest latest post', cls: 'dim' }, { text: '', cls: 'blank' }, ]; }, /* ── rodate ── afiseaza data si ora in romania */ rodate: () => { const now = new Date(); // ora si data afisate in fusul Bucuresti const ora = now.toLocaleTimeString('en-US', { timeZone: 'Europe/Bucharest', hour: '2-digit', minute: '2-digit', hour12: false, }); const data = now.toLocaleDateString('en-US', { timeZone: 'Europe/Bucharest', weekday: 'long', day: '2-digit', month: 'long', year: 'numeric', }); // detecteaza daca e ora de vara sau iarna in Romania const tzFull = Intl.DateTimeFormat('en', { timeZone: 'Europe/Bucharest', timeZoneName: 'longOffset' }) .formatToParts(now) .find(p => p.type === 'timeZoneName') .value .replace('GMT', ''); // ora curenta in Bucuresti pentru comparatie const oraBuc = Number(now.toLocaleString('ro-RO', { timeZone: 'Europe/Bucharest', hour: '2-digit', hour12: false, })); const disponibil = oraBuc >= 8 && oraBuc < 23; // 8:00 si 23:00 Bucuresti convertite in fusul vizitatorului const azi = now.toLocaleDateString('en-CA', { timeZone: 'Europe/Bucharest' }); const start = new Date(`${azi}T08:00:00${tzFull}`); const end = new Date(`${azi}T23:00:00${tzFull}`); const fmt = d => d.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit', hour12: false }); return [ { text: '', cls: 'blank' }, { text: ` ${ora}`, cls: 'info' }, { text: ` ${data}`, cls: 'output' }, { text: '', cls: 'blank' }, disponibil ? { text: ` available (until ${fmt(end)} your time)`, cls: 'success' } : { text: ` offline (back at ${fmt(start)} your time)`, cls: 'error' }, { text: '', cls: 'blank' }, ]; }, /* ── banner ── ASCII art cu "N" la pornire */ banner: () => [ { text: '', cls: 'blank' }, { text: ' N N NNNNNNNNN', cls: 'ascii glow' }, { text: ' NN N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N N N ', cls: 'ascii glow' }, { text: ' N N N N NN ', cls: 'ascii glow' }, { text: 'NNNNNNNNN N N ', cls: 'ascii glow' }, { text: '', cls: 'blank' }, ], theme: (args) => { const name = args[0]; const available = ['ouranos', 'venera']; if (!name) return [ { text: '', cls: 'blank' }, { text: 'available themes:', cls: 'info' }, { text: ' ouranos default', cls: 'output' }, { text: ' venera warm theme', cls: 'output' }, { text: '', cls: 'blank' }, { text: ' use: theme ', cls: 'dim' }, { text: '', cls: 'blank' }, ]; if (!available.includes(name)) return [ { text: `theme not found: ${name}`, cls: 'error' }, { text: ` available: ${available.join(', ')}`, cls: 'dim' }, ]; // sterge toate clasele de tema, aplica cea noua document.body.className = name === 'ouranos' ? '' : `theme-${name}`; localStorage.setItem('ouran-theme', name); return [ { text: '', cls: 'blank' }, { text: `theme: ${name}`, cls: 'success' }, { text: '', cls: 'blank' }, ]; }, /* ── clear ── sterge tot din #output si intoarce array gol output.innerHTML = '' sterge toti copiii elementului */ clear: () => { output.innerHTML = ''; return []; }, }; /* ── Aliasuri ── comenzi alternative cu acelasi efect */ COMMANDS.man = () => COMMANDS.help(); // "man" afiseaza help-ul COMMANDS['?'] = COMMANDS.help; // "?" afiseaza help-ul