diff --git a/.idea/.gitignore b/.idea/.gitignore
deleted file mode 100644
index b58b603..0000000
--- a/.idea/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-# Default ignored files
-/shelf/
-/workspace.xml
-# Editor-based HTTP Client requests
-/httpRequests/
diff --git a/.idea/inspectionProfiles/Project_Default.xml b/.idea/inspectionProfiles/Project_Default.xml
deleted file mode 100644
index 25719e5..0000000
--- a/.idea/inspectionProfiles/Project_Default.xml
+++ /dev/null
@@ -1,10 +0,0 @@
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/modules.xml b/.idea/modules.xml
deleted file mode 100644
index dcca0ed..0000000
--- a/.idea/modules.xml
+++ /dev/null
@@ -1,8 +0,0 @@
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/sador.dev.iml b/.idea/sador.dev.iml
deleted file mode 100644
index 24643cc..0000000
--- a/.idea/sador.dev.iml
+++ /dev/null
@@ -1,12 +0,0 @@
-
-
-
-
-
-
-
-
-
-
-
-
\ No newline at end of file
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
deleted file mode 100644
index 35eb1dd..0000000
--- a/.idea/vcs.xml
+++ /dev/null
@@ -1,6 +0,0 @@
-
-
-
-
-
-
\ No newline at end of file
diff --git a/assets/script.js b/assets/script.js
deleted file mode 100644
index 147531b..0000000
--- a/assets/script.js
+++ /dev/null
@@ -1,44 +0,0 @@
-if (typeof age !== 'undefined') {
- const difference = (Date.now() - 1182722400000) / (1000 * 60 * 60 * 24 * 365);
- age.textContent = difference.toFixed(4);
-}
-
-if (typeof arrow_top !== 'undefined' && typeof arrow_bottom !== 'undefined') {
- [arrow_top, arrow_bottom].forEach(arrow => {
- arrow.addEventListener('click', () => {
- console.warn('scroll!')
- window.scrollTo({
- top: arrow === arrow_top ? 0 : document.body.scrollHeight,
- behavior: 'smooth'
- });
- });
- });
-}
-
-const smoothScrollLinks = document.querySelectorAll('a[href^="#"]');
-smoothScrollLinks.forEach(link => {
- link.addEventListener('click', event => {
- event.preventDefault();
- const target = document.querySelector(event.target.hash);
-
- target.scrollIntoView({
- behavior: 'smooth'
- });
- });
-});
-
-const theme = localStorage.getItem('theme');
-
-switch_theme?.addEventListener('click', () => {
- if (theme === 'dark') {
- localStorage.setItem('theme', 'light');
- } else {
- localStorage.setItem('theme', 'dark');
- }
-
- document.body.classList.toggle('dark');
-});
-
-if (theme === 'dark') {
- document.body.classList.add('dark');
-}
\ No newline at end of file
diff --git a/assets/style.css b/assets/style.css
deleted file mode 100644
index fb78505..0000000
--- a/assets/style.css
+++ /dev/null
@@ -1,98 +0,0 @@
-@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap');
-
-@import url('style.mobile.css');
-@import url('style.dark.css');
-
-:root {
- --font: 'Inconsolata', monospace;
- --font-size: 16px;
- --columns: 2;
-}
-
-html,
-body {
- margin: 0;
- padding: 0;
- height: 100%;
- width: 100%;
-}
-
-body {
- font-family: var(--font);
- font-size: var(--font-size);
- transition: background-color 0.5s, color 0.5s;
-}
-
-body#centered {
- display: grid;
- place-items: center;
- align-content: center;
-}
-
-body>div:not([id]) {
- display: grid;
- overflow-y: auto;
- place-items: center;
- align-content: center;
-
- float: left;
- height: 100%;
- width: calc(100% / var(--columns));
-}
-
-body>div:first-child #arrow {
- bottom: 0;
- width: 100%;
- display: none;
- cursor: pointer;
-
- color: #fff;
- text-align: center;
- background-color: black;
-}
-
-body>div>div#inner {
- overflow-y: auto;
- height: 100%;
-}
-
-ul {
- padding: 0;
-}
-
-ul,
-ol {
- list-style-type: none;
-}
-
-ul li::before,
-ol li::before {
- content: "⮑ ";
-}
-
-#age {
- font-weight: 900;
-}
-
-*[data-deprecated] {
- text-decoration: line-through;
-}
-
-*[data-deprecated]::after {
- content: ' (' attr(data-deprecated-entry, "deprecated") ')';
- color: red;
-}
-
-#contact li:first-child::before {
- content: "✉ ";
-}
-
-#switch_theme {
- color: blue;
- cursor: pointer;
- text-decoration: underline;
-}
-
-body>#arrows {
- display: none;
-}
diff --git a/assets/style.dark.css b/assets/style.dark.css
deleted file mode 100644
index d91aa58..0000000
--- a/assets/style.dark.css
+++ /dev/null
@@ -1,37 +0,0 @@
-:root {
- --font: 'Inconsolata', monospace;
- --font-size: 16px;
- --columns: 2;
-
- --background-color: #171616;
- --color: #fff;
-}
-
-body.dark {
- background-color: var(--background-color);
- color: var(--color);
-}
-
-body.dark>div:first-child #arrow {
- color: #000;
- text-align: center;
- background-color: #fff;
-}
-
-body>div:first-child #arrow {
- background-color: var(--color);
- color: var(--background-color);
-}
-
-body.dark a {
- color: var(--color);
-}
-
-body.dark #switch_theme {
- color: var(--color);
-}
-
-body.dark>#arrows>div {
- background-color: var(--color);
- color: var(--background-color);
-}
\ No newline at end of file
diff --git a/assets/style.mobile.css b/assets/style.mobile.css
deleted file mode 100644
index ccea640..0000000
--- a/assets/style.mobile.css
+++ /dev/null
@@ -1,30 +0,0 @@
-@media screen and (max-width: 767px) {
- body>div:not([id]) {
- height: unset !important;
- width: 100% !important;
- border-bottom: 1px solid;
- }
-
- body>div>div#inner {
- overflow-y: hidden !important;
- height: auto !important;
-
- padding: 1em !important;
- }
-
- body>#arrows {
- display: flex !important;
- width: 100% !important;
- position: fixed;
- bottom: 0;
- right: 0;
- }
-
- body>#arrows>div {
- width: 50%;
- text-align: center;
- background-color: black;
- color: white;
- cursor: pointer;
- }
-}
\ No newline at end of file
diff --git a/css/style.css b/css/style.css
new file mode 100644
index 0000000..01edf81
--- /dev/null
+++ b/css/style.css
@@ -0,0 +1,82 @@
+@import url('https://fonts.googleapis.com/css2?family=Inconsolata:wght@200..900&display=swap');
+@import url('style.dark.css');
+
+:root {
+ --background: #fff;
+ --color: #000;
+}
+
+html,
+body {
+ width: 100%;
+ height: 100%;
+}
+
+body {
+ margin: 0;
+ padding: 0;
+ color: var(--color);
+ background-color: var(--background);
+ font-family: 'Inconsolata', sans-serif;
+ transition: background 0.3s ease-in-out;
+}
+
+#container {
+ text-align: center;
+ height: 100%;
+ width: 100%;
+
+ align-content: center;
+}
+
+[data-tooltip] {
+ text-decoration: underline;
+}
+
+[data-tooltip]:hover:after {
+ content: " (" attr(data-tooltip) ")";
+}
+
+a {
+ color: var(--color);
+}
+
+ul.vertical li {
+ display: inline-block;
+}
+
+ul.vertical li:not(:first-child)::before {
+ content: " - ";
+ font-weight: bold;
+}
+
+input, a, #result {
+ transition: background 0.3s ease-in-out;
+}
+
+input {
+ font-family: 'Inconsolata', sans-serif;
+ font-size: 150%;
+ outline: none;
+
+ border: none;
+ border-bottom: 1px solid;
+
+ color: var(--color);
+ background-color: transparent;
+}
+
+#result {
+ white-space: preserve;
+
+ background-color: transparent;
+ color: var(--color);
+
+ padding: 2em;
+ margin: 2em;
+}
+
+#switch_theme {
+ text-decoration: underline;
+ cursor: pointer;
+}
\ No newline at end of file
diff --git a/css/style.dark.css b/css/style.dark.css
new file mode 100644
index 0000000..41b41a0
--- /dev/null
+++ b/css/style.dark.css
@@ -0,0 +1,12 @@
+body.dark {
+ color: var(--background) !important;
+ background-color: var(--color) !important;
+}
+
+body.dark input {
+ color: var(--background) !important;
+}
+
+body.dark #result, body.dark a {
+ color: var(--background) !important;
+}
\ No newline at end of file
diff --git a/icons/github.png b/icons/github.png
deleted file mode 100644
index 9490ffc..0000000
Binary files a/icons/github.png and /dev/null differ
diff --git a/icons/instagram.png b/icons/instagram.png
deleted file mode 100644
index 3146658..0000000
Binary files a/icons/instagram.png and /dev/null differ
diff --git a/icons/youtube.png b/icons/youtube.png
deleted file mode 100644
index c3bd27f..0000000
Binary files a/icons/youtube.png and /dev/null differ
diff --git a/index.copy.html b/index.copy.html
new file mode 100644
index 0000000..5b898ec
--- /dev/null
+++ b/index.copy.html
@@ -0,0 +1,227 @@
+
+
+
+
+
+
+
+
+
+
+
+ sador
+
+
+
+
+
+
sador
+
? years old boy, who loves to code.
+
+
my hobbies
+
+ - programming
+ - music
+ - old automotive
+
+
+
my profiles
+
+
+
contact
+
+
+
go to section:
+
+
+
+
switch theme
+
+
+
+
+
my projects
+
+
+
my music taste
+
i'm a fan of:
+
+ -
+ genres
+
+ - heavy metal
+ - black metal
+ - death metal
+ - doom metal
+ - hard rock
+
+
+ -
+ rock/metal bands/artists
+
+ - Behemoth
+ - Draconian
+ - Poisonblack
+ - To/Die/For
+ - For My Pain...
+ - Death
+ - Charon
+ - Entwine
+ - Slipknot
+
+
+
+
+
i enjoy these artists, but I can't say I'm a fan:
+
+ - Lacrimas Profundere
+ - Beseech
+ - Darkthrone
+ - Dark Funeral
+ - Venom
+
+
+
i recently started to listen to:
+
+ - Deathstars
+ - Carpatian Forest
+ - Gorgoroth
+ - Immortal
+ - Emperor
+ - Imminence
+
+
+
my hardware
+
+ -
+ OSes
+
+ - arch linux (kde plasma)
+ - degoogled android (lineageos)
+
+
+ -
+ in general
+
+ - cpu: intel i5-4590
+ - gpu: radeon rx 5500 xt
+ - bluetooth & wi-fi: fenvi t919
+ - keyboard: microsoft all-in-one media keyboard
+ - headphones: beats studio buds
+ - display: lg m2450d
+ - motherboard: asrock h97m pro4
+ - ram: 16gb ddr3
+ - ssd: adata 500gb
+
+
+
+
+
my software
+
+ -
+ recording/editing
+
+ - gimp
+ - davinci resolve
+
+
+ -
+ programming/web dev.
+
+ - vscodium
+ - android studio
+ - ssh in terminal
+ - postman
+ - git
+
+
+ -
+ internet
+
+ - firefox
+
+
+ - music - tidal
+ -
+ other
+
+ - bitwarden (self-hosted)
+ - libreoffice
+
+
+
+
+
plans
+
i want to:
+
+ - change style/look
+ - have maximum privacy
+ - forget about my past
+ - be happy
+ - be successful
+ - meet some kind and respective people
+
+
+
+
+
+
+
+
diff --git a/index.html b/index.html
index 5dfe566..346c3f0 100644
--- a/index.html
+++ b/index.html
@@ -1,227 +1,30 @@
-
-
-
-
-
-
-
-
-
-
-
- sador
-
-
-
-
-
-
sador
-
? years old boy, who loves to code.
-
-
my hobbies
-
- - programming
- - music
- - old automotive
-
-
-
my profiles
-
-
-
contact
-
-
-
go to section:
-
-
-
-
switch theme
-
-
-
-
-
my projects
-
-
-
my music taste
-
i'm a fan of:
-
- -
- genres
-
- - heavy metal
- - black metal
- - death metal
- - doom metal
- - hard rock
-
-
- -
- rock/metal bands/artists
-
- - Behemoth
- - Draconian
- - Poisonblack
- - To/Die/For
- - For My Pain...
- - Death
- - Charon
- - Entwine
- - Slipknot
-
-
-
-
-
i enjoy these artists, but I can't say I'm a fan:
-
- - Lacrimas Profundere
- - Beseech
- - Darkthrone
- - Dark Funeral
- - Venom
-
-
-
i recently started to listen to:
-
- - Deathstars
- - Carpatian Forest
- - Gorgoroth
- - Immortal
- - Emperor
- - Imminence
-
-
-
my hardware
-
- -
- OSes
-
- - arch linux (kde plasma)
- - degoogled android (lineageos)
-
-
- -
- in general
-
- - cpu: intel i5-4590
- - gpu: radeon rx 5500 xt
- - bluetooth & wi-fi: fenvi t919
- - keyboard: microsoft all-in-one media keyboard
- - headphones: beats studio buds
- - display: lg m2450d
- - motherboard: asrock h97m pro4
- - ram: 16gb ddr3
- - ssd: adata 500gb
-
-
-
-
-
my software
-
- -
- recording/editing
-
- - gimp
- - davinci resolve
-
-
- -
- programming/web dev.
-
- - vscodium
- - android studio
- - ssh in terminal
- - postman
- - git
-
-
- -
- internet
-
- - firefox
-
-
- - music - tidal
- -
- other
-
- - bitwarden (self-hosted)
- - libreoffice
-
-
-
-
-
plans
-
i want to:
-
- - change style/look
- - have maximum privacy
- - forget about my past
- - be happy
- - be successful
- - meet some kind and respective people
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+ sador
+
+
+
+
welcome to my website
+
switch theme
+
+
press Enter to run command
+
use 'help' to list all commands
+
+
[guest@website ~]$
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/js/commands.js b/js/commands.js
new file mode 100644
index 0000000..f39fd82
--- /dev/null
+++ b/js/commands.js
@@ -0,0 +1,191 @@
+const COMMANDS = [
+ {
+ name: 'help',
+ description: 'shows list of all commands',
+ usage: '',
+ usage_examples: ['help help', 'help music'],
+ arguments: [
+ {
+ name: 'command',
+ description: 'command name',
+ required: false,
+ check: (command) => !!command && !COMMANDS.find(c => c.name === command)
+ ? "invalid command"
+ : null
+ }
+ ],
+ output: (command) => !!command
+ ? getCommandHelp(command)
+ : COMMANDS
+ .map(c => `${c.name}: ${c.description}`)
+ .join('\n')
+ },
+ {
+ name: 'music',
+ description: 'show information about my music taste',
+ usage: '',
+ usage_examples: ['music heavy metal', 'music gothic metal', 'music'],
+ arguments: [
+ {
+ name: 'genre',
+ description: 'music genre',
+ required: false,
+ multiword: true,
+ check: (genre) => !!genre && !Object.keys(MUSIC_METADATA).includes(genre)
+ ? "no informations about this music genre, run 'music' command to find available genres"
+ : null
+ }
+ ],
+ output: (...partialGenre) => {
+ const genre = partialGenre.join(' ')
+
+ return !!genre
+ ? getGenreHelp(genre)
+ : `
+ i am listening to these music genres:
+
+ ${Object.keys(MUSIC_METADATA)
+ .map(genre => `${genre} - more info: 'music ${genre}'`)
+ .join('\n')}
+ `
+ }
+ },
+ {
+ name: 'hardware',
+ description: 'show information about my hardware',
+ usage: '',
+ usage_examples: ['hardware'],
+ arguments: [],
+ output: () => [
+ 'cpu: intel i5-4590',
+ 'gpu: radeon rx 5500 xt',
+ 'bluetooth & wi-fi: fenvi t919',
+ 'keyboard: microsoft all-in-one media keyboard',
+ 'headphones: beats studio buds',
+ 'display: lg m2450d',
+ 'motherboard: asrock h97m pro4',
+ 'ram: 16gb ddr3',
+ 'ssd: adata 500gb'
+ ].join('\n')
+ },
+ {
+ name: 'software',
+ description: 'show information about my software',
+ usage: '',
+ usage_examples: ['software'],
+ arguments: [],
+ output: () => [
+ 'recording/editing:',
+ '',
+ 'gimp',
+ 'davinci resolve',
+ '',
+ 'programming/web dev.:',
+ '',
+ 'vscodium',
+ 'android studio',
+ 'ssh in terminal',
+ 'postman',
+ 'git',
+ 'internet:',
+ 'firefox',
+ '',
+ 'music - tidal',
+ '',
+ 'other:',
+ '',
+ 'bitwarden (self-hosted)',
+ 'libreoffice'
+
+ ].join('\n')
+ },
+ {
+ name: 'plans',
+ description: 'show my plans',
+ usage: '',
+ usage_examples: ['plans'],
+ arguments: [],
+ output: () => [
+ 'i want to:',
+ '',
+ 'change style/look',
+ 'have maximum privacy',
+ 'forget about my past',
+ 'be happy',
+ 'be successful',
+ 'meet some kind and respective people'
+ ].join('\n')
+ },
+ {
+ name: 'links',
+ description: 'show URLs related to me & my services',
+ usage: '',
+ usage_examples: ['links'],
+ arguments: [],
+ output: () => Object.entries(LINKS)
+ .map(([name, url]) => `${name}`)
+ .join('\n')
+ },
+ {
+ name: 'curl',
+ description: 'open website in new tab',
+ usage: '',
+ usage_examples: ['curl google.com'],
+ arguments: [
+ {
+ name: 'url',
+ description: 'URL to open',
+ required: true,
+ multiword: true,
+ check: (url) => URL_REGEX.test(url)
+ ? null
+ : "invalid URL"
+ }
+ ],
+ output: (url) => {
+ Object.assign(document.createElement('a'), {
+ target: '_blank',
+ rel: 'noopener noreferrer',
+ href: url,
+ }).click();
+
+ return ''
+ }
+ }
+]
+
+function getGenreHelp(genreName) {
+ const genre = MUSIC_METADATA[genreName];
+
+ if (!genre) return 'music: no informations about this music genre';
+
+ return `
+ i enjoy listening to ${genreName}.
+ example ${genreName} artists/bands that i am listening to:
+
+ ${genre.join('\n')}
+ `
+}
+
+function getCommandHelp(commandName) {
+ const command = COMMANDS.find(c => c.name === commandName)
+
+ if (!command) return 'help: invalid command';
+
+ return `
+ -> ${command.name} command
+
+ ${command.description}
+
+ usage:
+ ${command.name} ${command.usage}
+
+ arguments:
+ ${command.arguments
+ .map(a => `${a.required ? 'required: ' : ''}${a.name} - ${a.description}`)
+ .join('\n')}
+
+ examples:
+ ${command.usage_examples.join('\n')}
+ `
+}
\ No newline at end of file
diff --git a/js/data.js b/js/data.js
new file mode 100644
index 0000000..000d946
--- /dev/null
+++ b/js/data.js
@@ -0,0 +1,36 @@
+const URL_REGEX = /https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/g;
+const LINKS = {
+ 'gitea': 'https://git.sador.me',
+ 'instagram': 'https://instagram.com/sadorowo',
+ 'immich': 'https://photos.sador.me/',
+ 'email': 'mailto:contact@sador.me'
+}
+
+const MUSIC_METADATA = {
+ 'heavy metal': [
+ 'Poisonblack',
+ 'Metallica'
+ ],
+ 'gothic metal': [
+ 'Draconian',
+ 'Beseech',
+ 'To/Die/For',
+ 'For My Pain...',
+ 'Charon',
+ 'Entwine'
+ ],
+ 'death suicidal black metal': [
+ 'minuta agonii',
+ 'Decalius'
+ ],
+ 'black metal': [
+ 'Behemoth',
+ 'Venom',
+ 'Carpathian Forest',
+ 'Darkthrone'
+ ],
+ 'nu metal': [
+ 'Slipknot',
+ 'Evanescence'
+ ]
+}
\ No newline at end of file
diff --git a/js/script.js b/js/script.js
new file mode 100644
index 0000000..8dd9c6f
--- /dev/null
+++ b/js/script.js
@@ -0,0 +1,77 @@
+// update age
+function updateAge() {
+ const difference = (Date.now() - 1182722400000) / (1000 * 60 * 60 * 24 * 365);
+ age.textContent = difference.toFixed(4);
+}
+
+// wrapper for setInterval
+const runEvery = (task, ms) => {
+ task()
+ setInterval(task, ms)
+}
+
+// smooth scrolling
+const smoothScrollLinks = document.querySelectorAll('a[href^="#"]');
+smoothScrollLinks.forEach(link => {
+ link.addEventListener('click', event => {
+ event.preventDefault();
+ const target = document.querySelector(event.target.hash);
+
+ target.scrollIntoView({ behavior: 'smooth' });
+ });
+});
+
+// theme management
+switch_theme?.addEventListener('click', () => {
+ const theme = localStorage.getItem('theme');
+
+ localStorage.setItem('theme', theme === 'light' ? 'dark' : 'light');
+ document.body.classList.toggle('dark')
+})
+
+// command management
+const TAB = ' ';
+
+command?.addEventListener('focusout', () => {
+ setTimeout(() => command.focus(), 0);
+})
+
+command?.addEventListener('keydown', e => {
+ if (e.key === 'Enter') {
+ const [commandName, ...args] = e.target.value.split(' ') || [e.target.value]
+ const command = COMMANDS.find(c => c.name === commandName)
+
+ if (!command) {
+ result.textContent = 'invalid command';
+ return
+ }
+
+ const { arguments, output } = command;
+ for (const i in arguments) {
+ const argument = arguments[i]
+
+ if (argument.required && !args[i]) {
+ result.textContent = `argument ${argument.name} is missing`;
+ return
+ }
+
+ const errorMessage = argument.check(argument.multiword ? args.join(' ') : args[i])
+
+ if (typeof errorMessage === 'string') {
+ result.textContent = `error while executing ${command.name}: ${errorMessage}`;
+ return
+ }
+ }
+
+ result.innerHTML = output(...args).replaceAll(TAB, '')
+ }
+})
+
+// run all tasks
+if (typeof age !== 'undefined') runEvery(updateAge, 10 * 1000)
+command?.focus()
+
+// initial theme configuration
+const theme = localStorage.getItem('theme');
+if (theme === 'dark')
+ document.body.classList.add('dark')
\ No newline at end of file