diff --git a/README.md b/README.md
index 65a0b96..f18da5e 100644
--- a/README.md
+++ b/README.md
@@ -72,7 +72,7 @@ more.
- **Fonts:** the web theme uses the system sans stack; Monocraft (self-hosted in `public/`) is
used by the desktop theme's SMP menu and the in-desktop terminal.
- **Images:** `next/image`
-- **Version:** `2.2.4` (see `package.json`)
+- **Version:** `2.2.5` (see `package.json`)
## ๐ Running Locally
diff --git a/agents.md b/agents.md
index bd8a8b2..b4b2940 100644
--- a/agents.md
+++ b/agents.md
@@ -31,6 +31,19 @@ When in doubt, prefer updating. A stale README **or** agents.md is treated as a
---
+## Bump the version on EVERY prompt โ also do this every time
+
+Another standing requirement: increment the **patch** version (`x.x.+1`, so
+`2.2.5` โ `2.2.6` โ โฆ) as part of every task, even the small ones. Keep all of
+these in sync:
+
+- `package.json` โ `"version"`
+- `package-lock.json` โ the root `"version"` **and** `packages[""].version`
+- the `vX.Y.Z` label in `WebLayout.tsx`'s `.web-footer-label`
+- the version line in `README.md` and in this file
+
+---
+
## What this project is
Personal portfolio site for **dvorakmortonxd.dev**. It's not a normal website โ
@@ -43,7 +56,7 @@ frosted-glass **web** site, and an alternate KDE/Plasma **desktop simulation**
utilities in the components (Tailwind is present via PostCSS but the look is
hand-written CSS).
- Path alias: `@/*` โ `src/*`.
-- Version: `2.2.4` (see `package.json`).
+- Version: `2.2.5` (see `package.json`).
### Scripts
```bash
@@ -129,10 +142,13 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark),
section nav, light/dark switch, desktop switch, contact/clock) beside a single
content column of `.glass` panels: hero (with a compact "Find me" socials row),
a `.web-quick` 2-up ("Right now" + "Gym PRs"), then services, activity,
- projects, hardware, features (Revival/RustyWolf) and friends. Friend cards are
- **text-only** (name + domain) in a `.friends-grid` that collapses to a 2ร2
- grid on phones; the desktop friend icons use a ๐ง emoji glyph (the old
- per-friend SVGs are gone). Projects/hardware use a CSS multi-column
+ projects, hardware, features (Revival/RustyWolf) and friends. Friend cards show
+ a big `friend.emoji` glyph (sized ~the name + domain combined) beside the name +
+ domain; the `.friends-grid` is **always exactly 2 columns** so it stays a clean
+ rectangle/square and never ends in an orphan "L" row (a 4th friend made the old
+ 3-column grid do that). The same `friend.emoji` drives the desktop friend icon
+ (Josef is ๐ง goblin, the rest ๐ง โ the old per-friend SVGs are gone).
+ Projects/hardware use a CSS multi-column
**masonry** gallery (`.project-grid`) so short cards don't leave half-empty
grid slots; on phones it drops to 2 columns with a shorter card image. The two feature panels (`.web-split`) stretch
to equal height and share a fixed `16 / 10` `.web-shot` frame with
@@ -393,8 +409,9 @@ knows about icon ids, via `APP_BY_ID`.
two differ. `bukovinafilip.com`, `vlcak.me` and `k0t4tkos-portfolio.com` send
no framing headers, so they frame the live site. `skopec.dev` sends
`X-Frame-Options: SAMEORIGIN` on every path, so `friend.proxy` routes it
- through `/api/frame`. Friend icons are a ๐ง emoji everywhere (no per-friend
- images); the web theme's friends section is text-only.
+ through `/api/frame`. Friend icons come from the `friend.emoji` field (no
+ per-friend images) and are used both in the web friends section and on the
+ desktop.
- `/api/frame` is a **host-allowlisted** header-stripping proxy for sites that
refuse framing. It fetches the HTML server-side (so the upstream
`X-Frame-Options` never reaches the browser), injects ``
diff --git a/package-lock.json b/package-lock.json
index 1979b91..017c4c3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "dvorakmortonxd",
- "version": "2.2.4",
+ "version": "2.2.5",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "dvorakmortonxd",
- "version": "2.2.4",
+ "version": "2.2.5",
"dependencies": {
"next": "^16.3.5",
"react": "^19.3.0",
diff --git a/package.json b/package.json
index 41c0fae..8163f91 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "dvorakmortonxd",
- "version": "2.2.4",
+ "version": "2.2.5",
"private": true,
"scripts": {
"dev": "node scripts/dev-server.mjs",
diff --git a/src/app/components/WebLayout.tsx b/src/app/components/WebLayout.tsx
index b7e558d..0a0ea6c 100644
--- a/src/app/components/WebLayout.tsx
+++ b/src/app/components/WebLayout.tsx
@@ -336,6 +336,7 @@ export default function WebLayout() {
key={idx}
url={friend.realUrl || friend.url}
className="friend"
+ icon={friend.emoji}
name={friend.name}
desc={friend.desc}
/>
@@ -344,7 +345,7 @@ export default function WebLayout() {
diff --git a/src/app/components/WindowsDesktop.tsx b/src/app/components/WindowsDesktop.tsx
index c41f4d5..141fa99 100644
--- a/src/app/components/WindowsDesktop.tsx
+++ b/src/app/components/WindowsDesktop.tsx
@@ -76,7 +76,7 @@ const FRIEND_SHORTCUTS: Shortcut[] = friends.map((f, i) => ({
: f.url,
embed: f.iframe,
color: "#c0392b",
- icon: "๐ง",
+ icon: f.emoji,
desc: f.desc,
}));
@@ -362,7 +362,7 @@ export default function WindowsDesktop() {
id: f.id,
label: f.label,
title: f.label,
- glyph: "๐ง",
+ glyph: f.icon,
onOpen: () => openShortcut(f),
}));
const allEntries = [...leftEntries, ...serviceEntries, ...friendEntries];
diff --git a/src/app/components/apps.tsx b/src/app/components/apps.tsx
index ecedf2a..6fd8ea1 100644
--- a/src/app/components/apps.tsx
+++ b/src/app/components/apps.tsx
@@ -255,7 +255,7 @@ function FriendsApp() {
key={idx}
url={friend.realUrl || friend.url}
className="friend"
- icon="๐ง"
+ icon={friend.emoji}
name={friend.name}
desc={friend.desc}
/>
diff --git a/src/app/data.ts b/src/app/data.ts
index 6876eaa..1aed85b 100644
--- a/src/app/data.ts
+++ b/src/app/data.ts
@@ -24,6 +24,8 @@ export interface Friend {
/** The live site: the window's `www` button and the status check use this. */
realUrl: string;
desc: string;
+ /** Avatar glyph shown next to the name (web) and as the desktop icon. */
+ emoji: string;
/** Render in an iframe in a desktop window. */
iframe: boolean;
/**
@@ -245,6 +247,7 @@ export const friends: Friend[] = [
url: "https://skopec.dev/",
realUrl: "https://skopec.dev/",
desc: "skopec.dev",
+ emoji: "๐ง", // goblin, per the author
iframe: true,
proxy: true,
},
@@ -253,6 +256,7 @@ export const friends: Friend[] = [
url: "https://bukovinafilip.com/",
realUrl: "https://bukovinafilip.com/",
desc: "bukovinafilip.com",
+ emoji: "๐ง",
iframe: true,
},
{
@@ -260,6 +264,7 @@ export const friends: Friend[] = [
url: "https://vlcak.me",
realUrl: "https://vlcak.me",
desc: "vlcak.me",
+ emoji: "๐ง",
iframe: true,
},
{
@@ -268,6 +273,7 @@ export const friends: Friend[] = [
url: "https://k0t4tkos-portfolio.com/",
realUrl: "https://k0t4tkos-portfolio.com/",
desc: "k0t4tkos-portfolio.com",
+ emoji: "๐ง",
iframe: true,
},
];
diff --git a/src/app/web.css b/src/app/web.css
index 3c1f93f..afe2f5e 100644
--- a/src/app/web.css
+++ b/src/app/web.css
@@ -482,6 +482,11 @@ html[data-theme="web"] body {
.services-buttons.services-main {
grid-template-columns: repeat(3, minmax(0, 1fr));
}
+/* Friends: always exactly 2 columns at every width, so the grid stays a clean
+ rectangle/square instead of ending in an orphan "L" row. */
+.services-buttons.friends-grid {
+ grid-template-columns: repeat(2, minmax(0, 1fr));
+}
.service-button {
position: relative;
@@ -572,25 +577,37 @@ html[data-theme="web"] body {
color: var(--fg);
}
-/* Friend cards centre their text (the web theme has no icon; the desktop
- Friends app shows the wizard emoji above the name). */
+/* Friend cards: a big emoji beside the name + website, sized to roughly the
+ two text lines combined so the (wide) card doesn't look empty. */
.service-button.friend {
- flex-direction: column;
- text-align: center;
- gap: 10px;
- padding: 18px 16px;
+ flex-direction: row;
+ align-items: center;
+ gap: 16px;
+ padding: 16px 20px;
+}
+/* Let the corner status dot anchor to the card, not to the emoji. */
+.service-button.friend .service-icon-wrap {
+ position: static;
}
.service-button.friend .service-text {
- align-items: center;
- width: 100%;
- /* Leave room for the corner status dot so names don't run under it. */
- padding-inline: 10px;
+ align-items: flex-start;
+ width: auto;
+ min-width: 0;
}
-/* Friend cards are text-only now, so let long names wrap instead of running
- under the corner status dot. */
.service-button.friend .service-name {
white-space: normal;
}
+.service-button.friend .service-icon {
+ width: auto;
+ height: auto;
+ font-size: 2.5rem;
+ line-height: 1;
+ filter: none;
+}
+/* Long domains must wrap on narrow cards instead of overflowing. */
+.service-button.friend .service-desc {
+ overflow-wrap: anywhere;
+}
.service-button.friend .service-status-dot {
top: 12px;
right: 12px;
@@ -603,13 +620,6 @@ html[data-theme="web"] body {
transform: none;
filter: none;
}
-/* The desktop Friends app shows a big wizard emoji in place of the old avatars. */
-.service-button.friend .service-icon {
- width: 64px;
- height: 64px;
- font-size: 2.6rem;
- filter: none;
-}
/* ============================================================
PROJECTS / HARDWARE
@@ -1203,9 +1213,11 @@ html[data-theme="web"][data-scheme="dark"] .heatmap-legend-cell[data-level="4"]
.services-buttons.services-main {
grid-template-columns: 1fr;
}
- /* Friends are text-only cards now, so they read fine as a 2x2 grid. */
- .services-buttons.friends-grid {
- grid-template-columns: repeat(2, minmax(0, 1fr));
+ /* Friend cards stay 2-up here, so tighten the padding to give the text room
+ next to the big emoji. */
+ .service-button.friend {
+ gap: 12px;
+ padding: 12px 14px;
}
.social-buttons {
grid-template-columns: 1fr 1fr;