Commit Graph
39 Commits
Author SHA1 Message Date
DanSandClaude Opus 4.8 b37d3d97b6 fix(send/receive): unify card width, justify receive footer, fix recipient-row button height/clip/glyph
Send and Receive are now consistent in layout, and the Send recipient row's buttons
render correctly.

Card envelope (Send ⇄ Receive consistency):
- Add Layout::mainComposeCardBox(availW) — a single shared source for the compose card's
  width + centering (fill the available column up to content-max-width, then center). Both
  tabs derive their card from it, so they can't drift again. Previously Send capped at
  760dp and Receive at 860dp, so the Send card rendered ~150px narrower on any window wider
  than ~860dp; now they fill available width identically.

Receive:
- Justify the footer buttons edge-to-edge (equal shares over the live count) instead of
  left-clustering with dead space, matching Send's full-width footer rhythm.
- Build the address-dropdown preview to the combo's real pixel width so the trailing
  balance ("— 12.00000000 DRGX") no longer hard-clips at 150% (was char-count truncation).

Send recipient row (input | Paste | contacts-icon):
- Pin the contacts icon button to the frame height so the larger iconMed font doesn't
  auto-size it taller than Paste/the input.
- Reserve the real ItemSpacing.x gaps (not the smaller spacingSm token) so the row no
  longer overshoots the card and clips the icon's right border.

draw_helpers (root cause, app-wide):
- TactileButton's icon path measured/drew the label INCLUDING the "##id" suffix (which
  CalcTextSizeA/AddText don't strip the way ImGui's text render does), shoving the glyph
  off-center-left. Strip at "##" before measuring/drawing. Corrects any icon button that
  passes an explicit size and a "##id" label; no-op for labels without "##".

Verified via headless sweeps at 1.0x and 1.5x, plus a real 3800px-wide render (both cards
byte-identical at L=1174/R=2773). ctest 1/1.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-19 16:10:02 -05:00
DanSandClaude Opus 4.8 8778398d31 feat(ui): layout polish — fill dead space, pair/box/collapse, warning weight, dialog glass
Implements the layout-improvement suggestions from the layout audit (visual arrangement
only; no functionality added or removed):

- Send/Receive: the recent-activity list now grows to fill the space below the fixed
  compose/receive card (more history visible), with a centered empty-state when there is
  none — instead of leaving dead canvas.
- Shield/Merge: pair the Fee and UTXO-Limit fields on one row to tighten vertical rhythm.
- Market: extend + frame the portfolio group-list as one contained panel (with a bottom
  edge) and center its empty-state, closing the previously un-anchored gap.
- Overlay dialogs: raise the card glass fill/border alpha (35/50 -> 60/90 of 255) so the
  dialog card reads as a distinct surface over busy backdrops (global, all overlays).
- Wallets: size the list height to the actual wallet count instead of always reserving 7
  rows, removing the large gap before the scan/create prompts (still scrolls when many).
- Contacts: width-aware address truncation shows more of the address on wide rows.
- Transfer Funds: give the "sends the full balance" warning a warning icon + color so the
  stakes stand out from the neutral result-preview lines.
- First-run wizard: collapse a completed Step 1 (Appearance) to the compact pill like
  Step 2, so a finished step is no longer taller than the active one.
- Explorer: distribute the Chain card's two stats to match the density of the sibling
  metrics grid.
- Validate Address: a "Results will appear here" caption fills the pre-interaction blank.
- Change Passphrase: add the warning banner its sibling security dialogs have.
- Migration ShowSeed: box the 24-word mnemonic grid (a GlassSectionScope behind the
  existing RenderSeedWordGrid) so the critical secret reads as a distinct artifact —
  purely visual, no seed/logic/state change.

Verified at 1280 across full-node + Lite + Windows (ctest green) and an adversarial diff
review (clean). New i18n key backfilled into all 8 languages.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-19 13:56:13 -05:00
DanSandClaude Opus 4.8 9205addf55 feat(ui): width-responsiveness — content max-width cap + per-surface form/input clamps
Wide/ultrawide (1440-3440px) responsiveness was unhealthy: no page/content-level
max-width cap existed, and every card/form/table derived width from raw
GetContentRegionAvail().x with floor-only clamps, so surfaces stretched edge-to-edge
(2000-3000px inputs, ballooning cards, giant grid cells, 2000px+ dead row-voids).

Root cause: adopt the (previously dead-code) clamp helpers.
- New Layout::kContentMaxWidth() (~1600dp, tunable via ui.toml [layout]
  content-max-width; <=0 disables). Cap ##ContentArea to it and center the column in
  wider windows — every tab derives from this child, so one change tames the app at
  wide widths. No-op below the cap (fills as before), so 1080p/1440p are unaffected.

Per-surface upper-clamps (std::min(cap*dp, expr), floors preserved) where a single
element is still too wide even within the capped column:
- Settings: Theme/Layout/Language combos, the font-scale slider (~3000px -> 360dp),
  the effect sliders, Explorer URL and RPC credential fields.
- Send / Receive: cap the compose / receive cards to a readable form width and center
  them (Indent(pad+offset) so the auto-layout fields align with the hand-drawn card);
  the recent-tx lists below keep the full column width.
- Chat message bubbles + composer, mining pool URL/payout inputs + stats left/right
  split, contacts search, and the lite-network add-server row / server cards / status
  panel (capped + centered).
- Wizard: vertically center the cards when they fit (was top-anchored, leaving a void
  on tall monitors), compensating the content-height measurement so it can't oscillate.

The 1600 cap also subsumes the fixed-4-column balance grids (~400px cards) and the
right-anchored row dead-gaps (voids shrink from ~2700px to ~800px), so those are left
to the cap rather than blind column/row redesigns.

Verified at 1024/1280 (and via a temporary 900dp cap to exercise the cap+center path,
since the test display clamps to 1280) across full-node + Lite + Windows (ctest green)
and an adversarial diff review (clean). The true wide/ultrawide look and the 1600dp cap
value still want eyes on a real wide monitor.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-18 23:15:39 -05:00
DanSandClaude Opus 4.8 ce8c7696d4 fix(ui): finish HiDPI pass — cosmetic ×dpiScale, narrow-width reflow, recent-list reserves
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>
2026-08-18 21:29:10 -05:00
DanSandClaude Opus 4.8 99d1e73676 fix(ui): scale unscaled geometry at HiDPI — inputs, send progress cards, chat/pool overlaps, peer rows
At font_scale 1.5 ~13 sites read absolute geometry (schema .size/.width) straight
into ImGui without ×dpiScale(), so they stayed native-size while the font grew and
overlapped/clipped real text or money:

- Shield/Merge fee + UTXO inputs, Request Payment amount, Block Info height input:
  ×dpiScale() so the value no longer clips (e.g. 0.00010000 -> 0.00010).
- Send: the confirm-popup Amount Details divider (floored the row step at the scaled
  caption height so it no longer strikes the Fee row), the tx-progress error and
  sending/success cards, and the zero-balance CTA button — all ×dp.
- Mining pool row: ellipsis-truncate the hostname so it can't collide with the
  right-aligned hashrate.
- Chat conversation list: scale the pane-width clamp AND clip the peer name to the
  column left of the timestamp (measure-then-clip) so name and time never overlap.
- Explorer block-detail label column, Peers row offsets, and DialogConfirmFooter
  button height — ×dp.

transaction_details keeps its negative fill-sentinel widths unscaled (a content
margin, not raw px). Verified at font_scale 1.5 across full-node + Lite + Windows
(ctest green) and an adversarial diff review (three wrong-scale regressions fixed).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-18 20:53:29 -05:00
DanSandClaude Opus 4.8 e24ca015d1 feat(ui): implement UI/UX audit — i18n, HiDPI, theme, destructive-action & overflow fixes
Implements all 26 confirmed UI/UX audit findings plus the 4 dashboard/timeline
tile labels. Verified across full-node + Lite + Windows builds (ctest green) and
an adversarial diff review (one market-tab delete regression caught + fixed).

i18n coverage:
- Balance hero/quick-actions/toasts + dashboard & timeline tiles (Total Balance,
  Shielded, Transparent, Quick Send, Quick Receive, Click to open, Market)
- Send: Review Send / Cancel / Paste / view-only tooltip / memo byte counter
- Settings: Lite lifecycle errors, plaintext-RPC security warning, 8 toasts
- Mining pool-payout tooltip
- Resolve daemon_update_title double-assignment collision (new daemon_update_prompt_title)
- 23 new keys translated into all 8 locales; CJK subset font rebuilt

HiDPI: DPI-scale the Send amount bar, mining thread-tile clamp bounds, receive
loading skeleton, and sidebar notification-badge insets.

Light theme: theme-aware material::SurfaceOverlay() for balance bar tracks and
row-hover highlights; contacts active-pill foreground uses OnPrimary().

Destructive actions: arm/confirm for portfolio-group delete, saved pool/worker
remove, and avatar-image delete.

Interaction/overflow: route balance star/eye buttons and the console fold-toggle
through popup-safe guards; clip peer addr/subver; truncate the balance custom
label; measure Settings tool-button widths; mining stepper disabled-state
feedback; +/- stepper buttons restyled to match the thread tiles.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-18 17:54:15 -05:00
DanSandClaude Opus 4.8 6ee81a5abe fix: security-audit remediation (15 findings), empty-wallet warning, and send/chat/console/shutdown UX
Security audit remediation (15 confirmed findings from the codebase audit):
- H-02: scrub+delete the decrypt-flow plaintext key export on ALL exit paths
  (RAII guard) and purge stale obsidiandecryptexport* files at startup.
- M-01/L-03/L-04/L-05/L-07: sodium_memzero the Set-PIN and encrypt-PIN worker
  passphrase/PIN copies, the RPC Basic-auth string (auth_), the exported/imported
  key buffers (App::wipeSecrets, called from ~App and before main's _Exit), and
  the first-run wizard "Skip" buffers.
- M-03/M-04/M-05/L-06: return locked COPIES from XmrigManager/EmbeddedDaemon
  getters (dedicated error_mutex_; DaemonController::lastError now by value),
  route xmrig last_error_ writes through a locked setter, and wrap
  shutdown_status_/wizard_stop_status_ in a locking GuardedStatus
  (wizard_stopping_external_ -> std::atomic).
- M-02: persist after a console send/shield/import in the lite backend.
- L-01: require the confirm click for z_shieldcoinbase/z_mergetoaddress.
- L-02: quote/escape each Windows daemon argv per the MSDN CommandLineToArgvW rules.
- L-08: pin json/tomlplusplus/libwebp FetchContent to immutable commit SHAs.
- I-01: extract updater archives from the already-verified in-memory buffer
  (no disk re-read TOCTOU).

Feature: warn once (full-node) when the active wallet loads empty while a sibling
wallet file in the datadir holds keys. A funded salvage wallet.<ts>.bak routes to
the recovery/Restore flow; a funded sibling .dat routes to the wallet manager.
Per-wallet-file dismissal; gated on synced + address-list-loaded to avoid false
positives on warm reconnect / spent-down wallets.

UX fixes:
- send: show the TOTAL balance (with a spendable "available" note) in the source
  dropdown and keep pending-change addresses visible.
- chat: insert emoji at the cursor position; restrict new-chat recipients to
  shielded (z) addresses.
- console: optional auto-focus of the command input on tab open (off by default).
- shutdown: when "stop external daemon" is on, keep the shutdown screen up until
  the external node actually exits, showing live status.

Adversarially reviewed; verified across full-node, lite, and Windows builds; tests pass.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-18 13:18:49 -05:00
DanSandClaude Opus 4.8 ea26c0cbbb fix(balance): stop the displayed balance cratering during a pending shielded send
Sending a small amount from an address holding a large balance made the displayed
balance collapse to ~0 until the tx confirmed. A shielded spend consumes the whole
source note; the change returns as a 0-confirmation note, and every balance query
used the default minconf=1 — so the spent note dropped out and the change wasn't
counted yet.

Split every balance into two views:

- DISPLAY (balance / privateBalance / transparentBalance / totalBalance) — now
  queried at minconf=0, so it INCLUDES the user's own pending change and no longer
  craters. This is what the Overview, balance tab, market portfolio and receive
  tab show. unconfirmedBalance is now populated (= total - spendable).
- SPENDABLE (new spendableBalance / spendable*Balance) — confirmed (minconf>=1),
  what z_sendmany (run at minconf=1) can actually spend. The Send form's available/
  Max/validation, the from-address selection, the drag-to-transfer dialog cap, the
  chat pay-from and the auto-shield gate all size off these, so they never offer
  0-conf change the daemon would reject.

Implementation: a single z_listunspent(0)/listunspent(0), partitioned per-note by
"confirmations">=1; z_gettotalbalance called at minconf 0 (display) and 1
(spendable); the z_getbalance fallback queries both. applyPendingSendDelta (the
optimistic post-send debit) now touches ONLY the spendable fields — debiting the
display too would re-crater it on top of the honest minconf=0 RPC. Lite mirrors
spendableBalance = balance (its per-address balance is already confirmed) so lite
sends aren't zeroed. The confirmed-only gates (seed-migration/sweep z_gettotalbalance,
sweep z_getbalance(addr,1), z_sendmany's minconf arg) are untouched.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-13 16:40:14 -05:00
DanSandClaude Opus 4.8 001e85ac1a fix(ui): prevent text/button cutoff and scale hand-drawn geometry at HiDPI
Findings from a UI-cutoff audit — each is a spot where an in-tree helper
(truncateMiddle / TruncateToWidth / measured button width / the *dpiScale/*hs
factors) was bypassed:

- notifications: the toast-pill height/padding/icon-gap were raw logical px while
  the icon/text drawn inside are DPI-baked, so they clipped the pill at HiDPI.
  Scale the geometry by dpiScale (not the already-scaled glyph metrics).
- settings: in the two-column NODE & SECURITY layout the data-directory path could
  overrun into the Daemon-binary column (shared draw list, no clip rect between
  them). Middle-ellipsize it to the column width; the full path stays in the
  tooltip + click-to-open + copy.
- send: the "Confirm & Send" button width came straight from the schema and was
  never measured against the label, clipping the Russian translation on the
  pre-broadcast dialog. Size to max(schema width, measured label + padding).
- balance: the recent-tx address-column offset missed the `* hs` DPI factor its
  sibling (amount-right-margin) uses, overlapping the type label at HiDPI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-10 22:11:07 -05:00
DanSandClaude Opus 4.8 c3e81a5fa6 fix(send): accept P2SH/multisig recipients in the send + URI address gates
Same defect class as the import-key fix: a hardcoded prefix/length pre-filter
layered over the checksum validators rejected valid addresses before the daemon
saw them. The send-screen recipient gate required a[0]=='R', and the payment-URI
parser accepted only 'R'/'t' with rigid length bands — so every valid P2SH /
multisig address (DragonX SCRIPT_ADDRESS=85 → 'b…') was silently refused, leaving
the Send button disabled with no usable recipient.

Centralize recipient recognition in util/address_validation:
- isTransparentAddress: Base58Check with a 21-byte version+hash160 payload —
  covers P2PKH ('R…', v60) AND P2SH ('b…', v85) on every network, rejects WIF
  keys / typos by real checksum.
- isShieldedAddress: Bech32 + a Sapling payment-address HRP (zs / ztestsapling /
  zregtestsapling), distinguishing a payment address from a viewing key.
- isValidRecipientAddress: either of the above.

send_tab's two validity helpers (the single choke point for all 5 call sites) and
the payment-URI format check now route through these. The URI parser now
checksum-validates the recipient (fail-fast on transcription errors) rather than
being prefix/length-only.

Tests use real checksummed vectors (P2PKH/P2SH/shielded, WIF- and typo-rejection);
testPaymentUri updated off its old fake fixed-char addresses. Suite green (1/1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-08-09 00:12:17 -05:00
DanSandClaude Opus 4.8 dae677acd8 feat(send): migrate the Confirm-Send modal to the reference design
Presentation-only migration of RenderSendConfirmPopup (the confirm-before-broadcast
popup) — a fund-moving modal:
- struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth popupW,
  idSuffix confirmsend}) replacing the legacy positional overload; p_open stays
  nullptr (the popup keeps its own Escape + Cancel handling)
- TR() the one hardcoded status string in the synchronous live-revalidation guard
  ("Cannot send now …") -> new send_cannot_send_now key, 8 langs (em-dash preserved)

The fund path is byte-for-byte unchanged: the live re-validation guard, the
app->sendTransaction call + its success/failure callback, the lite-locked
requestLiteUnlock branch, the s_sending/s_show_confirm state, the Escape handler,
and the FROM/TO cards + fee-tier/summary rendering.

Verified: build + ctest + hygiene clean; a 2-lens adversarial review
(fund-path-untouched / ui-i18n) returned zero findings, with the fund-path lens
proving the send path diffs byte-for-byte identical to HEAD.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-14 17:58:11 -05:00
DanSandClaude Opus 4.8 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
DanSandClaude Opus 4.8 6c19fac6f5 fix(ui): DPI-scale hand-drawn overlays + freeze sweep demo state
Hand-drawn absolute geometry (dl->AddText offsets, SetNextWindowSize,
explicit ImVec2 button widths, SameLine strides) is immune to the app's
DPI machinery (font-atlas rebuild + ScaleAllSizes), so several overlays
rendered native-size (tiny) on HiDPI / Windows 150% displays. Multiply the
raw logical-px geometry by Layout::dpiScale():

- seed_display: content-aware, DPI-scaled seed-grid column stride (a raw
  130px stride let words collide with the next column's index at 150%)
- lock screen: card/logo/input/unlock-button + all vertical offsets
- migrate-to-seed + seed-backup dialogs: button widths
- send-confirm: confirm/cancel button widths
- about dialog: value-column x, edition inset, link/close button widths
- chat conversation list: row height + padding (fixes the preview clip)

Also freeze demo state during the full UI sweep: guard App::update() on
capture_mode_ so a running daemon's refresh can't clobber the injected
demo state (it blanked Send/Receive/History with a CDB error mid-sweep).

Verified on Windows 4K@150% and locally at font_scale=1.5 (same
dpiScale()==1.5 code path). Adds a DPI-scaling convention note to CLAUDE.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 18:58:54 -05:00
DanSandClaude Opus 4.8 a86cb8f0f3 feat(debug): full UI screenshot sweep — captures modals/dialogs/flows/states
The existing sweep only captures tabs. This adds a "Full UI sweep" (DEBUG
OPTIONS) that also drives every normally-hidden surface into view and captures
it under every skin, so all the UI a normal sweep misses can be reviewed at
once.

- The capture unit becomes a "surface" (SweepTarget: a base tab, optionally
  with a setup()/teardown() that forces a modal / multi-step flow / state
  overlay on top). Tabs are surfaces with a null setup. The existing tab sweep
  is folded into the same state machine (one code path). All sweep logic +
  the catalog + demo data move into a new src/app_sweep.cpp.
- Runs OFFLINE against injected demo data (installDemoWalletData snapshots then
  restores the real state_ — WalletState isn't copy-assignable, so a targeted
  field snapshot is used) and fires NO live op: capture_mode_ guards the
  seed-backup RPC, auto-lock, and the migration pumps; the async-firing dialogs
  are neutralized by pre-setting their state (fetch_started_ + demo phrase;
  seed_migration_step_ set directly). Verified: 0 RPC/secret/send calls.
- Catalog (v1): tabs + the bool-flag modals (import/export key, backup,
  seed-backup, about, settings) + the 6 migrate-to-seed steps + 4 wizard steps
  + lock/warmup/not-ready overlays + the send-confirm popup (via a new
  ui::SweepShowSendConfirm debug hook, since its state is send_tab-static).
- Overlay/blur surfaces settle 8 frames (blur backdrop freeze); setup re-runs
  each frame (OpenPopup popups must re-fire). updateScreenshotSweep now runs
  before the first-run-wizard early-return in render() so the sweep advances
  while a wizard step is shown. main.cpp drops vsync during a sweep so it isn't
  throttled to the compositor's unfocused rate. Output:
  <config>/screenshots-full/<surface>/<skin>.png + index.md.
- DRAGONX_FULL_SWEEP=1 runs the sweep headlessly then quits (CI / verification).

Verified end-to-end under WSLg: 288 PNGs (9 skins x 32 surfaces), all 1200x720,
[Sweep] done, clean quit, no daemon. Byte-identity across runs holds only for
static skins on data-free surfaces — the rest vary by animated theme effects /
live price / relative timestamps (expected for a review tool, not a bug).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-07 16:57:54 -05:00
DanSandClaude Opus 4.8 ddd53dc006 feat(send): add a shared contact picker to the recipient field
Phase 0c. The Send recipient field only offered Paste and a tx-history
suggestion list — no way to pick a saved contact. Add a compact contacts
icon button next to Paste that opens a picker popup over the App-owned
address book; selecting a contact fills the recipient (re-validated next
frame).

- New header-only src/ui/windows/contact_picker.h: ContactPickerPopup(id,
  book, outBuf, outSz) — a reusable popup listing contacts ([Z]/[T] tag +
  label + short address) that copies the chosen address into the caller's
  buffer. Kept out of the material layer so the design-system headers stay
  free of data/ deps; reserved for Chat "new conversation" later.
- send_tab: shrink the recipient input to make room for the icon button
  (ICON_MD_CONTACTS), wire the popup to app->addressBook(). Coexists with the
  existing Paste + suggestion affordances.
- i18n: send_contacts_button tooltip.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 17:04:58 -05:00
DanSandClaude Opus 4.8 658c0f355b fix(ui): validate and clamp dialog input
Tighten input handling across several dialogs so bad/edge input can't
produce a doomed request or a confusing display:

- app_security: passphrase strength meter now factors character-class
  diversity — an all-one-class string downgrades one tier so "aaaaaaaa"
  no longer scores as high as a mixed one.
- block_info: clamp the height field to the chain tip and hide/deny
  "Next" at the tip (was only yielding a raw RPC error).
- address_label: trim surrounding whitespace before saving; a
  whitespace-only label clears it (mirrors clearing the icon).
- send_tab: re-clamp the amount when a Max send has its fee bumped in the
  confirm popup (kept the total within budget) and NUL-terminate the
  strncpy'd address/memo when a payment URI overwrites the form.
- settings_page: reject an empty/whitespace-only lite wallet path before
  dispatching an unusable open/restore request.
- peers_tab: guard ExtractIP against an empty address.
- transaction_details: disable "View in explorer" when the configured
  explorer URL is empty/whitespace so we never open a garbage link.
- app: seed-backup "Skip" now requires a second, deliberate click with a
  fund-loss warning before it dismisses.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 15:08:59 -05:00
DanSandClaude Opus 4.8 dd9bc0bd2b fix: Tier-2 involved mediums — send confirm re-validation + console JSON errors
- Send: re-validate against LIVE state at the "Confirm & Send" click (connected,
  not syncing, amount > 0, total <= available). The confirm dialog persists
  across frames, so a balance drop / sync restart / fee bump after Review could
  otherwise broadcast a now-invalid transaction; it now shows a clear message
  instead of sending.
- Console: a malformed JSON argument ({...}/[...] that fails to parse) was
  silently sent to the daemon as a plain string. Now the command fails with a
  clear "Argument N is not valid JSON" line instead of being silently mangled.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 14:00:16 -05:00
DanSandClaude Opus 4.8 b2a8c4487e fix: Tier-2 quick mediums — view-only guard, memo counter, updater/close guards
- Send: block Review from a view-only source (no spending key) with a tooltip,
  instead of failing at submit; label the memo counter as bytes and warn in
  colour once the field hits its cap (Sapling memos are byte-limited).
- Shield: show "No shielded (z) address yet — create one on the Receive tab
  first" when the destination combo is empty, instead of an empty dropdown.
- Updater dialogs (xmrig/daemon): suppress the window X during an active
  download/verify/extract so closing can't orphan/block on the worker — the
  in-dialog Cancel is the way to abort.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 13:54:55 -05:00
DanSandClaude Opus 4.8 65bb98cd09 fix: Tier-2 mediums — input trims, confirmations, amount normalization
More robustness fixes from the audit (the import-key pattern, lower severity):

- Trim pasted input before use/validation: network add-server URL/label,
  lite-wallet key import (+ block empty), validate-address, address-book entry.
- Confirmations for destructive/irreversible actions:
  - Address-book Delete now needs a confirming second click (no undo).
  - Console `stop` needs a confirming second `stop` (it shuts down the node).
  - Wizard "Skip" encryption needs a confirming second click (keys stored
    unencrypted).
- Send amount: normalize to 8dp (satoshi precision) on both the DRGX and USD
  inputs so digits past 8dp aren't silently dropped between the preview/review
  and what's actually sent.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 13:18:57 -05:00
DanSandClaude Opus 4.8 7249d11899 feat(ui): clamp recent-activity lists to the 4 most recent
The overview "Recent Transactions" and the send tab "Recent Sends" lists showed
every matching transaction in a scroll region. Cap them at the 4 most recent
(state.transactions is sorted newest-first, so a simple head-limit). The receive
"Recent Received" list gets the same clamp in the Tier-1 robustness commit
(same file as the QR fix).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-05 12:53:46 -05:00
DanSandClaude Opus 4.8 1c5bc0d4d6 fix(theme): per-skin legibility polish from the sweep review
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>
2026-07-04 23:33:48 -05:00
DanSandClaude Opus 4.8 de11850c74 refactor(audit): batch 4 — UI design-system helpers + i18n
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>
2026-07-02 17:29:12 -05:00
DanSandClaude Opus 4.8 f8150434d4 fix(audit): batch 1 — latent unlock/path/rescan correctness bugs
Four correctness fixes surfaced by the codebase audit:

1. Lite address-book path bug: AddressBook::getDefaultPath() hardcoded
   "ObsidianDragon" while Settings::getDefaultPath() uses DRAGONX_APP_NAME,
   so the Lite build wrote addressbook.json into the full-node app's config
   dir instead of ObsidianDragonLite/. Now uses DRAGONX_APP_NAME on all
   platforms (no macOS path change — the getConfigDir consolidation, which
   would move the macOS dir, is deferred).

2. PIN-unlock lockout bypass: the PIN path duplicated unlockWallet's
   success/lockout logic and its RPC-error branch bumped the attempt counter
   but skipped the escalating-lockout math entirely — a PIN user hitting an
   RPC error escaped the lockout curve. Extracted App::applyUnlockSuccess()
   and App::applyUnlockFailure() and routed all unlock paths (passphrase,
   PIN vault-fail, PIN RPC-fail) through them, so the lockout curve now
   applies uniformly. (noVault stays a mode-switch, not a failed attempt.)

3. Witness/rescan reset drift: the ~11-line rescan+witness completion reset
   was copy-pasted at four sites (app.cpp x2, app_network.cpp x2); adding a
   witness field and missing a copy would leave stale progress. Folded into
   App::resetWitnessRescanProgress(). Left the distinct phase-transition
   reset in App::update() untouched (it sets witness_phase, not 0).

4. Truncation underflow: send_tab/receive_tab's size_t TruncateAddress
   copies computed (maxLen - 3) without guarding maxLen <= 3, which wraps
   and throws std::out_of_range on short inputs. Added the guard.

Full-node + Lite build clean; ctest 1/1; source-hygiene clean.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-02 15:54:36 -05:00
DanSandClaude Opus 4.8 2e8e214689 refactor(ui): app-wide compact, translucent tooltips
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>
2026-06-27 21:26:52 -05:00
DanSandClaude Opus 4.8 e40962cdf2 perf(ui): dedupe time-ago + allocation-free case-insensitive filter (audit #1-3)
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>
2026-06-10 13:54:27 -05:00
DanSandClaude Opus 4.8 070a516f4e fix(send): validate recipient address checksums (Base58Check + Bech32)
The send screen labelled any prefix+length match as a "Valid" address, so a
mistyped address that still matched the pattern passed the gate. Add pure,
offline checksum validation — Base58Check (transparent R-addresses) and Bech32
(Sapling zs-addresses) — and require it in the validity check. Both verifiers are
version-byte/HRP agnostic (the HRP is taken from the string, the Base58 checksum
is chain-independent), so a correct implementation never rejects a genuine
address while catching transcription errors. Works for both build variants
(no daemon round-trip), unit-tested against standard BIP173 / Base58Check vectors.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:43:34 -05:00
DanSandClaude Opus 4.8 6ed80d2d79 fix(send): result-driven status styling + full-precision USD preview
The transaction-status overlay decided error vs. success styling by searching the
status string for "Error"/"Failed" — so under a non-English locale a failed send
rendered as a green success. Drive it from the existing s_status_success flag
instead. Also show the USD-mode DRGX preview at 8 dp so it matches the confirm
panel and the amount actually sent (was 4 dp).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:25:43 -05:00
DanSandClaude Opus 4.8 8f22db5eea fix(send): resolve source balance by address, not list index
GetAvailableBalance() read state.addresses[s_selected_from_idx], but the index
desyncs from s_from_address (the value actually debited) after an address-list
refresh, and is left at -1 when the source is chosen from another tab's "Send
from this address" — which made the sufficiency check see a 0 balance and wrongly
block a valid send. Look the balance up by matching the source address string.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-07 14:18:16 -05:00
DanSandClaude Opus 4.8 3799330bb0 fix(ui): show real data and consistent values across tabs
- 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>
2026-06-07 14:17:42 -05:00
DanSandClaude Opus 4.8 d52d3d1b7f feat(lite): send-time unlock prompt for locked encrypted wallets
When the user confirms a send on a locked encrypted lite wallet, show an unlock
modal (passphrase -> unlockWallet) instead of letting the backend reject it with
"Wallet is locked". After unlocking, the user re-confirms the send (the form is
preserved). Balances remain viewable while locked; only spending needs unlock.

- send_tab: the Confirm-and-send button routes to App::requestLiteUnlock() when
  getWalletState().isLocked(), else sends as before.
- App::renderLiteUnlockPrompt(): centered modal, passphrase (Enter submits),
  Unlock/Cancel; the passphrase buffer is sodium-zeroed after every path.

Full-node unaffected (gated on liteWallet()/isLocked()). Builds clean, launches
clean, tests pass.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-05 18:22:15 -05:00
DanSandClaude Opus 4.8 235504657d polish(lite): lite-appropriate wording for no-wallet/connection states
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>
2026-06-05 17:11:41 -05:00
DanS 975743f754 feat(wallet): persist history and surface pending sends
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.
2026-05-05 03:22:14 -05:00
DanS 9edab31728 Refactor app services and stabilize refresh/UI flows
- Add refresh scheduler and network refresh service boundaries for typed
  refresh results, ordered RPC collectors, applicators, and price parsing.
- Add daemon lifecycle and wallet security workflow helpers while preserving
  App-owned command RPC, decrypt, cancellation, and UI handoff behavior.
- Split balance, console, mining, amount formatting, and async task logic into
  focused modules with expanded Phase 4 test coverage.
- Fix market price loading by triggering price refresh immediately, avoiding
  queue-pressure drops, tracking loading/error state, and adding translations.
- Polish send, explorer, peers, settings, theme/schema, and related tab UI.
- Replace checked-in generated language headers with build-generated resources.
- Document the cleanup audit, UI static-state guidance, and architecture updates.
2026-04-29 12:47:57 -05:00
DanS ee8a08e569 feat(addresses): improve address labeling and view-only handling
- Add expanded address icon picker with search, bottom-aligned actions, and improved modal sizing
- Embed a pickaxe icon font subset and wire it into typography/address icon rendering
- Track view-only shielded addresses and prevent sends from non-spendable z-addresses
- Improve address transfer dialog sizing, max amount handling, and text clipping
- Tune main header layout values in ui.toml
- Update README, codebase overview, and third-party license documentation
2026-04-27 13:54:28 -05:00
DanS aa26ab5fbd fix: sidebar nav text overflow for long translations
- Add text scaling for section labels (TOOLS, ADVANCED) in sidebar
- Separate explorer_section key from explorer nav label to fix ALL CAPS
- Shorten long sidebar translations: es/pt settings, pt overview, ru tools/advanced
- Fix explorer translations from ALL CAPS to proper case in all languages
2026-04-12 18:45:48 -05:00
DanS ddca8b2e43 v1.2.0: UX audit — security fixes, accessibility, and polish
Security (P0):
- Fix sidebar remaining interactive behind lock screen
- Extend auto-lock idle detection to include active widget interactions
- Distinguish missing PIN vault from wrong PIN; auto-switch to passphrase

Blocking UX (P1):
- Add 15s timeout for encryption state check to prevent indefinite loading
- Show restart reason in loading overlay after wallet encryption
- Add Force Quit button on shutdown screen after 10s
- Warn user if embedded daemon fails to start during wizard completion

Polish (P2):
- Use configured explorer URL in Receive tab instead of hardcoded URL
- Increase request memo buffer from 256 to 512 bytes to match Send tab
- Extend notification duration to 5s for critical operations (tx sent,
  wallet encrypted, key import, backup, export)
- Add Reduce Motion accessibility setting (disables page fade + balance lerp)
- Show estimated remaining time during mining thread benchmark
- Add staleness indicator to market price data (warning after 5 min)

New i18n keys: incorrect_pin, incorrect_passphrase, pin_not_set,
restarting_after_encryption, force_quit, reduce_motion, tt_reduce_motion,
ago, wizard_daemon_start_failed
2026-04-04 19:10:58 -05:00
DanS 2c5a658ea5 feat: Full UI internationalization, pool hashrate stats, and layout caching
- 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
2026-03-11 00:40:50 -05:00
DanS 1a5c4e8744 feat: blockchain rescan via daemon restart + status bar progress
- Fix z_importwallet to use full path instead of filename only
- Add rescanBlockchain() method that restarts daemon with -rescan flag
- Track rescan progress via daemon output parsing and getrescaninfo RPC
- Display rescan progress in status bar with animated indicator when starting
- Improve dark theme card contrast: lighter surface-variant, tinted borders, stronger rim-light
2026-02-28 15:06:35 -06:00
DanS 3aee55b49c ObsidianDragon - DragonX ImGui Wallet
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)
2026-02-27 00:26:01 -06:00