diff --git a/src/lib/components/CatalogGrid.svelte b/src/lib/components/CatalogGrid.svelte new file mode 100644 index 0000000..c437e8a --- /dev/null +++ b/src/lib/components/CatalogGrid.svelte @@ -0,0 +1,63 @@ + + + diff --git a/src/lib/components/ProjectIcon.svelte b/src/lib/components/ProjectIcon.svelte index 316f79f..5a9b5f0 100644 --- a/src/lib/components/ProjectIcon.svelte +++ b/src/lib/components/ProjectIcon.svelte @@ -15,6 +15,7 @@ import House from 'lucide-svelte/icons/house'; import Globe2 from 'lucide-svelte/icons/globe-2'; import Languages from 'lucide-svelte/icons/languages'; + import ListChecks from 'lucide-svelte/icons/list-checks'; import Music2 from 'lucide-svelte/icons/music-2'; import PawPrint from 'lucide-svelte/icons/paw-print'; import Trophy from 'lucide-svelte/icons/trophy'; @@ -32,7 +33,8 @@ 'language learner': Languages, 'money tracker': Wallet, 'cs prices': CandlestickChart, - 'house tracker': House + 'house tracker': House, + lists: ListChecks }; const Icon = $derived(icons[name as keyof typeof icons] ?? FolderKanban); diff --git a/src/lib/projects.ts b/src/lib/projects.ts index 01fdbf8..7a189d4 100644 --- a/src/lib/projects.ts +++ b/src/lib/projects.ts @@ -1,3 +1,4 @@ +// Real projects (tools and experiments). Personal data trackers live in `trackers`. export const projects = [ { name: 'vash esports', @@ -5,18 +6,6 @@ export const projects = [ href: 'https://esports.vash.software', external: true }, - { - name: 'game tracker', - subtitle: 'every pokemon in every game, and call of duty camo mastery', - href: '/projects/game-tracker', - external: false - }, - { - name: 'animaldex', - subtitle: 'add neighborhood pets to the world map', - href: '/projects/animaldex', - external: false - }, { name: 'osu! map finder', subtitle: 'find osu! maps for Spotify songs/albums/playlists', @@ -40,6 +29,16 @@ export const projects = [ subtitle: 'Dutch, Korean and English, from zero to fluent', href: '/projects/language-learner', external: false + } +] as const; + +// Trackers keep their existing /projects/... URLs (no moves, so no redirects needed). +export const trackers = [ + { + name: 'game tracker', + subtitle: 'every pokemon in every game, and call of duty camo mastery', + href: '/projects/game-tracker', + external: false }, { name: 'money tracker', @@ -58,5 +57,17 @@ export const projects = [ subtitle: 'Counter-Strike market prices across time', href: '/projects/cs-prices', external: false + }, + { + name: 'animaldex', + subtitle: 'add neighborhood pets to the world map', + href: '/projects/animaldex', + external: false + }, + { + name: 'lists', + subtitle: 'books, games, places and other things worth keeping', + href: '/lists', + external: false } ] as const; diff --git a/src/lib/seo.ts b/src/lib/seo.ts index 68c0b03..c936178 100644 --- a/src/lib/seo.ts +++ b/src/lib/seo.ts @@ -32,9 +32,14 @@ const publicPages: Record = { '/projects': { title: 'Projects', description: - 'Software projects by Stan Runge, including Game Tracker, Animaldex and Derby Lens.' + 'Software projects by Stan Runge, including osu! map finder, Derby Lens and Language Learner.' }, '/blog': { title: 'Blog', description: 'Articles and notes by Stan Runge.' }, + '/trackers': { + title: 'Trackers', + description: + 'Stan Runge’s trackers: games, money, houses, Counter-Strike prices, Animaldex and lists.' + }, '/lists': { title: 'Lists', description: 'Lists collected and maintained by Stan Runge.' }, '/projects/game-tracker': { title: 'Game Tracker', diff --git a/src/lib/title.ts b/src/lib/title.ts index 9b16c51..fdd0da8 100644 --- a/src/lib/title.ts +++ b/src/lib/title.ts @@ -17,6 +17,7 @@ export function getTitle(path: string, environment: SiteEnvironment = null): str const titles: Record = { '/': 'Home', '/projects': 'Projects', + '/trackers': 'Trackers', '/blog': 'Blog', '/lists': 'Lists', '/login': 'Sign in', diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 06f6e54..f9a7ce2 100755 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -7,7 +7,7 @@ import { getSeo } from '#lib/seo.js'; import CopyButton from '#lib/components/CopyButton.svelte'; import ProjectIcon from '#lib/components/ProjectIcon.svelte'; - import { projects } from '#lib/projects.js'; + import { projects, trackers } from '#lib/projects.js'; import { Button, buttonVariants } from '#lib/components/ui/button/index.js'; import * as Sheet from '#lib/components/ui/sheet/index.js'; import ArrowRight from 'lucide-svelte/icons/arrow-right'; @@ -31,15 +31,24 @@ let { data, children } = $props(); const seo = $derived(getSeo(page.url.pathname, page.data, data.environment)); let menuOpen = $state(false); - let projectsMenuOpen = $state(false); + // Which header dropdown is open (desktop): '/projects', '/trackers' or none. + let openMenu = $state(null); type ThemePreference = 'dark' | 'system' | 'light'; let theme = $state('dark'); const navigation = [ { label: 'home', href: '/' }, { label: 'projects', href: '/projects' }, + { label: 'trackers', href: '/trackers' }, { label: 'blog', href: '/blog' }, { label: 'lists', href: '/lists' } ] as const; + const menus: Record< + string, + { label: string; items: readonly (typeof projects | typeof trackers)[number][] } + > = { + '/projects': { label: 'projects', items: projects }, + '/trackers': { label: 'trackers', items: trackers } + }; const socialLinks = [ { icon: SiMail, label: 'email', href: 'mailto:stan@stanrunge.dev' }, { icon: SiGithub, label: 'github', href: 'https://github.com/stanrunge' }, @@ -102,14 +111,14 @@ return page.url.pathname === href || (href !== '/' && page.url.pathname.startsWith(href + '/')); } - function handleProjectsPointerEnter(event: PointerEvent) { + function handleMenuPointerEnter(event: PointerEvent, href: string) { if (event.pointerType === 'touch') return; - projectsMenuOpen = true; + openMenu = href; } - function handleProjectsPointerLeave(event: PointerEvent) { + function handleMenuPointerLeave(event: PointerEvent) { if (event.pointerType === 'touch') return; - projectsMenuOpen = false; + openMenu = null; } @@ -170,7 +179,14 @@ class="sr-only focus:not-sr-only focus:absolute focus:z-50 focus:bg-[var(--site-bg)] focus:px-4 focus:py-2" >skip to content -
+ +
-
{@render children()}
+
{@render children()}