Commit Graph

18 Commits

Author SHA1 Message Date
54a14485a5 feat(wallets): show created date + sort the wallet list
Parse the earliest keymeta nCreateTime out of the wallet.dat btree (the daemon's
"wallet birthday") and surface it in each row's metadata line ("created Aug 2025").

Add a sort control above the list — Date created / Address count / Transaction
count / Wallet size, with an ascending/descending toggle (descending default:
newest / most / largest first). Sorting reorders a display-index array
(s_order) rather than s_rows, so the async, index-aligned probe batch is
untouched; sort keys read a single frame-consistent snapshot of the probe
results. Address count prefers the authoritative cached index value, else the
btree key count.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:37:36 -05:00
c4f69b5a85 fix(wallets): probe synchronously during the offline screenshot sweep
The async probe fills badges/counts a frame after the dialog opens, which the
single-frame screenshot sweep captured before it landed (badges/counts missing).
Run the probe synchronously when App::isScreenshotSweeping(), so the captured
modal-wallets frame is populated; interactive use keeps the detached-thread path.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 16:03:32 -05:00
171b705429 feat(wallets): smooth wheel scrolling for the wallet list
Adopt the shared ApplySmoothScroll helper (with NoScrollWithMouse) for the
##walletList child, matching the Settings page and the other scroll areas, so
the wallet list eases on wheel input instead of jumping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 15:51:55 -05:00
175950a2ec feat(wallets): exact address/tx counts via a minimal BDB btree parser
parseWalletBtree() walks the wallet.dat Berkeley DB btree directly (no libdb, no
daemon): validate the metapage magic, follow the "main" sub-database (its pgno is
stored big-endian in the master map), traverse internal/leaf pages, and tally
records by their length-prefixed type name — transparent + shielded spendable
keys, address-book, and tx count, plus exact encryption/seed flags. Every offset
is bounds-checked, pages are deduped at push time (stack stays O(npages)), and a
visited-set + page/key caps make it safe on a corrupt/adversarial imported file;
files using DB_CHKSUM/DB_ENCRYPT (which shift the page layout) are rejected so the
byte-scan fallback runs instead.

The async probe now uses this as the primary path (exact badges + counts), falling
back to the byte-scan only when the btree can't be fully parsed. The wallets list
shows "N keys · M txs" for probed rows (labeled "keys", not "addresses", since the
count includes change keys the daemon's address list omits). Validated read-only
against real wallets (counts cross-checked; enc/seed match the byte-scan) and a
hand-built minimal btree fixture in the unit suite.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 13:20:35 -05:00
36efe99a0f feat(wallets): vertical label+icon badge stack + async probing
Redesign the wallet.dat status badges as a right-aligned vertical stack of
"label icon" rows ("Seed phrase 🌱" over "Encrypted 🔒", "Legacy", or "Unknown"),
each with a hover tooltip; name/metadata reserve the stack width and truncate
before it.

Move the wallet.dat probing off the UI thread: scan() now builds the row list
synchronously (filename/size only) and hands the file reads to a detached
background thread whose results land in a shared, index-aligned batch that
render() reads under a mutex. A re-scan supersedes the old batch (cancel + swap);
the thread touches only its own heap batch (never s_rows/statics), so it's safe
across re-scans and shutdown. The dialog opens instantly even with a large
active wallet; badges fill in over the next few frames.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:47:27 -05:00
112c581247 feat(wallets): recursive subdir scan + wallet.dat encryption/seed badges
Search user-added wallet folders RECURSIVELY (bounded: depth/hit/visit caps,
skip_permission_denied, no symlink-follow, node-junk + node-subtree pruning);
the datadir stays top-level. r.dir is each file's real parent so Open/import
resolve subdir wallets. Auto-suffix the import destination on a name collision.

Add util/wallet_file_probe.h: read encryption/seed/shielded flags straight off
a wallet.dat WITHOUT loading it — validate the Berkeley DB btree magic, then a
bounded streaming byte-scan for the length-prefixed record markers the daemon
writes (mkey -> encrypted; hdseed/hdchain -> seed/HD; zkey/sapzkey -> shielded).
Reads no key material. The Wallets dialog shows Encrypted / Seed / Legacy badges,
plus an Unknown badge when a large file couldn't be fully scanned (so absence of
a lock never falsely reads as unencrypted). Unit-tested + validated read-only
against real wallets.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:32:59 -05:00
ed4d61c364 fix(wallets): folder picker opens in the DRAGONX data directory
"Scan another folder" now anchors the picker at getDragonXDataDir() (where the
datadir wallets live) instead of home, so the user starts from a familiar spot
and navigates up/out to find wallets elsewhere.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:53:18 -05:00
3df516be70 fix(wallets): cap card at viewport + flex list so many wallets never clip
The wallets dialog hand-computed a content-sized card height. With many wallets
on a small / HiDPI (150%) window the computed height could exceed the viewport,
and the framework clips a too-tall card — footer and all.

Cap the card at 0.86 of the viewport height. When capped, the wallet list flexes
to the space left above the create/scan/footer controls and scrolls internally,
so those controls stay pinned and visible; uncapped (the common case, few
wallets) nothing changes and there's no spurious scrollbar. Add a
modal-wallets-many sweep surface (8 wallets, self-cleaning teardown) to exercise
the capped-scroll path. Verified at 100% + 150%.

This is the one real fixed-height clip risk from the modal audit; the other 44
modals use auto-height (grow-to-fit, capped at a viewport ratio) and were
confirmed non-clipping — e.g. the 24-word seed-backup grid renders fully at 150%
in a 720px window.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:47:59 -05:00
06c8b99bce feat(wallets): in-app folder picker + roomier, full-width list controls
Wallets dialog refinements:
- More space between wallet cards (cardGap: spacingSm -> spacingMd).
- "Scan another folder" is now always visible as a full-width button (no
  expandable toggle) that opens the new picker.
- The Create-wallet row and the scan button span the full list width, so their
  right edges align with the wallet cards / Open|Import buttons.

New in-app folder picker (folder_picker.h): a Material-styled modal replacing
the raw path input. Browse the filesystem (Up / Home / click a sub-folder),
see the *.dat wallet files present + a count, and pick a folder to scan. The
picker takes over the modal surface while open (the overlay framework doesn't
nest); it remembers the last browsed location across opens. Fixed-height card
(no auto-height feedback), UTF-8-aware truncation, full-node icons confirmed
in the embedded MaterialIcons range. Added a modal-folder-picker sweep surface;
verified at 100% + 150%, dark + light.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 14:11:39 -05:00
5eff3adc3c fix(wallets): roomier cards, no spurious scrollbar
Two issues on the card list: it showed a scrollbar (and clipped the last card)
with only a few wallets, and the cards felt cramped.

- Scrollbar: the per-card advance added a trailing gap after EVERY card (incl.
  the last) and ImGui inserted its default ItemSpacing between the Dummy items,
  so the content ran ~1 gap taller than the child's height budget -> overflow ->
  scrollbar + clipped card. Zero ItemSpacing inside the list child, register each
  card's footprint with a Dummy, and gap only BETWEEN cards (no trailing gap), so
  the content matches the budget exactly. Sized the list to fit up to 7 cards
  before it scrolls.
- Roominess: bigger name (subtitle1), more vertical padding (spacingMd), and a
  touch more space after the icon.
- Removed the full-card InvisibleButton (hover is IsMouseHoveringRect again): its
  overlap with the action button tripped ImGui's ConfigDebugHighlightIdConflicts,
  drawing a red debug border around the list at 150% DPI.

Verified via the headless sweep at 100% and font_scale 1.5 — no scrollbar, no red
border, cards fully visible with breathing room.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:50:19 -05:00
e010822a74 feat(wallets): card-based wallet list — cleaner Material look, no table
Replace the wallet table with a stack of Material-style cards inside a scrolling
child. Each wallet is a rounded card: a leading wallet icon, the filename as the
primary line, and a muted metadata subtitle (size · N addresses · balance DRGX ·
last opened). The action sits on the right — Open / Import buttons, or a green
"Active" chip for the current wallet. The current wallet's card gets a green tint,
border, and icon; external files show an info icon + tooltip; cards highlight on
hover. The dialog stays content-sized + fixed-height (computed from the wallet
count, up to 6 before it scrolls), so the monitor-drag stability is unaffected.

Adversarially reviewed (two-lens workflow + synthesis): the reviewers' DPI
unit-mixing and broken-scroll findings were false positives (LegacySize is the
DPI-scaled atlas size, and BeginChild takes physical px while the framework's
cardHeight takes logical) — the only real issue was byte-wise truncation that
could split a multibyte UTF-8 char, now fixed to pop whole code points. Verified
via the headless sweep at 100%, 150%, and light skin.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 02:25:12 -05:00
b2882095bb feat(wallets): redesign the modal layout — content-sized, framed, aligned
Act on the design review of the Wallets modal (all six points):

1. Content-sized card — size the card to the wallet count (up to 8 rows, then the
   list scrolls) instead of a fixed 620px, killing the huge empty void a short
   list floated in. Computed from the KNOWN row count, so it stays a fixed-height
   dialog (no auto-height feedback — that's what slid content off-screen on a
   monitor move).
2. Framed wallet list — BordersOuter + PadOuterX so the list reads as a contained
   panel with room to grow, not loose rows on the backdrop.
3. Right-aligned numeric columns (Size / Addresses / Balance) and their headers,
   via a custom header row + a right-aligned cell helper, so the numbers line up.
4. Consistent action column — the active wallet's row gets a subtle green tint and
   an "Active" chip (badge), distinct from the Open/Import buttons.
5. De-cluttered bottom — "Create wallet" stays the primary action; the folder-scan
   is tucked behind a "+ Scan another folder…" toggle.
6. Polish — the external-file marker is now a real info icon + tooltip (was
   ICON_MD_FOLDER rendering as a "?" tofu, since that glyph isn't in the subset);
   Refresh is an icon-only button, de-emphasized next to Close.

New i18n keys (wallets_add_folder_toggle, wallets_external_tt) + 8 langs; CJK
subset rebuilt. Verified via the headless sweep at 100% and font_scale 1.5.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 01:58:27 -05:00
13ff20d791 fix(ui): console-commands modal slide + wallets footer clipping
Two follow-ups to the modal float work:

- Console RPC command reference (renderCommandsPopup) had the SAME auto-height
  feedback bug as Wallets: BlurFloat with no cardHeight, and its fill-height
  command list read GetContentRegionAvail() inside the AutoResizeY child ->
  self-referential, sliding content off-screen after a monitor move. Give it a
  FIXED viewport-relative cardHeight (fills ~84% of the viewport). Also removed
  its duplicate body H6 heading (the framework h6 title already shows it) —
  missed earlier because it's spec-form, not the positional overload.

- Wallets footer clipping: after the fixed-height conversion the content child
  clips overflow (NoScrollbar), and the hardcoded 130*dp reserve below the table
  was too small for the create/add-folder rows + separator + footer (~160*dp),
  so the buttons clipped — worse at 150%. Compute the reserve from real widget
  metrics (GetFrameHeightWithSpacing + caption height) so it holds at any DPI.

Added a modal-console-commands sweep surface (debug-gated ConsoleTab setter) so
the popup is captured. Verified both at 100% and font_scale 1.5 (150%): footers
on-screen, single headings, lists fill, nothing clipped.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 22:40:26 -05:00
d8cc1e9ee6 fix(wallets): stop the modal bottom section sliding off-screen after a monitor move
The Wallets modal used the auto-height BeginOverlayDialog overload, whose content
child is AutoResizeY. It sized its table with `tableH = GetContentRegionAvail().y
- 130*dp` INSIDE that child — but on an AutoResizeY child GetContentRegionAvail().y
derives from the child's Size.y, carried over from the previous frame's content
height. That makes tableH self-referential: a fixed point on one monitor, but a
monitor move jumps dpiScale() (the 130*dp reserve + table column/row sizes switch
to new units while the child's Size.y is still old units), knocking it off
equilibrium. With only a lower clamp and no upper bound, the excursion runs
unbounded and pushes the create/folder/footer rows off the bottom.

Give the dialog a FIXED cardHeight (620) via the spec form — the pattern Manage
Portfolio already uses. The content child is then fixed-height (not AutoResizeY),
so GetContentRegionAvail().y is a stable, DPI-current "space left in the card",
tableH stops feeding back, and the fixed-height path already clamps the card to
the viewport (min(cardHeight*dp, vp_size.y-32)). Root-caused via a multi-agent
audit against the ImGui internals.

Verified via the headless sweep at 100% and font_scale 1.5 (150%): the table
absorbs the slack and the create/folder inputs + footer stay on-screen.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 22:17:44 -05:00
e7a3f90102 feat(ui): float all modals card-less like Manage Portfolio
Drop the glass card + boxed title bar from every dialog so content floats
directly on the blur backdrop under a plain h6 heading — the Manage-Portfolio
look — unifying the whole modal system on one visual language (reverses the
earlier card-for-small / float-for-large split).

- Framework: the positional BeginOverlayDialog overload (used by all ~34 card
  dialogs) now defaults to floatingContent + plainHeading (draw_helpers.h). The
  9 spec-form BlurFloat dialogs are unchanged; any dialog can still opt back to a
  card via an explicit OverlayStyle::GlassCard spec. Authored widths are kept
  (floating never resizes), so width-coupled layouts are unaffected.
- Dedup: the framework h6 title made each dialog's own repeated title heading
  redundant. Removed the duplicate body heading from Import/Export key, Backup,
  daemon update, seed backup, seed migration, Defender-blocked (app.cpp),
  Wallets, and the send-confirm popup.
- Close paths: dropping the title-bar close X is covered — every dialog has a
  footer button and/or the framework's outside-click dismiss (all pass p_open);
  send-confirm keeps Esc + Confirm/Cancel.

Verified via the headless full UI sweep on dark + light: 17 modal surfaces float
card-less with a single clean heading, nothing clipped, close controls present.
Build clean, ctest passes.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 21:57:04 -05:00
5db7941fbc feat(market): portfolio row-style redesign + per-wallet scope
Replace the drag/resize dashboard grid on the Market tab's portfolio with
three full-width row styles — compact (single-line), detailed (two-line),
and value-hero — cycled with Left/Right arrows like the Overview layouts.
Rows live in an internal scroll region bounded to the portfolio's height
budget, so the section stays put as groups are added.

- pfDrawRow renders each group in the selected style, reusing the icon /
  value / DRGX / 24h / sparkline pieces; per-entry field toggles honored.
- portfolio_style setting (0/1/2) persisted; arrow-key switcher + toast.
- Portfolio groups gain a per-wallet scope: new groups default to the
  active wallet's identity hash; legacy groups (empty scope) stay visible
  in every wallet. Scope filter drives both the height budget and the rows.
- Remove the now-dead grid machinery (mktDrawCard, pfComputeGridLayout,
  PfCell, PfGridDrag, MktCardCtx + MktCtx grid fields) — net code drop.
- Full UI sweep: seed demo portfolio groups + wavy price history and add
  market-rows-{compact,detailed,featured} surfaces so the styles are
  captured per skin (snapshot/restore the real portfolio + market data).
- i18n: portfolio_style_{label,compact,detailed,featured} + 8 langs; CJK
  subset rebuilt.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 19:03:07 -05:00
d23ee9b75f feat(wallet): new/import wallets + multi-wallet i18n (P3)
Completes the wallet-files dialog and localizes the feature.

- Create a new wallet: a name is normalized to wallet-<name>.dat and opened
  via switchToWallet; the daemon mints it (seed-backed, -usemnemonic=1) on
  first load. Rejects blank names + name collisions.
- Import: out-of-datadir wallets get an Import action that copies the file
  into the datadir (never overwriting) under a wallet-*.dat name, then opens
  it — the daemon only loads plain filenames from the datadir.
- i18n: the 32 new multi-wallet / per-wallet-contacts / mining-payout strings
  translated into all 8 languages (de/es/fr/ja/ko/pt/ru/zh), CJK subset font
  rebuilt. Verified: the Chinese Wallets dialog renders with no missing glyphs.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 17:54:29 -05:00
e8a8ce68b2 feat(wallet): wallet-files list + switching (P2)
Second phase of multi-wallet: list wallet files and switch the active one.

- Daemon plumbing: EmbeddedDaemon::setWalletFile -> start() passes -wallet=<name>
  (non-default only; skipped during the isolated seed-migration start), and
  DaemonController::syncSettings pushes active_wallet_file on each start.
- App::switchToWallet: persist the new active wallet, then stop + restart the
  node on -wallet=<name> (rescan only if it was never synced in this datadir).
  Reuses the seed-adopt restart coordination; WalletState clears on disconnect
  and the P1 identity-scoped caches re-key, so no previous-wallet data leaks.
  Guarded: full-node, embedded daemon, not mid-restart, no pending send.
- Wallets dialog (Settings -> Backup & Data -> "Wallets…"): a table of wallet
  files (datadir wallet*.dat + user-added folders' *.dat) with size (disk) and
  cached balance / address count / last-opened (wallet index), a current/Active
  badge, Open (switch), and Add folder. Out-of-datadir wallets show "import to
  open" (P3). Added as a sweep surface.
- Mining payout safety: non-destructive warning if the pool-mode worker looks
  like a DragonX address not in the current wallet (stale after a switch).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-09 16:20:41 -05:00