From a0fd2fa2a1fbe118fee718ae5ec0b3f923faf619 Mon Sep 17 00:00:00 2001 From: SirBlobby Date: Mon, 26 Oct 2026 16:08:26 -0400 Subject: [PATCH] Add mobile styles, social icons and README badges --- README.md | 197 +++++------------- content/desktop.json | 46 ++-- content/experience.json | 3 +- content/projects/hackathons.json | 4 +- content/projects/research.json | 2 +- src/app.html | 2 +- src/lib/components/desktop/Desktop.svelte | 11 +- .../components/desktop/DesktopIcons.svelte | 28 ++- src/lib/components/desktop/Launcher.svelte | 17 +- src/lib/components/desktop/Window.svelte | 12 ++ src/lib/components/models/ModelViewer.svelte | 7 + src/lib/components/shell/Bar.svelte | 7 +- src/lib/components/shell/Clock.svelte | 14 +- src/lib/components/shell/PopupCard.svelte | 10 + src/lib/components/shell/Workspaces.svelte | 2 +- src/lib/components/terminal/Fetch.svelte | 1 - src/lib/components/terminal/Terminal.svelte | 12 ++ src/lib/components/ui/ProfileIntro.svelte | 14 ++ src/lib/content/types.ts | 8 +- src/lib/terminal/commands/whoami.ts | 1 - src/routes/+layout.svelte | 1 + src/routes/layout.css | 18 ++ src/routes/portfolio/+page.svelte | 2 +- 23 files changed, 235 insertions(+), 184 deletions(-) diff --git a/README.md b/README.md index c8ccd53..2bc2568 100644 --- a/README.md +++ b/README.md @@ -1,152 +1,65 @@ -# Sir Blob's Desktop +
+

Sir Blob's Desktop

+

My portfolio website, built as a web version of my Arch Linux 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. + Arch Linux + Hyprland + SvelteKit + TypeScript + Tailwind CSS + Three.js + Bun +
+ +--- + +The site recreates my [blomarchy](https://git.sirblob.co/SirBlob/blomarchy) setup in the browser: a Hyprland-style tiling desktop, the Quickshell bar, and kitty terminals you can type into. + +## Features + +- **Tiling windows** - every page opens as a window that tiles like Hyprland's dwindle layout, with gradient borders. +- **Quickshell bar** - app launcher, workspaces, clock, live Discord status, battery and a theme picker. +- **Kitty terminals** - a real command line with history and tab completion. Try `fastfetch`, `whoami` or `cowsay`. +- **Desktop icons** - grouped into pages, terminal scripts and socials (GitHub, LinkedIn, Devpost, Discord). +- **13 themes** - Flats, Retro 82, Tokyo Night, Catppuccin, Arch Linux, Gruvbox, Nord, Kanagawa, Wintry, Rose, Cerberus, Catppuccin Latte and Rose Pine Dawn, each with its own wallpaper. +- **Portfolio** - about, portfolio, projects (open source, packages, hackathons, research), blog with RSS, 3D models and links with QR codes. +- **Mobile friendly** - on small screens windows go fullscreen with tabs to switch between them. + +## Keybinds + +| Keys | Action | +| --- | --- | +| Alt + Enter | Open a terminal | +| Alt + Space | App launcher | +| Alt + Q | Close window | +| Alt + F | Fullscreen page | +| Alt + J / K | Next / previous window | +| Alt + 1-9 | Switch workspace | +| Alt + T | Next theme | + +Alt stands in for Super, since browsers can't use it. + +## Terminal commands + +| Command | Does | +| --- | --- | +| `help` | list every command | +| `whoami` | who I am | +| `fastfetch` | system info | +| `skills` | skills by category | +| `experience` | work experience | +| `projects [category]` | list my projects | +| `contact` | where to find me | +| `ls` / `cd ` | browse and open pages | +| `theme [name]` | list or switch themes | +| `cowsay ` | moo | ## Running -`start` builds the site with Bun (so it does not depend on the system Node version) and serves it with Bun on `PORT` (default 3000), reading `.env` for the Discord variables. - ```bash bun install bun run dev -bun run build bun run start -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. - -```json -{ - "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: - -```json -{ "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. - -```markdown ---- -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 `, `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`. +Site content lives in `content/`. diff --git a/content/desktop.json b/content/desktop.json index f8b21da..85061f4 100644 --- a/content/desktop.json +++ b/content/desktop.json @@ -9,20 +9,38 @@ { "number": 6, "name": "portfolio", "path": "/portfolio" }, { "number": 7, "name": "models", "path": "/models" } ], - "icons": [ - { "label": "about", "icon": "ph:user-circle-duotone", "open": { "page": "/about" } }, - { "label": "portfolio", "icon": "ph:briefcase-duotone", "open": { "page": "/portfolio" } }, - { "label": "projects", "icon": "ph:folder-open-duotone", "open": { "page": "/projects" } }, - { "label": "hackathons", "icon": "ph:trophy-duotone", "open": { "page": "/projects/hackathons" } }, - { "label": "blog", "icon": "ph:notebook-duotone", "open": { "page": "/blog" } }, - { "label": "models", "icon": "ph:cube-duotone", "open": { "page": "/models" } }, - { "label": "links", "icon": "ph:qr-code-duotone", "open": { "page": "/links" } }, - { "label": "kitty", "icon": "ph:terminal-window-duotone", "open": { "terminal": "" } }, - { "label": "whoami.sh", "icon": "ph:identification-card-duotone", "open": { "terminal": "whoami" } }, - { "label": "fastfetch.sh", "icon": "ph:cpu-duotone", "open": { "terminal": "fastfetch" } }, - { "label": "skills.sh", "icon": "ph:lightning-duotone", "open": { "terminal": "skills" } }, - { "label": "contact.sh", "icon": "ph:envelope-simple-duotone", "open": { "terminal": "contact" } }, - { "label": "github", "icon": "ph:github-logo-duotone", "open": { "url": "https://github.com/SirBlobby" } } + "iconGroups": [ + { + "name": "pages", + "icons": [ + { "label": "about", "icon": "ph:user-circle-duotone", "open": { "page": "/about" } }, + { "label": "portfolio", "icon": "ph:briefcase-duotone", "open": { "page": "/portfolio" } }, + { "label": "projects", "icon": "ph:folder-open-duotone", "open": { "page": "/projects" } }, + { "label": "hackathons", "icon": "ph:trophy-duotone", "open": { "page": "/projects/hackathons" } }, + { "label": "blog", "icon": "ph:notebook-duotone", "open": { "page": "/blog" } }, + { "label": "models", "icon": "ph:cube-duotone", "open": { "page": "/models" } }, + { "label": "links", "icon": "ph:qr-code-duotone", "open": { "page": "/links" } } + ] + }, + { + "name": "scripts", + "icons": [ + { "label": "kitty", "icon": "ph:terminal-window-duotone", "open": { "terminal": "" } }, + { "label": "fastfetch.sh", "icon": "ph:cpu-duotone", "open": { "terminal": "fastfetch" } }, + { "label": "whoami.sh", "icon": "ph:identification-card-duotone", "open": { "terminal": "whoami" } }, + { "label": "skills.sh", "icon": "ph:lightning-duotone", "open": { "terminal": "skills" } }, + { "label": "contact.sh", "icon": "ph:envelope-simple-duotone", "open": { "terminal": "contact" } } + ] + }, + { + "name": "socials", + "icons": [ + { "label": "github", "icon": "ph:github-logo-duotone", "open": { "url": "https://github.com/SirBlobby" } }, + { "label": "linkedin", "icon": "ph:linkedin-logo-duotone", "open": { "url": "https://www.linkedin.com/in/gmanjunatha/" } }, + { "label": "devpost", "icon": "simple-icons:devpost", "open": { "url": "https://devpost.com/Sir_Blob_" } }, + { "label": "discord", "icon": "ph:discord-logo-duotone", "open": { "url": "https://discord.com/users/sir_blob_" } } + ] + } ], "startupCommand": "fastfetch" } diff --git a/content/experience.json b/content/experience.json index 5e4bc67..3d313ed 100644 --- a/content/experience.json +++ b/content/experience.json @@ -1,7 +1,8 @@ [ { "role": "Undergraduate Research Assistant, CREATE Lab", - "organization": "George Mason University, College of Engineering and Computing", + "organization": "College of Engineering and Computing", + "school": "George Mason University", "period": "May 2026 – Present", "highlights": [ "Built Astroswarm, a visual coding game that turns swarm control into a playable simulator, generating trial data at scale on which algorithms best defend a target.", diff --git a/content/projects/hackathons.json b/content/projects/hackathons.json index 9dc2eed..142acb2 100644 --- a/content/projects/hackathons.json +++ b/content/projects/hackathons.json @@ -15,7 +15,7 @@ "image": "/hacks/learningbuddy.png", "tags": ["C++", "ESP32-S3", "WebSocket", "MongoDB", "IoT"], "featured": true, - "event": { "name": "HackFax x PatriotHacks 2026", "university": "George Mason University", "location": "Fairfax, VA", "year": "2026" }, + "event": { "name": "HackFax x PatriotHacks 2026", "year": "2026" }, "awards": [{ "track": "Best use of MongoDB", "place": "Winner" }], "links": { "code": "https://github.com/SirBlobby/LearningBuddy", "devpost": "https://devpost.com/software/studybuddy-bc0i3j" } }, @@ -25,7 +25,7 @@ "image": "/hacks/carbin.png", "tags": ["AI", "Azure", "CloudConvert", "Python", "React", "TypeScript", "API"], "featured": true, - "event": { "name": "PatriotHacks 2024", "university": "George Mason University", "location": "Fairfax, VA", "year": "2024" }, + "event": { "name": "PatriotHacks 2024", "year": "2024" }, "awards": [ { "track": "Save the World", "place": "2nd Place" }, { "track": "Microsoft X Cloudforce", "place": "2nd Place" } diff --git a/content/projects/research.json b/content/projects/research.json index 658c57f..d390c92 100644 --- a/content/projects/research.json +++ b/content/projects/research.json @@ -1,7 +1,7 @@ [ { "name": "Astroswarm", - "organization": "CREATE Lab, George Mason University", + "organization": "CREATE Lab", "period": "May 2026 – Present", "description": "A visual coding game that turns swarm control into a playable simulator, generating trial data at scale on which algorithms best defend a target. Part of a research program on controlling robot fleets with one operator.", "tags": ["Python", "ROS", "Swarm Robotics"], diff --git a/src/app.html b/src/app.html index 14eef7c..3c73ab6 100644 --- a/src/app.html +++ b/src/app.html @@ -2,7 +2,7 @@ - + @@ -151,12 +150,8 @@ } @media (max-width: 767px) { - .icons-layer :global(.desktop-icons) { - grid-auto-flow: row; - grid-template-columns: repeat(auto-fill, 112px); - grid-template-rows: none; - grid-auto-rows: 104px; - justify-content: center; + .icons-layer { + overflow-y: auto; } } diff --git a/src/lib/components/desktop/DesktopIcons.svelte b/src/lib/components/desktop/DesktopIcons.svelte index d78a3d3..2892b96 100644 --- a/src/lib/components/desktop/DesktopIcons.svelte +++ b/src/lib/components/desktop/DesktopIcons.svelte @@ -2,10 +2,12 @@ import Icon from '@iconify/svelte'; import { desktop } from '#lib/content'; import { runDesktopAction } from '#lib/desktop/actions'; + + const icons = desktop.iconGroups.flatMap((group) => group.icons);