Compare commits

...

3 Commits

  1. 26
      bun.lock
  2. 8
      package.json
  3. 13
      src/components/Container.tsx
  4. 9
      src/pages/main/Work.tsx
  5. 79
      src/pages/main/data/project.ts

26
bun.lock

@ -6,23 +6,21 @@
"dependencies": {
"@emotion/css": "^11.13.5",
"date-fns": "^2.30.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "npm:@preact/compat@18.3.1",
"react-dom": "npm:@preact/compat@18.3.1",
"wouter": "^2.12.1",
},
"devDependencies": {
"@svgr/rollup": "^8.1.0",
"@types/gm": "^1.25.4",
"@types/marked": "^5.0.2",
"@types/react": "^18.3.20",
"@types/react-dom": "^18.3.6",
"@vitejs/plugin-react": "^4.3.4",
"gm": "^1.25.1",
"imagen": "github:MKRhere/imagen",
"marked": "^9.1.6",
"rollup-plugin-visualizer": "^5.14.0",
"typescript": "^5.8.3",
"vite": "^6.2.5",
"vite": "^6.2.6",
},
},
},
@ -401,12 +399,6 @@
"@types/parse-json": ["@types/parse-json@4.0.2", "", {}, "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw=="],
"@types/prop-types": ["@types/prop-types@15.7.14", "", {}, "sha512-gNMvNH49DJ7OJYv+KAKn0Xp45p8PLl6zo2YnvDIbTd4J6MER2BmWN49TG7n9LvkyihINxeKW8+3bfS2yDC9dzQ=="],
"@types/react": ["@types/react@18.3.20", "", { "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" } }, "sha512-IPaCZN7PShZK/3t6Q87pfTkRm6oLTd4vztyoj+cbHUF1g3FfVb2tFIL79uCRKEfv16AhqDMBywP2VW3KIZUvcg=="],
"@types/react-dom": ["@types/react-dom@18.3.6", "", { "peerDependencies": { "@types/react": "^18.0.0" } }, "sha512-nf22//wEbKXusP6E9pfOCDwFdHAX4u172eaJI4YkDRQEZiorm6KfYnSC2SWLDMVWUOWPERmJnN0ujeAfTBLvrw=="],
"@vitejs/plugin-react": ["@vitejs/plugin-react@4.3.4", "", { "dependencies": { "@babel/core": "^7.26.0", "@babel/plugin-transform-react-jsx-self": "^7.25.9", "@babel/plugin-transform-react-jsx-source": "^7.25.9", "@types/babel__core": "^7.20.5", "react-refresh": "^0.14.2" }, "peerDependencies": { "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" } }, "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug=="],
"abbrev": ["abbrev@1.1.1", "", {}, "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q=="],
@ -589,8 +581,6 @@
"lodash.debounce": ["lodash.debounce@4.0.8", "", {}, "sha512-FT1yDzDYEoYWhnSGnpE/4Kj1fLZkDFyqRb7fNt6FdYOSxlUWAtp42Eh6Wb0rGIv/m9Bgo7x4GhQbm5Ys4SG5ow=="],
"loose-envify": ["loose-envify@1.4.0", "", { "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, "bin": { "loose-envify": "cli.js" } }, "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q=="],
"lower-case": ["lower-case@2.0.2", "", { "dependencies": { "tslib": "^2.0.3" } }, "sha512-7fm3l3NAF9WfN6W3JOmf5drwpVqX78JtoGJ3A6W0a6ZnldM41w2fV5D490psKFTpMds8TJse/eHLFFsNHHjHgg=="],
"lru-cache": ["lru-cache@5.1.1", "", { "dependencies": { "yallist": "^3.0.2" } }, "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w=="],
@ -655,9 +645,11 @@
"postcss": ["postcss@8.5.3", "", { "dependencies": { "nanoid": "^3.3.8", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" } }, "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A=="],
"react": ["react@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ=="],
"preact": ["preact@10.26.5", "", {}, "sha512-fmpDkgfGU6JYux9teDWLhj9mKN55tyepwYbxHgQuIxbWQzgFg5vk7Mrrtfx7xRxq798ynkY4DDDxZr235Kk+4w=="],
"react": ["@preact/compat@18.3.1", "", { "peerDependencies": { "preact": "*" } }, "sha512-Kog4PSRxtT4COtOXjsuQPV1vMXpUzREQfv+6Dmcy9/rMk0HOPK0HTE9fspFjAmY8R80T/T8gtgmZ68u5bOSngw=="],
"react-dom": ["react-dom@18.3.1", "", { "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" }, "peerDependencies": { "react": "^18.3.1" } }, "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw=="],
"react-dom": ["@preact/compat@18.3.1", "", { "peerDependencies": { "preact": "*" } }, "sha512-Kog4PSRxtT4COtOXjsuQPV1vMXpUzREQfv+6Dmcy9/rMk0HOPK0HTE9fspFjAmY8R80T/T8gtgmZ68u5bOSngw=="],
"react-refresh": ["react-refresh@0.14.2", "", {}, "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA=="],
@ -691,8 +683,6 @@
"safe-buffer": ["safe-buffer@5.2.1", "", {}, "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ=="],
"scheduler": ["scheduler@0.23.2", "", { "dependencies": { "loose-envify": "^1.1.0" } }, "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ=="],
"semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="],
"set-blocking": ["set-blocking@2.0.0", "", {}, "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw=="],
@ -755,7 +745,7 @@
"util-deprecate": ["util-deprecate@1.0.2", "", {}, "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw=="],
"vite": ["vite@6.2.5", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-j023J/hCAa4pRIUH6J9HemwYfjB5llR2Ps0CWeikOtdR8+pAURAk0DoJC5/mm9kd+UgdnIy7d6HE4EAvlYhPhA=="],
"vite": ["vite@6.2.6", "", { "dependencies": { "esbuild": "^0.25.0", "postcss": "^8.5.3", "rollup": "^4.30.1" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^18.0.0 || ^20.0.0 || >=22.0.0", "jiti": ">=1.21.0", "less": "*", "lightningcss": "^1.21.0", "sass": "*", "sass-embedded": "*", "stylus": "*", "sugarss": "*", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-9xpjNl3kR4rVDZgPNdTL0/c6ao4km69a/2ihNQbcANz8RuCOK3hQBmLSJf3bRKVQjVMda+YvizNE8AwvogcPbw=="],
"webidl-conversions": ["webidl-conversions@3.0.1", "", {}, "sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ=="],

8
package.json

@ -18,22 +18,20 @@
"dependencies": {
"@emotion/css": "^11.13.5",
"date-fns": "^2.30.0",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react": "npm:@preact/compat@18.3.1",
"react-dom": "npm:@preact/compat@18.3.1",
"wouter": "^2.12.1"
},
"devDependencies": {
"@svgr/rollup": "^8.1.0",
"@types/gm": "^1.25.4",
"@types/marked": "^5.0.2",
"@types/react": "^18.3.20",
"@types/react-dom": "^18.3.6",
"@vitejs/plugin-react": "^4.3.4",
"gm": "^1.25.1",
"imagen": "github:MKRhere/imagen",
"marked": "^9.1.6",
"rollup-plugin-visualizer": "^5.14.0",
"typescript": "^5.8.3",
"vite": "^6.2.5"
"vite": "^6.2.6"
}
}

13
src/components/Container.tsx

@ -62,10 +62,12 @@ const Container: React.FC<{
} catch {}
};
const relevantLocation = location.split("/").slice(0, 2).join("/");
const handlePrev = (e: MouseKb) => {
animateArrow(e);
const current = MenuPaths.findIndex(path => location === path);
const current = MenuPaths.findIndex(path => relevantLocation === path);
const index = (current - 1) % MenuPaths.length;
const prev = MenuPaths.at(index)!;
timer(() => navigate(prev), 300);
@ -74,7 +76,7 @@ const Container: React.FC<{
const handleNext = (e: MouseKb) => {
animateArrow(e);
const current = MenuPaths.findIndex(path => location === path);
const current = MenuPaths.findIndex(path => relevantLocation === path);
const index = (current + 1) % MenuPaths.length;
const next = MenuPaths.at(index)!;
timer(() => navigate(next), 300);
@ -89,9 +91,8 @@ const Container: React.FC<{
}
useEffect(() => {
const depth = location.split("/").length;
// scroll back to top when new page is loaded, only for top-level pages
if (depth === 1) window.scrollTo({ top: 0 });
window.scrollTo({ top: 0 });
if (highlightCircle.current) {
highlightCircle.current.classList.add("highlight");
@ -115,12 +116,12 @@ const Container: React.FC<{
// cleanup
return () => (window.removeEventListener("keydown", kbnav), clear());
}, [location, context.contact.on]);
}, [relevantLocation, context.contact.on]);
// on first render
useLayoutEffect(handleResize, []);
const end = location === MenuEntries[MenuEntries.length - 1][1];
const end = relevantLocation === MenuEntries[MenuEntries.length - 1][1];
return (
<div

9
src/pages/main/Work.tsx

@ -174,16 +174,19 @@ const Exp: React.FC = () => {
`}>
<a
className={cx(
unit.wip &&
(unit.wip || unit.private) &&
css`
color: var(--text-subdued);
cursor: default;
`,
)}
href={unit.wip ? undefined : unit.url}
href={unit.wip || unit.private ? undefined : unit.url}
target="_blank"
rel="noreferrer"
title={unit.title + (unit.wip ? " (WIP)" : "")}>
title={
unit.title +
(unit.wip ? " (WIP)" : unit.private ? " (private)" : "")
}>
{unit.title}
</a>
</td>

79
src/pages/main/data/project.ts

@ -5,6 +5,7 @@ export type Project = {
cat: string;
tags: string[];
wip?: boolean;
private?: boolean;
};
export const projects: Project[] = [
@ -21,7 +22,7 @@ export const projects: Project[] = [
description: "Suite of fast, reactive web-app development libraries.",
url: "https://github.com/codefeathers/hyperactive",
cat: "lib",
tags: ["reactive", "ui-framework"],
tags: ["reactive", "ui-framework", "typescript"],
},
{
title: "deno shims",
@ -49,16 +50,6 @@ export const projects: Project[] = [
tags: ["hyperactive", "calendar"],
},
{
title: "Telecraft",
description: "Pluggable Minecraft server administration toolkit.",
url: "https://github.com/MadrasMC/telecraft",
cat: "cli",
tags: ["minecraft", "node"],
},
];
export const otherProjects: Project[] = [
{
title: "true-pg",
description:
"The most complete PostgreSQL schema generator for TypeScript, Kysely, Zod, and others.",
@ -66,13 +57,54 @@ export const otherProjects: Project[] = [
cat: "lib",
tags: ["postgresql", "schema", "typescript", "kysely", "zod"],
},
];
export const otherProjects: Project[] = [
{
title: "Telecraft",
description: "Pluggable Minecraft server administration toolkit.",
url: "https://github.com/MadrasMC/telecraft",
cat: "cli",
tags: ["deno", "minecraft"],
},
{
title: "wiretap",
description:
"Extremely tiny debug logging utility for all JavaScript runtimes. Published as npm/yarn.",
"Extremely tiny universal debug logging utility for all JavaScript runtimes. Published as npm/w.",
url: "https://github.com/feathers-studio/wiretap",
cat: "lib",
tags: ["debug", "logging", "typescript"],
tags: ["typescript", "debug", "logging"],
},
{
title: "hyperweb",
description:
"An implementation of WebMentions and a self-hosted microblog. A distributed social hyperweb!",
url: "https://github.com/feathers-studio/hyperweb",
cat: "web",
tags: ["typescript", "webmentions", "microblog"],
},
{
title: "ts-parser",
description:
"A TypeScript definitions (d.ts) parser from scratch using parser-generators.",
url: "https://github.com/feathers-studio/ts-parser",
cat: "lib",
tags: ["typescript", "parser"],
},
{
title: "hypercss",
description: "A perfectly spec-compliant CSS parser for TypeScript.",
url: "https://github.com/feathers-studio/hypercss",
cat: "lib",
tags: ["typescript", "parser", "css"],
},
{
title: "zappymail",
description:
"A tiny proxy email server to reformat emails from a thirdparty utility.",
cat: "cli",
tags: ["typescript", "proxy", "email"],
private: true,
},
{
title: "storymap",
@ -80,7 +112,7 @@ export const otherProjects: Project[] = [
"Reverse-engineered thirdparty map renderer for Vintage Story in Zig ⚡️",
url: "https://github.com/MadrasMC/storymap",
cat: "cli",
tags: ["vintage-story", "zig"],
tags: ["zig", "map-renderer", "vintage-story"],
wip: true,
},
{
@ -88,16 +120,31 @@ export const otherProjects: Project[] = [
description: "TypeScript bindings for the i3 window manager.",
url: "https://github.com/feathers-studio/i3-ts",
cat: "lib",
tags: ["i3", "typescript", "bindings"],
tags: ["typescript", "bindings", "i3"],
},
{
title: "mkr/mail",
description:
"A personal utility to fetch new emails from an IMAP server and send them to Telegram.",
url: "https://github.com/MKRhere/mail",
cat: "bot",
tags: ["typescript", "imap", "telegram"],
},
{
title: "pg-extract",
description: "Extract data from PostgreSQL tables into a JSON array.",
description: "Extract data from PostgreSQL tables into a JSON collection.",
url: "https://github.com/feathers-studio/pg-extract",
cat: "lib",
tags: ["postgresql", "json", "data-extraction"],
},
{
title: "mkr/bin",
description: "A small personal bin.",
url: "https://github.com/MKRhere/bin",
cat: "web",
tags: ["typescript", "bin"],
},
{
title: window.location.hostname.split(".").slice(0, 2).join("."),
description: "Did you find all the easter eggs? Keep looking.",
url: "https://github.com/MKRhere/pw2",

Loading…
Cancel
Save