A coinbase/transparent→shielded move (often auto-shield) was drawn with a red
"Sent" label and send arrow yet a green "+X" amount — an internally
contradictory row that reads as "money leaving" to anyone who auto-shielded. It
now renders as a neutral "Shielded" row (shield icon + the shielded accent
colour for label and amount), and since a shield is your own funds going
private — not a spend — it's excluded from the Sent card total and the Sent
filter (it merges into one row visible under "All").
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Adds a "View transactions" action that opens the History tab pre-filtered
to a chosen address. SetTransactionsAddressFilter() stores a pending filter
that RenderTransactionsTab consumes on its next frame — pre-filling the
search box and resetting the type filter to All — then the caller navigates
to NavPage::History. This reuses the existing "prefill + navigate" pattern
(cf. SetSendFromAddress) and all of History's rendering, detail and export.
Triggers:
- Contacts: a context-menu item and a 4th per-row action icon (copy /
view-transactions / edit / delete; the trailing width is widened to fit).
- Balance address list: a per-address context-menu item (own z/t addresses).
- Receive tab: the recent-received rows now carry their address into the
filter instead of jumping to History unfiltered.
The History search also now matches from_address, so filtering by one of your
own addresses catches sends FROM it, not only receives INTO it.
Scoping is honest, not exhaustive: transparent is well covered, but shielded
counterparties are private and the transparent sender of received funds is
never recorded on-chain, so this shows transactions *involving* the address.
On Lite, from_address is thin, so own-address send filtering is weaker.
New i18n keys view_transactions / tt_view_transactions translated into all
eight locales (no new glyphs). Builds + test suites pass on both variants.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The inline detail panel was tracked by the filtered list index. The
display list is rebuilt and re-sorted on every data change, sort switch
or filter edit, so a stored index pointed at a different transaction
after the rebuild, and a page reset was triggered by any change in the
row count, bouncing the view back to page 1.
- The open row is now tracked by a stable content key (txid, display
type, address and the amount's exact bits) so the right row stays open
across rebuilds.
- The page resets and the open row collapses only when a filter input
changes (type, sort or the full search text). A new or removed row is
data and now just clamps the current page.
- The open row is also cleared on page navigation and when the list
empties, so a wallet switch does not resurrect an old expansion.
Adds pure helpers rowKey/filterInputsChanged to
transactions_tab_helpers.h.
Tests: rowKey distinguishes each field and separates +0.0 from -0.0, and
filterInputsChanged latches each input change but not the first frame.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The History tab built a txid-to-indices map twice when its inputs
changed, once for the summary-card totals and once for the display list,
each a full pass over every transaction with string-keyed hashing.
- The rows are now grouped by txid a single time whenever the
transaction list changes, into an index-only structure (first/next
chains with no string copies), and both the summary pass and the
display pass reuse it. A sort-combo change re-sorts without regrouping.
Adds a pure, unit-testable groupByTxid helper in
transactions_tab_helpers.h.
Tests: the chains match a reference map over a random id stream, and the
empty and single-row cases.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Every frame the History tab:
- hashed the txid, amount, confirmations, time, type and address of
all N transactions (about 0.6-5 ms at 5k-20k) to decide whether its
totals and its merged, sorted row list were stale;
- copied every chat txid into a fresh set;
- ran the type and search filter over all N rows, with up to three
case-insensitive substring searches each.
The totals and the row list now key on tx_revision (the row list also
on the sort mode), the Message badge and the Chat filter use the chat
store's txid index, and the filtered rows are rebuilt only when the row
list, the type filter, the search text or, under the Chat filter, the
chat store changes. A frame is now a few compares plus one txid lookup
per row of the current page. An unchanged full-node refresh no longer
rebuilds the totals and the row list either: the old fingerprint folded
in the refresh time.
On lite, a change to only a memo or a sender address now shows at once.
The old fingerprint left those fields out and relied on a refresh time
that lite never sets, so such a row stayed stale until something else
in the list changed, such as the next block.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Immediate-mode render functions re-run every frame; these rebuilt O(N)
state each time even when nothing changed. From a 6-lens perf audit, each
finding verified on a hot-path basis:
- Console: ConsoleModel gains revision(); the full-model filter scan and the
glyph-by-glyph text-layout pass (BuildConsoleLayout) rebuild only when the
model / filter / wrap-width / zoom change — previously it re-shaped up to
10,000 lines every frame even when idle/scrolled. clear() force-invalidates
the memo mid-render (no OOB on the just-emptied visible set).
- Transactions: the summary-card totals memoize behind the tab's existing
FNV-1a fingerprint (also folds away a now-duplicate O(N) display-key pass).
- Send / Receive recent lists: early-exit the prefix scan (state.transactions
is kept newest-first) instead of filtering the whole tx history every frame.
- network_refresh_service: O(new x total) txid find-and-replace -> hash map.
- Sidebar unconfirmed-tx badge cached on last_tx_update + tx count; the
daemon-memory probe (/proc scan on Linux, popen on macOS) throttled to ~1.5s.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Bundles the session's UI work (the touched files carry several of these
changes together, so they are committed as one coherent UI batch):
- FAQ: new RenderFaqDialog + data-driven faq_content, opened from a
status-bar "?" (and the Windows title bar), styled like the Wallets
modal with search, Wallet/Daemon tabs, and smooth scroll.
- DPI/font-scale audit: multiply hand-drawn absolute geometry by
Layout::dpiScale() across ~30 files so nothing renders native-size at
HiDPI / font_scale 1.5 (verified with a full sweep at 1.5x).
- Mining: chart now fills the horizontal space; thread stepper +/- buttons
match the input-box height; move the stratum-host toggle into
Node & Security (v1.3.0+).
- Settings: fix the auto-shield status text overlapping the grid.
- Sidebar: drop the peer-count badge on the Network button.
- i18n: wrap 193 hardcoded literals with TR() (keys/translations added in
the preceding i18n commit), so the security/PIN/lock flow, seed-backup
wizard, and witness-rebuild dialog localize.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Addresses the cross-screen inconsistencies from the layout audit by routing screens onto
the design system's own (previously under-used) shared helpers:
- Dialog footers: migrate ~9 overlay dialogs off hand-rolled placement onto the shared
helpers — DialogActionFooter (primary+Close), DialogConfirmFooter, or
BeginOverlayDialogFooter for custom/multi-button rows — so footers share one centered
treatment. All footer/action buttons now use TactileButton (glass press) instead of the
bare StyledButton some dialogs used.
- Empty states: add a shared material::DrawEmptyState(icon, title, hint) (centered icon +
title + wrapped hint) and adopt it in Peers, Transactions, and Market-portfolio, which
previously showed a bare left-aligned caption.
- Security dialogs: add the missing Cancel to Change Passphrase and Set PIN so the whole
security family shares a two-button footer (Cancel dismisses without applying).
- Transactions pager: shared TactileButton helpers (matching Explorer).
- Frosted-pane rounding: Contacts/Chat use Layout::glassRounding() instead of hardcoded
12/10/8px literals, matching Peers.
- "Set Label..." title loses its stray trailing ellipsis.
Preserves every button's label and action; the transfer footer's order becomes
[Confirm][Cancel] to match the shared helper's primary-first convention. Verified at 1280
across full-node + Lite + Windows (ctest green) and an adversarial diff review (clean).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The tail of the DPI/font-scale/responsiveness audit — ~26 remaining findings.
Container / recent-list (Theme-1 leftovers):
- Send: drop NoScrollbar|NoScrollWithMouse on ##SendFormScroll so Recent Sends is
reachable at font_scale 1.5 (parity with receive).
- Receive: cap the QR/form card via std::min(mainCardTargetH, availH - recentReserve)
so RECENT RECEIVED stays on-screen (identity at 1.0x).
- Wallets dialog: size the capped-mode list to whole rows so it no longer clips a
partial row / crowds "Create a new wallet".
Narrow-width (1024px) reflow:
- Console toolbar reserves space for ALL trailing controls (both icon toggles + zoom
buttons) so the +/- zoom no longer runs off-window.
- History sort combo sized to its measured widest localized label ("Newest first").
- Settings Theme/Layout/Language row: scale the wide→stacked breakpoint by dpiScale so
it drops to full-width stacked combos at 1.5x (Consolidated Card no longer clips).
- Recent-tx type label: derive the address column X from the measured label width so it
can't collide at narrow widths.
- Mining Recent Pool Payouts: floor the panel height to fit the empty-state caption.
Cosmetic ×dpiScale() on absolute geometry (no-ops at 1.0x): mining SOLO|POOL toggle &
idle combos, market pair-chips, password/PIN strength bars, receive/send currency
toggles, explorer search bar/rows/rounding, About-card logo, chat empty-state wrap,
recent-list address/time offsets, address-toolbar & two-row action buttons, console
line-gap/status-dot/pane rounding.
Verified at font_scale 1.5 and at 1024px across full-node + Lite + Windows (ctest
green) and an adversarial diff review (one over-reserve regression fixed).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Transactions that carried a chat message (sent or received, messages + contact
requests) now show a "Message" badge in the History tab, and a new "Chat" option
in the type-filter combo shows only those transactions.
Detection reuses what the chat store already tracks: ChatStore gains an inline
chatTxids() returning the set of on-chain txids that carried a chat message; the
tab builds it once per frame (cheap — O(chat messages)) and tests txid membership
for the badge and the filter. Empty when chat is disabled or the wallet has no
chat identity. Both variants populate the chat store before this tab renders, so
the same code serves full-node and lite with no separate handling.
The "Message" badge reuses the stacked top-pill slot (chat txs are send/receive,
not the autoshield "shield" type, so it and the "Shielded" badge are mutually
exclusive; chat takes precedence) in a distinct Secondary() colour. Guards the
summary-card accent idx_map so the new filter value (4) can't index it OOB.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add util::formatClockDateTime/formatClockTime driven by a process-wide flag (setClock12h,
synced from the time_format setting each frame). Switch the primary user-facing timestamp
displays to it — the transaction list, wallet-state tx + banned-peer times, the explorer
block time, and the block-info dialog — so one preference drives every clock.
Log files, export filenames/content, console line prefixes, the market chart axis, and the
worker-thread "last updated" string intentionally stay fixed 24h.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Address the high-severity, systemic legibility issues found in the per-skin
screenshot review (verified across all 9 skins):
- Status pills (history): "Confirmed" text was 55%-alpha (dim); make it full
opacity, and give status/shield pills a visible tinted fill (a48) + 1px border
(a90) so they stop vanishing on dark-red / near-white / gradient skins.
- Console light theming: light skins now use a near-white terminal surface, and
the log text/JsonBrace colors switch to the dark defaults in light themes (the
schema colors are dark-terminal-authored — honor them only in dark themes) so
text isn't pale-on-white.
- Dark-skin muted text: bump --on-surface-medium 0.85 / --on-surface-disabled
0.58 on dragonx/dark/color-pop-dark/obsidian (obsidian addresses were ~1.1:1).
- Disabled Send button: the disabled fill was hardcoded white (invisible on
light skins) — use a dark overlay in light themes.
- Light-skin inputs: stronger frame fill + border color and a 1px frame border
for light themes so fields (and buttons) have defined boundaries on white/
pastel surfaces.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Three UI de-duplications (rendering changes; the test suite skips the GUI, so
these need a manual screenshot check of the Balance/Peers/Transactions tabs).
1. Classic balance layout — the DEFAULT layout — hand-rolled its own ~550-line
address-list + recent-tx renderers that had DIVERGED from the shared
RenderSharedAddressList/RenderSharedRecentTx used by the other 9 layouts.
Deleted the inline copies and called the shared renderers (as the minimal
layout already does), so the default layout now gains set-label +
add-to-portfolio context items, custom address icons, drag-to-reorder,
keyboard nav, copy-flash, and fully TR()'d strings. Hero row and Classic
sizing (tabs.balance.classic address-table-height=340) preserved; addrH is
computed before the call exactly as before. This is a deliberate
feature-parity behavior change for the default layout.
2. peers_tab info cards — extracted drawStatCell() (label + value/em-dash with
the shared offset math) and one drawCardDivider() replacing two duplicate
divider lambdas; plain cells drive off a per-card loop. Bespoke cells
(Blocks "(X left)", copy-on-click Best Block, TLS check-icon) kept inline.
3. transactions_tab — one drawSummaryCard() replaces the three near-identical
Received/Sent/Mined blocks (same glass panel, icon, hover outline,
click-to-filter). Byte-equivalent.
Full-node + Lite build clean (net -651 lines, no new warnings); ctest 1/1;
hygiene clean. NEEDS SCREENSHOT VERIFICATION.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Mechanical, behavior-preserving consolidations from the audit. Rendering
extractions were kept byte-equivalent; sites that couldn't be made identical
were left as-is (noted below).
Shared helpers:
- util::truncateMiddle(s,maxLen) / (s,front,back) in text_format.h — replaces
6 file-local middle-ellipsis truncators + several inline substr sites
(send/receive/transactions/balance_recent_tx/explorer + app.cpp + 3 dialogs).
Carries the maxLen<=3 guard, fixing the latent unsigned-underflow copies.
- material::LoadingDots() in draw_helpers.h — one animated-ellipsis source for
8 copy-pasted spinner sites (identical GetTime()*3 phase preserved).
- Reuse the existing FormatHashrate() in explorer_tab + peers_tab (dropped two
inline hashrate ladders).
- material::DrawButtonGlassOverlay() — the glass-fill/rim/tactile overlay block
shared by TactileButton / TactileSmallButton / schema TactileButton.
- material::CollapsibleHeader() — the invisible-button + label + chevron idiom
(3 of 5 settings_page sites; RPC/Debug headers skipped — non-identical).
- material::GlassCardScope (RAII) — the ChannelsSplit/Indent/DrawGlassPanel/
ChannelsMerge card scaffold (5 settings_page cards; About/send/receive skipped
— different padding / logo interleaving).
- material::DialogWarningHeader()/DialogConfirmFooter() — warning header +
50/50 Cancel/danger footer across the confirm dialogs; button height moved
from a hardcoded 40px into ui.toml (components.overlay-dialog.confirm-btn-height).
- File-local enterLowSpec()/exitLowSpec() collapse the 3 low-spec snapshot copies.
i18n: wrapped hardcoded English in the shared balance render paths and the
whole Lite lifecycle/security section in TR(), with English defaults added to
loadBuiltinEnglish() (res/lang/*.json left for the translation tooling — TR
falls back to English, so output is unchanged).
Full-node + Lite build clean; ctest 1/1; hygiene clean. These are rendering
changes — the Settings page (cards/headers/confirm dialogs), all tactile
buttons, and the Lite settings section warrant a screenshot check.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add material::Tooltip / BeginTooltip / EndTooltip wrappers (tooltip_style.h)
that scope a small window padding (8x4, dpi-scaled) and ~85% opacity to
tooltips only, then route the tooltip call sites through them. Menus and combo
dropdowns are untouched (they keep the global opaque PopupBg).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a sort selector next to the type filter with four modes: Newest first
(default), Oldest first, Largest amount, Smallest amount. The mode folds into
the merged-list memoization cache key (so the list re-sorts only when the mode
changes) and the comparator branches on it, keeping txid as a deterministic
tiebreak. Changing the sort resets to page 1.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The Sent summary card showed 0 while selecting the Sent filter listed
transactions. The card counted only plain "send" rows and deliberately excluded
both legs of an autoshield pair as an "internal move", but the list shows the
merged "shield" row under the Sent filter. With only shielding transactions and
no plain sends, the card read 0 against a non-empty Sent list.
Count each shield pair toward the Sent card (with the shielded receive-leg
amount, which is what the merged row displays), so the card and the filter agree.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
History looked unsorted because every merged "shield" row carried
confirmations=0, and the list sorts 0-conf (pending) transactions to the very
top. So long-confirmed shielding transactions floated above newer ones — and
when the type filter was switched off "All" they vanished (shield rows only
match the "Sent" filter), which read as "transactions disappear when sorting".
Root cause: the autoshield merge set the row's confirmations to
min(send, recv). Both legs are the SAME transaction (one real confirmation
count), but the send leg (parsed from z_viewtransaction) routinely arrives with
confirmations=0, so min() picked 0. Use max() to take the populated value.
Also give the sort a txid tiebreak so same-block transactions keep a stable
order instead of reshuffling every time a new block bumps confirmations.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Per-frame hot paths in the immediate-mode UI were allocating needlessly:
- Address filtering in the balance tab rebuilt a std::string filter per address AND
containsIgnoreCase() lower-cased two fresh copies per call — ~6×N allocations/frame
on large wallets. New util::containsIgnoreCase(string_view, string_view) is
allocation-free, and the filter is now built once outside the loop.
- Four duplicated "time ago" implementations (balance_tab_helpers, balance_recent_tx,
send_tab, transactions_tab) are consolidated into util::formatTimeAgo (localized long
form) + util::formatTimeAgoShort (compact "5s ago"), preserving each call site's exact
display style. Both use snprintf, no per-row string concatenation.
- The send-tab address-suggestion scan (a walk over the whole tx list) is memoized on the
typed text + tx count, so it no longer recomputes every frame while the user pauses.
New src/util/text_format.{h,cpp}; the two existing containsIgnoreCase/timeAgo definitions
now delegate to it. Added to both the app and test targets (test target also gains i18n.cpp,
which text_format's localized path needs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The History tab rebuilt its entire display list on every render frame: indexing all
transactions by txid, merging autoshield send+receive pairs into "shield" rows, and
std::sort-ing the result — O(N log N) plus several heap allocations at ~60fps, only to
show one 50-row page. The data is already sorted newest-first by the refresh service,
so the per-frame sort was redundant on top.
Memoize the merged+sorted list, rebuilding only when the underlying transactions
actually change. The cache key is a cheap, allocation-free FNV-1a fingerprint over the
display-relevant fields (count, last update time, and each tx's confirmations /
timestamp / type+address first char) — a new block bumps every confirmation so the key
changes and we rebuild; otherwise (the common read/scroll case) the cache is reused.
Filtering, search, and pagination still run per-frame over the cached list (cheap linear
scans that depend on interactive state).
Also document that App::shouldRefreshTransactions() is block-height/dirty driven (not
interval-gated) — the Transactions timer only paces the check; the recent-poll handles
between-block mempool/unconfirmed deltas.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Balance card hashrate now uses the shared FormatHashrate() (TH/GH/MH/KH/H)
instead of a bespoke two-tier KH/s formatter.
- Recent-tx rows show the full untruncated address on hover — two z-addresses can
truncate to the same first/last window — and the truncate helpers guard maxLen<=3.
- Remove the unused viewTop/viewBot "viewport culling" locals in the tx list
(pagination already bounds per-frame work).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Market chart now plots the real accumulated price_history instead of a
rand()-generated curve; the hover tooltip no longer claims a specific "Xh ago"
price and the x-axis only labels the truthful "Now" point. Falls back to the
existing empty state until there are >=2 real samples.
- Transactions summary cards exclude autoshield legs (same txid send + receive-to-z)
so a shield isn't double-counted into both Sent and Received, matching the list.
- Send/Receive sync banners use verification_progress like every other surface,
instead of the blocks/headers ratio that over-reports during early sync.
- Fix printf format/type mismatches: %.0f<-int (market % shielded), %d<-size_t
(peer counts), %ld<-int64_t (peer byte counters, wrong on Windows).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
In lite builds there is no daemon, and isConnected() now tracks the lite wallet,
so the full-node "not connected / waiting for daemon" wording was misleading when
no wallet is open. Add two strings (lite_no_wallet, lite_no_wallet_short; English
built-ins, so other languages fall back until translated) and use them in lite:
- receive/send address preview + receive empty-state overlay + send "can't send"
tooltip + transactions empty state -> "No wallet open [— create or open one in
Settings]" instead of daemon wording.
- Status bar: the red indicator shows "No wallet open" (not "Disconnected") in
lite; the P2P peer count is skipped (lite has no peers); and the redundant
full-node connection-detail line is suppressed (connection_status_ set to
"Connected"/"" from the lite wallet state).
Full-node wording unchanged (all gated on isLiteBuild()). Build + run clean
(no RPC noise), tests pass.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add an encrypted SQLite transaction history cache with cached tip metadata and
per-address shielded scan progress so startup and full refreshes avoid
re-scanning every z-address while still invalidating on wallet/address/rescan
changes.
Improve wallet history loading by paging transparent transactions, preserving
cached shielded and sent rows, keeping recent/unconfirmed activity visible, and
classifying mining-address receives. Show z_sendmany opid sends immediately in
History and Overview, pin pending rows through refreshes, and apply optimistic
address/balance debits until opids resolve.
Add timestamped RPC console tracing by source/method without logging params or
results, reduce redundant refresh/RPC calls, and cache Explorer recent block
summaries in SQLite.
Expand focused tests for transaction cache encryption, scan-progress
persistence/invalidation, history preservation, operation-status parsing,
pending send visibility, and Explorer/RPC refresh behavior.
- Replace all hardcoded English strings with TR() translation keys across
every tab, dialog, and component (~20 UI files)
- Expand all 8 language files (de, es, fr, ja, ko, pt, ru, zh) with
complete translations (~37k lines added)
- Improve i18n loader with exe-relative path fallback and English base
fallback for missing keys
- Add pool-side hashrate polling via pool stats API in xmrig_manager
- Introduce Layout::beginFrame() per-frame caching and refresh balance
layout config only on schema generation change
- Offload daemon output parsing to worker thread
- Add CJK subset fallback font for Chinese/Japanese/Korean glyphs
Windows identity:
- Add VERSIONINFO resource (.rc) with ObsidianDragon file description
- Embed application manifest for DPI awareness and shell identity
- Patch libwinpthread/libpthread to remove competing VERSIONINFO
- Set AppUserModelID and HWND property store to override Task Manager cache
- Link patched pthread libs to eliminate "POSIX WinThreads" description
Address creation (+New button):
- Move z_getnewaddress/getnewaddress off UI thread to async worker
- Inject new address into state immediately for instant UI selection
- Trigger background refresh for balance updates
Mining tab:
- Add pool mining dropdown with saved URLs/workers and bookmarks
- Add solo mining log panel from daemon output with chart/log toggle
- Fix toggle button cursor (render after InputTextMultiline)
- Auto-restart miner on pool config change
- Migrate default pool URL to include stratum port
Transactions:
- Sort pending (0-conf) transactions to top of history
- Fall back to timereceived when timestamp is missing
Shutdown:
- Replace blocking sleep_for calls with 100ms polling loops
- Check shutting_down_ flag throughout daemon restart/bootstrap flows
- Reduce daemon stop timeout from 30s to 10s
Other:
- Fix market chart fill artifact (single concave polygon vs per-segment quads)
- Add bootstrap checksum verification state display
- Rename daemon client identifier to ObsidianDragon
Full-node GUI wallet for DragonX cryptocurrency.
Built with Dear ImGui, SDL3, and OpenGL3/DX11.
Features:
- Send/receive shielded and transparent transactions
- Autoshield with merged transaction display
- Built-in CPU mining (xmrig)
- Peer management and network monitoring
- Wallet encryption with PIN lock
- QR code generation for receive addresses
- Transaction history with pagination
- Console for direct RPC commands
- Cross-platform (Linux, Windows)