Skip to content

Releases: luther-rotmg/POE2GPS

v0.41.3 — Atlas Detection Fix

Choose a tag to compare

@github-actions github-actions released this 17 Jul 19:30

The web UI wrongly said "atlas closed" for anyone whose UI child structure drifted with the 2026-07-16 game patch. Auto-recovers now.

Fixed

  • 🗺 Atlas open-detection auto-recovers from UI child-index drift. The atlas panel lives at UiRoot child index 22 (validated 2026-06-08); today's game patch (same one v0.40.1 chased for AreaInstance offsets) shifted UI children on some setups so index 22 no longer points at it — result: "atlas closed — open it in-game + Refresh" even when the atlas is wide open. Fix: AtlasPanelOpen() now checks the 18-child signature at the primary index, and if it doesn't match, scans ±6 neighbor indices for the panel. First success is cached; subsequent calls hit the fast path. Fail-safe: still degrades to "closed" if the panel truly isn't findable (no per-tick BFS).

Upgrade

Recommended if you saw "atlas closed" reported despite having the atlas open in-game.

Full Changelog: v0.41.2...v0.41.3

v0.41.2 — Scrollable Tabstrip

Choose a tag to compare

@github-actions github-actions released this 17 Jul 16:45

Fix for the v0.41.0 tabstrip overflow.

Fixed

  • 📜 Dashboard tabstrip now scrolls horizontally on narrow viewports. v0.41.0 added four new tabs (Radar Filter / Layouts / Nav / Widget) that pushed the strip past the visible edge for anyone whose dashboard window was narrower than ~1900px, hiding the newest tabs entirely with no way to reach them. Fix: overflow-x: auto + white-space: nowrap + a subtle 6px scrollbar (thin scrollbar-color variant on Firefox, styled ::-webkit-scrollbar on Chromium). Scroll with shift-wheel, trackpad, or the scrollbar. Tab labels stay fully readable — no ellipsis-truncation.

Upgrade

Recommended for everyone on v0.41.0 / v0.41.1 whose window doesn't fit the full 15-tab strip.

Full Changelog: v0.41.1...v0.41.2

v0.41.1 — Ops Hotfix

Choose a tag to compare

@github-actions github-actions released this 17 Jul 11:16

Supporter roster nudge — no user-visible functional change.

Changed

  • 🔑 Expanded the embedded supporter-code roster with one additional entry — restores a code that missed initial delivery. Anyone on v0.41.0 without a supporter code sees zero change; anyone whose delivered code was affected can now paste it and unlock the supporter tier as intended.

Upgrade

Optional for most users. Recommended only if you're the specific supporter waiting on a re-delivered code.

Full Changelog: v0.41.0...v0.41.1

v0.41.0 — Supporter Bundle

Choose a tag to compare

@github-actions github-actions released this 17 Jul 06:56

Focused play. Your loadout follows your zone.

Added — 🎯 Four supporter-tier features (free tier byte-identical to v0.40.1)

  • 🎯 Focused Radar Filter — per-zone whitelist / blacklist. Blacklist entities skip the render walk entirely (real FPS win in town: a hideout with 40 decorative NPCs / vendor pets / critters now costs zero draw time). Configurable via a new Radar Filter tab in Settings with per-preset match pattern (glob: *_town, T17_*, Delirium_*, or exact zone code) + whitelist chips + blacklist chips + add-from-current-zone helper. First-match-wins on zone entry; caps: 20 presets, 50 patterns per list.
  • 🧩 Zone-Aware Overlay Layouts — save up to 10 dashboard-panel loadouts and let them auto-swap on zone entry. Town preset hides drop timeline + boss HP + XP chart; boss-arena preset hides everything except vitals + boss HP; maps get your full loadout. New Layouts tab with capture-current-layout helper (snapshots visible panels into a draft preset). Same wildcard-glob matcher as Radar Filter.
  • 📍 Custom Auto-Nav Destinations — name any grid position ("chest room at 145,220 in T17-Necropolis"). New Nav tab with per-row inline editor + capture-current-position helper (pre-fills zoneCode + coords from /api/state). Overlay draws cyan diamond markers with → <name> labels; floating chip strip in top-right shows saved destinations for the current zone. Cap: 50 destinations.
  • 📊 Session Stat Widget — floating dashboard-chrome widget with 6 configurable chips (drops / XP gained / bosses killed / deaths / time-in-zone / avg-map-clear-time). New Widget tab picks which chips render + x/y position. Refreshes every 2s; hidden entirely for non-supporters.

Under the hood

  • New POE2Radar.Core.Support.SupporterGate static — canonical replacement for the scattered s.isSupporter predicate. Existing v0.35 palette gate refactored to use it behind the scenes (user-visible behavior unchanged). JS mirror window.__supporterGate.isSupporter() for the 4 UI features + retrofit.
  • New POE2Radar.Core.Zones.ZoneCodeMatcher static — wildcard-glob matcher (*_town, T17_*, *) reused by A + B for zone-code matching. Case-sensitive, null-safe.
  • New window.__supporterHint JS component — inline card ("Supporter feature — Save your Ko-fi code in Settings to unlock this.") that renders in the editor's place for non-supporters. Retrofitted into the v0.35 palette-select gate (no more silent-revert UX).
  • New window.__panelInventory JS helper + 12 stable data-panel-id handles on canonical dashboard panels — foundation for Layouts auto-swap (B3) and future Layout Designer work.
  • 4 new POE2Radar.Core.* stores (RadarFilters / OverlayLayouts / NavDestinations / SessionWidget) — all whole-file config/*.json envelopes with atomic .tmp + File.Move writes, mirroring v0.39 RulesFileStore pattern.
  • 4 new HTTP endpoint families in ApiServer.cs/api/radar-filters, /api/overlay-layouts, /api/nav-destinations (per-id CRUD + ?zone= filter), /api/session-widget (single-record + bundled allowedChips metadata). Loopback-Host-gated writes; standard 400/403/405 semantics.
  • Renderer wire-ups: blacklist early-continue in OverlayRenderer.cs entity draw loop (saves both Rules.TryMatch cost and EntityView allocation for skipped entities) + Nav Destination markers drawn after the tile-landmark loop.
  • LayoutAutoSwap IIFE polls /api/state every 2s; on zone change, GETs /api/overlay-layouts, first-matching preset applies via window.__panelInventory.get(slug).
  • SessionMetricProviders — 6 pure formatters (CultureInfo.InvariantCulture, thousand-separators, mm:ss / Hh Mm time buckets, em-dash fallback for missing data).

Tests

  • ~230 new xUnit facts across 15 new test files. Full suite grows from 1112 → 1370 (+258 tests, all green, 2 pre-existing SSE skips).

Upgrade

Fully additive — no config migration. Free-tier experience is byte-identical to v0.40.1. Every supporter feature is dormant until a valid Ko-fi code is present in Settings; when it is, the new tabs light up. Existing v0.35 palette gate keeps working exactly as before (retrofit is behind-the-scenes).

Full Changelog: v0.40.1...v0.41.0

v0.40.1 — Patch-Day Hotfix

Choose a tag to compare

@github-actions github-actions released this 17 Jul 02:23

Path of Exile 2 shifted five internal offsets today; POE2GPS follows.

Fixed — 🔧 AreaInstance offsets for the 2026-07-16 game patch (+0x8 shift)

  • 🧭 Reads work again. Today's Path of Exile 2 patch shifted five fields inside the AreaInstance block by +0x08. Without the update, the overlay reads garbage for player position, entity map, terrain grid, and server data — heatmap goes blank, entities disappear, terrain draws wrong. Fixed by merging upstream Sikaka/POE2Radar@2615bec.
  • Shifts applied: LocalPlayer 0x5B8→0x5C0, ServerDataPtr 0x598→0x5A0, AwakeEntities 0x6D8→0x6E0, SleepingEntities 0x6E8→0x6F0, TerrainMetadata 0x8B8→0x8C0.
  • Low-offset fields (AreaInfo/Level/Hash) sit below the insertion and are unchanged.
  • Life-component vitals, entity walk, terrain, inventory, and league detect all re-validated upstream against a live 2026-07-16 client session.

Under the hood

  • New --chaindbg Research probe: wide-scans the AreaInstance block for the LocalPlayer metadata gate, so future patch-day drift can be localized in seconds instead of manual byte-hunting.
  • Doc comments on the AreaInstance layout summary + entity-map / inventory / terrain seams updated to the new offsets to prevent future readers from anchoring on stale values.

Upgrade

Recommended for everyone playing today or later. No config changes; if v0.40.0 is running against today's game patch it will silently misread — grab this build.

Full Changelog: v0.40.0...v0.40.1

v0.40.0 — Cartographer

Choose a tag to compare

@github-actions github-actions released this 17 Jul 01:39

Every zone you cleared, drawn.

Added — 🗺 Cartographer (movement heatmap + route replay)

  • 🗺 Movement heatmap. New Cartographer tab in Settings shows which parts of each zone you've walked, colored by density (log-normalized 64×64 grid, transparent→dark-blue→teal→yellow→warm-orange viridis ramp). The tool has been silently sampling your position at 1 Hz since v0.40 launched — run the same map twice and coverage builds up over time.
  • ▶️ Route replay. Play / Pause / Jump-first / Jump-last controls plus a scrub slider let you replay your movement through any zone you've cleared. Watch where you backtracked, spot side rooms you missed. Speed presets: 1×, 4×, 16×, and Max (roughly 60 samples/sec at 60 FPS — 30 minutes of gameplay replays in ~30 seconds). Timestamp readout stays in sync with the scrub position.
  • 🔐 Loopback-gated /api/tracks. Character-name query is served only to localhost — same privacy posture as v0.37 Codex. Position samples never leave your machine. /state continues to strip character identity.
  • 🎯 Additive. No overlay changes; ignore the tab and nothing about your rig changes. Sample rate is a fixed 1 Hz on the render thread (30× downsampled from the world tick) — cheap enough to leave running forever.
  • 💡 Discoverability card on the dashboard first-load flags the new feature for existing users. Dismissable with [×]; the dismissal persists via localStorage.

Under the hood

  • New POE2Radar.Core.Tracks namespace: TrackSample record + TrackStore static class handles append/load/list against config/tracks/<sanitized-character>/<zone-code>.jsonl. Character-name + zone-code sanitize via the same letter/digit/underscore/hyphen rule as codex + palettes. 10K-sample ring cap with read-rewrite trim keeps the newest 9K on overflow. All exceptions swallowed on the render hot path.
  • New TrackRecorder — 30-tick character-name stability gate (v0.37 pattern) + 1-Hz Stopwatch downsample gate + zone-change clock reset. Wired into RadarApp's per-tick observer next to SessionEventLog.
  • New /api/tracks?character=<name>&zone=<code> (loopback-gated), /api/tracks/characters, /api/tracks/zones?character=<name> (both also loopback-gated). Non-GET methods return 405.
  • New Cartographer dashboard IIFE: cascading character/zone selects → 64×64 density render → offscreen heatmap cache → route dotted-line overlay + gold marker at scrubbed sample index. requestAnimationFrame loop drives playback advance.

Tests

  • 51 new xUnit facts across TrackStoreTests (16: append/roundtrip/sanitize/ring-cap/perf-under-cadence-budget), TrackRecorderTests (9: stability-gate/downsample/zone-change/silent-exception/happy-path), ApiTracksEndpointTests (13: 3 routes × gate/missing-param/happy/method), DashboardCartographerTabTests (6: HTML/JS/CSS structural), DashboardCartographerReplayTests (4: playback controls + speed presets + JS symbols), DashboardCartographerHintTests (3: hint card + dismiss). Full suite grows from 1061 to 1112 (all green).

Known limitations

  • Per-session split — currently multiple runs of the same zone APPEND to the same track file (density accumulates). A "session filter" UI (view only the last N sessions) is v0.40.1 scope.
  • Overlay heatmap layer — v1 is dashboard-only. A translucent grid drawn on the actual overlay is v0.41 scope.
  • Export as PNG / GIF for streaming clip use — v0.40.1.
  • Cross-zone aggregate ("all your Delirium mirror clears overlaid") — v0.40.1.

Upgrade

Fully additive — no config migration required. Position sampling starts writing the moment your character name stabilizes; if you don't want it, ignore the new dashboard tab. Existing installs upgrade cleanly.

Full Changelog: v0.39.1...v0.40.0

v0.39.1 — Ring, Label, Pulse

Choose a tag to compare

@github-actions github-actions released this 17 Jul 00:17

The rest of the effects, wired.

Added — 🎨 Ring, Label, and Pulse effects go live

  • Ring — outlines the entity with a #rrggbb-parsed color at 1.4× the icon radius. Stacks on top of hide/tint from v0.39.0.
  • 🏷 Label — overrides the entity's default label with your custom text. Four token expansions: {name} (entity token), {level} (entity level), {metadata} (full metadata path), {zone} (current area code). Unknown tokens are left as literal {foo} — never crashes.
  • 💓 Pulse — alpha modulation on the entity's brush. slow = 1 Hz (breathing), fast = 3 Hz (heartbeat). Alpha bounded to [0.4, 1.0] so entities never disappear entirely mid-pulse. Composes with tint (pulse alpha applies to whatever color the entity ends up with).

Sound remains deferred — needs an audio playback subsystem with first-sight dedup + WAV file resolution from config/sounds/. Sound-effect rules save + load fine today; they just don't play yet.

Under the hood

  • RuleEffectApplier gains 3 new pure helpers: HasEffect<T>(effects, out T?) generic lookup, ExpandLabelTokens(template, EntityView, WorldSnapshotView) deterministic substitution, ApplyPulseAlpha(baseColor, PulseEffect, elapsedMs) = 0.7 + 0.3·sin(2π·hz·t) mapped to [0.4, 1.0].
  • OverlayRenderer gains a _renderStopwatch field (single Stopwatch.StartNew for the app lifetime) that feeds elapsedMs to the pulse calculation. Entity draw loop applies pulse alpha before tint override, then draws icon, then draws ring outline as a DrawEllipse with the brush color restored afterward, then uses effectiveLabel = LabelEffect.Text expanded (or falls back to rule.Label) for the label draw.

Tests

  • 35 new xUnit facts in RuleEffectApplierTests (total grows 16 → 51): HasEffect for each of 3 kinds present/missing, all 4 label tokens plus unknown/empty edge cases, pulse alpha at slow/fast × start/mid/end waypoints, alpha boundedness. Full suite grows from 1026 to 1061 (all green).

Upgrade

Fully additive. Rules authored in v0.39.0 that used Ring/Label/Pulse effects (which persisted through Save/Load but were inert on the overlay) now render as intended without any config change.

Full Changelog: v0.39.0...v0.39.1

v0.39.0 — Rule Engine

Choose a tag to compare

@github-actions github-actions released this 17 Jul 00:04

One place. One schema. One Save.

Added — 🎛 The Rule Engine (unified when-selector → then-effects[] pipeline)

  • 🎛 New "Rule Engine" tab in Settings. Every rule is when this matches, then do this. Selectors filter by metadata regex, entity token, rarity, zone code, in-hideout flag, level range, or buff presence — any combination, flat AND (v1.1 will add nested boolean logic). Effects: hide (force-suppress the entity), tint (#rrggbb color override), ring (outline — deferred to v0.39.1), label (custom text with {name}/{level}/{metadata}/{zone} tokens — v0.39.1), sound (WAV file — v0.39.1), pulse (slow/fast — v0.39.1). v1.0 ships hide + tint live end-to-end; the other effect types persist through Save/Load but are inert on the overlay until R3.1 wires them.
  • 🔄 Composable priority + enabled toggle. Higher priority wins conflicts; toggle any rule on/off without deleting it. Save → apply live on next entity tick — no restart, no page reload.
  • 🚪 Legacy migration on demand. Existing Affix Nameplates / Buff Nameplates / Rules (item filter) tabs each get a Migrate to Rule Engine → button that opens the Rules Engine editor pre-filled with a starting-point rule derived from that tab. Original legacy rule stays put — you review + Save the new one; delete the legacy source manually when confident.
  • 📮 Share-code roadmap. Rule sharing (paste-safe RUNE1-style codes like v0.38 palettes) is planned for v0.39.1 — v1.0 ships local-only.
  • 🛡 Fully additive. All six legacy rule surfaces (AffixNameplates, BuffNameplates, WaystoneRedFlags, AutoNav, AtlasTags, CustomLandmarks, ItemFilterEngine) continue to work byte-identically. The new engine is opt-in. Ignore the new tab and nothing changes about how the overlay behaves for you.

Under the hood

  • New POE2Radar.Core.Rules namespace: RulesFile envelope + RuleRecord (id/name/priority/enabled/when/then) + Selector (8 optional predicates) + polymorphic Effect base with 6 sealed subtypes (HideEffect/TintEffect/RingEffect/LabelEffect/SoundEffect/PulseEffect). [JsonPolymorphic] + [property: JsonIgnore] discriminator per the v0.37 CodexEvent pattern.
  • New RulesFileStore static class: Load/Save/Upsert/Delete/ValidateRule against config/rules.json (whole-file atomic-rename envelope, NOT JSONL). Strict slug/hex/rarity/speed/sound-name validation; 100-rule cap enforced at Save.
  • New RuleEngine.Compile(RulesFile)CompiledRuleSet.TryMatch(EntityView, WorldSnapshotView) → IReadOnlyList<Effect>. Regex pre-compiled with Compiled | IgnoreCase | CultureInvariant; rules sorted by descending priority; TryMatch never throws (compile-time validation catches all).
  • New RuleEffectApplier helper: HexToColor4("#rrggbb") → Color4 + TryApply(effects, ref Color4) → bool hide. Used inline by OverlayRenderer in the entity draw loop; exported public + tested for future R3.1 wire-up sites.
  • New /api/rules HttpListener routes in ApiServer.cs: GET list ungated, GET/POST/DELETE by id, POST 409-on-duplicate-name, all writes loopback-Host-gated. Mirrors the v0.38 F1 /api/palettes pattern.
  • New OverlayRenderer.Rules + ItemFilterEngine.Rules public properties, defaulting to RuleEngine.Empty (safe null pattern — no rule effects apply until loaded). RadarApp loads + compiles on startup via silent try/catch (malformed rules.json can't crash startup).

Tests

  • 89 new xUnit facts across RulesFileStoreTests (19: round-trip, cap, invalid hex/rarity/sound-name), RuleEngineTests (25: every predicate × effect combo, perf bench 100 rules × 10K iterations @ 0.02ms/call avg), ApiRulesEndpointTests (13: CRUD, 403/404/409/400/500 semantics), DashboardRulesTabTests (6: structural — tab button, editor ids, JS symbols), RuleEffectApplierTests (16: hex parsing + effect application), ItemFilterEngineRuleFilterTests (6: pass-through, Hide filters, TintEffect no-op, disabled skip), DashboardRulesMigrationTests (4: migrate-button presence + prefill-event listener). Full suite grows from 937 to 1026 (all green).

Known limitations

  • Ring / label / sound / pulse effects persist through Save/Load but don't render on the overlay yet. Follow-up bead R3.1 wires them in v0.39.1.
  • Nested selector boolean logic (not: / any: / all: wrappers) — flat AND only in v1.0. v1.1 based on user feedback.
  • Live match count ("matches ~N of last 100 entities") — deferred to v0.39.1 to keep R5's dashboard tab tractable.
  • Legacy migration copies one representative rule per tab, not full auto-migration of every legacy rule. User reviews + Saves the migrated draft; original legacy rule stays put.
  • Buff-based selectors don't fire on filter-path (only on renderer-path) because ItemFilterEngine doesn't have world-snapshot context at Match-time — selectors that check zoneCode/inHideout/hasBuff are renderer-only.

Upgrade

Fully additive — no config migration required. Every existing settings file, filter, and rule works unchanged. config/rules.json starts empty; the new tab is inert until you author your first rule.

Full Changelog: v0.38.0...v0.39.0

v0.38.0 — The Forge

Choose a tag to compare

@github-actions github-actions released this 16 Jul 17:37

Ten built-in palettes was a start. Now you make your own.

Added — 🔨 The Color Forge

  • 🎨 Full 13-var color designer in Settings → Color Forge. Every dashboard CSS variable — --gold, --gold-bright, --gold-deep, --ink, --ink-dim, --ink-faint, --panel, --panel2, --bg, --bg-alt, --line, --line-soft, --good — gets its own row with HSL sliders + hex text input, kept in bidirectional sync (drag the slider, the hex updates; type a hex, the sliders snap). Invalid hex marks the input .invalid; the rest of the palette keeps working.
  • 🖼 Live sample preview shows a mock kill-card, drop-card, vitals bar, chart, button, and tooltip using the values you're dialing in. Preview is scoped to its own <div> via --fp-* vars — the applied dashboard palette doesn't change until you hit Save, so you can experiment without wrecking your current look.
  • 💾 Save / Load / Delete presets via a Name field + Save button + Load-from dropdown. Presets persist to config/palettes/<slug>.json (13 vars + preview thumbnail auto-derived from [--bg, --panel, --gold, --ink]). Slug validation: ^[a-z0-9-]{1,32}$, the 10 built-in slugs are reserved. Duplicate-name Save returns 409 Conflict — never silent overwrite.
  • 🎯 Presets appear in the palette dropdown + chip strip immediately after Save — no reload needed. Selected user palette persists via the same settings flow as the built-ins. Same supporter-gate logic: user palettes are supporter-only, same as the built-ins.

Added — 📮 Share codes (paste-safe color exchange)

  • 📤 Copy current encodes whatever palette is rendering right now into a RUNE1-<base64url>-<crc6> wire string and puts it on your clipboard. Reads live computed CSS vars, so it captures built-ins AND Forge presets AND imports identically.
  • 📥 Import pasted decodes a RUNE1 code from the textarea, applies it to <body> via a dedicated <style id="importedPaletteStyles"> block, and persists to localStorage['poe2gps.importedPalette'] as a single slot (overwrite on each import). Survives page reload without re-import.
  • 🛡 FNV-1a checksum on the last 6 hex chars catches paste corruption before decode — malformed codes silently return null rather than half-applying a broken palette.
  • Want to keep an imported palette? Import it, then open the Forge, name it, and hit Save. Import lives in localStorage until overwritten; Save turns it into a real preset in config/palettes/.

Added — 🖼 Preset Gallery

  • 🖼 Thumbnail grid of the 10 built-in palettes in the Settings tab, one card per palette with a 4-swatch mini-preview + display name. Swatches read live from the CSS palette blocks (no hardcoded hex map to drift), so any palette CSS tweak reflects in the gallery automatically.
  • 🔀 Clone button on each card forks that palette into the Forge editor pre-populated with its 13 vars + a suggested name (<slug>-fork). Save it under a new name and you've got a variant. Fastest way to riff on an existing look.

Under the hood

  • New POE2Radar.Core.Palettes.UserPalette sealed record + UserPaletteStore static class handles read/list/save/delete against config/palettes/. Strict slug + hex validation at the store layer, not the API layer, so shell-level tinkering (dropping a file directly) still enforces the invariants.
  • New /api/palettes HttpListener routes in ApiServer.cs: GET list is open (needed by the dashboard chip strip), POST/DELETE are loopback-Host-gated (writes never happen from a non-localhost origin). Duplicate-name POST returns 409 Conflict.
  • New paletteCodec.js module: self-contained encode/decode with FNV-1a-32 checksum, exposed as window.__paletteCodec = {encode, decode, MAGIC, KEYS}. C# mirror in PaletteCodecTests.cs asserts byte-for-byte parity — decode a JS-encoded string, and vice versa.
  • dashboard.js: new ColorForge IIFE + Preset Gallery renderer + Share/Import IIFE + user-palettes-changed CustomEvent dispatch/listen. Shared _userPalettesPromise cache serves both the <select> refresh and the preview chip strip from a single GET /api/palettes on load, invalidated on save/delete.
  • dashboard.css: .forge-panel, .forge-row, .forge-preview, .forge-preset-card, .palette-share blocks. Preview vars scoped to #forgePreview via --fp-* custom-property names so authoring never leaks into the applied palette.
  • dashboard.html: new <style id="user-palette-styles"> block populated at runtime with body[data-palette="user-<slug>"]{...} rules — the same selector shape as the built-in palette blocks, so user palettes drop into the existing CSS pipeline without a code-path fork.

Tests

  • 40+ new xUnit facts across UserPaletteStoreTests (round-trip, list, delete, reserved-slug rejection, invalid-hex rejection, preview auto-derivation), PaletteCodecTests (round-trip, hex-lower normalization, corrupt-input handling, 8KB decoded cap, JS/C# parity), DashboardPalettePreviewTests (extended to allow user- keys via regex), DashboardForgePresetGalleryTests (HTML container, CSS selectors, JS symbols, no hardcoded hex map), DashboardForgeCloneTests (Clone button wiring). Full suite grows from 925 to 937 (all green).

Upgrade

Fully additive. Existing installs upgrade cleanly, no config migration. Every existing built-in palette + supporter code + settings file works unchanged. If you don't want any of this, ignore the new "Color Forge" and "Preset Gallery" widgets in Settings; the classic dropdown still works exactly as before.

Full Changelog: v0.37.0...v0.38.0

v0.37.0 — Book of the Exile

Choose a tag to compare

@github-actions github-actions released this 16 Jul 15:11

📖 Book of the Exile

Every character's story, written as it happens.

Your character now keeps a running journal. Level-ups, boss kills, deaths, and notable drops all land as dated entries in a per-character on-disk book — one file per character, appended in real time as you play. Open the dashboard and turn to any page in the book: today, yesterday, this week, any earlier chapter you want to revisit.

Added — 📓 Character Codex (per-character auto-written journal)

Every session, POE2GPS quietly writes four kinds of events to config/codex/<character>.jsonl:

  • 🌟 Level-ups — captured at the exact tick your character advances
  • 🐉 Boss kills — attributed via the boss encounter catalog, strict allowlist (unknown uniques never logged, so the codex stays clean)
  • 💀 Deaths — with zone, area level, and character level at the moment of death
  • 💎 Notable drops — unique-rarity items on their first sighting per character

Character-name stability gate (30 ticks of unchanged name at 30 Hz) prevents opening a codex file during the login flicker window, so events never get attributed to the wrong character. Switch characters and the codex swaps files cleanly after the new name settles.

Added — 📚 Dashboard Codex tab

A new tab in the dashboard renders your character's book:

  • Per-day chapter groupings (jump straight to Today / Yesterday / This Week)
  • Per-kind filter chips (level / boss / death / drop) — click to narrow the view
  • Icons per event kind, chronological within each day

Everything is served over the new /api/codex?character=<name> endpoint, loopback-gated so the character-name query param never leaks past your machine. /state continues to strip character identity per the existing privacy posture.

Under the hood

  • New POE2Radar.Core.Session.CodexEvent polymorphic type hierarchy with JSONL round-trip via System.Text.Json [JsonPolymorphic] (base + LevelUpEvent, BossKillEvent, DeathEvent, NotableDropEvent).
  • New SessionEventLog — mirrors DropTimeline's mature load-on-construct + append + flush-on-dispose pattern, adds character-name stability gating and stateless per-character reads (SnapshotForCharacter) for cross-character API queries without disturbing live tracking.
  • Observers wired into RadarApp's render-thread world tick: CodexBossObserver (unique alive→dead + catalog hit), CodexDropForwarder (subscribes to a new DropTimeline.Recorded event), SessionTracker.CodexEmit for level-ups and deaths.

Known limitation

The boss-attribution validation test (D1 in the plan — a playtest-derived xUnit theory that asserts zero false positives on ~30 non-boss uniques) will ship in v0.37.1 once we've captured the metadata paths from a live PoE2 session. The runtime attribution logic is already strict-allowlist (unknown uniques dropped silently), so the risk of a false-positive boss entry in your codex is low; the follow-up formalizes the guarantee.

Install / Upgrade

  • New install? Grab POE2GPS-v0.37.0-win-x64.zip below and unzip anywhere; portable, no installer.
  • Existing users: the auto-updater will prompt on next launch.
  • Fully additive — no config migration, nothing you have to change. The codex starts writing the moment your character name stabilizes; if you don't want it, ignore the new dashboard tab and never look in config/codex/.

Full Changelog: v0.36.1...v0.37.0