Skip to content

Architecture

The following diagram shows the complete data flow from user input to simulation results:

USER PASTES SimC addon export string
┌─────────────────────────────────────────┐
│ parser.ts (frontend, pure TypeScript) │
│ SimC string → SimcProfile │
│ • Extracts character metadata, talents │
│ • Parses equipped gear (isEquipped: true) │
│ • Parses bag items (isEquipped: false) │
└──────────────┬──────────────────────────┘
│ SimcProfile
┌─────────────────────────────────────────┐
│ GEAR & OPTIMIZATION PANEL (React UI) │
│ • GearSlotCard per slot │
│ • GemSelector / EnchantSelector │
│ • ItemSearch for unowned gear │
│ • SimSettings panel │
└──────────────┬──────────────────────────┘
│ user selections
┌─────────────────────────────────────────┐
│ optimization-assembler.ts │
│ Collects OptimizationAxis[] from all │
│ UI sections. Exposes live combo count. │
└──────────────┬──────────────────────────┘
│ OptimizationAxis[]
┌─────────────────────────────────────────┐
│ combinator.ts (pure function) │
│ Cartesian product of all axes │
│ → CombinationSpec[] │
│ Enforces 1000-combination hard cap │
└──────────────┬──────────────────────────┘
│ CombinationSpec[]
┌─────────────────────────────────────────┐
│ profileset-builder.ts (pure function) │
│ CombinationSpec[] + SimcProfile │
│ + SimSettings → .simc file string │
└──────────────┬──────────────────────────┘
│ .simc content
┌─────────────────────────────────────────┐
│ Tauri IPC: invoke("run_top_gear") │
│ → run_simc.rs (Rust, async) │
│ 1. Write .simc to temp file │
│ 2. Spawn SimC sidecar │
│ 3. Read json2 output │
│ 4. Clean up temp files │
└──────────────┬──────────────────────────┘
│ json2 output
┌─────────────────────────────────────────┐
│ parseSimCResults() (frontend) │
│ json2 + manifest → SimResult[] │
│ Sorted by DPS descending │
└──────────────┬──────────────────────────┘
│ SimResult[]
┌─────────────────────────────────────────┐
│ RESULTS TABLE (React UI) │
│ Ranked by DPS with delta vs equipped │
└─────────────────────────────────────────┘
  1. combinator.ts and profileset-builder.ts are pure functions — no I/O, no side effects, fully testable without Tauri.

  2. All SimC knowledge lives in docs/, not in code comments. Code is clean; docs explain the why.

  3. season-config.ts is the only file edited for seasonal updates — no other file should contain season-specific IDs or ilvl values.

  4. The SimC binary exit code is not reliable — always check whether the json2 output file exists rather than checking exit_code == 0. SimC exits 1 on warnings even for successful simulations.

  5. Gem axes are conditional — they have a parentItemId and only participate in combinations where that item is selected.

  6. ProfileSet parallel mode is always enabled via profileset_work_threads=2.

WoW Gear Sim has no separate backend server. All “backend” logic runs as Tauri commands in Rust, called from TypeScript via invoke(). This eliminates port conflicts, firewall issues, and the “is the server running?” problem.

// Frontend calls Rust directly via IPC
const result = await invoke<string>('run_top_gear', { simcContent });
User types in search box (debounced 400ms)
├──► SQLite FTS5 query on items.db (instant, offline)
└──► Wowhead search API (parallel, online)
Results merged, deduplicated by item_id, up to 10 shown
User selects item → gear track picker appears
Track → bonus_id from GEAR_TRACKS in season-config.ts
Final SimC line: slot=,id=ITEM_ID,bonus_id=TRACK_BONUS_ID

All season-specific data flows from a single file:

src/lib/presets/season-config.ts
├── GEAR_TRACKS → ItemSearch track picker
├── GEM_PRESETS → GemSelector dropdowns
├── ENCHANT_PRESETS → EnchantSelector dropdowns
├── SOCKET_BONUS_ID → "Assume socket" checkbox
└── CURRENT_SEASON → build scripts

Validation: pnpm season:validate (run before every release).