Skip to content

Repository files navigation

WarcraftJournal

warcraftjournal.app

A web-based World of Warcraft Adventure Guide and Mythic+ companion. Browse dungeon and raid encounters across every expansion, study boss abilities and loot, reference trash spells, and plan Mythic+ pulls on interactive dungeon maps.

Built with React, TypeScript, and Vite. All WoW data is prepared at build time and served as static JSON - the app makes zero runtime API calls. Dungeon-journal content (instances, encounters, boss abilities, loot) is imported from an in-game capture by the WarcraftJournalDump addon; trash "zone spells" are scraped from Wowhead; M+ run data comes from raider.io and Warcraft Logs.

Features

Adventure Journal

  • Browse dungeons and raids by expansion or by Mythic+ season (an expansion appears once its journal data has been imported)
  • View boss abilities organized as a recursive section tree that mirrors the in-game journal
  • Heroic and Mythic tags on abilities restricted to those modes, resolved at render time (nothing is filtered out)
  • Loot tables with item-quality coloring
  • Zone spell / trash ability reference for dungeons, including interrupt tagging
  • Inline [bracketed] spell references rendered as Wowhead tooltip links
  • Per-boss strategy guides (a Guide tab) rendered from static markdown in src/content/guides/
  • Separate Retail and PTR journal data, browsable under the /ptr URL prefix

Mythic+ Routes

  • Interactive dungeon map editor (Leaflet-based) covering the current M+ season in retail mode and the next season's pool under /ptr (16 vendored MDT dungeon tables in total)
  • Select mob spawns (or whole packs) to build pulls, with map notes, drag-reorder, undo/redo, 1-9 pull hotkeys, and an abilities panel switchable between the current pull, all pulls, and every mob in the dungeon
  • Map layer toggles (outlines, labels, lines, patrols, trash) and a right-click menu to move a spawn between pulls or remove it
  • Paste an MDT export string to import a route (it is saved automatically), or export the current route as an MDT string to copy back into the addon
  • NPC tier coloring in a Plater-style palette (boss, miniboss, caster, elite, trivial), customizable from a nameplate colors popover in the header and persisted to localStorage
  • Top-10 Raider.IO runs per dungeon, auto-refreshed daily via GitHub Actions and served as featured routes
  • Top-10 Warcraft Logs runs per dungeon, surfaced as cards that open a picker of companion WCL views (overview, enemy auras, enemy casts, damage taken, player debuffs) for studying a pull; a paste-a-link input opens the same picker for any Warcraft Logs report URL
  • Save up to 50 routes to localStorage (renameable in place from the editor header); the landing page pairs Create/Import actions and the Warcraft Logs link input with a tabbed Raider.IO / Warcraft Logs runs grid, dungeon filter chips, and your saved routes

Season & Navigation

  • Mythic+ season overview with quick access to the season's dungeons and raids (PTR mode shows the upcoming season)
  • Global search (Ctrl/Cmd+K) across expansions, instances, and encounters in the active client mode
  • Breadcrumb navigation and a collapsible sidebar that becomes a mobile drawer at 768px and below
  • Dark and light themes (persisted to localStorage)
  • Curated list of community tools (Raider.IO, Wowhead, Warcraft Logs, Three Chest, etc.)

Getting Started

Prerequisites

  • Node.js 22 (see .nvmrc)
  • The repo ships with pre-generated data, so no API credentials are needed to build or run the app. Refreshing M+ run data requires a free Warcraft Logs API client (see Data below).

Installation

npm install

Development

npm run dev       # Vite dev server on port 5173 (or `$PORT`)
npm run build     # TypeScript check (tsc -b) + Vite production build
npm run lint      # ESLint
npm run preview   # Preview the production build locally

There are no tests; tsc -b && vite build is the primary correctness check.

CI additionally fails any push or PR containing an em-dash (U+2014) in a tracked file, via the no-em-dashes job in .github/workflows/lint.yml. npm run lint does not catch this.

Data (optional refresh)

The bundled JSON in src/data/generated/ is already current; refreshing it is only needed when adding content or updating run data.

Dungeon-journal content (instances, encounters, boss abilities, loot) is imported from an in-game capture, not fetched from any API:

npm run import-journal-dump -- "<path to WarcraftJournalDump.lua>" [--expansion <slug>] [--category raid|dungeon]

The -- separator is required: npm consumes any flags placed before it. Every flag is optional. --expansion <slug> (default midnight) and --category raid|dungeon (default raid) only supply the fallback for instances not yet in the hand-maintained expansion skeleton; anything already listed there resolves its own expansion. --target retail|ptr forces the destination pool, --instance <slug,...> limits the import to those slugs.

This parses the SavedVariables written by the WarcraftJournalDump addon and merges the entries into instances.json / encounters.json (or their ptr/ counterparts). It is the sole source of journal data.

Other refreshers:

npm run fetch-zone-spells         # Dungeon trash abilities from Wowhead (no credentials); rewrites both pools
                                  # optional: -- --instance <slug,...> --force (skips the 24h freshness cache)
npm run fetch-raiderio-routes     # Top M+ routes per dungeon from raider.io (no credentials)
npm run fetch-warcraftlogs-runs   # Top M+ runs per dungeon from Warcraft Logs

Only the Warcraft Logs fetch needs credentials. Copy .env.example to .env and add a Warcraft Logs v2 API client from warcraftlogs.com/api/clients:

cp .env.example .env
# WARCRAFTLOGS_CLIENT_ID=...
# WARCRAFTLOGS_CLIENT_SECRET=...

Architecture

Overview

WarcraftJournal is a single-page application with no backend. Upstream sources (Wowhead, wago.tools, raider.io, keystone.guru, Warcraft Logs) are only consulted at build time (or by the scheduled GitHub Actions). At runtime the app reads from static JSON bundled into the build.

Data Pipeline

Journal content and zone spells exist once per client version and never mix. The retail pool lives at src/data/generated/ and the PTR pool at src/data/generated/ptr/, each with its own instances.json, encounters.json, and zone-spells.json. src/data/index.ts builds one data set per pool and dataForSource(source) (via the useJournalData() hook) selects exactly one, driven by the /ptr URL prefix. The expansion skeleton, M+ run data, and boss guides sit outside the pools.

Dungeon-journal content is not fetched from any game-data API. scripts/import-journal-dump.ts parses an in-game capture from the WarcraftJournalDump addon (the SavedVariables Lua) and merges JournalInstance / JournalEncounter entries into the pool matching the dump's client. This is the single source of instance, encounter, boss-ability, and loot data, and it works for not-yet-live (PTR) content too. The expansion skeleton (expansions.json, all-expansions.json) is hand-maintained and shared by both modes. The importer only reaches the network to resolve spell icons and boss portrait art.

scripts/fetch-zone-spells.ts pulls dungeon trash NPC abilities from Wowhead, enriches them with spell mechanic tags from wago.tools DB2 exports and the bundled MDT data, applies manual overrides (ZONE_NPC_OVERRIDES, INSTANCE_IGNORED_NPC_NAMES) for accuracy, and routes each instance to the pool its Wowhead mapping targets. Every run rewrites both zone-spells.json and ptr/zone-spells.json, carrying forward the cached entries it did not refresh. It uses no credentials. A third workflow (.github/workflows/fetch-zone-spells.yml) runs it on manual dispatch, one instance per job, so a Wowhead block on one instance does not stall the rest and the runner's IP can stand in when a local scrape is refused.

scripts/fetch-raiderio-routes.ts pulls the top-ranked timed M+ runs for each current-season dungeon from raider.io, resolves the attached keystone.guru route, validates the MDT string with the app's own decoder, and writes raiderio-routes.json. A GitHub Action (.github/workflows/update-raiderio-routes.yml) runs this daily at 06:00 UTC and commits any diff.

scripts/fetch-warcraftlogs-runs.ts authenticates with the Warcraft Logs v2 API (OAuth2 client credentials), queries the current M+ zone for encounter ids, fetches the top fightRankings per encounter, and writes report-link metadata for the top runs per dungeon to warcraftlogs-runs.json. A second GitHub Action (.github/workflows/update-warcraftlogs-runs.yml) runs this daily at 06:15 UTC, staggered 15 minutes after the raider.io job so the two don't race on the same ref. The script also supports --list-zones for discovering the current M+ zone id when the season rotates.

All three refreshers are built to fail safe on an unattended daily run. Each retries rate limits and transient upstream errors with exponential backoff, reloads the previously committed JSON, and carries a dungeon's prior entries forward when this run captures nothing for it, so a single 403 never erases committed data. If a run captures nothing anywhere and has nothing prior to fall back on, the script exits non-zero instead of writing an empty file, failing the workflow rather than committing a regression. A red scheduled job therefore means stale data, not lost data.

The data layer (src/data/index.ts) imports these JSON files and builds Map<slug, T> and Map<id, T> lookups for O(1) access. Helper functions (getBrowsableExpansions, getInstancesForExpansion, getEncountersForInstance) are the query interface used by page components.

Current-season configuration lives in src/data/currentSeason.ts as dungeon and raid slug arrays, each entry tagged with the pool (source) it resolves against.

Routing

React Router 7 with nested, slug-based routes. The whole tree below is mounted twice: once at / (retail) and once under /ptr (PTR), which is how client mode is encoded in the URL.

Route Page
/ Home (browse by season or expansion)
/season Current M+ season overview
/season/:instanceSlug Instance within the current season
/season/:instanceSlug/:bossSlug Encounter within the current season
/tools Community tool links
/tools/mdt-route Mythic+ route editor (map, pulls, MDT import/export)
/:expansionSlug Expansion page (instance grid)
/:expansionSlug/:instanceSlug Instance page (encounter grid + zone spells)
/:expansionSlug/:instanceSlug/:bossSlug Encounter page (overview, abilities, loot, guide when authored)
* Not found

Routes are defined in src/router.tsx; pages live in src/pages/.

State Management

URL-driven, minimal contexts - no Redux or external store:

  • JournalContext - active tab via useSearchParams
  • ClientModeContext - retail vs PTR, read from the /ptr URL prefix (hooks only, no provider or storage)
  • ThemeContext - dark/light theme, persisted to localStorage, falling back to the OS preference
  • LayoutContext - wide content container and sticky-header visibility (used by the MDT map)
  • DevModeContext - dev info panel, persisted to localStorage
  • NameplateColorsContext - user-customizable Plater-style NPC tier palette

Component Organization

src/components/
  NameplateColorsSettings.tsx, JournalIcon.tsx   (top level)
  cards/          InstanceCard, EncounterCard, SeasonInstanceCard (grid display)
  encounter/      OverviewTab, AbilitiesTab, LootTab, GuideTab
  sections/       SectionTree, SectionNode, spellLinks (recursive boss ability trees)
  zone-spells/    ZoneSpellSection (dungeon trash abilities)
  mdt/            DungeonMap, DungeonPicker, RouteBuilderControls, RouteEditorHeader,
                  MobInfoPanel, SpawnContextMenu, RouteLandingView, SavedRouteCard,
                  FeaturedRouteCard, WarcraftLogsRunCard, WarcraftLogsLinkInput,
                  WarcraftLogsViewsModal, MapLayersControl, MapNoteEditor
  navigation/     ExpansionMenu (sidebar), SearchBar (Ctrl+K), BreadcrumbNav,
                  BackButton, ModeLink
  loot/           LootItemCard
  markdown/       Markdown, Mermaid, remarkCallouts (boss guide rendering)
  home/           ExpansionBrowser
  ui/, dev/       Shared UI primitives and dev tools

src/layouts/
  AppLayout.tsx   Root layout with responsive sidebar/drawer + sticky header

MDT route encoding/decoding utilities live under src/lib/mdt/ (shared by both the editor and the raider.io scraper) and use pako for the LibDeflate-compatible raw-deflate step, plus hand-ported LibSerialize / AceSerializer readers.

Styling

  • Tailwind CSS 4 utilities
  • Ant Design 6 components (cards, menus, tabs, tooltips, modals)
  • Leaflet / react-leaflet for dungeon maps
  • CSS custom properties in src/theme/global.css define the WoW-themed dark and light palettes
  • Ant Design theme tokens in src/theme/tokens.ts
  • Responsive breakpoint at 768px

Types

TypeScript types in src/types/ mirror the in-game Encounter Journal structure:

  • JournalExpansionJournalInstanceJournalEncounterJournalSection (recursive)
  • JournalItem for loot
  • ZoneSpellData for dungeon trash spells, NPC tiers, and interrupt flags
  • Union types ItemQuality and SectionHeaderIcon, plus the Role enum

Tech Stack

Deployed on Vercel with SPA rewrite (vercel.json).

About

Static React SPA that recreates WoW's in-game Adventure Journal with difficulty filtering, zone spells, and MDT route tools.

Topics

Resources

Stars

Watchers

Forks

Releases

Packages

Contributors

Languages