- drop the player-card test (components removed)
- new: '+ слот' grows attunements to 4+ and a new slot binds an item
- new: armor class starts at 16 (mock) and persists through a reload
- PlayerCard removed — it duplicated the sidebar editors; the character
page now shows only prepared spells and attunements
- headings are plain RU: Заготовленные заклинания / Атюнменты
(no EN/RU double labels; h6 no longer lowercased)
- Attunements is Vec<Option<String>> instead of a fixed 3-slot array;
'+ слот' appends a slot, so custom campaigns can track more items
(old saved JSON parses: arrays map to Vec)
- PlayerData.ac (u8, serde default 0): manual armor-class input in the
sidebar HP block (hp/оз, ac/КД), editable like the HP numbers
- ROADMAP: all Phase 2 boxes checked; done-notes (HP block already in
the sidebar; optional Spell fields keep old JSON parseable; rows are
plain functions inside For's let children)
- ARCHITECTURE: character/*.rs in the tree, Character route contents,
gaps list drops the character-page items
- README: character page in 'What works today'
- player card shows the mock character (name, class, level, XP)
- add a spell via a vacant slot, undo with Ctrl+Z, redo with Ctrl+Y
- remove a spell empties its slot back to vacant
- a prepared spell survives a reload (autosave round trip)
- attune an item, reload keeps it, clear empties the slot
- PlayerCard: read-only portrait, name, class, level, XP (the sidebar
holds the editors for this data)
- PreparedSpells: growable slot list; a vacant slot has a free-text
input and '+' to fill it, an occupied slot shows name/level/
description and '×' to empty it; '+ слот' appends a vacant slot
- Attunements: three slots, free-text entry and per-slot clear
- rows are plain functions called from For's let(...) children —
component tags are not allowed directly inside them (view macro)
- Field<...> conversions for the Subfield prop types, state-in-key
keys so changed rows re-render (same convention as sidebar pips)
- SCSS: _character.scss with card, slot rows, buttons (mixins)
- Spell is no longer a name-only stub: optional level and description
(serde skips missing fields, so old saved JSON stays parseable)
- mock: demo prepared spells — two vacant slots, Огненный шар (level 3,
with description) and Магическая броня (level 1)
- ROADMAP: all Phase 1.5 boxes checked; done-notes (clone snapshots,
Copy handle, no Instant on wasm, button enables with the pending burst)
- ARCHITECTURE: undo/redo in the header tree and a paragraph on the
snapshot history; gaps list no longer mentions undo
- README: undo/redo in 'What works today'
- undo/redo via buttons and via Ctrl+Z outside text fields
- focus guard: Ctrl+Z inside the campaign name field leaves the app
history untouched (the browser owns the field's undo)
- an undone state survives a reload (undo writes through autosave)
- Clear is undoable: one undo step restores the wiped dashboard
- App provides HistoryHandle as context (both build modes; stacks stay
empty on the server) and passes it to persist::init_hydrated
- persist: init_hydrated starts undo tracking after the localStorage
load (first captured state = saved state, not mock); clear_all and
apply_import call undo::before_replace so they are undoable
- header: Отменить/Вернуть buttons with disabled states driven by the
reactive stack depths; Ctrl+Z / Ctrl+Shift+Z / Ctrl+Y via a window
keydown listener that skips editable targets (browser owns those)
- SCSS: disabled control buttons dim out
- History: capped undo/redo stacks of whole Dashboard clones; a pending
pre-burst snapshot becomes a real undo step when the tracking effect's
settle timer fires (1 s of quiet) — one undo step per edit burst
- undo/redo consume the pending burst first, so a fresh edit is
undoable before it settles; a new edit clears the redo branch
- suppress flag: wholesale replacements (Clear, import) flush the
pending burst, push the current state, and mark their own write as
non-capturable — Clear and import are undoable themselves
- HistoryHandle is Copy (RwSignal fields): context-friendly (Send+Sync)
and safe to capture in view closures under edition-2024 rules
- no std::time::Instant: it panics on wasm32-unknown-unknown; the
browser timer is the only clock
- 9 unit tests: burst coalescing, redo, cap, flush order, suppress
- effort S (~0.5-1 day): rides on Phase 1 machinery (serde snapshots,
replace_state, the store.track() autosave trigger) — no component
changes needed, page-agnostic
- coalesced snapshots (one per ~1 s window), capped stacks, Ctrl+Z /
Ctrl+Shift+Z with focus guard for text fields
- sequencing note: lands before the edit-heavy Phases 2-7 so their edits
are undoable from day one; nothing depends on it
- Phase 8 e2e item: drop the persistence round trip (already covered by
persistence.spec.ts), add undo shortcuts
- ARCHITECTURE gaps list: note the missing undo/redo
- campaign name survives a reload (autosave debounce respected)
- export/import round trip: Save downloads dashboard.json with the
current state, Clear blanks it via the confirm modal, Load restores it
- specs take BASE_URL env override (default stays localhost:3000)
- both new tests and the three smoke tests pass on chromium
Save downloads dashboard.json, Load opens a file picker and imports
through apply_import with errors reported in the modal, Clear asks for
confirmation then wipes to Dashboard::blank(). App provides the store
and ModalState and mounts the modal; init_hydrated starts autosave.
ModalState carries title/message/action as RwSignals; the action is
read at click time so a late confirm wins. Overlay click and Cancel
close; the confirm button runs the stored action. Edition-2024 note:
method calls on non-Copy captures move by value inside nested move
closures, so handlers touch state.open/state.action fields directly.
- STORAGE_KEY aex.dashboard, 500 ms debounced autosave via store.track()
- deferred load after hydration keeps SSR markup authoritative
- parse_dashboard version-checks the file (the migration hook), errors
are RU messages via ImportError (thiserror)
- clear writes the blank dashboard back so a reload stays blank
- export_download builds a Blob URL and revokes it after the click
- 5 unit tests: round trip, blank, garbage, newer and older versions
Needed for localStorage, FileReader and Blob download in the client.
The features were already enabled transitively by leptos-use; this
makes the dependency explicit.
Store and serde derives coexist on all 23 dashboard types (prototype
confirmed). Settings carries the saved-format version (1) as the
migration hook anchor; Dashboard::blank() is the post-Clear empty
campaign. mock() now builds Settings via Default.
README is now project-specific (features, stack, dev commands, docs).
The Playwright spec checks home/inventories/wiki render and asserts no
console errors.
Remove unused imports (nav, sidebar, state, mock, inventory, item,
numput), the unused qty closure, the unused #![feature] gate, and a
module-scoped allow(dead_code) for the unmounted search stubs.
lazy_static removed from Cargo.toml (the only dependency removal for
now, per decision). ITEMS_REFS/ITEMS_NAMES are now LazyLock statics; the
duplicate foundry.json load is dropped and the no-op HashMap roundtrip
is gone. Cargo.lock is now tracked: the app is a binary and builds
should be reproducible.
Verified dependency reductions (13 unused or replaceable crates) and
idiomatic cleanups for the current codebase. Not applied yet; linked from
ROADMAP phase 0. Toolchain stays on nightly.
Doc comments on all modules, components, entities, helpers and type
aliases; larger comments on the important entities (Dashboard, PlayerData,
Context, App, Numput, item data). No behavior changes.
- leptos 0.8.2 -> 0.8.20, meta/router/actix to latest 0.8.x
- reactive_stores 0.2.5 -> 0.4.3 (matches reactive_graph used by leptos 0.8.20)
- wasm-bindgen =0.2.101 -> =0.2.126, js-sys =0.3.78 -> =0.3.103
Fixes the build with the current nightly (tachys 0.2.18 no longer
needs the re-gated min_adt_const_params feature).