Skip to content

kristopheles/monarium

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Monarium

A customizable Pokédex generator and tracker. Build a personal dex for any mainline Pokémon game (Red/Blue through Legends Z-A + Pokémon HOME), configure 15+ filter options, mark every catch, track shiny hunts, and sync across devices.

11 languages. Offline-first. Installable as a PWA.

Satoshi's Pokémon HOME dex — the first Kanto boxes with every Pokémon Ash caught in the anime marked as documented.

No live demo. Due to German legal requirements (Impressumspflicht, DSGVO), the original author does not operate a public instance. This software is provided as source code for self-hosting only. If third-party instances exist, they are operated independently and at their own legal responsibility.


Features

Dex building

  • Custom dex for every mainline game from Red/Blue through Legends Z-A, plus Pokémon HOME
  • 15+ toggleable filters: regional forms, DLC regions, shinies, Mega/Gigantamax, gender forms, and sub-dex groupings (Paldea + Kitakami + Blueberry, Isle of Armor + Crown Tundra, etc.)
  • 5-step creator wizard that generates the correct boxes, sort order, and form variants for the chosen game
  • Duplicate an existing dex, or start from scratch with a fresh config

Tracking

  • Per-entry catch state with one-click polaroid pop / unpop
  • Per-entry metadata: gender, original-trainer flag, "caught in correct ball" flag, Pokémon HOME region, Pokémon GO origin, and a custom flag
  • Shiny hunt tracker with configurable method (full-odds, Masuda, chain, outbreak, …), cumulative-odds math, and per-encounter counting
  • Box renaming, per-box progress, dex rename, delete with undo

Stats & comparison

  • Per-dex completion % and per-box progress with hand-drawn (rough.js) progress bars
  • Dex-compare view across multiple games (overlap, missing, exclusive per game)
  • Achievements system with unlock toasts

Cross-device

  • Offline-first: every action works without internet; IndexedDB is the local source of truth
  • Lamport-clock op log + outbox → SSE pipeline for realtime sync across devices
  • Guest mode (no account) with one-click reconciliation on login
  • Full data export (JSON per dex) for GDPR Art. 20 portability
  • Account deletion (GDPR Art. 17) that cascades everything

UX

  • 11 languages: English, German, Spanish, French, Italian, Japanese, Korean, Simplified & Traditional Chinese, Hindi, Thai
  • Responsive layout with mobile drawer navigation
  • Light / dark / system theme + adjustable UI scale
  • Fuzzy search that finds "pika" → Pikachu, Pichu, Raichu
  • Keyboard shortcuts and a context-menu for power users
  • Dex sharing via signed public links

Installation

  • PWA: install to home screen on iOS / Android / desktop
  • Service-worker caching of the app shell + 1.8 MB tileset for offline rendering
  • OAuth login via Google or Discord (optional), or email + password

Contributing — report translation & dex errors

The app ships with 11 languages. English and German have been reviewed and are considered correct. The remaining 9 languages (Spanish, French, Italian, Japanese, Korean, Simplified Chinese, Traditional Chinese, Hindi, Thai) were generated by AI and have not yet been fully verified — errors are likely. The Pokémon data layer (1 025+ species across 20 games) was hand-curated but may contain inaccuracies for edge cases (rare forms, event-only variants, regional availability).

If you find a mistake:

  • Translation error (wrong word, broken grammar, missing key) — open an issue with the locale code, the key path (e.g. DexView.contextMenu.flagHome), and the correction.
  • Dex data error (wrong Pokémon in a game's dex, missing form, incorrect shiny availability) — open an issue with the game, the Pokémon, and what's wrong.
  • UI bug — open an issue with a screenshot and the steps to reproduce.

All issues are welcome at the project's GitHub Issues page.

Known limitations & open work

The project is in active polish, not a finished product. Contributions — or just bug reports — on any of the following are especially welcome.

Translation review — native-speaker proofreading wanted. Each of the 9 AI-generated locales needs a pass for grammar, idioms, and Pokémon-community terminology:

  • Spanish (es)
  • French (fr)
  • Italian (it)
  • Japanese (ja)
  • Korean (ko)
  • Simplified Chinese (zh-Hans)
  • Traditional Chinese (zh-Hant)
  • Hindi (hi)
  • Thai (th)

Cross-device testing — the app has mainly been exercised on desktop Chrome. Reports on any of the following help a lot:

  • Display on narrow mobile viewports (iPhone SE, small Android)
  • Display on tablet widths (iPad portrait / landscape)
  • Display on ultra-wide monitors
  • Touch interactions on mobile (polaroid pop, sidebar drawer, context menus)
  • Performance on low-end Android devices (rough.js paints, store rerenders)
  • Performance on slow networks (offline PWA shell, SSE reconnect behaviour)
  • iOS Safari PWA install flow + service worker lifecycle
  • Firefox & Safari rendering parity with Chromium

Other open work:

  • Human-designed logo — the current sun-and-book shield was generated with AI assistance; a hand-crafted mark matching the paper/polaroid aesthetic would be a better fit.
  • Sketch-style charts — admin and stats pages use Recharts in its default clean style; they should match the app's hand-drawn visual language (rough.js fill, wobbly axes).
  • Per-route browser tab titles — most pages fall back to the generic "Monarium" title; each route should set a translated <title> visible in the browser tab.

Origin & authorship

This project was rebuilt from a hand-coded prototype (a Vite + Mantine app) with the help of generative AI (Claude, Anthropic).

What comes from the original author:

  • The dex generation algorithm (src/lib/generator/Generator.ts) — the core logic that turns a game + config into a set of 30-slot boxes with the correct Pokémon, forms, regional variants, shinies, and sub-dex groupings. Ported verbatim; covered by snapshot tests.
  • The complete Pokémon data layer (src/lib/pokemon/DataCollections.ts, availablePokemon/*, gameConfigs.ts) — 1 025+ Pokémon across 20 games, hand-curated over years.
  • The visual design language — paper/wood textures, polaroid cards, tape effects, per-game colour palettes, handwritten headings. Concept and assets from the original app.
  • Feature design, UX decisions, and product direction throughout.

What was generated / co-authored with AI:

  • The Next.js App Router architecture, component structure, and server/client split.
  • The Postgres schema, Drizzle ORM integration, and sync pipeline (Lamport clocks, outbox, SSE).
  • All shadcn/ui component wiring, Tailwind styling, and responsive layout code.
  • The authentication system (session management, email verification, password reset).
  • Translations for 10 non-English locales (AI-generated, then human-reviewed and corrected).
  • Test infrastructure (Vitest unit tests, Playwright E2E tests).
  • This README.

The generator algorithm and data layer are the intellectual core of the project. The application shell around them is the product of human direction + AI implementation.


Self-hosting disclaimer

This software is designed to be self-hosted. If you deploy it publicly, you are the data controller under GDPR / DSGVO and must:

  1. Fill in the imprint — edit Imprint.values.* in src/messages/{locale}.json with your name, address, and contact information (required by law in the EU).
  2. Review the privacy policy — the template at /privacy describes the app's actual data processing. Adjust the "Third-party processors" section to list your hosting provider, database provider, and email service.
  3. Comply with GDPR — the app provides account deletion (Art. 17) and full data export (Art. 20) out of the box. Ensure your hosting setup meets the remaining obligations (e.g. data processing agreements with your providers).

The built-in imprint and privacy policy pages are templates, not legal advice.


Tech stack

Layer Technology
Framework Next.js 16 (App Router, Node runtime)
UI shadcn/ui (base-nova) + Tailwind CSS v4
Database PostgreSQL 16 + Drizzle ORM
Auth Hand-rolled sessions (argon2id hashing, httpOnly cookies)
Sync Lamport-clock op log + IndexedDB outbox + SSE (pg LISTEN/NOTIFY)
i18n next-intl (11 languages)
PWA Serwist service worker + Web App Manifest
Charts Recharts
Testing Vitest (unit) + Playwright (E2E)
Fonts Caveat (headings), Geist Sans (body), CJK/Indic/Thai handwriting fallbacks

Local development setup

Prerequisites

  • Node.js 20+
  • pnpm 9+
  • Docker (for Postgres + MailHog)

1. Clone and install

git clone <repo-url>
cd monarium
pnpm install

2. Start infrastructure

docker compose up -d

This starts:

  • Postgres 16 on port 5433 (not 5432 to avoid conflicts with a local Postgres)
  • MailHog on port 1025 (SMTP) and 8025 (web UI for viewing verification emails)

3. Configure environment

cp .env.example .env.local

The defaults work for local development:

DATABASE_URL=postgres://postgres:postgres@localhost:5433/monarium
DATABASE_URL_DIRECT=postgres://postgres:postgres@localhost:5433/monarium
AUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=http://localhost:3000
MAIL_FROM=Monarium <noreply@localhost>

4. Set up the database

pnpm db:push        # Sync schema to Postgres
pnpm db:seed        # Seed default achievements

5. Start the dev server

pnpm dev

Open http://localhost:3000. Register an account — the verification email appears in MailHog at http://localhost:8025.


Admin accounts

There is no self-service admin promotion. Use the CLI script:

# Promote a verified user to admin
pnpm admin:promote you@example.com

# Demote back to regular user
pnpm admin:promote you@example.com --demote

The script:

  • Requires the user to already exist and have a verified email
  • Is idempotent (re-running on an admin is a no-op)
  • Uses DATABASE_URL from .env.local — same privilege as direct DB access

Admin users see an /admin page with usage statistics and achievement management (CRUD).


Production deployment

The app runs on any platform that can host a Node.js 20+ server and connect to a PostgreSQL 16+ database. There is no vendor lock-in.

Requirements

  • Node.js 20+ with the Node runtime (not Edge) — needed for the native argon2 binding, pg LISTEN/NOTIFY, and httpOnly session cookies.
  • PostgreSQL 16+ — the database stores users, dex data, achievements, and the sync event log.
  • Two database connections — if you use a connection pooler (e.g. PgBouncer), you need a second direct (non-pooled) connection string. The SSE endpoint (/api/dexes/[id]/stream) runs LISTEN which poolers do not forward. Set both:
    • DATABASE_URL — pooled, used for normal queries
    • DATABASE_URL_DIRECT — direct, used by the SSE subscriber
    • If you don't use a pooler, both can point to the same database.
  • SMTP service — for sending verification and password-reset emails. Any SMTP-capable provider works (Resend, Mailgun, AWS SES, self-hosted Postfix, etc.).

Environment variables

# Required
DATABASE_URL=postgres://user:pass@host:5432/monarium
DATABASE_URL_DIRECT=postgres://user:pass@host:5432/monarium
AUTH_SECRET=<generate with: openssl rand -base64 32>
NEXTAUTH_URL=https://your-domain.com

# SMTP (any provider with SMTP credentials)
SMTP_HOST=smtp.your-provider.com
SMTP_PORT=587
SMTP_SECURE=false           # true only for port 465
SMTP_USER=...
SMTP_PASS=...
MAIL_FROM=Monarium <noreply@your-domain.com>

# Optional — OAuth login providers
AUTH_GOOGLE_ID=...
AUTH_GOOGLE_SECRET=...
AUTH_DISCORD_ID=...
AUTH_DISCORD_SECRET=...

Steps

  1. Build the app:

    pnpm install
    pnpm build          # Uses webpack (required for service worker)
  2. Set up the database:

    pnpm db:migrate     # Apply the initial schema migration
    pnpm db:seed        # Seed default achievements
  3. Start the server:

    pnpm start           # Starts on port 3000 by default
    # or: PORT=8080 pnpm start
  4. Register + promote an admin — see the section below.

  5. Fill in legal pages — edit Imprint.values.* and review Privacy.sections.* in the locale files under src/messages/. See the self-hosting disclaimer above.

Architecture notes

  • The build command is next build --webpack because the Serwist service worker plugin requires webpack. Development uses Turbopack (the default pnpm dev).
  • The SSE endpoint holds one PostgreSQL connection per open browser tab. This is fine for personal or small-community use. If you need to scale beyond ~100 concurrent tabs, swap pg LISTEN for a Redis pub/sub layer — the app code change is isolated to src/app/api/dexes/[id]/stream/route.ts.
  • The service worker (public/sw.js) caches the app shell and tileset for offline use. It is compiled at build time — no runtime compilation needed.
  • Static assets (tileset, textures, fonts, icons) are served from /public/ and cached aggressively by the service worker.

Tileset generation

The app displays Pokémon as a single sprite sheet (public/tileset.webp) instead of loading individual images. The tileset and its coordinate mapping are checked into the repo, but if you need to regenerate them (e.g. when a new game adds Pokémon), run:

pnpm tileset:create

This downloads ~3 000 individual sprites from PokéAPI's sprite repo, composites them into a 96×96 tile grid (30 tiles per row), converts to WebP, and writes:

  • public/tileset.webp — the sprite sheet served to users (~1.8 MB)
  • public/tileset-mapping.json{ "key": { "x": col, "y": row } } lookup

The script takes 5–15 minutes depending on network speed. It is idempotent — re-running overwrites the output.

When to regenerate:

  • A new Pokémon game is added to DataCollections.ts with new species or forms
  • The ImageMappings table in scripts/create-tileset.ts is updated with new form→sprite-ID mappings
  • PokéAPI updates their sprite set (e.g. higher-resolution art)

How the app uses it: The SpriteTile component reads tileset-mapping.json at import time and renders each Pokémon via background-image: url(/tileset.webp) + background-position computed from the tile's (x, y) coordinates. The grayscale() CSS filter dims uncaught entries — no second sprite set needed.


Scripts reference

Script Description
pnpm dev Start dev server (Turbopack)
pnpm build Production build (webpack, for Serwist)
pnpm start Start production server
pnpm test Run Vitest unit tests
pnpm e2e Run Playwright E2E tests
pnpm typecheck TypeScript type check
pnpm analyze Bundle analyzer (opens treemap)
pnpm db:push Sync schema to database (dev)
pnpm db:migrate Apply SQL migrations (production)
pnpm db:seed Seed default achievements
pnpm db:studio Open Drizzle Studio (DB browser)
pnpm admin:promote <email> Promote user to admin
pnpm tileset:create Download sprites + generate tileset

Legal

Source code

The Monarium source code is released under the MIT License — see LICENSE for the full text. You may use, modify, and redistribute it (including commercially) provided the copyright notice and license text stay attached.

Third-party assets

The MIT License covers the code I wrote. Other elements bundled in this repo have their own terms, and those terms take precedence for the material they cover:

  • Pokémon names, sprites, and trademarks are property of Nintendo / Game Freak / Creatures Inc. / The Pokémon Company. Monarium is a personal, non-commercial fan project and is not affiliated with, endorsed, sponsored, or approved by any of them.
  • Sprite data sourced from PokéAPI/sprites — the repository is distributed under CC0 1.0, but the sprite images themselves remain © The Pokémon Company (community fan use).
  • Bundled fonts and textures each have their own license — several (Naver Nanum Son Geulssi, Maniackers Design Holiday MDJP, f0nt.com Book Akhanake) are freeware with restrictions that may affect commercial redistribution.

See the in-app /credits page (or the source file) for the complete list of third-party resources, their authors, and licenses. Before deploying Monarium publicly or commercially, check each entry — a few restrict bundling or require attribution that doesn't come automatically with MIT.

About

Customizable Pokédex generator and tracker. Self-hosted, offline-first, 11 languages.

Topics

Resources

License

Stars

Watchers

Forks

Contributors

Languages