diff --git a/README.md b/README.md index ec0369e..0fd40d2 100644 --- a/README.md +++ b/README.md @@ -76,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.9` (see `package.json`) +- **Version:** `2.2.10` (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**, @@ -172,7 +172,7 @@ they contend for `:3000` and `.next/dev/lock`. | Route | Method | What it does | | --- | --- | --- | -| `/api/heatmap` | `GET` | Proxies the user's contribution heatmap from `git.dvorakmortonxd.dev`. | +| `/api/heatmap` | `GET` | Proxies the user's contribution heatmap from `git.dvorakmortonxd.dev`. Returns `502` (and the panel shows a friendly fallback) if the upstream is unreachable. | | `/api/activities` | `GET` | Fetches the GitDXD activity feed and keeps only the user's own events. | | `/api/frame?url=…` | `GET` | Header-stripping proxy for a friend's site that refuses framing (`skopec.dev`). **Allow-listed hosts only**; fetches the HTML server-side (so the site's `X-Frame-Options` never reaches the browser), injects a `` for relative assets and a click shim for in-site links. Rejects other hosts, non-http(s) schemes and credentials, and never follows redirects. | | `/api/ping?url=…` | `GET` | Health-checks an **allow-listed** service (the site's own `*.dvorakmortonxd.dev` hosts and the friends' sites). Handles `minecraft://host:port` via mcstatus.io; treats any non-5xx response as "up". Other hosts and schemes are rejected and redirects are never followed, so it can't be used to probe the LAN (SSRF guard). | diff --git a/agents.md b/agents.md index 5a3ce2d..4f5644d 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.8` → `2.2.9` → …) as part of every task, even the small ones. Keep all of +`2.2.9` → `2.2.10` → …) 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.9` (see `package.json`). +- Version: `2.2.10` (see `package.json`). ### Scripts ```bash @@ -96,6 +96,7 @@ src/app/ revival-inc/page.tsx → fullscreen iframe embedding /public/revival-inc/index.html api/ heatmap/route.ts → proxy for git.dvorakmortonxd.dev contribution heatmap + (try/catch → 502 on upstream failure; the widget degrades to a fallback) activities/route.ts → proxy for the user's GitDXD activity feed ping/route.ts → service health check (HTTP + minecraft:// via mcstatus.io); host allowlist frame/route.ts → header-stripping proxy so an unframeable friend site loads in a window; host allowlist diff --git a/package-lock.json b/package-lock.json index 339dbe6..8155748 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "dvorakmortonxd", - "version": "2.2.9", + "version": "2.2.10", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dvorakmortonxd", - "version": "2.2.9", + "version": "2.2.10", "dependencies": { "next": "^16.3.8", "react": "^19.3.0", diff --git a/package.json b/package.json index e9f45f3..ccb998a 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "dvorakmortonxd", - "version": "2.2.9", + "version": "2.2.10", "private": true, "scripts": { "dev": "node scripts/dev-server.mjs", diff --git a/src/app/api/heatmap/route.ts b/src/app/api/heatmap/route.ts index 14189bc..5440782 100644 --- a/src/app/api/heatmap/route.ts +++ b/src/app/api/heatmap/route.ts @@ -1,5 +1,15 @@ export async function GET() { - const res = await fetch("https://git.dvorakmortonxd.dev/api/v1/users/dvorakmortonxd/heatmap"); - const data = await res.json(); - return Response.json(data); -} \ No newline at end of file + try { + const res = await fetch( + "https://git.dvorakmortonxd.dev/api/v1/users/dvorakmortonxd/heatmap", + { headers: { Accept: "application/json" } } + ); + if (!res.ok) { + return Response.json({ error: "failed to load" }, { status: 502 }); + } + const data = await res.json(); + return Response.json(data); + } catch { + return Response.json({ error: "failed to load" }, { status: 502 }); + } +} diff --git a/src/app/components/ContributionHeatmap.tsx b/src/app/components/ContributionHeatmap.tsx index 6143b78..9faea3b 100644 --- a/src/app/components/ContributionHeatmap.tsx +++ b/src/app/components/ContributionHeatmap.tsx @@ -39,9 +39,16 @@ export default function ContributionHeatmap({ variant = "terminal" }: { variant? try { const res = await fetch("/api/heatmap"); if (!res.ok) { - throw new Error("Failed to fetch"); + setError(true); + setLoading(false); + return; } const data: HeatmapData[] = await res.json(); + if (!Array.isArray(data)) { + setError(true); + setLoading(false); + return; + } const map = new Map(); let total = 0; let maxContribs = 0; @@ -90,7 +97,7 @@ export default function ContributionHeatmap({ variant = "terminal" }: { variant? setTotalContribs(total); setLoading(false); } catch (err) { - console.error("Failed to load heatmap:", err); + console.warn("Failed to load heatmap:", err); setError(true); setLoading(false); } @@ -106,7 +113,7 @@ export default function ContributionHeatmap({ variant = "terminal" }: { variant? setActivities(valid); } } catch (err) { - console.error("Failed to load activities:", err); + console.warn("Failed to load activities:", err); } } diff --git a/src/app/components/WebLayout.tsx b/src/app/components/WebLayout.tsx index 946add8..2fe9261 100644 --- a/src/app/components/WebLayout.tsx +++ b/src/app/components/WebLayout.tsx @@ -318,7 +318,7 @@ export default function WebLayout() { diff --git a/src/app/web.css b/src/app/web.css index 4a686e9..3cae6a7 100644 --- a/src/app/web.css +++ b/src/app/web.css @@ -649,44 +649,44 @@ html[data-theme="web"] body { .fw-tabs { display: flex; align-items: flex-end; - gap: 6px; - height: 32px; - padding: 7px 10px 0; + gap: 5px; + height: 26px; + padding: 5px 10px 0; } .fw-tab { display: inline-flex; align-items: center; - gap: 7px; + gap: 6px; max-width: 62%; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; - padding: 6px 13px; - border-radius: 9px 9px 0 0; + padding: 3px 11px; + border-radius: 8px 8px 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; + font-size: 0.62rem; color: var(--fg); } .fw-tab-dot { flex: none; - width: 8px; - height: 8px; + width: 7px; + height: 7px; border-radius: 50%; background: var(--accent); } .fw-tab-plus { - padding: 0 6px; + padding: 0 5px; color: var(--web-muted); - font-size: 0.9rem; + font-size: 0.82rem; line-height: 1; } .fw-toolbar { display: flex; align-items: center; - gap: 8px; - padding: 8px 12px; + gap: 6px; + padding: 5px 10px; border-top: 1px solid var(--glass-border); border-bottom: 1px solid var(--glass-border); background: var(--fw-chrome); @@ -695,12 +695,12 @@ html[data-theme="web"] body { flex: none; display: grid; place-items: center; - width: 22px; - height: 22px; + width: 20px; + height: 20px; border-radius: 50%; background: var(--btn-bg, var(--glass-lo)); color: var(--web-muted); - font-size: 0.85rem; + font-size: 0.78rem; line-height: 1; } .fw-url { @@ -708,13 +708,13 @@ html[data-theme="web"] body { display: flex; align-items: center; gap: 8px; - height: 26px; - padding: 0 12px; + height: 22px; + padding: 0 11px; border-radius: 999px; border: 1px solid var(--glass-border); background: var(--glass-lo); font-family: var(--font-custom-mono); - font-size: 0.66rem; + font-size: 0.62rem; color: var(--web-muted); } .fw-lock {