- 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
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
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.
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.
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.