diff --git a/README.md b/README.md index d0a1674..9bce609 100644 --- a/README.md +++ b/README.md @@ -2,16 +2,25 @@ > The source code for my personal website, **[dvorakmortonxd.dev](https://dvorakmortonxd.dev)**. -This is not a normal portfolio page. It ships **two fully interactive shells** and picks -which one to show based on the saved theme and the device, all from the same content: +This is not a normal portfolio page. It ships a hand-built **skeuomorphic console** plus a +**phone-only desktop simulation**, both rendering the same content: -- **`skeuo`** (default) — a KDE Plasma / Windows-style **desktop simulation**: draggable - snap-to-grid icons, resizable/floating windows, a Start menu, taskbar and live clock. - On phones it becomes an **iOS-style home screen** with a widget grid, dock, drag-to-reorder - and an iOS launch animation. -- **`terminal`** — a **Gruvbox TUI** version of the site: TUI panels, an animated ASCII - train background, a clickable easter-egg logo, and a working in-browser terminal (Alacritty) - with commands like `help`, `ls`, `cat`, `neofetch`, `emerge`, `ssh` and more. +- **`skeuo`** (default) — the site itself: a walnut-and-aluminium home-lab console on a workshop + bench. Every section is a physical object — an engraved nameplate, an aluminium patch bay, + analogue load meters for the gym numbers, a clipboard, a leather logbook, a rack of service + modules with live pilot lamps, a framed screenshot, a chart recorder, a game cartridge, a + software box, a pegboard of manila tags and a card-index drawer. Materials are CSS + inline-SVG + noise only: the theme ships **no images of its own**. One light source (top-left), layered + shadows for raised/recessed/embossed/stitched parts, and a deliberate two-font pairing: a + condensed engraved panel face for metal, a typewriter for anything on paper. +- **`desktop`** (phone-width viewports only) — a KDE Plasma / Windows-style **desktop + simulation**: draggable snap-to-grid icons, resizable/floating windows, a Start menu, taskbar + and live clock. On phones it becomes an **iOS-style home screen** with a widget grid, dock, + drag-to-reorder and an iOS launch animation. + +The desktop simulation is only offered at ≤ 768 px: on anything wider the switch is hidden and +the page always renders `skeuo`, so a desktop visitor can never end up stuck in a mode whose +switch they cannot reach. Both shells surface the same personal content: socials, self-hosted services with **live up/down health checks**, a hardware collection, coding projects, a **GitDXD contribution @@ -19,8 +28,16 @@ heatmap**, a gym SBD panel and links to friends' sites. ## ✨ Features -- **Two themes / shells** — KDE-style desktop and Gruvbox terminal, toggled from either UI - and persisted in `localStorage` (applied pre-paint to avoid a flash). +- **Two shells** — the skeuo console (default everywhere) and the KDE-style desktop simulation + (phone-width viewports only, flipped by the physical switch on the nameplate). The choice is + persisted in `localStorage` under `dmx-theme` and applied pre-paint to avoid a flash; legacy + `theme` values are migrated once. +- **Desktop mode is phone-only** — at ≤ 768 px the switch appears and both shells are reachable; + at 768 px and above it is hidden and any saved `desktop` preference is overridden, so wide + viewports can never get stuck. +- **CSS-only materials** — wood, brushed aluminium, brass, leather, black bakelite, paper, glass + and rubber are built from gradients, `color-mix()` and an inline-SVG `feTurbulence` grain; no + texture images ship with the theme. - **Phone vs. desktop detection** — a user-agent sniff on `` decides whether the desktop or the mobile home screen is shown. - **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per @@ -58,11 +75,14 @@ heatmap**, a gym SBD panel and links to friends' sites. - **Framework:** Next.js 16 (App Router, Turbopack) - **Language:** TypeScript, React 19 (client components everywhere interactivity lives) -- **Styling:** hand-written CSS — `src/app/page.css` (terminal theme), `src/app/skeuo.css` - (desktop + mobile), `src/app/globals.css` (base/reset). Tailwind v4 is imported for the - base layer but the UI is custom CSS. -- **Fonts:** Monocraft (self-hosted in `public/`, used by the terminal theme; the desktop - theme uses the system sans stack) +- **Styling:** hand-written CSS — `src/app/skeuo.css` (the skeuo console: materials, objects, + depth), `src/app/desktop.css` (KDE desktop + mobile home), `src/app/shared.css` (styles for + components both shells render: service buttons, heatmap, project cards, logo) and + `src/app/globals.css` (base/reset). Tailwind v4 is imported for the base layer but the UI is + custom CSS. +- **Fonts:** Barlow Semi Condensed (engraved panel labels, headings, controls) + Courier Prime + (everything printed or typewritten), both via `next/font/google` with explicit fallback + stacks. Monocraft stays self-hosted in `public/` for the desktop's terminal windows. - **Images:** `next/image` - **Version:** `2.1.0` (see `package.json`) @@ -164,11 +184,12 @@ they contend for `:3000` and `.next/dev/lock`. ``` src/app/ - page.tsx → renders , imports the two theme stylesheets - layout.tsx → html shell + metadata, injects ThemeScript + page.tsx → renders , imports the three stylesheets + layout.tsx → html shell + metadata, loads the two fonts, injects ThemeScript globals.css → base/reset styles + Tailwind import - page.css → terminal theme styles - skeuo.css → desktop + mobile home styles + skeuo.css → the skeuo console: materials, objects, depth + desktop.css → KDE desktop + mobile home styles + shared.css → styles for components both shells render data.ts → socials, codingProjects, techProjects, friends, wallpapers revival-inc/page.tsx → embeds the bundled game at /public/revival-inc/index.html api/ @@ -179,18 +200,17 @@ src/app/ ThemeRoot.tsx → picks which shell(s) to mount ThemeScript.tsx → pre-paint script: theme + device detection useTheme.ts → theme state via useSyncExternalStore - TerminalLayout.tsx → the Gruvbox terminal shell + SkeuoLayout.tsx → the skeuo console shell WindowsDesktop.tsx → desktop shell: icons, windows, taskbar, Start menu MobileHome.tsx → phone shell: iOS home screen + app view WinWindow.tsx → one draggable/resizable window apps.tsx → APPS registry, window contents, in-app terminal useDesktopIcons.ts → desktop icon grid + drag/drop logic useDragOrder.ts → list reordering (mobile grid/dock) - ServiceButton.tsx → service link + live status dot + ServiceButton.tsx → service link + live status lamp ContributionHeatmap.tsx → heatmap + recent commits widget Clock.tsx → live clock JokerLogo.tsx → logo with click easter egg - SlBackground.tsx → animated ASCII train background scripts/ dev-server.mjs → custom dev server: live updates without the dev-only debug surface deploy/ diff --git a/agents.md b/agents.md index d947705..5512a77 100644 --- a/agents.md +++ b/agents.md @@ -34,13 +34,18 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a ## What this project is Personal portfolio site for **dvorakmortonxd.dev**. It's not a normal website — -it ships **two fully interactive desktop/OS simulations** plus a terminal, and -picks which one to show based on theme and device. +it ships a hand-built **skeuomorphic console** (`skeuo`, the default) plus a +KDE-style **desktop simulation** that is only offered on phone-width viewports. - Framework: **Next.js 16** (App Router, Turbopack), **React 19**, TypeScript. -- Styling: plain CSS files (`page.css`, `skeuo.css`), no CSS-in-JS, no Tailwind - utilities in the components (Tailwind is present via PostCSS but the look is - hand-written CSS). +- Styling: plain CSS files — `skeuo.css` (the console), `desktop.css` (KDE sim + + mobile home), `shared.css` (components both shells render) and `globals.css` + (base/reset). No CSS-in-JS, no Tailwind utilities in the components (Tailwind + is present via PostCSS but the look is hand-written CSS). **Materials are CSS + + inline-SVG noise only — no texture images.** +- Fonts: Barlow Semi Condensed (metal labels, headings, controls) + Courier Prime + (paper, readouts) via `next/font/google` with explicit fallbacks; Monocraft + stays self-hosted in `public/` for the desktop's terminal windows. - Path alias: `@/*` → `src/*`. - Version: `2.1.0` (see `package.json`). @@ -73,12 +78,13 @@ token.** Do not print it, copy it into new files, or push it anywhere else. ``` src/app/ - page.tsx → renders , imports page.css + skeuo.css - layout.tsx → html shell, metadata, ThemeScript + page.tsx → renders , imports shared.css + skeuo.css + desktop.css + layout.tsx → html shell, metadata, fonts, ThemeScript globals.css → base/reset styles + Tailwind import data.ts → socials, codingProjects, techProjects, friends, wallpapers - page.css → base / terminal theme styles - skeuo.css → Windows/Plasma desktop + mobile home styles + skeuo.css → the skeuo console: materials, objects, depth + desktop.css → Windows/Plasma desktop + mobile home styles + shared.css → styles for components both shells render revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html api/ heatmap/route.ts → proxy for git.dvorakmortonxd.dev contribution heatmap @@ -87,9 +93,9 @@ src/app/ frame/route.ts → header-stripping proxy so an unframeable friend site loads in a window; host allowlist components/ ThemeRoot.tsx → picks which shell to show (the router of the UI) - TerminalLayout.tsx → "terminal" theme (Gruvbox) - WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar - MobileHome.tsx → "skeuo" theme, phone: app grid + dock + SkeuoLayout.tsx → "skeuo" theme (the console): every section as an object + WindowsDesktop.tsx → "desktop" theme, desktop: icons, windows, taskbar + MobileHome.tsx → "desktop" theme, phone: app grid + dock WinWindow.tsx → a single draggable/resizable window apps.tsx → APPS registry + the content of each app + in-app terminal + embedded-site windows + the Minecraft-style SMP menu @@ -101,23 +107,31 @@ src/app/ ContributionHeatmap.tsx → heatmap + recent-commit widget (uses the two proxies) Clock.tsx → live clock (useSyncExternalStore + interval) JokerLogo.tsx → site logo with a click easter egg - SlBackground.tsx → animated ASCII train background (terminal theme) scripts/ dev-server.mjs → custom dev server: live updates minus the dev-only debug surface ``` ### How UI selection works -1. `ThemeScript` runs before paint. It reads `localStorage.theme` and sets - `data-theme` (`skeuo` | `terminal`) and `data-device` (`phone` | `desktop`) - on `` from a user-agent sniff. +1. `ThemeScript` runs before paint. It reads `localStorage['dmx-theme']`, + migrating the legacy `theme` key once (`terminal` → `skeuo`, `skeuo` → + `desktop`), then sets `data-theme` (`skeuo` | `desktop`) and `data-device` + (`phone` | `desktop`, from a user-agent sniff) on ``. Viewports wider + than 768 px always get `skeuo`. 2. `useTheme()` (in `useTheme.ts`) reads `data-theme` via `useSyncExternalStore` and dispatches a `theme-change` event on toggle. -3. `ThemeRoot` shows the terminal when `theme === "terminal"`; otherwise it - mounts **both** `WindowsDesktop` and `MobileHome` and lets CSS - (`html[data-device="phone"]`) decide which is visible. The desktop stays - mounted while hidden so the Welcome window isn't recreated. + `setTheme("desktop")` is refused when the viewport is wider than 768 px, so + nothing can leave a desktop visitor stuck in the sim. +3. `ThemeRoot` always renders `SkeuoLayout` (kept mounted, just hidden, while + the sim is up so its scroll position survives). On phone-width viewports it + additionally mounts **both** `WindowsDesktop` and `MobileHome` and lets CSS + (`html[data-device="phone"]`) decide which is visible; they stay mounted while + hidden so the Welcome window isn't recreated. The sim is never mounted wider + than that. -Themes: `skeuo` = KDE Plasma / Windows desktop, `terminal` = Gruvbox terminal. +Themes: `skeuo` = the site itself (walnut + aluminium console), `desktop` = the +phone-only KDE Plasma / Windows simulation. The physical switch between them sits +on the nameplate (`.sk-desktop-switch`, styled as `.sk-switch`) and is only +visible at ≤ 768 px. --- @@ -217,7 +231,31 @@ list reordering, not free placement. Don't confuse the two. - Components are client components (`"use client"` at the top) — they use pointer events and window APIs freely. - CSS classes are prefixed by area: `win-` (desktop), `mobile-` (phone), - `app-` / `kickoff` / `terminal` etc. Grep by prefix to find styles. + `app-` / `kickoff` / `terminal` etc. Grep by prefix to find styles. The skeuo + theme's own classes all start with `sk-` (`skeuo.css`) and that prefix is + reserved for it — `desktop.css` only ever uses `skeuo-*` for its two roots. +- **The skeuo theme is CSS-only.** No texture images, no `url()` beyond the + social SVG masks: wood, aluminium, brass, leather, bakelite, paper, glass and + rubber are gradients + `color-mix()` + an inline-SVG `feTurbulence` grain + (`--sk-noise`). Build new surfaces from those tokens (`--sk-raise-1/2`, + `--sk-sink-1`, `--sk-alu-*`, `--sk-brass-*`, `--sk-wood-*`, `--sk-paper-*`) + rather than inventing new colours, and keep the light coming from the + top-left. Simplify (drop noise, flatten shadows) at ≤ 768 px. +- **`shared.css` is shared.** Both shells render `ServiceButton`, + `ContributionHeatmap`, `JokerLogo`, the project cards and the social buttons. + Change their base styles only if you check the desktop (`.app-surface` / + `.win-body` in `desktop.css` layers its own overrides on top). The skeuo theme + restyles them from `skeuo.css`, scoped to `html[data-theme="skeuo"]`, so the + desktop keeps its look. The `:root` block in `shared.css` is only the fallback + palette those components inherit. +- **Motion stays cheap.** Transitions are 100–150 ms and only ever animate + `transform` / `opacity`; pressed states move down and swap their shadow + without animating it. Anything animated respects + `@media (prefers-reduced-motion: reduce)`. +- **The desktop sim is phone-only.** `.sk-switch` is hidden above 768 px and + `setTheme("desktop")` refuses to apply there; `ThemeRoot` also force-switches + back to `skeuo` when the viewport grows. Don't add another way into the sim + without keeping those three guards in place. - Several places deliberately use **imperative DOM work** (drag ghost, transform-origin for the mobile launch animation). This is intentional for performance; prefer matching the existing pattern over "Reactifying" it. diff --git a/public/gruvbox.webp b/public/gruvbox.webp deleted file mode 100644 index f4fe2d8..0000000 Binary files a/public/gruvbox.webp and /dev/null differ diff --git a/src/app/api/ping/route.ts b/src/app/api/ping/route.ts index 0bc19d4..fbd795b 100644 --- a/src/app/api/ping/route.ts +++ b/src/app/api/ping/route.ts @@ -8,7 +8,7 @@ import { NextResponse } from 'next/server'; // // Host matching is exact or by subdomain (e.g. anything under // dvorakmortonxd.dev). Keep this in sync with the ServiceButton `url`/`checkUrl` -// values in components/apps.tsx and components/TerminalLayout.tsx, and with the +// values in components/apps.tsx and components/SkeuoLayout.tsx, and with the // friend list in data.ts. const ALLOWED_HOSTS = [ 'dvorakmortonxd.dev', // ai / jelly / navidrome / git / proxmox / mc (SMP) diff --git a/src/app/components/SkeuoLayout.tsx b/src/app/components/SkeuoLayout.tsx new file mode 100644 index 0000000..271014b --- /dev/null +++ b/src/app/components/SkeuoLayout.tsx @@ -0,0 +1,519 @@ +"use client"; + +import Image from "next/image"; +import type { CSSProperties, ReactNode } from "react"; +import JokerLogo from "./JokerLogo"; +import ServiceButton from "./ServiceButton"; +import ContributionHeatmap from "./ContributionHeatmap"; +import { setTheme } from "./useTheme"; +import { socials, codingProjects, techProjects, friends } from "../data"; + +/* ------------------------------------------------------------------ parts */ + +/** A small engraved brass / enamel plate — every object is labelled like a machine. */ +function Plate({ + children, + tone = "brass", + className = "", +}: { + children: ReactNode; + tone?: "brass" | "red" | "ivory"; + className?: string; +}) { + return {children}; +} + +/** A recessed dark-glass window: where the console prints machine output. */ +function Readout({ children, className = "" }: { children: ReactNode; className?: string }) { + return ( +
+
+ ); +} + +/** Weight → needle angle on a 240° dial. */ +const GAUGE_MAX = 180; +const needle = (kg: number) => `${(-120 + (kg / GAUGE_MAX) * 240).toFixed(1)}deg`; + +const GYM = [ + { key: "squat", label: "SQUAT", weight: 125, date: "5.1.2025", icon: "/squat.svg" }, + { key: "bench", label: "BENCH", weight: 111, date: "29.5.2026", icon: "/bench.svg" }, + { key: "deadlift", label: "DEADLIFT", weight: 165, date: "17.12.2024", icon: "/deadlift.svg" }, +]; + +/* ------------------------------------------------------------------- page */ + +export default function SkeuoLayout() { + return ( +
+
+ {/* ------------------------------------------------------ nameplate */} +
+ + + +

dvorakmortonxd.dev

+ +
+ +
+ {/* ------------------------------------------------------ welcome */} +
+ + README.MD + +
+ + martin@dvorakmortonxd + :~$ whoami + +

+ Welcome my personal website! Here I show off my projects and hardware I own, + aswell as my self-hosted services :) +

+
+ +
+ + {/* ------------------------------------------------------ socials */} +
+ + SOCIALS.JS + +

Patch bay — every line is a live feed

+ +
+ {socials.map((s) => ( + + {s.name} + {s.name} + + ))} +
+ +

+

+
+ + {/* ---------------------------------------------------------- gym */} +
+ + + GYM_SBD_AT_73KG_BW.DB + + +

Analogue load meters — best lifts, bodyweight 73 kg

+ +
+ {GYM.map((g) => ( +
+
+ +
+
{g.label}
+
+ {g.weight}kg +
+
{g.date}
+
+ ))} +
+
+ + {/* ---------------------------------------------------------- now */} +
+ + + NOW.TXT + + +
+
+
+ + {/* -------------------------------------------------- about me */} +
+
+ + {/* ----------------------------------------------------- services */} +
+
+ SERVICES.HTML +
+ +
+ + + + + + +
+
+ + {/* ------------------------------------------------------- gitdxd */} +
+ + GITDXD.GIT + +
+

GitDXD

+

My Self-hosted Git Instance

+ +
+
+ +
+

+ My very own forgejo git instance, originally started as a private git instance for me + and my friends to boycott GitHub but I've made it public now so. Join at{" "} + git.dvorakmortonxd.dev. +

+
+
+
+ + {/* ------------------------------------------------ contributions */} +
+
+ + CONTRIBUTIONS@GITDXD.GIT + +
+
+ +
+
+ + {/* ----------------------------------------------------- revival */} +
+ + + REVIVAL.INC + + +
+
+
+ +
+

What is it?

+

+ It's a game I made for the game jam "Mini Jame Gam #55", the theme was + Necromancy and the special item was Milk, the game also includes a tutorial so if you + wanna try it do it. Check it out at{" "} + itch.io or play{" "} + here. +

+
+
+
+ + {/* --------------------------------------------------- rustywolf */} +
+ + RUSTYWOLF.RS + +
+
+ + RustyWolf Browser Preview +
+ +
+

Introducing RustyWolf — what makes it so cool???

+

+ A lightweight browser made in Rust with WebKitGTK. It's the successor to my previous + project Venturesome, aiming to provide a fast and minimalist browsing experience. + It's a perfect browser for old machines that struggle with modern browsers. Check it + out at{" "} + GitDXD. +

+
+
+
+ + {/* ----------------------------------------------------- hardware */} +
+
+ TECH_LIST.JSON +

All my hardware equipment :)

+
+ +
+
+ {techProjects.map((project) => ( +
+
+ ))} +
+
+
+ + {/* ----------------------------------------------------- projects */} +
+
+ + PROJECTS.DOCX + +

All the stuff I've worked on :))

+
+ +
+
+
+ {codingProjects.map((project, idx) => ( + + +
+
+
+
+ + {/* ------------------------------------------------------ friends */} +
+ + FRIENDS.TSX +

+ Friend's websites and projects +

+ +
+ {friends.map((friend, idx) => ( + } + name={friend.name} + desc={friend.desc} + /> + ))} +
+
+
+ + {/* -------------------------------------------------------- footer */} +
+
+
+
+ ); +} diff --git a/src/app/components/SlBackground.tsx b/src/app/components/SlBackground.tsx deleted file mode 100644 index 4f9ad36..0000000 --- a/src/app/components/SlBackground.tsx +++ /dev/null @@ -1,106 +0,0 @@ -"use client"; - -import { useEffect, useState } from "react"; - -const TRAIN_ASCII = ` ==== ________ ___________ - _D _| |_______/ \\__I_I_____===__|_________| - |(_)--- | H\\________/ | | =|___ ___| _________________ _________________ _________________ _________________ _________________ _________________ - / | | H | | | | ||_| |_|| _| \\_____| \\_____| \\_____| \\_____| \\_____| \\_____A - | | | H |__--------------------| [___] | =| | | | | | | - | ________|___H__/__|_____/[][]~\\_______| | -| | | | | | | - |/ | |-----------I_____I [][] [] D |=======|____|________________________|_______________________|_______________________|_______________________|_______________________|_______________________|_ -__/ =| o |=-~~\\ /~~\\ /~~\\ /~~\\ ____Y___________|__|__________________________|_______________________|_______________________|_______________________|_______________________|_______________________| - |/-=|___|= || || || |_____/~\\___/ |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| |_D__D__D_| - \\_/ \\O=====O=====O=====O_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/ \\_/`; - -export default function SlBackground() { - const [pos, setPos] = useState(150); - - useEffect(() => { - let animationFrameId: number; - let lastTime = 0; - - const animate = (timestamp: number) => { - if (!lastTime) lastTime = timestamp; - const delta = timestamp - lastTime; - - // Update position every ~30ms to create a retro sliding terminal refresh feel - if (delta > 45) { - setPos((prev) => { - if (prev <= -150) return 150; - return prev - 1.5; - }); - lastTime = timestamp; - } - animationFrameId = requestAnimationFrame(animate); - }; - - animationFrameId = requestAnimationFrame(animate); - return () => cancelAnimationFrame(animationFrameId); - }, []); - - return ( - <> - -
-
-          {TRAIN_ASCII}
-        
-
- - ); -} - - - - - - - - - - diff --git a/src/app/components/TerminalLayout.tsx b/src/app/components/TerminalLayout.tsx deleted file mode 100644 index e48b482..0000000 --- a/src/app/components/TerminalLayout.tsx +++ /dev/null @@ -1,603 +0,0 @@ -import Image from "next/image"; -import type { CSSProperties } from "react"; -import SlBackground from "./SlBackground"; -import JokerLogo from "./JokerLogo"; -import ServiceButton from "./ServiceButton"; -import ContributionHeatmap from "./ContributionHeatmap"; -import { useTheme } from "./useTheme"; -import { socials, codingProjects, techProjects, friends } from "../data"; - -type PanelStyle = CSSProperties & { "--panel-color"?: string }; - -const panelStyle = (color: string, style: CSSProperties = {}): PanelStyle => ({ - ...style, - "--panel-color": color, -}); - -export default function TerminalLayout() { - const { toggle } = useTheme(); - - return ( - <> - -
-
-
- -

dvorakmortonxd.dev

- -
-
- -
-
- - README.MD - -

$ whoami

-

- Welcome my personal website! Here I show off my projects and hardware I own, - aswell as my self-hosted services :) -

-
- -
-
- - SOCIALS.JS - -
- {socials.map((s) => ( - - {s.name} - {s.name} - - ))} -
-
- mail if u ever wanna contact me:{" "} - dvorak@dvorakmortonxd.dev -
-
-
- -
-
- - GYM_SBD_AT_73KG_BW.DB - -
-
- Squat -
SQUAT
-
125kg
-
5.1.2025
-
-
- Bench -
BENCH
-
111kg
-
29.5.2026
-
-
- Deadlift -
DEADLIFT
-
165kg
-
17.12.2024
-
-
-
-
- -
-
- - NOW.TXT - -
-

- martin@dvorakmortonxd: - ~$ cat now.txt -

-
-

- I'm currently hosting an SMP (dm me on discord). Also I'm pretty - much cooking daily, because im bulking through out the non-summer seasons. - And me and my pal Pepa are working on an - education app so we can study more efficiently, It's called{" "} - maturita.c! -

-
-
-
-
- -
-
- - README.MD - -
-

- martin@dvorakmortonxd: - ~$ cat about_me.md -

-
-

- Hey, I'm Martin Dvořák — a 17(soon to be 18)-year-old IT student based - in the Czech Republic, specializing in network management and - administration at sssvt.cz. I'm a - sophomore and I have wanted to go to this school since 7th grade (2022) so - I'm happy I got in in 2025, the school is pretty cool so if you are in - middle school and interested in IT, make sure to check it out. I'm a - homelabber and developer heavily focused on self-hosting{" "} - literally everything, writing code (for example games for gamejams), - tinkering with GNU/Linux or Musl/Linux systems and retro hardware. I own a - small collection of laptops ranging from 2009 to 2014 (and ofcourse my 2025 - macbook), most of these found in ewaste. In my free time I mostly spend - time with my gf {"<"}3, I also spend a bit of time setting up my self - hosted stuff (like jellyfin or gitdxd), and also when I'm in my gaming - mood I play terraria (around 1300hr playtime btw) and minecraft modpacks - with mekanism and/or create. I also go to the gym around 3-5 times a week, - running a PPL split focused on strength/powerlifting and especially SBD. I - take a big focus on my health and physique. - -

-
-
-
-
- -
-
- - SERVICES.HTML - -
- - - - - - -
-
-
- -
-
- - GITDXD.GIT - -
-

- GitDXD -

-

- My Self-hosted Git Instance -

-
- GitDXD Preview -
-
-

- My very own forgejo git instance, originally started as a private git - instance for me and my friends to boycott GitHub but I've made it - public now so. Join at{" "} - - git.dvorakmortonxd.dev - - . -

-
-
-
-
- -
-
- - CONTRIBUTIONS@GITDXD.GIT - -
- -
-
-
- -
-
- - REVIVAL.INC - -
-

- Revival inc. -

-
- revival inc thumbnail -
-

- What is it? -

-
-

- It's a game I made for the game jam "Mini Jame Gam #55", the - theme was Necromancy and the special item was Milk, the game also includes - a tutorial so if you wanna try it do it. Check it out at{" "} - - itch.io - {" "} - or play{" "} - - here - - . -

-
-
-
-
- -
-
- - RUSTYWOLF.RS - -
-

- Introducing RustyWolf -

-
- RustyWolf Browser Preview -
-

- What makes it so cool??? -

-
-

- A lightweight browser made in Rust with WebKitGTK. It's the successor - to my previous project Venturesome, aiming to provide a fast and minimalist - browsing experience. It's a perfect browser for old machines that - struggle with modern browsers. Check it out at{" "} - - - GitDXD - - - . -

-
-
-
-
-
- -
-
-
- - TECH_LIST.JSON - -

- All my hardware equipment :) -

-
- {techProjects.map((project) => ( -
-
-

{project.title}

-

{project.desc}

-
- ))} -
-
-
- -
-
- - PROJECTS.DOCX - -

- All the stuff I've worked on :)) -

-
- {codingProjects.map((project, idx) => ( - - -
-
- -
-
- - FRIENDS.TSX - -

- Friend's websites and projects -

-
- {friends.map((friend, idx) => ( - - } - name={friend.name} - desc={friend.desc} - /> - ))} -
-
-
-
- -
- v2.1.0 - © 2025-2026 Martin Dvořák. All rights reserved (not really I dont care, just follow - the GPL3.0 License) -
-
- - ); -} diff --git a/src/app/components/ThemeRoot.tsx b/src/app/components/ThemeRoot.tsx index 8b49383..12f4ccc 100644 --- a/src/app/components/ThemeRoot.tsx +++ b/src/app/components/ThemeRoot.tsx @@ -1,32 +1,57 @@ "use client"; -import { useTheme } from "./useTheme"; -import TerminalLayout from "./TerminalLayout"; +import { useEffect, useState } from "react"; +import { setTheme, useTheme } from "./useTheme"; +import SkeuoLayout from "./SkeuoLayout"; import WindowsDesktop from "./WindowsDesktop"; import MobileHome from "./MobileHome"; export default function ThemeRoot() { const { theme } = useTheme(); - const terminal = theme === "terminal"; + const [phone, setPhone] = useState(false); + + // The desktop simulation only exists on phone-sized viewports. On anything + // wider the page is always the skeuo theme, and a stale "desktop" + // preference is overridden here so nobody is stuck in a mode whose switch + // they cannot reach. + useEffect(() => { + if (typeof window.matchMedia !== "function") return; + const mq = window.matchMedia("(max-width: 768px)"); + const sync = () => { + setPhone(mq.matches); + if (!mq.matches && document.documentElement.getAttribute("data-theme") === "desktop") { + setTheme("skeuo"); + } + }; + sync(); + mq.addEventListener("change", sync); + return () => mq.removeEventListener("change", sync); + }, []); + + const desktop = theme === "desktop"; - // The desktop is kept mounted (just hidden) while the Gruvbox/terminal theme - // is active. That way the Welcome window is only created on a fresh page - // load and isn't reopened by toggling themes. return ( <> -
- + {/* + The skeuo theme stays mounted (just hidden) while the desktop is up so + its scroll position survives a toggle. The desktop is mounted once the + viewport is known to be a phone and then kept mounted, so the Welcome + window is only created on demand and isn't reopened by toggling. + */} +
+
-
- -
- {terminal && } + + {phone ? ( + <> +
+ +
+
+ +
+ + ) : null} ); } diff --git a/src/app/components/ThemeScript.tsx b/src/app/components/ThemeScript.tsx index 2546edf..bf34d65 100644 --- a/src/app/components/ThemeScript.tsx +++ b/src/app/components/ThemeScript.tsx @@ -7,7 +7,27 @@ import { useServerInsertedHTML } from "next/navigation"; // without a flash. It is injected with useServerInsertedHTML so the