diff --git a/README.md b/README.md index 2def698..ec0369e 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,8 @@ from the saved theme, all from the same content: - **`web`** (default) — a light, frosted-glass **website**: a sticky full-height sidebar (section nav, light/dark switch, desktop switch) beside a column of glass cards — hero with - a socials row, quick tiles, the Revival Inc. / RustyWolf feature panels, live services, + a socials row, quick tiles, the Revival Inc. / RustyWolf feature panels (drawn as little + CSS windows, not screenshots), live services, activity heatmap, projects and hardware (a masonry gallery), and friends. Dark by default with a persisted light mode, and fully responsive, so phones and desktops get the same shell. - **`skeuo`** — an alternate KDE Plasma / Windows-style **desktop simulation** (desktop @@ -31,6 +32,9 @@ more. - **Glassmorphic web layout** — a sticky sidebar with section navigation beside a column of frosted-glass panels (subtle gradients + `backdrop-filter` blur), reorganised into hero / quick-tile / section cards. +- **Asset-free feature windows** — the two feature panels (Revival Inc. / RustyWolf) are + hand-built CSS mock windows instead of screenshots: a little necromancy game scene with a + blinking "PRESS START", and a little browser window with a tab strip, toolbar and start page. - **Draggable desktop icons** — free placement that snaps to a measured grid, one icon per cell, with a follow-the-cursor drag ghost. Icons shrink on cramped viewports so they never collide. @@ -72,7 +76,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.8` (see `package.json`) +- **Version:** `2.2.9` (see `package.json`) - **Dependencies:** `npm audit` reports **0 vulnerabilities**. `next` is kept at a patched release (`>=16.3.6`) for the `next/og` RCE, and `brace-expansion` is force-upgraded via `overrides`. `braces` (a transitive ESLint/`fast-glob` dependency) has **no upstream fix**, @@ -194,6 +198,7 @@ src/app/ useTheme.ts → theme state via useSyncExternalStore useColorScheme.ts → light/dark state via useSyncExternalStore WebLayout.tsx → default web shell: sidebar + glass content + FeatureWindow.tsx → decorative CSS windows for the two feature panels WindowsDesktop.tsx → desktop shell: icons, windows, taskbar, Start menu WinWindow.tsx → one draggable/resizable window apps.tsx → APPS registry, window contents, in-app terminal diff --git a/agents.md b/agents.md index 7e82d9e..5a3ce2d 100644 --- a/agents.md +++ b/agents.md @@ -34,7 +34,7 @@ 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.7` → `2.2.8` → …) as part of every task, even the small ones. Keep all of +`2.2.8` → `2.2.9` → …) as part of every task, even the small ones. Keep all of these in sync: - `package.json` → `"version"` @@ -56,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.8` (see `package.json`). +- Version: `2.2.9` (see `package.json`). ### Scripts ```bash @@ -102,6 +102,7 @@ src/app/ components/ ThemeRoot.tsx → picks which shell to mount (the router of the UI) WebLayout.tsx → "web" theme (default): sticky sidebar + glass content + FeatureWindow.tsx → decorative CSS "windows" for the two feature panels (game / browser) WindowsDesktop.tsx → "skeuo" theme, desktop: icons, windows, taskbar WinWindow.tsx → a single draggable/resizable window apps.tsx → APPS registry + the content of each app + in-app terminal @@ -157,10 +158,16 @@ Themes: `web` = default frosted-glass website (sidebar + content, light/dark), 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 - `object-fit: cover` (+ `object-position: center top`), so source images with - different aspect ratios still line up. The old stacked TUI panel layout and the - `SlBackground` ASCII train were removed with it. + to equal height and share a fixed `16 / 10` `.fw` window (the `FeatureWindow` + component) — a decorative, **asset-free CSS mock-up** rather than a screenshot: + Revival Inc. is a little necromancy game scene (night sky, moon, tombstones, + blinking "PRESS START") and RustyWolf is a little browser window (tab strip, + toolbar, address bar, start page). Both are `aria-hidden` and purely + presentational; the real copy stays in the `.web-feature-text` paragraph below. + The old raster feature shots (`.web-shot` + `next/image`) were removed from the + web theme only — `revivalinc.webp` / `RustyWolfPreview.webp` are still used by + the desktop theme and the projects gallery, so the files stay. The old stacked + TUI panel layout and the `SlBackground` ASCII train were removed earlier. - **Not a terminal.** The web theme deliberately has no TUI chrome: the hero is a name + blurb (no `$ whoami` prompt, no blinking cursor), section eyebrows are plain words (not `README.MD` / `TECH_LIST.JSON` filenames), and the activity diff --git a/package-lock.json b/package-lock.json index 6100b1e..339dbe6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dvorakmortonxd", - "version": "2.2.8", + "version": "2.2.9", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dvorakmortonxd", - "version": "2.2.8", + "version": "2.2.9", "dependencies": { "next": "^16.3.8", "react": "^19.3.0", diff --git a/package.json b/package.json index ea775ee..e9f45f3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dvorakmortonxd", - "version": "2.2.8", + "version": "2.2.9", "private": true, "scripts": { "dev": "node scripts/dev-server.mjs", diff --git a/src/app/components/FeatureWindow.tsx b/src/app/components/FeatureWindow.tsx new file mode 100644 index 0000000..f8ae684 --- /dev/null +++ b/src/app/components/FeatureWindow.tsx @@ -0,0 +1,103 @@ +"use client"; + +import type { ReactNode } from "react"; + +/** + * Decorative, asset-free mock windows for the two feature panels (Revival Inc. + * and RustyWolf). They replace the old raster screenshots so the panels read as + * a little OS window instead of a flat image. Purely presentational — the real + * copy lives in the `.web-feature-text` paragraph below each window. + */ + +function Chrome({ title, children }: { title: string; children: ReactNode }) { + return ( + + ); +} + +function GameBody() { + return ( +
+ + + +
+ + + + +
+ +
+ ♥ ♥ ♥ + SCORE 0000 +
+ PRESS START +
+ ); +} + +function BrowserBody() { + const tiles = ["G", "Y", "R", "D", "X"]; + return ( +
+
+ + + New Tab + + + +
+
+ ‹ + › + ⟳ + + + rustywolf://start + +
+
+
+ RustyWolf +
+
+ + Search or enter address +
+
+ {tiles.map((t) => ( + + {t} + + ))} +
+
+
+ ); +} + +export default function FeatureWindow({ variant }: { variant: "game" | "browser" }) { + if (variant === "game") { + return ( + + + + ); + } + return ( + + + + ); +} diff --git a/src/app/components/WebLayout.tsx b/src/app/components/WebLayout.tsx index 30b7e19..946add8 100644 --- a/src/app/components/WebLayout.tsx +++ b/src/app/components/WebLayout.tsx @@ -5,6 +5,7 @@ import type { ReactNode } from "react"; import JokerLogo from "./JokerLogo"; import ServiceButton from "./ServiceButton"; import ContributionHeatmap from "./ContributionHeatmap"; +import FeatureWindow from "./FeatureWindow"; import Clock from "./Clock"; import { useTheme } from "./useTheme"; import { useColorScheme } from "./useColorScheme"; @@ -139,24 +140,6 @@ function Gym() { ); } -function FeatureShot({ - src, - alt, - width, - height, -}: { - src: string; - alt: string; - width: number; - height: number; -}) { - return ( -
- {alt} -
- ); -} - export default function WebLayout() { const { toggle } = useTheme(); const { scheme, toggle: toggleScheme } = useColorScheme(); @@ -253,12 +236,7 @@ export default function WebLayout() {
- +

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 @@ -269,12 +247,7 @@ export default function WebLayout() {

- +

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 @@ -345,7 +318,7 @@ export default function WebLayout() {

diff --git a/src/app/web.css b/src/app/web.css index afe2f5e..4a686e9 100644 --- a/src/app/web.css +++ b/src/app/web.css @@ -422,7 +422,7 @@ html[data-theme="web"] body { } .web-split { display: grid; - grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); + grid-template-columns: repeat(auto-fit, minmax(min(340px, 100%), 1fr)); gap: 26px; align-items: stretch; } @@ -430,22 +430,402 @@ html[data-theme="web"] body { display: flex; flex-direction: column; } -.web-shot { - /* One fixed frame for every feature shot, regardless of the source image's - aspect ratio, so the two feature panels line up. */ +/* ============================================================ + FEATURE WINDOWS + Asset-free decorative mock windows that replace the old + feature screenshots. Same fixed frame as before so the two + feature panels still line up; the "screen" is pure CSS. + ============================================================ */ +.fw { + --fw-chrome: var(--bg-hard); + display: flex; + flex-direction: column; aspect-ratio: 16 / 10; border-radius: 16px; overflow: hidden; border: 1px solid var(--glass-border); margin-bottom: 14px; + background: var(--glass-lo); box-shadow: 0 18px 36px -22px rgba(23, 32, 60, 0.5); } -.web-shot img { - display: block; - width: 100%; - height: 100%; - object-fit: cover; - object-position: center top; +.fw-bar { + display: flex; + align-items: center; + gap: 10px; + height: 32px; + padding: 0 12px; + background: var(--fw-chrome); + border-bottom: 1px solid var(--glass-border); +} +.fw-dots { + display: flex; + gap: 6px; + flex: none; +} +.fw-dot { + width: 9px; + height: 9px; + border-radius: 50%; +} +.fw-dot:nth-child(1) { + background: #ff5f57; +} +.fw-dot:nth-child(2) { + background: #febc2e; +} +.fw-dot:nth-child(3) { + background: #28c840; +} +.fw-title { + min-width: 0; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + font-size: 0.68rem; + font-weight: 600; + color: var(--web-muted); +} +.fw-body { + position: relative; + flex: 1; + min-height: 0; + overflow: hidden; +} + +/* ---------- Revival Inc.: a little necromancy game scene ---------- */ +.fw-scene { + position: absolute; + inset: 0; + overflow: hidden; + background: + radial-gradient(120% 90% at 50% 125%, rgba(124, 58, 237, 0.55), transparent 70%), + linear-gradient(180deg, #090e22 0%, #171a3c 46%, #2b1c48 100%); +} +.fw-stars { + position: absolute; + inset: 0; + background-image: + radial-gradient(1.6px 1.6px at 12% 22%, rgba(255, 255, 255, 0.9), transparent), + radial-gradient(1.4px 1.4px at 28% 12%, rgba(255, 255, 255, 0.7), transparent), + radial-gradient(1.8px 1.8px at 44% 28%, rgba(255, 255, 255, 0.85), transparent), + radial-gradient(1.3px 1.3px at 62% 14%, rgba(255, 255, 255, 0.6), transparent), + radial-gradient(1.6px 1.6px at 78% 30%, rgba(255, 255, 255, 0.8), transparent), + radial-gradient(1.4px 1.4px at 88% 10%, rgba(255, 255, 255, 0.7), transparent), + radial-gradient(1.2px 1.2px at 8% 44%, rgba(255, 255, 255, 0.5), transparent), + radial-gradient(1.5px 1.5px at 36% 40%, rgba(255, 255, 255, 0.55), transparent), + radial-gradient(1.3px 1.3px at 70% 44%, rgba(255, 255, 255, 0.5), transparent); + animation: fw-twinkle 4s ease-in-out infinite alternate; +} +@keyframes fw-twinkle { + from { + opacity: 0.55; + } + to { + opacity: 1; + } +} +.fw-moon { + position: absolute; + top: 13%; + right: 12%; + width: 15%; + aspect-ratio: 1; + border-radius: 50%; + background: radial-gradient(circle at 35% 30%, #fff8dc, #f2e6b0 60%, #d8c778); + box-shadow: 0 0 26px 7px rgba(255, 246, 200, 0.35); +} +.fw-hills { + position: absolute; + left: -12%; + right: -12%; + bottom: 0; + height: 44%; + background: linear-gradient(180deg, #2c2350, #120e26); + border-radius: 50% 50% 0 0 / 42% 42% 0 0; +} +.fw-graves { + position: absolute; + left: 0; + right: 0; + bottom: 7%; + display: flex; + align-items: flex-end; + justify-content: center; + gap: 7%; +} +.fw-grave { + position: relative; + width: 12%; + max-width: 50px; + aspect-ratio: 5 / 7; + background: linear-gradient(160deg, #8b93b0, #575d76); + border-radius: 50% 50% 4px 4px / 32% 32% 4px 4px; + box-shadow: + inset -3px -4px 8px rgba(0, 0, 0, 0.35), + 0 6px 12px -6px rgba(0, 0, 0, 0.6); +} +.fw-grave::before, +.fw-grave::after { + content: ""; + position: absolute; + left: 50%; + transform: translateX(-50%); + background: rgba(18, 20, 38, 0.62); + border-radius: 2px; +} +.fw-grave::before { + top: 20%; + width: 12%; + height: 42%; +} +.fw-grave::after { + top: 33%; + width: 44%; + height: 11%; +} +.fw-grave:nth-child(2) { + transform: translateY(-7%) scale(1.06); +} +.fw-grave:nth-child(4) { + transform: scale(0.88); +} +.fw-scan { + position: absolute; + inset: 0; + pointer-events: none; + background: repeating-linear-gradient( + 0deg, + rgba(0, 0, 0, 0.16) 0 1px, + transparent 1px 3px + ); + opacity: 0.45; +} +.fw-hud { + position: absolute; + top: 8%; + left: 5%; + right: 5%; + display: flex; + justify-content: space-between; + font-family: var(--font-custom-mono); + font-size: 0.58rem; + letter-spacing: 0.08em; + color: #ffd9e0; + text-shadow: 0 1px 2px rgba(0, 0, 0, 0.7); +} +.fw-hearts { + color: #ff5d7a; +} +.fw-start { + position: absolute; + left: 50%; + top: 45%; + transform: translate(-50%, -50%); + padding: 6px 15px; + border-radius: 999px; + border: 1px solid rgba(255, 255, 255, 0.55); + background: rgba(124, 58, 237, 0.4); + color: #fff; + font-family: var(--font-custom-mono); + font-size: 0.6rem; + letter-spacing: 0.16em; + white-space: nowrap; + animation: fw-blink 1.1s steps(2, start) infinite; +} +@keyframes fw-blink { + 50% { + opacity: 0.35; + } +} + +/* ---------- RustyWolf: a little browser window ---------- */ +.fw-browser { + position: absolute; + inset: 0; + display: flex; + flex-direction: column; + background: linear-gradient(180deg, var(--glass-hi), var(--glass-lo)); +} +.fw-tabs { + display: flex; + align-items: flex-end; + gap: 6px; + height: 32px; + padding: 7px 10px 0; +} +.fw-tab { + display: inline-flex; + align-items: center; + gap: 7px; + max-width: 62%; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; + padding: 6px 13px; + border-radius: 9px 9px 0 0; + border: 1px solid var(--btn-border, var(--glass-border)); + border-bottom: none; + background: var(--btn-bg, var(--glass-lo)); + font-size: 0.66rem; + color: var(--fg); +} +.fw-tab-dot { + flex: none; + width: 8px; + height: 8px; + border-radius: 50%; + background: var(--accent); +} +.fw-tab-plus { + padding: 0 6px; + color: var(--web-muted); + font-size: 0.9rem; + line-height: 1; +} +.fw-toolbar { + display: flex; + align-items: center; + gap: 8px; + padding: 8px 12px; + border-top: 1px solid var(--glass-border); + border-bottom: 1px solid var(--glass-border); + background: var(--fw-chrome); +} +.fw-nav { + flex: none; + display: grid; + place-items: center; + width: 22px; + height: 22px; + border-radius: 50%; + background: var(--btn-bg, var(--glass-lo)); + color: var(--web-muted); + font-size: 0.85rem; + line-height: 1; +} +.fw-url { + flex: 1; + display: flex; + align-items: center; + gap: 8px; + height: 26px; + padding: 0 12px; + border-radius: 999px; + border: 1px solid var(--glass-border); + background: var(--glass-lo); + font-family: var(--font-custom-mono); + font-size: 0.66rem; + color: var(--web-muted); +} +.fw-lock { + position: relative; + flex: none; + width: 8px; + height: 8px; + border: 1.5px solid currentColor; + border-radius: 2px; +} +.fw-lock::before { + content: ""; + position: absolute; + left: 50%; + top: -5px; + width: 6px; + height: 5px; + border: 1.5px solid currentColor; + border-bottom: none; + border-radius: 4px 4px 0 0; + transform: translateX(-50%); +} +.fw-page { + flex: 1; + min-height: 0; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + gap: 12px; + padding: 12px; +} +.fw-wordmark { + font-size: clamp(1rem, 2.4vw, 1.35rem); + font-weight: 800; + letter-spacing: -0.03em; + color: var(--fg); +} +.fw-wordmark span { + color: #d97757; +} +.fw-search { + display: flex; + align-items: center; + gap: 9px; + width: min(82%, 320px); + padding: 8px 15px; + border-radius: 999px; + border: 1px solid var(--btn-border, var(--glass-border)); + background: var(--btn-bg, var(--glass-lo)); + box-shadow: var(--btn-shadow, none); + font-size: 0.72rem; + color: var(--web-muted); +} +.fw-search-dot { + position: relative; + flex: none; + width: 11px; + height: 11px; + border: 1.6px solid var(--accent); + border-radius: 50%; +} +.fw-search-dot::after { + content: ""; + position: absolute; + right: -4px; + bottom: -3px; + width: 6px; + height: 1.6px; + border-radius: 2px; + background: var(--accent); + transform: rotate(45deg); +} +.fw-tiles { + display: flex; + gap: 11px; +} +.fw-tile { + display: grid; + place-items: center; + width: 38px; + height: 38px; + border-radius: 12px; + color: #fff; + font-size: 0.9rem; + font-weight: 800; + box-shadow: + inset 0 1px 0 rgba(255, 255, 255, 0.3), + 0 6px 12px -8px rgba(0, 0, 0, 0.6); +} +.fw-tile:nth-child(1) { + background: linear-gradient(150deg, #6d5efc, #2f8bff); +} +.fw-tile:nth-child(2) { + background: linear-gradient(150deg, #ec4899, #db2777); +} +.fw-tile:nth-child(3) { + background: linear-gradient(150deg, #f59e0b, #d97757); +} +.fw-tile:nth-child(4) { + background: linear-gradient(150deg, #10b981, #0d9488); +} +.fw-tile:nth-child(5) { + background: linear-gradient(150deg, #8b5cf6, #6366f1); +} + +@media (prefers-reduced-motion: reduce) { + .fw-stars, + .fw-start { + animation: none; + } } .web-feature-text { flex: 1;