mirror of
https://github.com/pepaskopec-blip/skopec.dev.git
synced 2026-10-27 15:14:33 +00:00
Compare commits
No commits in common. "a1db8ae3e4c5286fb44c2510b60e353ecc152933" and "12e472d7509edece42feef656dcedefe82583f6e" have entirely different histories.
a1db8ae3e4
...
12e472d750
9 changed files with 42 additions and 680 deletions
4
404.html
4
404.html
|
|
@ -28,7 +28,7 @@
|
|||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20261027">
|
||||
<link rel="stylesheet" href="style.css?v=20261023">
|
||||
</head>
|
||||
<body>
|
||||
<a class="skip-link" href="#main">Skip to content</a>
|
||||
|
|
@ -82,6 +82,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js?v=20261027" defer></script>
|
||||
<script src="script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20261027">
|
||||
<link rel="stylesheet" href="style.css?v=20261023">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
|
|
@ -100,7 +100,7 @@
|
|||
<li>
|
||||
<span class="hw-overview-label">Total cores</span>
|
||||
<strong>34 cores</strong>
|
||||
<span class="hw-overview-sub">50 threads across 4 devices</span>
|
||||
<span class="hw-overview-sub">48 threads across 4 devices</span>
|
||||
</li>
|
||||
<li>
|
||||
<span class="hw-overview-label">Combined memory</span>
|
||||
|
|
@ -136,7 +136,7 @@
|
|||
<span class="project-status">Artix Linux · SwayFX</span>
|
||||
</div>
|
||||
<h3>Custom build · 2022</h3>
|
||||
<p>Main PC running Artix Linux with SwayFX wm/(wayland) compositor, also I use Waybar as my top bar (you can customize it with css), Wofi for application opener — assembled in 2022.</p>
|
||||
<p>Main PC running Artix Linux with SwayFX wm — assembled in 2022.</p>
|
||||
<ul class="project-pills" aria-label="Key desktop specs">
|
||||
<li>Ryzen 5 5600</li>
|
||||
<li>RX 6700 10 GB</li>
|
||||
|
|
@ -216,7 +216,7 @@
|
|||
<span class="project-status">macOS · Daily driver</span>
|
||||
</div>
|
||||
<h3>MacBook Air 13″ · M5 · base</h3>
|
||||
<p>Portable machine for school and on-the-go work. Now I use it more than my "main" PC, because I bought usb hub with HDMI port, so I keep it plugged in to my monitor.</p>
|
||||
<p>Portable machine for school and on-the-go work.</p>
|
||||
<ul class="project-pills" aria-label="Key laptop specs">
|
||||
<li>Apple M5 (10C)</li>
|
||||
<li>16 GB Unified</li>
|
||||
|
|
@ -461,6 +461,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js?v=20261027" defer></script>
|
||||
<script src="script.js?v=20261022" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
22
index.html
22
index.html
|
|
@ -1,5 +1,5 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="cs">
|
||||
<html lang="cs" xmlns="http://www.w3.org/1999/html">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20261027">
|
||||
<link rel="stylesheet" href="style.css?v=20261023">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
|
|
@ -73,7 +73,7 @@
|
|||
"name": "Who are you?",
|
||||
"acceptedAnswer": {
|
||||
"@type": "Answer",
|
||||
"text": "Josef — a 16-year-old student at SSŠVT in the Czech Republic, into IT, web development, and home labbing."
|
||||
"text": "Josef — a 16-year-old student at SSŠVT in the Czech Republic, into graphic design, web development, and home labbing."
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -81,15 +81,15 @@
|
|||
"name": "Can I get access to your services?",
|
||||
"acceptedAnswer": {
|
||||
"@type": "Answer",
|
||||
"text": "If you want to try one of my services, message skopcaak1 on Instagram or email josef@skopec.dev."
|
||||
"text": "Most *.skopec.dev services are private. If you know me, ask — otherwise they are not open to the public."
|
||||
}
|
||||
},
|
||||
{
|
||||
"@type": "Question",
|
||||
"name": "Is this site open source? Can I see the code?",
|
||||
"name": "Where is the source for this site?",
|
||||
"acceptedAnswer": {
|
||||
"@type": "Answer",
|
||||
"text": "Yes. The source is on GitHub: pepaskopec-blip/skopec.dev."
|
||||
"text": "On GitHub: pepaskopec-blip/skopec.dev."
|
||||
}
|
||||
},
|
||||
{
|
||||
|
|
@ -162,15 +162,15 @@
|
|||
<p class="about-text">
|
||||
I’m a 16-year-old student from the Czech Republic specializing in graphic design and web development at SSŠVT.
|
||||
I’m a home lab enthusiast and developer — I run Linux on my desktop and my own Proxmox server where I host things like Jellyfin for movies and TV shows,
|
||||
Navidrome for music, Ollama for local AI, SearXNG as a search engine, and more. Now I have joined Terra.hackclub, that requires at least 10 hours of human coding or 30 hours of ai coding per week for three months, the final reward for completing this is Framework laptop or iPad, so I want to complete it.
|
||||
Navidrome for music, Ollama for local AI, SearXNG as a search engine, and more. Now I have joined Terra.hackclub, that requires at least 10 hours of human coding or 30 hours of ai coding per week for 3 three months, the final reward for completing this is Framework laptop or iPad, so I want to complete it.
|
||||
</p>
|
||||
<p class="about-text">
|
||||
Outside of screens I train five times a week on a UL / PPL split, and I play airsoft.
|
||||
</p>
|
||||
<div class="meta-row">
|
||||
<span><strong>School</strong> SSŠVT</span>
|
||||
<span><strong>Focus</strong> Development, Tinkering & web</span>
|
||||
<span><strong>OS</strong> Linux, macOS & Android</span>
|
||||
<span><strong>Focus</strong> Developement, Tinkering & web</span>
|
||||
<span><strong>OS</strong> Linux, MacOS & Android</span>
|
||||
<span><strong>Home Lab</strong> Proxmox</span>
|
||||
<span><strong>Gym</strong> 4-5× / week · UL/PPL</span>
|
||||
<span><strong>Hobby</strong> Airsoft, Gym</span>
|
||||
|
|
@ -429,7 +429,7 @@
|
|||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Can I get access to your services?</summary>
|
||||
<p>If you want to try one of my services, dm me on ig <a href="https://www.instagram.com/skopcaak1">skopcaak1</a> or email me at <a href="mailto:josef@skopec.dev">josef@skopec.dev</a>.</p>
|
||||
<p>If you want to try one of my services, dm me on ig <a href="https://www.instagram.com/skopcaak1">skopcaak1</a> or email me to <a href="mailto:josef@skopec.dev">josef@skopec.dev</a> .</p>
|
||||
</details>
|
||||
<details class="faq-item">
|
||||
<summary>Is this site open source? Can I see the code?</summary>
|
||||
|
|
@ -507,6 +507,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js?v=20261027" defer></script>
|
||||
<script src="script.js?v=20261022" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
1
llms.txt
1
llms.txt
|
|
@ -30,7 +30,6 @@ Local 10.0.0.x addresses work only on the home network or with Josef’s Tailsca
|
|||
- BatchGym
|
||||
- Graduately — https://github.com/pepaskopec-blip/Graduately
|
||||
- Terra
|
||||
- Jellyfin, Navidrome, Ollama, SearXNG, ConvertX, Pi-hole, Joplin, Immich — services from the homelab, also listed on the Projects page
|
||||
|
||||
## Friends
|
||||
- Martin Dvořák — https://dvorakmortonxd.dev
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@
|
|||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20261027">
|
||||
<link rel="stylesheet" href="style.css?v=20261023">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
|
|
@ -118,6 +118,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js?v=20261027" defer></script>
|
||||
<script src="script.js" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
285
projects.html
285
projects.html
|
|
@ -3,11 +3,11 @@
|
|||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<meta name="description" content="Josef’s projects — Graduately, skopec.dev, the Proxmox homelab, Jellyfin, Immich, Ollama, and more.">
|
||||
<title>Projects — websites, services & games · Josef</title>
|
||||
<meta name="description" content="Josef’s projects — skopec.dev, Proxmox homelab, Snake in Pygame, BatchGym, Graduately, and more.">
|
||||
<title>Projects — websites & games · Josef</title>
|
||||
<link rel="canonical" href="https://skopec.dev/projects.html">
|
||||
<meta property="og:title" content="Projects — Josef">
|
||||
<meta property="og:description" content="Projects, websites, homelab services, games, and school experiments.">
|
||||
<meta property="og:description" content="Projects, websites, homelab, games, and school experiments.">
|
||||
<meta property="og:type" content="website">
|
||||
<meta property="og:url" content="https://skopec.dev/projects.html">
|
||||
<meta property="og:image" content="https://skopec.dev/og-image.jpg">
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
<meta property="og:site_name" content="skopec.dev">
|
||||
<meta name="twitter:card" content="summary_large_image">
|
||||
<meta name="twitter:title" content="Projects — Josef">
|
||||
<meta name="twitter:description" content="Websites, homelab services, games, and school experiments.">
|
||||
<meta name="twitter:description" content="Websites, homelab, games, and school experiments.">
|
||||
<meta name="twitter:image" content="https://skopec.dev/og-image.jpg">
|
||||
<link rel="preconnect" href="https://fonts.googleapis.com">
|
||||
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
||||
|
|
@ -34,7 +34,7 @@
|
|||
}
|
||||
})();
|
||||
</script>
|
||||
<link rel="stylesheet" href="style.css?v=20261027">
|
||||
<link rel="stylesheet" href="style.css?v=20261023">
|
||||
<script type="application/ld+json">
|
||||
{
|
||||
"@context": "https://schema.org",
|
||||
|
|
@ -85,13 +85,12 @@
|
|||
<div class="projects-hero-top">
|
||||
<div>
|
||||
<h1>Projects</h1>
|
||||
<p>Websites, self-hosted services, games, and school projects.</p>
|
||||
<p>Websites, the homelab, games, and school projects.</p>
|
||||
</div>
|
||||
<div class="project-filters" role="toolbar" aria-label="Filter projects">
|
||||
<button class="filter-chip is-active" type="button" data-filter="all">All <span>14</span></button>
|
||||
<button class="filter-chip is-active" type="button" data-filter="all">All <span>6</span></button>
|
||||
<button class="filter-chip" type="button" data-filter="apps">Apps & Web</button>
|
||||
<button class="filter-chip" type="button" data-filter="infra">Homelab & Program</button>
|
||||
<button class="filter-chip" type="button" data-filter="services">Services</button>
|
||||
<button class="filter-chip" type="button" data-filter="games">Games</button>
|
||||
</div>
|
||||
</div>
|
||||
|
|
@ -114,7 +113,7 @@
|
|||
<h3>
|
||||
<a href="https://github.com/pepaskopec-blip/Graduately" target="_blank" rel="noopener noreferrer">Graduately</a>
|
||||
</h3>
|
||||
<p>My biggest project yet. It is an educational SwiftUI/ Kotlin app for maturita/graduation preparation — The app is cross-platform and it is available on Mac, iPhone, Android, iPad, Windows and Linux. I am really proud of it.</p>
|
||||
<p>My biggest project yet. It is an educational SwiftUI/ Kotlin app for maturita/graduation preparation — The app is crossplatform and it is available on Mac, iPhone, Android, iPad, Windows and Linux. I am really proud of it.</p>
|
||||
<ul class="project-pills" aria-label="Platforms and technologies">
|
||||
<li>SwiftUI</li>
|
||||
<li>Kotlin + Compose</li>
|
||||
|
|
@ -338,7 +337,7 @@
|
|||
<h3>
|
||||
<a href="https://terra.hackclub.com" target="_blank" rel="noopener noreferrer">Terra</a>
|
||||
</h3>
|
||||
<p>Terra is a You Ship, We Ship program run by Hack Club. It combines gamified farming mechanics with real-world software development, where coding translates into in-game resources to earn a free modular laptop or tablet.</p>
|
||||
<p>Terra is You Ship, We Ship program rub by Hack Club. It combines gamified farming mechanics with real-world software developement, where coding translates into in-game resources to earn a free modular laptop or tablet.</p>
|
||||
<ul class="project-pills" aria-label="Program details">
|
||||
<li>Hack Club</li>
|
||||
<li>10h / 30h weekly</li>
|
||||
|
|
@ -348,274 +347,10 @@
|
|||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Jellyfin -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="https://jelly.skopec.dev" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>jellyfin</span>
|
||||
<span class="mock-svc-host">jelly.skopec.dev</span>
|
||||
</div>
|
||||
<div class="mock-posters" aria-hidden="true"><i></i><i></i><i></i><i></i></div>
|
||||
<div class="mock-now"><strong>Library</strong> · movies & TV on the NAS</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Jellyfin</span>
|
||||
<span class="project-status">Movies & TV</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="https://jelly.skopec.dev" target="_blank" rel="noopener noreferrer">Jellyfin</a>
|
||||
</h3>
|
||||
<p>Movies and TV on my own server, with the library on the NAS. This is the one service with a public address — jelly.skopec.dev — the rest stay on the LAN or Tailscale.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Movies & TV</li>
|
||||
<li>jelly.skopec.dev</li>
|
||||
</ul>
|
||||
<a class="project-link" href="https://jelly.skopec.dev" target="_blank" rel="noopener noreferrer">Open Jellyfin →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Navidrome -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.103:4533" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>navidrome</span>
|
||||
<span class="mock-svc-host">10.0.0.103:4533</span>
|
||||
</div>
|
||||
<div class="mock-track">
|
||||
<div class="mock-album" aria-hidden="true"></div>
|
||||
<div>
|
||||
<div class="mock-track-title">Library</div>
|
||||
<div class="mock-track-by">Music · on the LAN</div>
|
||||
<div class="mock-bar"><i style="width: 46%"></i></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Navidrome</span>
|
||||
<span class="project-status">Music</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.103:4533" target="_blank" rel="noopener noreferrer">Navidrome</a>
|
||||
</h3>
|
||||
<p>Music from the homelab. Navidrome serves the library over the Subsonic API, so the same collection plays in the browser or in a phone app on my network.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Subsonic API</li>
|
||||
<li>Music</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.103:4533" target="_blank" rel="noopener noreferrer">Open Navidrome →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Ollama -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.106:3000" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>ollama</span>
|
||||
<span class="mock-svc-host">10.0.0.106:3000</span>
|
||||
</div>
|
||||
<div class="mock-chat">
|
||||
<div class="mock-bubble ask">run it locally</div>
|
||||
<div class="mock-bubble reply">model on the homelab · stays on the network</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Ollama</span>
|
||||
<span class="project-status">Local AI</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.106:3000" target="_blank" rel="noopener noreferrer">Ollama</a>
|
||||
</h3>
|
||||
<p>Local AI on the Proxmox box. Models run on my hardware, so a prompt stays on the network instead of going out to a hosted API. I do not have dedicated GPU, so all of the llms run on CPU, which limits me for only some weaker models under 15B parameters.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Local LLM</li>
|
||||
<li>Private</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.106:3000" target="_blank" rel="noopener noreferrer">Open Ollama →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- SearXNG -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.104:8080" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>searxng</span>
|
||||
<span class="mock-svc-host">10.0.0.104:8080</span>
|
||||
</div>
|
||||
<div class="mock-search"><span class="mock-search-icon" aria-hidden="true">⌕</span><span>search the web</span></div>
|
||||
<div class="mock-search-meta"><span>general</span><span>images</span><span>news</span></div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">SearXNG</span>
|
||||
<span class="project-status">Search</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.104:8080" target="_blank" rel="noopener noreferrer">SearXNG</a>
|
||||
</h3>
|
||||
<p>Search I run myself. SearXNG asks other engines and brings the results back, without an account on a big search site.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Metasearch</li>
|
||||
<li>Private</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.104:8080" target="_blank" rel="noopener noreferrer">Open SearXNG →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- ConvertX -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.107:3000" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>convertx</span>
|
||||
<span class="mock-svc-host">10.0.0.107:3000</span>
|
||||
</div>
|
||||
<div class="mock-jobs">
|
||||
<div><span>image</span><em>→</em><span>png</span><b>local</b></div>
|
||||
<div><span>document</span><em>→</em><span>pdf</span><b>local</b></div>
|
||||
<div><span>audio</span><em>→</em><span>mp3</span><b>local</b></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">ConvertX</span>
|
||||
<span class="project-status">File conversion</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.107:3000" target="_blank" rel="noopener noreferrer">ConvertX</a>
|
||||
</h3>
|
||||
<p>File conversion on the lab. When something needs another format, it converts here instead of uploading the file to a random website.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Files</li>
|
||||
<li>Private</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.107:3000" target="_blank" rel="noopener noreferrer">Open ConvertX →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Pi-hole -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.100/admin" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>pi-hole</span>
|
||||
<span class="mock-svc-host">10.0.0.100</span>
|
||||
</div>
|
||||
<div class="mock-stat">
|
||||
<strong>DNS</strong>
|
||||
<span>ads & trackers</span>
|
||||
</div>
|
||||
<div class="mock-bar"><i style="width: 38%"></i></div>
|
||||
<div class="mock-stat-rows">
|
||||
<div><span>Blocking</span><span>on</span></div>
|
||||
<div><span>Resolver</span><span>LAN</span></div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Pi-hole</span>
|
||||
<span class="project-status">DNS</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.100/admin" target="_blank" rel="noopener noreferrer">Pi-hole</a>
|
||||
</h3>
|
||||
<p>DNS for the network, with ad blocking in front of it. Probably the most useful service on my homelab, it is able to block ads even in apps on mobile, also it blocks malware and dangerous content. Not a long time ago, I have set up DNS over HTTPS for maximum privacy.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>DNS</li>
|
||||
<li>Ad blocking</li>
|
||||
<li>Network</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.100/admin" target="_blank" rel="noopener noreferrer">Open Pi-hole →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Joplin -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.111:22300" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>joplin</span>
|
||||
<span class="mock-svc-host">10.0.0.111:22300</span>
|
||||
</div>
|
||||
<div class="mock-notes">
|
||||
<div><i></i>Notes</div>
|
||||
<div><i></i>Sync server</div>
|
||||
<div><i></i>On the homelab</div>
|
||||
</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Joplin</span>
|
||||
<span class="project-status">Notes</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.111:22300" target="_blank" rel="noopener noreferrer">Joplin</a>
|
||||
</h3>
|
||||
<p>Notes that sync to the homelab. Joplin is the notebook, and the sync target is my own server instead of someone else’s cloud.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Notes</li>
|
||||
<li>Sync</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.111:22300" target="_blank" rel="noopener noreferrer">Open Joplin →</a>
|
||||
</div>
|
||||
</li>
|
||||
|
||||
<!-- Immich -->
|
||||
<li class="project-card" data-category="services">
|
||||
<a class="project-preview" href="http://10.0.0.109:2283" target="_blank" rel="noopener noreferrer" aria-hidden="true" tabindex="-1">
|
||||
<div class="mock-svc">
|
||||
<div class="mock-svc-top">
|
||||
<span class="mock-status-dot"></span>
|
||||
<span>immich</span>
|
||||
<span class="mock-svc-host">10.0.0.109:2283</span>
|
||||
</div>
|
||||
<div class="mock-photos" aria-hidden="true"><i></i><i></i><i></i><i></i><i></i><i></i></div>
|
||||
<div class="mock-now"><strong>Library</strong> · photos on the NAS</div>
|
||||
</div>
|
||||
</a>
|
||||
<div class="project-card-body">
|
||||
<div class="project-card-meta">
|
||||
<span class="tag">Immich</span>
|
||||
<span class="project-status">Photos</span>
|
||||
</div>
|
||||
<h3>
|
||||
<a href="http://10.0.0.109:2283" target="_blank" rel="noopener noreferrer">Immich</a>
|
||||
</h3>
|
||||
<p>Photo library on the NAS. Immich is where the pictures live, with a web UI instead of a shared folder.</p>
|
||||
<ul class="project-pills" aria-label="Details">
|
||||
<li>Self-hosted</li>
|
||||
<li>Photos</li>
|
||||
<li>NAS</li>
|
||||
</ul>
|
||||
<a class="project-link" href="http://10.0.0.109:2283" target="_blank" rel="noopener noreferrer">Open Immich →</a>
|
||||
</div>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</section>
|
||||
|
|
@ -633,6 +368,6 @@
|
|||
</div>
|
||||
</footer>
|
||||
|
||||
<script src="script.js?v=20261027" defer></script>
|
||||
<script src="script.js?v=20261022" defer></script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
48
script.js
48
script.js
|
|
@ -57,12 +57,6 @@
|
|||
document.addEventListener("keydown", (event) => {
|
||||
if (event.key === "Escape") setOpen(false);
|
||||
});
|
||||
|
||||
document.addEventListener("click", (event) => {
|
||||
if (!nav.classList.contains("is-open")) return;
|
||||
if (nav.contains(event.target) || toggle.contains(event.target)) return;
|
||||
setOpen(false);
|
||||
});
|
||||
}
|
||||
|
||||
const radar = document.querySelector("[data-radar]");
|
||||
|
|
@ -163,14 +157,9 @@
|
|||
const projectCards = document.querySelectorAll(".project-card[data-category]");
|
||||
if (filterChips.length && projectCards.length) {
|
||||
filterChips.forEach((chip) => {
|
||||
chip.setAttribute("aria-pressed", chip.classList.contains("is-active") ? "true" : "false");
|
||||
chip.addEventListener("click", () => {
|
||||
const filter = chip.dataset.filter;
|
||||
filterChips.forEach((c) => {
|
||||
const on = c === chip;
|
||||
c.classList.toggle("is-active", on);
|
||||
c.setAttribute("aria-pressed", on ? "true" : "false");
|
||||
});
|
||||
filterChips.forEach((c) => c.classList.toggle("is-active", c === chip));
|
||||
projectCards.forEach((card) => {
|
||||
const match = filter === "all" || card.dataset.category === filter;
|
||||
card.hidden = !match;
|
||||
|
|
@ -183,14 +172,9 @@
|
|||
const hwCards = document.querySelectorAll(".hw-card[data-hw-category]");
|
||||
if (hwChips.length && hwCards.length) {
|
||||
hwChips.forEach((chip) => {
|
||||
chip.setAttribute("aria-pressed", chip.classList.contains("is-active") ? "true" : "false");
|
||||
chip.addEventListener("click", () => {
|
||||
const filter = chip.dataset.hwFilter;
|
||||
hwChips.forEach((c) => {
|
||||
const on = c === chip;
|
||||
c.classList.toggle("is-active", on);
|
||||
c.setAttribute("aria-pressed", on ? "true" : "false");
|
||||
});
|
||||
hwChips.forEach((c) => c.classList.toggle("is-active", c === chip));
|
||||
hwCards.forEach((card) => {
|
||||
const match = filter === "all" || card.dataset.hwCategory === filter;
|
||||
card.hidden = !match;
|
||||
|
|
@ -234,29 +218,11 @@
|
|||
}
|
||||
};
|
||||
|
||||
const snakePalette = () => {
|
||||
const light = document.documentElement.dataset.theme === "light";
|
||||
return light
|
||||
? {
|
||||
grid: "rgba(17, 21, 28, 0.08)",
|
||||
food: "#dc3c11",
|
||||
head: "#11151c",
|
||||
body: "179, 47, 8",
|
||||
}
|
||||
: {
|
||||
grid: "rgba(238, 242, 246, 0.04)",
|
||||
food: "#ff4a1c",
|
||||
head: "#eef2f6",
|
||||
body: "255, 122, 82",
|
||||
};
|
||||
};
|
||||
|
||||
const drawSnake = () => {
|
||||
if (!ctx) return;
|
||||
const palette = snakePalette();
|
||||
ctx.clearRect(0, 0, snakeCanvas.width, snakeCanvas.height);
|
||||
|
||||
ctx.strokeStyle = palette.grid;
|
||||
ctx.strokeStyle = "rgba(238, 242, 246, 0.04)";
|
||||
ctx.lineWidth = 1;
|
||||
for (let x = 0; x <= cols; x++) {
|
||||
ctx.beginPath();
|
||||
|
|
@ -271,14 +237,14 @@
|
|||
ctx.stroke();
|
||||
}
|
||||
|
||||
ctx.fillStyle = palette.food;
|
||||
ctx.fillStyle = "#ff4a1c";
|
||||
ctx.beginPath();
|
||||
ctx.arc(food.x * cell + cell / 2, food.y * cell + cell / 2, 4.5, 0, Math.PI * 2);
|
||||
ctx.fill();
|
||||
|
||||
snake.forEach((seg, idx) => {
|
||||
const alpha = Math.max(0.28, 1 - idx * 0.09);
|
||||
ctx.fillStyle = idx === 0 ? palette.head : `rgba(${palette.body}, ${alpha})`;
|
||||
ctx.fillStyle = idx === 0 ? "#eef2f6" : `rgba(255, 122, 82, ${alpha})`;
|
||||
ctx.fillRect(seg.x * cell + 2, seg.y * cell + 2, cell - 4, cell - 4);
|
||||
});
|
||||
};
|
||||
|
|
@ -330,10 +296,6 @@
|
|||
if (!window.matchMedia("(prefers-reduced-motion: reduce)").matches) {
|
||||
setInterval(stepSnake, 180);
|
||||
}
|
||||
new MutationObserver(drawSnake).observe(document.documentElement, {
|
||||
attributes: true,
|
||||
attributeFilter: ["data-theme"],
|
||||
});
|
||||
}
|
||||
|
||||
const revealItems = document.querySelectorAll(".reveal");
|
||||
|
|
|
|||
|
|
@ -2,19 +2,19 @@
|
|||
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
||||
<url>
|
||||
<loc>https://skopec.dev/</loc>
|
||||
<lastmod>2026-10-18</lastmod>
|
||||
<lastmod>2026-10-12</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>1.0</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://skopec.dev/projects.html</loc>
|
||||
<lastmod>2026-10-18</lastmod>
|
||||
<lastmod>2026-10-12</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
<url>
|
||||
<loc>https://skopec.dev/hardware.html</loc>
|
||||
<lastmod>2026-10-18</lastmod>
|
||||
<lastmod>2026-10-12</lastmod>
|
||||
<changefreq>monthly</changefreq>
|
||||
<priority>0.8</priority>
|
||||
</url>
|
||||
|
|
|
|||
336
style.css
336
style.css
|
|
@ -1590,17 +1590,6 @@ a:hover {
|
|||
color: var(--accent-ink);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-arcade {
|
||||
background: #f4f6f8;
|
||||
box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-arcade-hud {
|
||||
color: var(--ink-soft);
|
||||
background: rgba(17, 21, 28, 0.04);
|
||||
border-bottom-color: var(--line);
|
||||
}
|
||||
|
||||
.snake-canvas {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
|
@ -1669,30 +1658,6 @@ a:hover {
|
|||
background: var(--accent);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-terminal {
|
||||
background: #f7f8fa;
|
||||
box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-terminal-bar {
|
||||
color: var(--muted);
|
||||
background: #eef1f4;
|
||||
border-bottom-color: var(--line);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-terminal-body {
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .term-stats,
|
||||
:root[data-theme="light"] .term-log {
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .term-prompt {
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
/* Terra challenge preview */
|
||||
.mock-terra {
|
||||
width: 100%;
|
||||
|
|
@ -1757,280 +1722,6 @@ a:hover {
|
|||
font-weight: 650;
|
||||
}
|
||||
|
||||
/* Self-hosted service previews */
|
||||
.mock-svc {
|
||||
width: 100%;
|
||||
max-width: 24rem;
|
||||
padding: 0.9rem 1rem;
|
||||
background: var(--surface-solid);
|
||||
border: 1px solid var(--line-strong);
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 14px 30px rgba(0, 0, 0, 0.28);
|
||||
}
|
||||
|
||||
.mock-svc-top {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.45rem;
|
||||
margin-bottom: 0.75rem;
|
||||
font-size: 0.72rem;
|
||||
font-weight: 650;
|
||||
letter-spacing: 0.04em;
|
||||
color: var(--ink);
|
||||
}
|
||||
|
||||
.mock-svc-host {
|
||||
margin-left: auto;
|
||||
color: var(--muted);
|
||||
font-weight: 600;
|
||||
letter-spacing: 0;
|
||||
font-variant-numeric: tabular-nums;
|
||||
}
|
||||
|
||||
.mock-posters {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.mock-posters i {
|
||||
display: block;
|
||||
aspect-ratio: 2 / 3;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(165deg, var(--accent) 0%, #24140f 78%);
|
||||
}
|
||||
|
||||
.mock-posters i:nth-child(2) {
|
||||
background: linear-gradient(200deg, #3d5a80 0%, #121820 80%);
|
||||
}
|
||||
|
||||
.mock-posters i:nth-child(3) {
|
||||
background: linear-gradient(150deg, #c45c26 0%, #1a120e 78%);
|
||||
}
|
||||
|
||||
.mock-posters i:nth-child(4) {
|
||||
background: linear-gradient(185deg, #6b4c7a 0%, #141820 80%);
|
||||
}
|
||||
|
||||
.mock-now {
|
||||
margin-top: 0.65rem;
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-now strong {
|
||||
color: var(--ink);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.mock-track {
|
||||
display: grid;
|
||||
grid-template-columns: 3.1rem minmax(0, 1fr);
|
||||
gap: 0.7rem;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.mock-album {
|
||||
width: 3.1rem;
|
||||
height: 3.1rem;
|
||||
border-radius: 2px;
|
||||
background:
|
||||
linear-gradient(145deg, transparent 42%, rgba(0, 0, 0, 0.28) 43% 57%, transparent 58%),
|
||||
linear-gradient(145deg, var(--accent) 0%, #1c2430 72%);
|
||||
}
|
||||
|
||||
.mock-track-title {
|
||||
font-weight: 700;
|
||||
font-size: 0.86rem;
|
||||
color: var(--ink);
|
||||
line-height: 1.2;
|
||||
}
|
||||
|
||||
.mock-track-by {
|
||||
margin: 0.12rem 0 0.45rem;
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-chat {
|
||||
display: grid;
|
||||
gap: 0.4rem;
|
||||
}
|
||||
|
||||
.mock-bubble {
|
||||
max-width: 92%;
|
||||
padding: 0.42rem 0.6rem;
|
||||
font-size: 0.75rem;
|
||||
line-height: 1.4;
|
||||
color: var(--muted);
|
||||
background: var(--surface);
|
||||
border: 1px solid var(--line);
|
||||
}
|
||||
|
||||
.mock-bubble.ask {
|
||||
margin-left: auto;
|
||||
color: var(--ink);
|
||||
border-color: rgba(255, 74, 28, 0.35);
|
||||
background: rgba(255, 74, 28, 0.1);
|
||||
}
|
||||
|
||||
.mock-search {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.55rem 0.7rem;
|
||||
border: 1px solid var(--line-strong);
|
||||
background: var(--bg);
|
||||
color: var(--ink-soft);
|
||||
font-size: 0.82rem;
|
||||
}
|
||||
|
||||
.mock-search-icon {
|
||||
color: var(--accent);
|
||||
font-weight: 700;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mock-search-meta {
|
||||
display: flex;
|
||||
gap: 0.75rem;
|
||||
margin-top: 0.65rem;
|
||||
font-size: 0.7rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-search-meta span:first-child {
|
||||
color: var(--accent-ink);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.mock-jobs {
|
||||
display: grid;
|
||||
gap: 0.45rem;
|
||||
font-size: 0.76rem;
|
||||
}
|
||||
|
||||
.mock-jobs div {
|
||||
display: grid;
|
||||
grid-template-columns: 5.2rem auto auto 1fr;
|
||||
gap: 0.45rem;
|
||||
align-items: center;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.mock-jobs em {
|
||||
font-style: normal;
|
||||
color: var(--accent);
|
||||
}
|
||||
|
||||
.mock-jobs b {
|
||||
justify-self: end;
|
||||
font-weight: 650;
|
||||
font-size: 0.68rem;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-stat {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: baseline;
|
||||
gap: 0.75rem;
|
||||
margin-bottom: 0.5rem;
|
||||
}
|
||||
|
||||
.mock-stat strong {
|
||||
font-family: var(--font-display);
|
||||
font-size: 1.35rem;
|
||||
font-weight: 750;
|
||||
letter-spacing: -0.03em;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.mock-stat span {
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-stat-rows {
|
||||
display: grid;
|
||||
gap: 0.35rem;
|
||||
margin-top: 0.7rem;
|
||||
font-size: 0.72rem;
|
||||
color: var(--muted);
|
||||
}
|
||||
|
||||
.mock-stat-rows div {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.mock-stat-rows div span:last-child {
|
||||
color: var(--ink-soft);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.mock-notes {
|
||||
display: grid;
|
||||
}
|
||||
|
||||
.mock-notes div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.55rem;
|
||||
padding: 0.42rem 0;
|
||||
border-bottom: 1px solid var(--line);
|
||||
font-size: 0.8rem;
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
.mock-notes div:first-child {
|
||||
color: var(--ink);
|
||||
font-weight: 650;
|
||||
}
|
||||
|
||||
.mock-notes div:last-child {
|
||||
border-bottom: 0;
|
||||
}
|
||||
|
||||
.mock-notes i {
|
||||
width: 0.4rem;
|
||||
height: 0.4rem;
|
||||
border-radius: 50%;
|
||||
background: var(--line-strong);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.mock-notes div:first-child i {
|
||||
background: var(--accent);
|
||||
box-shadow: 0 0 8px var(--glow);
|
||||
}
|
||||
|
||||
.mock-photos {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 0.35rem;
|
||||
}
|
||||
|
||||
.mock-photos i {
|
||||
display: block;
|
||||
aspect-ratio: 3 / 2;
|
||||
border-radius: 2px;
|
||||
background: linear-gradient(160deg, var(--line-strong), var(--glow-cool));
|
||||
}
|
||||
|
||||
.mock-photos i:nth-child(2),
|
||||
.mock-photos i:nth-child(6) {
|
||||
background: linear-gradient(200deg, rgba(255, 74, 28, 0.55), #1a140f);
|
||||
}
|
||||
|
||||
.mock-photos i:nth-child(3),
|
||||
.mock-photos i:nth-child(4) {
|
||||
background: linear-gradient(145deg, #3d5a80, #121820);
|
||||
}
|
||||
|
||||
.tag {
|
||||
display: inline-block;
|
||||
margin-bottom: 0.5rem;
|
||||
|
|
@ -2306,25 +1997,6 @@ a:hover {
|
|||
.fetch-palette i:nth-child(5) { background: #818cf8; }
|
||||
.fetch-palette i:nth-child(6) { background: #e2e8f0; }
|
||||
|
||||
:root[data-theme="light"] .mock-fetch {
|
||||
background: #f7f8fa;
|
||||
box-shadow: 0 14px 30px rgba(17, 21, 28, 0.08);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-fetch-bar {
|
||||
color: var(--muted);
|
||||
background: #eef1f4;
|
||||
border-bottom-color: var(--line);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .mock-fetch-body {
|
||||
color: var(--ink-soft);
|
||||
}
|
||||
|
||||
:root[data-theme="light"] .fetch-palette i:nth-child(6) {
|
||||
box-shadow: inset 0 0 0 1px rgba(17, 21, 28, 0.28);
|
||||
}
|
||||
|
||||
/* Laptop Apple M5 SoC widget */
|
||||
.mock-soc {
|
||||
width: 100%;
|
||||
|
|
@ -2451,17 +2123,11 @@ a:hover {
|
|||
|
||||
.server-bays {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 0.45rem;
|
||||
margin-bottom: 0.6rem;
|
||||
}
|
||||
|
||||
@media (min-width: 640px) {
|
||||
.server-bays {
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
}
|
||||
}
|
||||
|
||||
.server-bay {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue