5.9 KiB
Sir Blob's Desktop
A portfolio website that recreates my Arch Linux desktop (Hyprland, Quickshell bar, kitty terminals) in the browser. Built with SvelteKit 3, Svelte 5 runes and Tailwind CSS 4.
Running
bun install
bun run dev
bun run build
bun run check
How the desktop works
- The top bar mirrors the blob-shell Quickshell bar: chai menu (app launcher), workspaces 1-9, clock, Discord status and theme picker.
- Every page (
/about,/projects/hackathons, ...) opens as a tiled window, so old resume links still work. - Terminals open next to it and tile with the Hyprland dwindle layout.
- Desktop icons open a page, a terminal running a command, or an external link.
- On small screens the focused window goes fullscreen and open windows are listed as tabs.
Keybinds
| Keys | Action |
|---|---|
| Alt + Enter | Open a kitty terminal |
| Alt + Space | App launcher |
| Alt + Q | Close focused window |
| Alt + F | Fullscreen the page window |
| Alt + J / K | Focus next / previous window |
| Alt + 1-9 | Switch workspace |
| Alt + T | Next theme |
Alt stands in for Super, which browsers do not receive.
Editing content
Everything you would normally change lives in content/. No code changes needed.
| File | Contents |
|---|---|
content/profile.json |
name (shown only on the portfolio page), bio, avatar, education, socials (also used by the links page), skills, interests |
content/experience.json |
experience with highlights, tech, awards and links |
content/leadership.json |
leadership roles |
content/about.json |
hobbies, games, shows, movies |
content/projects/opensource.json |
open source projects |
content/projects/packages.json |
published packages |
content/projects/hackathons.json |
hackathon projects, events and awards |
content/projects/research.json |
research roles and projects |
content/models.json |
3D models (files go in static/models/) |
content/desktop.json |
default theme, workspaces, desktop icons, startup command |
content/blog.json |
blog name, tagline, posts per page |
content/blog/*.md |
blog posts |
Projects
Every project in every category uses the same shape. Only name, description, tags and links are required.
{
"name": "HushMap",
"description": "What it does.",
"image": "/hacks/hushmap.png",
"tags": ["Svelte", "FastAPI"],
"featured": true,
"event": { "name": "Bitcamp 2026", "university": "University of Maryland", "location": "College Park, MD", "year": "2026" },
"awards": [{ "track": "Best UI/UX Hack", "place": "Winner" }],
"demoMayBeOffline": false,
"version": "2.2.0",
"links": { "demo": "https://...", "code": "https://...", "devpost": "https://...", "package": "https://...", "article": "https://..." }
}
Featured projects are listed first.
Desktop icons
Each icon in content/desktop.json has a label, an Iconify icon and one open action:
{ "label": "about", "icon": "mdi:account", "open": { "page": "/about" } }
{ "label": "skills.sh", "icon": "mdi:script-text", "open": { "terminal": "skills" } }
{ "label": "github", "icon": "mdi:github", "open": { "url": "https://github.com/SirBlobby" } }
{ "terminal": "" } opens an empty terminal.
Blog posts
Add a markdown file to content/blog/. The file name is the URL slug.
---
title: My Post Title
author: Sir Blob
date: 2026-01-01
tags: meta, notes
excerpt: One line summary for the list and the RSS feed.
---
Post content in markdown.
The RSS feed is at /blog/rss.xml.
Themes
Themes live in src/lib/theme/themes.ts and use the same keys as a blomarchy colors.toml, so a theme can be copied over from the dotfiles. Each theme can set a wallpaper (from static/wallpapers/) and an activeBorder gradient. The default theme is set in content/desktop.json.
Shipped themes: Flats (default), Retro 82, Tokyo Night, Catppuccin, Arch Linux, Gruvbox, Nord, Kanagawa, Wintry, Rose, Cerberus, Catppuccin Latte, Rose Pine Dawn.
Terminal commands
help, whoami, fastfetch, skills, contact, email, experience, projects [category], ls, cd <page>, theme [name], echo, cowsay, date, clear, exit. Page names (about, hackathons, ...) and social names (github, linkedin, ...) also work as commands. Tab completes commands, pages after cd, themes after theme and categories after projects; when several match it lists them like bash. Up/Down walk history, Ctrl+L clears.
Commands live in src/lib/terminal/commands/.
Code layout
| Path | Contents |
|---|---|
src/lib/content/ |
typed loader for content/ |
src/lib/blog/ |
markdown renderer, frontmatter, highlighting, dates |
src/lib/theme/ |
theme palettes, stylesheet generation, current theme state |
src/lib/desktop/ |
window manager, dwindle tiling, keybinds, desktop actions |
src/lib/terminal/ |
terminal output model and commands |
src/lib/site/pages.ts |
page list used by the terminal and launcher |
src/lib/components/shell/ |
bar, workspaces, clock, theme menu, Discord status |
src/lib/server/discord.ts |
Discord bot and presence lookup |
src/lib/components/desktop/ |
desktop, windows, icons, launcher |
src/lib/components/terminal/ |
kitty terminal |
src/lib/components/ui/ |
shared page pieces |
src/routes/ |
pages |
Discord status
The bar shows live Discord presence (status, avatar, guild tag, activities). A discord.js bot starts with the server (src/hooks.server.ts) and the bar polls /api/discord every 30 seconds. Copy .env.example to .env and fill in:
| Variable | Value |
|---|---|
DISCORD_TOKEN |
bot token, with the Server Members and Presence intents enabled |
DISCORD_USER_ID |
your user id |
DISCORD_GUILD_ID |
a server both you and the bot are in |
Variables are declared in src/env.ts. Without them the widget shows your profile name as offline.
Analytics
Umami is loaded in src/app.html.