Skip to content
Pokered Save Editor 2
Pokemon Red & Blue save file editor - Qt 6 C++/QML
Loading...
Searching...
No Matches
Version History — July 2026

← All months (index)


2026-07-11 – Keyboard: a real modifier drops the latched page (0.16.6-alpha)

The bug Twilight found, and it was a real one. Latched and held modifiers were OR'd together, so: click "Uppercase" in the page strip to poke around, then start typing — and Shift does nothing, because the page is already latched shifted. Every letter comes out capital and the keyboard has quietly stopped telling the truth about itself. Same for Ctrl and Alt.

Touching a physical modifier now drops the latch. The moment you press a real Shift/Ctrl/Alt you're saying "I'm driving" — so the clicked-in page gets out of the way and the deck goes back to being exactly the keyboard under your hands. (Caps Lock is not cleared: it's a lock, not a latch, and Shift inverts it rather than cancelling it — as it does on any keyboard.)

Also: no modifier chords for the tileset. Ctrl+Tab is gone; Tab alone opens the picker on the tile pages and Outdoor is a button you click. A modifier's one job on this deck is to change page, and giving it a second meaning is how a keyboard stops being predictable.

And: the tiles no longer overflow their keys (the glyph — and the white card behind the picture tiles — is sized to fit inside the cap, with the cap clipping as a backstop), and the footer preview is centred in its fixed height rather than shoved against the bottom edge with a slab of white above it.

2026-07-11 – Keyboard: no text field in keyboard mode, Tab drives the tileset, and the modifiers stand out (0.16.5-alpha)

Keyboard mode has no text field at all. It never should have: a text field draws an underline, a placeable caret and a placeholder — every affordance of something you type into — while the thing you actually type into is the deck below. It's now a plain label with a soft caret pulsing at the end of it, a small NAME caption sitting well above, and no form chrome whatsoever. Edit mode still gets a real field, because there it's the truth.

Tab drives the tileset (on the tile pages, where it's the only thing that matters): Tab opens the picker — then Up/Down walks it and Tab or Enter chooses — and Ctrl+Tab flips Outdoor. Tab is coloured with the modifiers, because it's a key that controls the keyboard, not one that types. It stays dead on every other page.

Shift / Ctrl / Alt / Caps / Tab now read apart from the plain structural keys (Enter, Backspace) — a shade lighter with an accent edge, so the eye finds the modifiers without reading them.

Picture tiles get a white card behind them. Tileset tiles are transparent wherever the game would show the background through, so on a coloured cap they rendered patchy — some looking like they had a card behind them and some not. Only the pictures get it; the font glyphs sit straight on the cap, where they belong.

The bold letters lost their corner legends. A little "B" in the corner of a key showing a big bold B tells you nothing you can't see. (A contraction is not its letter, though — <'s> prints "'s", and its legend stays, because it's the only thing saying which key produced it.)

Also: tiles C0–DF all moved to Tiles III, which now says what it is — "Usually blank: these only hold anything in certain game states" — instead of leaving you wondering why the keys look empty. <mdot> and <.> swapped on the Ctrl page. tile14 sits on Shift+Ctrl+1. The name-row buttons are square (they were rectangles). The Simulated tray has room above it and a properly sized Outdoor button. And Tiles II no longer starts with "More".

2026-07-11 – Keyboard: the box you can type, no fake key combos, and two modes that behave (0.16.4-alpha)

Type a border. Twilight's idea, and a better one than mine: the box-frame glyphs are laid out as the box on the Tiles page —

Q W E ╔ ═ ╗
A S D --> ║ ║ (S is the box's hollow middle, and is empty)
Z X C ╚ ═ ╝

— so a border is literally Q W W W EZ X X X C. The keys are the picture. This is why the horizontal and vertical edges are the only two duplicated tiles in the whole map (you can't draw a box with one vertical edge), and tst_font_keyboard now pins exactly that: every tile reachable, and those two the only ones on more than one key. The cursor arrows moved to , . / — arrow-ish keys, and it keeps the "things that point" family together.

No key may pretend. The rule, stated: a tile goes where a real keyboard would put it whenever it can, and must never fake it. An alternate period on Ctrl+/ — while a perfectly good period sits on the period key one layer down — is a false affordance, so it moved to a spare key. And when the deck types, on this key with these modifiers, exactly what the physical keyboard would (a on A, ! on Shift+1, ? on Shift+/), the corner legend disappears: there is nothing left to teach, because the key is already telling you by being that key. It stays on every Ctrl/Alt key, where a real keyboard types nothing and the legend is the only clue you have.

Edit mode live-updates. Everything you type in the text field now reaches the name immediately, so the preview keeps up with you — and discard still discards (the value from when you entered edit mode is remembered, and the cross puts it back). In keyboard mode the field is grey, not black, with a soft caret pulsing at the end of the text — the invitation to type, sitting exactly where the next key will land.

Name/Example can no longer resize the keyboard. The footer was sized to its preview, so flipping that toggle grew the footer, which shrank the body, which shrank the deck — the whole keyboard re-flowed under your hands because you asked to see an example sentence. The footer is a fixed height now, and its content hangs up from the bottom so the byte counter sits at the edge in both modes.

Also: the "Simulated" controls moved out of the top bar (where they floated over a name field they have nothing to do with) to a tray directly above the keyboard, on the tile pages only — next to the only thing they affect. A quiet page description sits under the deck. The key detail pane scrolls long descriptions instead of eliding them, and fits the previews of the codes that expand to fifteen characters instead of slicing them off. Every button in the name row has the same chrome (some had a background and some didn't). And the idle text was simply wrong — a key is not "one game character", it's one byte, which might print one character, several, a whole name, or nothing at all while it changes the text.

2026-07-11 – Keyboard: punctuation lives on its own keys now (36 keys -> 47) (0.16.3-alpha)

Twilight: "Have some of the symbols like '?', '.', ',' amongst others actually be on their corresponding key." Right — and that isn't a tweak, it's the map. The punctuation keys ( ` - = [ ] \ ; ' , . /) were dead filler, which meant . and , were exiled onto a number row they have no business being on. They're real keys now, so the deck went from 36 assignable keys to 47 and the whole tile→key map was re-authored around them:

  • Base layer: . types ., , types ,, / types /, ; types ;, ' types ', - types -, [ and ] type [ and ].
  • Shift layer: the real shifted keys, wherever the game has that glyph — Shift+/ = ?, Shift+; = :, Shift+' = "</strong>, plus the number row's <strong>! $ × ( )</strong> (<tt>×</tt> on the <tt>*</tt> key, because <tt>*</tt> is multiply). The glyphs the game simply doesn't have (<tt>\@ \# % ^ \& _ + { } | \< \></tt>) leave their keys empty; the spare number-row slots take the Normal characters with nowhere else to be (♂ ♀ Pk Mn). - <strong>Ctrl layer:</strong> the punctuation that has a real partner one layer up — the closing <tt>\"</tt> under the opening one on <tt>'</tt>, the narrow colon under <tt>:</tt> on <tt>;</tt>, the <strong>ellipsis on the <tt>.</tt> key</strong> (dots on the dot key), the middle dot on <tt>,</tt>, and the open/close single quotes on the brackets. Every one of the 255 tiles still appears <strong>exactly once</strong> — <tt>tst_font_keyboard</tt> (54 cases) says so. <strong>And when a key types itself, the corner legend is gone.</strong> It exists to say "this tile lives on THAT key"; on the <tt>A</tt> key showing <tt>a</tt>, or the <tt>.</tt> key showing <tt>.</tt>, it was just repeating the glyph back at you. It stays wherever the two differ (the <tt>?</tt> on the <tt>/</tt> key still tells you which key to press), and the glyph now sits dead centre on the keys that don't need it. Also: the mode caption under the name box (<em>"Keyboard mode — type or click the keys..."</em>) is <strong>gone</strong> — long and ugly, and it said in twenty words what an icon says instantly. There's a <strong>mode icon</strong> in the top-left instead: a keyboard when the deck is live, a pen when you're editing the text directly. The "Simulated" controls are in one tray now (they were a bold word, an unpadded toggle and a bare combo, all floating), and the footer is tightened so the byte counter sits just off the bottom edge. @subsection autotoc_md2026-07-11----keyboard-caps-locks-the-shift-page-and-the-whole-screen-got-its-look-fixed-0162-alpha 2026-07-11 &ndash; Keyboard: Caps locks the Shift PAGE, and the whole screen got its look fixed (0.16.2-alpha) <strong>Caps Lock is a page now, not a letter-case rule.</strong> The previous cut gave it the real-keyboard behaviour (letters only; the number row kept typing digits) — and Twilight immediately hit the problem that creates: <em>"caps lock and holding down shift are 2 ways to get page 2, so why are there different page 2 depending on caps lock, i think something got crossed."</em> Nothing was crossed, but the model was wrong: caps-on-letters-only produces a layer that <strong>isn't one of the 8 pages</strong>, so the page strip could not say where you were. A model the UI cannot display is a bad model however "correct" it is. Caps now locks the <strong>Shift page</strong> entire — Shift inverts it, Ctrl/Alt ignore it (Ctrl+B is still bold B) — so every state the deck can be in is exactly one page and the strip is always right. The trade: the punctuation row rides along with caps, so typing a digit means tapping caps off. Rare (digits aren't in-game-legal in a name), and the deck <em>shows</em> the row change. <strong>The look.</strong> Twilight: <em>"colour was supposed to be visible at a glance, not only on mouseover"</em>, <em>"keyboard looks really bad but i cant place my finger on why"</em>, <em>"the colours on the top and bottom are really ugly and the top and bottom bar is way too tall"</em>. All fair, and all diagnosable: - <strong>No figure/ground.</strong> Light-grey caps on a light-grey chassis on a light-grey pane. The deck now has a <strong>dark slate body</strong> with light caps, like a real keyboard — and the structural keys (Caps/Shift/ Ctrl/Alt/Enter/⌫) are <strong>dark</strong> with light text, so the eye instantly separates "keys that type something" from "keys that do something". A held or latched modifier goes <strong>bright</strong>. - <strong>The category colour did nothing.</strong> It was a 6% wash behind a 35% border — literally only visible on hover, which defeats the entire point of having a colour legend. Caps now carry a <strong>real wash</strong> of their category colour with a solid border. On the dark body the deck reads as coloured regions the moment you look at it. - <strong>The bars.</strong> The washed-out <tt>lighter(accent, 1.5)</tt> blue stripes are gone: both are now a clean light surface with a hairline divider. The header went <strong>132 → 88px</strong> and the footer <strong>119 → ~104px</strong>, and the keyboard got the difference — it's the point of the page and it was being squeezed into whatever the bars left over. - <strong>The spacebar looked like a dead key</strong>, because it was rendering the Space <em>tile</em>, and the Space tile is — correctly — blank. It says <strong>"Space"</strong> across it now. It is, and always was, Space on every page. - <strong>You couldn't read the key legend</strong> — the small letter in the corner that tells you which key types this tile, i.e. the thing that makes "just type it" discoverable at all. It was 9px of mid-grey at 75% on a near-white cap. It now scales with the key, sits at full opacity, and is drawn in a dark shade of the cap's own category colour. - <strong>The deck didn't look like a keyboard.</strong> It was a floating block of 36 caps — roomier than a real layout, and somehow worse. The shape you recognise as "a keyboard" comes from the ragged edges as much as from the letters, so the deck now draws the <strong>full ANSI silhouette</strong>: <tt> ` </tt>, <tt>-</tt>, <tt>=</tt>, Tab, <tt>[ ]</tt>, <tt>\\</tt>, <tt>;</tt>, <tt>'</tt>, <tt>,</tt>, <tt>.</tt>, <tt>/</tt> and Win/Menu are all there as <strong>dead keys</strong> — muted, inert, unclickable, purely there so the thing reads as the object it's imitating. The real keys give up ~10% of their size for it; the deck scales, so a wider window hands that straight back. @subsection autotoc_md2026-07-11----keyboard-lowercase-is-the-base-layer-caps-lock-is-real-and-the-field-has-two-modes-0161-alpha 2026-07-11 &ndash; Keyboard: lowercase is the base layer, Caps Lock is real, and the field has two modes (0.16.1-alpha) Twilight, on the first cut: <em>"its counterintuitive to have shift lowercase... a-z should be without shift and uppercase with."</em> She's right, and it's the one place the deck contradicted every keyboard its user has ever touched. (My reasoning had been that Gen 1 names are all-caps — but that's an argument for a good Caps Lock, not for inverting the alphabet.) So: <strong>the base layer is now lowercase</strong> (<tt>a–z</tt> + <tt>0–9</tt>) and <strong>Shift is uppercase</strong> (<tt>A–Z</tt> + the punctuation row) — exactly like the keyboard under your hands. The number row didn't move: digits unshifted, symbols shifted. No other page changed, and every tile still appears exactly once. <strong>Caps Lock is a real Caps Lock, not a latched Shift.</strong> It affects the <strong>letters only</strong>, so the number row keeps typing numbers and you can type <tt>PIKA2</tt> without unlocking; it's <strong>ignored under Ctrl/Alt</strong> (Ctrl+B is bold B whether or not the light is on); and <strong>Shift inverts it</strong> (caps + Shift = lowercase). That's three rules the user already knows, so the rules live in C++ (<tt>FontKeyboard::pageForKey</tt>) where <tt>tst_font_keyboard</tt> pins each one. It also means the deck can honestly show two pages at once — uppercase letters above a digit row — which is what a keyboard does. Qt gives no portable way to <em>read</em> the caps light, so if it was already on before the screen opened the deck corrects itself from the OS's own answer (<tt>event.text</tt>) on the first letter you press. <strong>Physical modifiers are momentary; clicking latches.</strong> Hold Ctrl and the deck flips to that page; let go and it drops straight back — nothing sticks. Clicking an on-screen modifier cap (or a page button) still latches, because a mouse can't hold a chord and click a key at once, and because a latched page is the only way in when the OS eats the chord. <strong>The name field now has two explicit modes</strong>, and the screen says which one you're in: - <strong>Keyboard mode</strong> (the default): the field is a read-only display of what the deck is building, and Backspace removes a whole <strong>tile</strong>. - <strong>Edit mode</strong>: the pen button turns it into an ordinary text field — caret, selection, Ctrl+C/V/Z, and a character-by-character Backspace, as a text field should behave. <strong>The whole keyboard fades out and goes dead</strong>, because it has no say in what you're typing and shouldn't pretend otherwise. The pen is replaced by a <strong>check</strong> (apply) and a <strong>cross</strong> (discard) — so an edit is something you commit or throw away, never something that half-happened. Nothing you type in edit mode reaches the name until you press the check. @subsection autotoc_md2026-07-11----the-full-keyboard-is-an-actual-keyboard-now-0160-alpha 2026-07-11 &ndash; The full keyboard is an actual keyboard now (0.16.0-alpha) The full-screen name editor has been rebuilt from scratch. It used to be a filter form next to a wrapping list of coloured chips — a thing you had to <em>search</em>. It is now a <strong>keyboard</strong>: an ASDF/QWERTY deck whose 36 letter and number keys each carry one game tile, with the physical key that types it printed in the corner of every cap. Click a key, or just type it. <strong>255 tiles over 36 keys needs 8 pages — and Shift/Ctrl/Alt give exactly 8 combinations.</strong> One page per chord, nothing left over. You reach a page by <em>holding</em> the modifiers (like a real shift layer), by <em>clicking</em> the on-screen modifier caps (they latch, so mouse and touch users never hold a chord — and so a page stays reachable when Windows eats the combo: Shift+Alt and Ctrl+Shift switch keyboard layout on multi-language setups, and Ctrl+Alt is AltGr), or by clicking its button in the page strip, which shows the chord so it teaches the shortcut instead of hiding it. The map is <strong>not</strong> a linear dump of tiles into slots. Three rules, in order: <strong>identity beats everything</strong> (page 1 is A–Z + 0–9 and page 2 is a–z, exactly where a keyboard puts them, so there is nothing to learn); <strong>mnemonic beats ergonomics</strong> (<tt>'s</tt> on <strong>S</strong>, bold <strong>B</strong> on <strong>B</strong> — Ctrl+B, the same thing it means in every other program — <tt>\<player\></tt> on <strong>P</strong> with <tt>\<rival\></tt> on <strong>O</strong> beside it, <tt>\<pc\></tt> on <strong>C</strong>, <tt>\<dex\></tt> on <strong>X</strong>); and <strong>ergonomics breaks ties</strong> (the more-used tile takes the better key — home row, then upper, then bottom, then the number row). Pages run in the category order, so the four <em>cheapest</em> chords hold every Normal, Single-Char, Multi-Char and Variable tile — i.e. everything you'd ever legitimately put in a name — while the Pictures and the Control codes that <em>glitch</em> a name sit behind two- and three-key chords. You cannot hit <tt>\<end\></tt> by accident. The six box-frame glyphs are laid out <strong>as a box on the keys</strong> (Q ╔, W ═, E ╗ / A ║ / Z ╚, X ╝) and the three cursor arrows take the right home keys. The <strong>Space tile rides the real spacebar</strong> — the one tile not on a letter or a number, because that is where every human already expects it. Every one of the 255 tiles appears <strong>exactly once</strong> across the 8 pages. That isn't a claim in a comment — <tt>tst_font_keyboard</tt> (39 cases) fails if any tile is duplicated or, far worse, silently stranded with no key at all. Also: keys animate their tiles with the tileset (one shared sheet for the whole deck, so 36 animated tiles cost one render per frame rather than 36 tileset rebuilds); Backspace deletes a whole <tt>\<code\></tt> rather than biting one character out of the middle of one; a key that won't fit shakes the name field instead of failing silently; hovering any key fills the detail split with its render, name, code, category and description; and the old category <em>filter</em> on the left is now a colour <em>legend</em> (there's nothing left to filter — the deck shows everything). The name box stays a real text field: click into it and Ctrl+C/V/Z work as normal, and the deck dims its key legends to show it has handed over the keys. Gone with it: the character-chip list, the filter sidebar, the raw tilemap view and its Grid/Tileset toggle, and the <tt>FontSearchModel</tt> behind them. @subsection autotoc_md2026-07-11----tests-the-item-exchanges-economics-are-now-pinned-tst_item_exchange 2026-07-11 &ndash; Tests: the item exchange's economics are now pinned (<tt>tst_item_exchange</tt>) New test suite (14 cases) over a real Bridge, covering the part of the exchange a user cannot check by eye and that touches their save. It pins the whole-trade pricing rule — including the exact example that exposed the bug (3 Fresh Water = ₽600 = exactly 2 Potions, refund ₽0) and the uneven case (4 Fresh Water = 3 Potions + ₽100, <strong>not</strong> 4 Potions + ₽400) — plus the invariant behind it: a refund is always the single rounding leftover (<tt>0 \<= refund \< the given item's price</tt>, since a whole unit of change would mean one item too many was taken), and money never goes down. Also pinned: <tt>checkout()</tt> writes <strong>exactly</strong> the preview, spilling across bag + PC storage (bag drained first); the give list is owned-only while the get list is every item; unaffordable targets are flagged (and un-flag when the stock grows); the two "+" buttons are <strong>never both disabled</strong> on any selectable pair; and the Healing tab defaults to Potion ⇄ Fresh Water, preferring a potion over a status heal. A <tt>priceData_isWhatTheseTestsAssume()</tt> guard asserts the Gen 1 buy prices the worked examples rely on, so a future <tt>items.json</tt> edit fails loudly there instead of inside the arithmetic. These should have existed before the feature shipped — a screenshot review passed the buggy pricing because the numbers on screen were self-consistent, just wrong. The rule is now written into <tt>notes/plans/testing.md</tt>: a model that does arithmetic on save data gets its tests in the same commit. Full <tt>ctest</tt> 73/73 green. Tests + notes only — no app code, no <tt>VERSION</tt> bump. @subsection autotoc_md2026-07-11----exchange-prices-the-whole-trade-not-each-step-no-more-phantom-refunds 2026-07-11 &ndash; Exchange prices the WHOLE trade, not each step (no more phantom refunds) Fixed the exchange economics. The trade was priced <strong>per step</strong> — each item you asked for was separately rounded up to a whole number of the item you were paying with, and each one handed back its own leftover as money. That massively overcharged and over-refunded: asking for 3 Fresh Water (₽200 each) took <strong>3</strong> Potions (₽300 each) and refunded ₽300, when 3 Fresh Water is ₽600, which is <strong>exactly 2 Potions and no change at all</strong>. Now the whole trade is priced <strong>once</strong>: the total value being bought is rounded up to a whole number of the given item, and only that single leftover comes back as money. A trade that divides evenly costs nothing extra and <strong>refunds nothing</strong> — and the rate line under the button drops the "+₽0" entirely rather than showing a refund that isn't happening. Verified live: 3 Awakening (₽600) from Antidotes (₽100) now reads exactly <tt>-6 Antidote</tt> with no refund and money unchanged; 3 Fresh Water from Super Potions (₽700) takes 1 Super Potion + ₽100 back instead of the old 3 + ₽1,500. The rate lines also now describe the <strong>queued</strong> trade rather than a fictional per-unit rate, so what's written on Checkout is literally what the card shows. <tt>perAGive</tt>/<tt>perARefund</tt>/<tt>perBGive</tt>/<tt>perBRefund</tt> are replaced by <tt>giveFor(dir, steps)</tt> / <tt>refundFor(dir, steps)</tt>, which the preview, the "+" gating, and <tt>checkout()</tt> all share — one pricing path, so they cannot disagree. Money still only ever goes up. <tt>ctest</tt> green (72/72). @subsection autotoc_md2026-07-11----exchange-give-what-you-have-get-anything--potion---fresh-water-default 2026-07-11 &ndash; Exchange: give what you have, get anything (+ Potion <-> Fresh Water default) Reworked the item Exchange dropdowns (Healing / Custom) so you're no longer boxed in by what's already in your bag. The two sides are now <strong>asymmetric</strong>: the <strong>left</strong> side is what you <strong>give</strong>, so it lists the items you actually <strong>own</strong>; the <strong>right</strong> side is what you <strong>get</strong>, so it lists <strong>every</strong> exchangeable item — owned or not. You can now trade a stack of Antidotes into a Fresh Water you've never carried. To keep that honest, the get-side list <strong>greys out (and blocks) the items your stock can't cover</strong> — with 25 Antidotes (₽2,500) the list shows Calcium, Carbos and Full Restore dimmed, because no whole number of Antidotes buys even one of them. That greying is also what <strong>guarantees the two "+" buttons are never both disabled</strong>: whatever you <em>can</em> select is by definition affordable, so the "get" side always has a live "+". (Once you've taken a step, the opposite "+" comes back too, so you can always walk it back.) The <strong>Healing</strong> tab now opens on <strong>Potion ⇄ Fresh Water</strong>. The give side prefers the potion family in order — Potion, Super Potion, Hyper Potion, Max Potion, Full Restore, then the rest — picking the best one you actually have (so a save with 8 Super Potions and no Potions opens on Super Potion, never on Antidote); the get side prefers Fresh Water. If you own nothing in the category the give list falls back to showing everything, so the dropdown is never empty. New <tt>ItemExchangeModel</tt> API: <tt>sourceItems()</tt> / <tt>targetItems()</tt> (replacing <tt>ownedItems()</tt>), <tt>canGainTarget()</tt>, and <tt>pickDefaults()</tt>, plus a <tt>revision</tt> counter the QML <tt>model:</tt> bindings depend on so the lists — and their affordability flags — rebuild whenever counts or money move (the list getters are invokables, which a binding otherwise has nothing to re-run on). Verified live over the debug channel on a real save. No save bytes change. <tt>tst_qml_screens</tt> green. @subsection autotoc_md2026-07-10----market-exchange-sub-tabs-currency--healing--custom-item-item-trading 2026-07-10 &ndash; Market Exchange sub-tabs: Currency / Healing / Custom (item<->item trading) The Market's <strong>Exchange</strong> tab now has a sub-tab bar. <strong>Currency</strong> is the existing money<->coins converter (unchanged, just moved under the tab). <strong>Healing</strong> and <strong>Custom</strong> are new: they trade one item for another. Each side is an item dropdown (owned, non-zero, exchangeable items — Healing filters to healing/drink items, Custom lists everything you own) with the item's count and a <strong>+</strong> button; the two lists exclude each other's pick so you can't trade an item for itself. <strong>The trade:</strong> each "+X" step gains ONE of that item and draws the <strong>minimum whole number of the other item whose BUY value covers it</strong>, refunding the leftover value as <strong>money</strong> (money only ever goes up). So with Antidote (₽100) and Burn Heal (₽250): <strong>+Antidote</strong> takes 1 Burn Heal and refunds ₽150; <strong>+Burn Heal</strong> takes 3 Antidotes and refunds ₽50. Steps accumulate, the after-counts and money are previewed live, and <strong>nothing is written until Checkout</strong> (which routes to the item exchange on those sub-tabs). Each <strong>+</strong> disables independently the moment <em>its</em> direction becomes impossible — not enough of the source item, no room for the gained item, or money would exceed ₽999,999 — leaving the other side usable, exactly like the Currency tab's buttons. New <tt>ItemExchangeModel</tt> (<tt>brg.itemExchangeModel</tt>) drives both item sub-tabs off a single net axis (the same shape as the money<->coins coin axis); it reads/writes items across the <strong>bag + PC storage combined</strong> (consuming and adding bag-first, then storage). Backed by new <tt>ItemStorageBox</tt> helpers — <tt>capacityForInd()</tt> / <tt>addAmount()</tt> / <tt>removeAmount()</tt> (stack to 99, spill into new rows, drain and drop emptied rows). Values come from each item's money <strong>buy price</strong> (the price players see in-game). <tt>tst_qml_screens</tt> green. Also added non-visual automation hooks for the debug channel (<tt>marketPage.debugExchangeSub</tt>, <tt>exAddA</tt>/ <tt>exAddB</tt>) so the harness can drive the Exchange sub-tabs without simulating clicks. @subsection autotoc_md2026-07-10----trainer-card-playtime-polish--button-column-alignment 2026-07-10 &ndash; Trainer card Playtime polish + button-column alignment Follow-up polish to the trainer-card Playtime group. The <strong>Enabled / Paused</strong> toggles are now two <strong>separate</strong> rounded buttons with a gap (rather than one connected segmented group, which wrongly implied a mutually-exclusive choice). The <strong>"Playtime" title and the clock digits are centered</strong> (the group title horizontally centered; each digit field's text <tt>AlignHCenter</tt> instead of right-aligned). The <strong>gym-badge grid</strong> was lined up with the Money field's top (both sides of the card now start at the same height). Finally, the field <strong>action buttons were aligned into shared columns with the Playtime group's buttons</strong>: the ID / Money / Coins / Starter dice (and the Money/Coins trash) now sit in the same <strong>random column / delete column</strong> as the Playtime <tt>[dice | trash]</tt>. This was done by nudging the right-hand field column left (Player ID <tt>rightMargin</tt> 92 → 100) so its <tt>[dice | trash]</tt> groups land on the same x as the Playtime group's buttons (whose right edge is the group border minus its padding). Verified against the live screenshot over the debug channel. No save bytes change. <tt>tst_qml_screens</tt> green. @subsection autotoc_md2026-07-10----hot-reload-now-keeps-you-on-the-screen-youre-editing-debug-harness 2026-07-10 &ndash; Hot-reload now keeps you on the screen you're editing (debug harness) Improved the DEBUG-only QML hot-reload so live iteration doesn't fight you. A reload rebuilds the whole QML tree from the root (there's no safe per-file partial reload in a single-<tt>QQuickWidget</tt> app — the engine caches whole compiled components, so a partial swap would risk stale copies of shared components), which previously dumped you back at Home + the New File modal on every save. <tt>MainWindow::reloadQml()</tt> now records the current screen first (reverse-looking-up the top of <tt>Router::stack</tt> to its registered name) and, after the reload re-seats App.qml's startup stack, dismisses the New File modal and navigates back to that screen (<tt>pokemonDetails</tt> re-opens party mon 0). The loaded save was already preserved (it lives in C++, not QML), so you keep your save and stay on the screen with changes applied. Home and modals are intentionally not auto-restored. Verified over the debug TCP channel (<tt>title</tt>→<tt>reload</tt>→<tt>title</tt> returns the same screen). DEBUG-only — release builds are unaffected. See <tt>notes/reference/dev-harness.md</tt>. @subsection autotoc_md2026-07-10----trainer-card-playtime-grouping-box--wider-card-fix-clockart-overlap 2026-07-10 &ndash; Trainer card: "Playtime" grouping box + wider card (fix clock/art overlap) Follow-up to the trainer-card action-groups pass. The playtime controls are now gathered into a light <strong>titled "Playtime" grouping box</strong>: a subtle rounded border wrapping the clock digit fields + the [dice | trash] randomize/clear button on one row, with the <strong>[Enabled | Paused] toggles moved to a second row below</strong> the fields (they were floating above). New <tt>PlaytimeGroup.qml</tt> composes <tt>PlaytimeEdit</tt> (now just the clock digits) + the shared <tt>RandomButton</tt> (re-anchored beside the clock) + <tt>PlaytimeToggles</tt>. The group's right border lines up with the Money/Coins action buttons. <strong>Also widened the card</strong> (SwipeView 500×250 → <strong>600×310</strong>). The previous layout let the playtime clock sit right up against — and slightly overlap — the trainer artwork on the left; the extra width gives the right-hand column and the Playtime group clear separation from the art, and the extra height fits the now-two-row group inside the border. Verified by capturing and <strong>actually inspecting</strong> the headless trainer-card screenshot (the overlap was only caught because Twilight flagged it — manual screenshot review is now a standing default for UI work). No save bytes change (same fields written). <tt>tst_qml_screens</tt> green. @subsection autotoc_md2026-07-10----trainer-card-dice--trash-action-groups-on-moneycoinsplaytime--enabledpaused-toggles 2026-07-10 &ndash; Trainer card: [dice | trash] action groups on Money/Coins/Playtime + Enabled/Paused toggles Brought the trainer card's field actions up to the Pokémon-details "combo" style. The app-wide <tt>RandomButton</tt> gained an optional trailing <strong>clear/delete</strong> segment (<tt>showClear</tt>, <tt>clear()</tt>, <tt>clearTip</tt>) so it can render as a single rounded <strong>[dice | trash]</strong> pill; off by default, so Starter and Player ID stay a lone dice exactly as before. <strong>Money</strong> and <strong>Coins</strong> now use it — the trash clears the value to 0. The <strong>playtime</strong> clock, which previously hid its actions behind a hover ⋮ overflow menu, now shows the same always-visible <strong>[dice | trash]</strong> group (<tt>randomizePlaytime()</tt> / <tt>clearPlaytime()</tt>), matching the other fields. The overflow menu's <strong>Enabled</strong> and <strong>Paused</strong> toggles moved out of the menu and up onto the card as a combined <strong>[Enabled | Paused]</strong> group sitting just above the clock — built on a new shared <tt>SegToggle</tt> (an independent on/off segment in the same visual language as the details <tt>SegSel</tt>/<tt>SegBtn</tt> combos; <tt>active</tt> binds to the underlying bit, <tt>onClicked</tt> flips it) via <tt>PlaytimeToggles</tt>. Enabled mirrors <tt>playtime.clockMaxed</tt> (inverted); Paused mirrors <tt>area.general.countPlaytime</tt> (inverted). The right-hand column was shifted slightly left (Player ID <tt>rightMargin</tt> 65 → 92) and the top spacing tightened so the extra toggle row and the second (trash) segment all sit inside the card border; the whole card was verified against the headless screenshot. No save bytes change — the clear buttons write the same fields (money/coins/playtime) the existing keystroke/randomize paths already write. Frames overflow menu removed. New QML: <tt>SegToggle.qml</tt>, <tt>PlaytimeToggles.qml</tt> (both in <tt>app.qrc</tt>). <tt>tst_qml_screens</tt> green (trainer card loads clean). @subsection autotoc_md2026-07-09----resolved-the-type-2-single-truth-single-types-write-duplicate-of-type1 2026-07-09 &ndash; Resolved the type-2 "single truth": single types write duplicate-of-type1 Settled the last tracked type-2 decision straight from the pokered disassembly. A single-type Pokémon is stored as a <strong>duplicate of type 1</strong> (<tt>data/pokemon/base_stats/*.asm</tt> — e.g. Charmander is <tt>db FIRE, FIRE</tt>), <tt>0xFF</tt> is not a valid type at all (<tt>constants/type_constants.asm</tt> runs <tt>\$00..\$1A</tt>), and <tt>engine/pokemon/add_mon.asm</tt> copies both type bytes verbatim from the base-stats header into party/box data. So the game's — and now the editor's — canonical written form for a single type is duplicate-of-type1. <tt>save()</tt> already emitted that for a non-explicit single type, so on-disk output is unchanged for normal saves; <strong>byte fidelity is untouched</strong> — a save that literally stored <tt>0xFF</tt> (only ever a hacked/glitch save) still round-trips as <tt>0xFF</tt> via <tt>type2Explicit</tt>. The real fix: every editor (re)generation path — <tt>randomize()</tt>, <tt>correctTypes()</tt>, and <tt>update(resetType)</tt> — now clears <tt>type2Explicit</tt>, so a <em>generated or corrected</em> single type serialises as the duplicate rather than a stale <tt>0xFF</tt>. <tt>isCorrected()</tt>'s tolerance (accept either <tt>0xFF</tt> or the duplicate as a valid single type) is now the finalized, intended behaviour — the "temporary dirty patch" framing is gone. New regression test <tt>box_singleTypeCanonicalForm</tt> pins it; the load/save/isCorrected comments and <tt>reference/gen1-knowledge.md</tt> now document the resolved truth. <hr> @subsection autotoc_md2026-07-06----adopted-the-updated-fairyfox-docs-site-chrome--reader-menu 2026-07-06 &ndash; Adopted the updated fairyfox docs-site chrome + reader menu Refreshed the read-only hub clone (0.11.2 → 0.14.4) and adopted the hub's updated <strong>docs-site</strong> standard into the Doxygen theme, mirroring how the sibling <tt>random-ai-prompt</tt> did it (adapted from its JSDoc stack to Doxygen — a faithful hand-reimplementation, the standard's intended model). The standing <tt>adopt-standards-by-default</tt> authorization covered it, so it applied without the report-then-wait pause; full verification still ran (local Doxygen 1.17 build + live in-browser check across dark / light / sepia, API vs prose pages, and the reader). What changed in <tt>docs/fairyfox/</tt>: - <strong>Chrome</strong> — replaced the old brand bar + "Back to Fairy Fox" button with the current mesh chrome: a fixed primary nav (Home · Projects · Games · Docs · Updates · About) with the <strong>hub fox logo</strong> as the way home, and a project subnav (Overview · Notes · Classes · Files · Screenshots · Repository · Notes). <strong>Projects</strong> is the active primary item for a project's docs. Footer redesigned to the standard's brand · Explore · This project columns + bottom bar. - <strong>Reader menu</strong> (<tt>ff-docs.js</tt>, new) — the "Aa" reading-settings panel: theme (Light / Sepia / Dark
    • Auto), accent swatches, text-size slider, line-spacing and width, saved under the shared, origin-wide key fairyfox:reader:b. It also drives doxygen-awesome's light-mode/dark-mode class (the reader is now the single theme authority), measures --ff-header-h, and marks the active subnav item.
  • API-only sidebar — the treeview shows only on the class/file/namespace API pages; Overview, the notes/markdown pages render full-width prose (like the sibling). A guard freezes doxygen_width on prose pages so the hidden sidebar can't corrupt the API-page sidebar width.
  • Theme-conflict fix — the token mapping is now scoped to all three of doxygen-awesome's theme states so our fairyfox tokens win over its html:not(.light-mode) / html.dark-mode dark rules (previously Light mode on a dark OS washed the text out to doxygen-awesome's dark foreground). HTML_COLORSTYLE moved AUTO_DARK → LIGHT so Doxygen stops injecting its own competing dark script. Added the data-theme light + sepia palettes.
  • Self-hosted fonts — Fraunces / Inter / JetBrains Mono woff2 + fonts.css under docs/fairyfox/fonts/ (no Google Fonts; removes the third-party IP exposure). pages.yml no longer injects DoxygenLayout user-tabs — the subnav carries those links.

Follow-up polish (same day), from a visual review in Chrome against the reference site: hid doxygen-awesome's own #titlearea + #main-menu (they duplicated our chrome's nav and, with its fixed layout, produced a second scrollbar); switched to a single-page scroll (content flows; the treeview is a fixed column on API pages, absent on prose pages); and made the reader's accent links theme-aware and vivid (the old formula mixed the accent toward the near-white body text, washing links out to a muddy pastel on dark — now it lightens on dark / darkens on light, keeping saturation). Note: this also hides Doxygen's built-in search box (it lived in #main-menu), matching the reference site's chrome.

Docs-site only — no app code changed, so no VERSION bump; under git-flow this rides to main with the next real release rather than merging on its own. Process report: notes/fairyfox-reports/2026-07-06-adopting-updates.md.