Commit Graph

40 Commits

Author SHA1 Message Date
aa60188665 fix(ui): tighter, darker, non-clipping card drop shadow
Rework DrawCardDropShadow (Light/Marble): a Gaussian-sampled ring stack
matching a `0 0 4px rgb(50 53 58 / 34a)` box-shadow — offset-free, darker,
and tighter than the old faint 14px black halo. Clip to the shadow's own
bounding box (card ± reach) instead of the child window bounds so top/bottom
shadows are no longer chopped where a card sits flush with its container,
while staying on the card's own draw list (correct z-order in modals/popups).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 12:32:42 -05:00
cff958c21e fix(themes): card shadow no longer cut off at child-window edges
Cards are drawn inside clipping child windows, so the shadow (which extends past
the card edge) was cut at the child boundary — most visibly a hard line on the
sidebar's inner edge, which also made the shadow look non-uniform. Widen the
shadow's clip horizontally (card + spread, clamped to the viewport) so the side
shadows reach into the gaps between panels, while keeping the vertical clip at the
child bounds so a card scrolled to a child's top/bottom edge can't bleed its
shadow into the header/footer. Verified: sidebar + settings cards now show an
even shadow on all four sides.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:42:08 -05:00
c50125523c change(themes): make the card shadow uniform (no directional offset)
Drop the downward offset so the soft shadow is equal on all four sides — an even
halo around each card rather than a directional cast, matching the requested
look. Slightly more rings for a smoother falloff. Still stroke-based (no body
contamination), Light/Marble only.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:24:56 -05:00
063f1fa4be feat(themes): subtle card drop shadow for Light + Marble
Cards on the pale Light / Marble backgrounds now lift off the surface with a
subtle drop shadow. Drawn in DrawGlassPanel (behind the card fill) as fading
rounded-rect STROKES offset down — strokes never paint across the card body, so
only the outer soft blur remains once the fill covers the interior (the
"render then mask out the UI area" result, no FBO needed). Gated to the Light and
Marble themes (cached per theme-generation); dark themes are unchanged. Verified
across marble / light (shadow present, smooth, body clean) and dark (none).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 20:07:24 -05:00
c352364bdc fix(modals): a fixed-card modal can no longer wheel-scroll as a whole
The overlay content child had NoScrollbar (hides the scrollbar) but not
NoScrollWithMouse — so when a fixed-height card's content overflowed by even a
few px, the mouse wheel silently drifted the ENTIRE dialog (title + panes +
footer). Add NoScrollWithMouse to the content child: a modal is a fixed frame;
inner scroll regions (version list, release notes, command list) still scroll on
their own, and auto-height cards resize to content so they never overflow.

Also fix the daemon updater's footer budget, which under-counted the
Spacing/Separator/Spacing + inter-item gaps and produced exactly that few-px
overflow. Verified daemon / console / wallets / portfolio render intact.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 16:52:43 -05:00
e6bea8eb04 fix(modals): hold dialog hidden until its height settles (no reveal-time slide)
An ImGui auto-resize child reports a too-small height on its first render and
the true height a frame later, so centering on last frame's height made the card
visibly slide for ~2 frames as it converged. Hiding only the very first frame
wasn't enough — the next frame or two still moved.

Track per-dialog height stability: keep the card hidden (laid out + measured, but
clipped) until its measured height holds steady across two frames, then reveal it
already centered. A dialog re-arms on (re)open so each open reveals stable; once
shown it won't re-hide on a mid-dialog content change (just repositions); a
frame cap prevents an ever-changing height from hiding it forever. Fixed-height
dialogs are unaffected. Verified migrate / about / seed-backup / import-key
reveal centered at the settled frame.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 15:29:13 -05:00
5e5f167fb0 fix(modals): hide the auto-height measuring frame so dialogs only appear centered
Auto-height dialogs need one frame to measure their content height before they
can be centered. Previously that first frame was drawn near the top, so a
first-ever open flashed off-center for a frame before snapping to center.

On the measuring frame, lay the content out (so the child's auto-height is still
measured) but clip the whole card to a zero rect and skip the glass panel — the
card draws nothing while the blur backdrop still shows, then it appears already
centered on the next frame. Clip covers both ImGui widgets and hand-drawn
content; layout/measurement is unaffected (verified migrate / about / import-key
still render centered at the settled frame).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 15:09:05 -05:00
1fd794ccc1 fix(modals): vertically center auto-height dialogs in the window
Fixed-height overlay cards were already centered, but auto-height cards
hard-anchored at 15% from the top, so shorter dialogs (e.g. Migrate to a seed
wallet, Import Key, About) rendered top-heavy with a large empty area below.

Center auto-height cards on last frame's measured content height (already cached
per dialog id in g_overlayCardHeights). A re-opened dialog centers from frame 1;
only the very first open of a given dialog this session briefly anchors near the
top before it re-centers. Content taller than the window top-anchors at a 16px
margin. One framework change centers every modal (verified across migrate /
import-key / about / seed-backup at 100%).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-10 15:00:15 -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
dfca9bbd92 refactor(ui): add LabeledInputRow + LabeledInputMultiline; complete inputs layer
Extend the inputs layer with the two remaining form-field idioms, pixel-identical:
- material::LabeledInputRow(label, labelPos, ...) — horizontal label-beside-input
  (Text + SameLine + SetNextItemWidth + InputText). Adopt at the settings RPC
  host/port/user/pass rows.
- material::LabeledInputMultiline(label, id, buf, size, flags) — label above a
  sized multiline input. Adopt at the address-book notes and request-payment
  memo (drops the redundant SetNextItemWidth that InputTextMultiline's explicit
  size already governs).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 21:41:42 -05:00
840ead3645 refactor(ui): add material::LabeledInput helper + adopt at 8 form fields
UI-standardization audit: the labeled single-line form field (a plain
ImGui::Text label above a fixed-width InputText/InputTextWithHint) was
open-coded across dialogs. Add material::LabeledInput(label, id, buf, size,
width=-1, hint=nullptr, flags=0) mirroring that idiom exactly — pixel-identical,
but a single chokepoint so input styling (label typography, frame, spacing) can
later evolve in one place. Returns the input's edited bool.

Adopt at the clean vertical (label-above) single-line sites: address-book
add/edit (label + address), shield from-address, export-all-keys + export-
transactions filename, validate-address input, request-payment label + URI.
Left as-is: horizontal label-beside-input rows (settings RPC host/port/user/
pass use Text + SameLine), multiline (InputTextMultiline), numeric (InputDouble),
combos, and hint-only inputs — none match the vertical single-line idiom.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 21:11:02 -05:00
c83348ab63 refactor(ui): add material::IconButton helper + adopt at 8 frameless icon buttons
UI-standardization audit (icon-button discovery pass): the app had ~22 clean
hand-rolled icon buttons repeating the same "InvisibleButton hit-target +
centered glyph + hover feedback + tooltip" boilerplate. Add a shared
material::IconButton(id, glyph, font, size, IconButtonStyle) — centered glyph,
optional resting + hover backgrounds (pill/rounded-rect), hover recolor, hand
cursor + tooltip; caller resolves state-dependent glyph/colors and returns
clicked. Adopt at the homogeneous, self-contained sites, pixel-faithful:
- mining_mode_toggle: pool/worker dropdown + bookmark + reset (×5)
- network: server hide/unhide eye (×1)
- mining_controls: pool benchmark start (×1)
- mining_stats: pool-balance refresh (×1)

Deferred (own follow-up + sweep): the idle/scale/gpu active-pill toggles use a
"draw pill+glyph earlier, hit-test later" idiom needing restructuring; the
raw-rect (IsRectHovered, no InvisibleButton) sites in market/mining_stats/
balance are a different hit-test mechanism left intentionally. Framed icon
pagers already ride TactileButton after the prior commit.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 19:25:20 -05:00
ad59d62b82 refactor(ui): extract shared ProgressBar / CopyableTextOverlay / DrawPill helpers
UI-standardization audit: three draw patterns were copy-pasted across tabs and
dialogs. Promote each to a material:: helper and adopt at every verbatim site.
Output is pixel-identical (same colors/geometry threaded through as params).

- material::DrawProgressBar / ProgressBar — replaces 3 byte-identical rounded
  fill bars in the daemon/xmrig/bootstrap download dialogs.
- material::CopyableTextOverlay — the click-to-copy affordance (hit button +
  hand cursor + tooltip + hover underline + clipboard) shared by the peers
  best-block hash and the 3 mining stats (difficulty/block/address). Returns
  "copied this frame" so callers keep their own toast (no ui-layer dependency).
- material::DrawPill / PillSize — rounded bg + optional border + inset text
  badge, shared by the explorer status pill, the market Z/T chip, and the
  network official/custom tag.

Divergent one-offs left in place (transactions status pills use schema rounding
around pre-positioned multi-line text; the mining filter-pill is a button bg).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 18:48:08 -05:00
07f6d2efc9 refactor(ui): delete dead button code (buttons.h + ApplyTactile)
UI-standardization audit found two dead, contradictory "how to draw a button"
surfaces with zero call sites:
- src/ui/material/components/buttons.h (353 lines: TextButton/Outlined/Contained/
  Button(spec)/IconButton/FAB) — included by 3 mining files but never used.
- ApplyTactile (draw_helpers.h) — a retrofit-tactile-onto-plain-button wrapper,
  never called.
Remove the file, the 3 dead includes, and ApplyTactile. TactileButton/StyledButton
remain the canonical button helpers. No behavior change.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 18:34:12 -05:00
a523611779 feat(theme): flat light-theme buttons (match the portfolio modal)
Per the request for a flatter look like the Manage-portfolio modal's plain
buttons, render TactileButton FLAT on light themes: DrawButtonGlassOverlay now
early-returns in light themes with just a subtle defining edge (--rim-light),
skipping the white glass sheen (--glass-fill) and the tactile 3D depth (bright top
edge + the hardcoded bottom shadow that couldn't be removed via TOML).
ImGui::Button still draws the fill/hover/active (the darkened ImGuiCol_Button
overlays), so the button reads as a flat, defined element. Dark themes keep the
raised glass/tactile look. Re-adds the IsLightTheme() luminance helper (early in
the header so the overlay can use it). Applies to all TactileButtons app-wide in
light themes, not just Settings.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 17:16:22 -05:00
bb2337b552 feat(theme): darker text + buttons in light themes (revert modal backing)
Replace the light-theme modal backing (19e3138, now reverted) with a root-cause
fix: darken the light themes' UI elements for contrast.
- Buttons: the light-theme ImGuiCol_Button overlays were barely-there grey
  (5/8/12% black); bump to 13/20/28% so buttons read as buttons on light
  surfaces (one edit in ApplyColorThemeToImGui covers all light skins).
- Text: raise --on-surface-medium (60-72% -> 86%) and --on-surface-disabled
  (38-40% -> 52%) across the light skins (light, color-pop-light, dune, marble,
  iridescent) — the dominant secondary/label text was too faint. Base
  --on-surface was already near-black, left as-is.
The active theme is TOML-skin-driven, so the code GetMaterialLightTheme() is not
the live source; edits are in the skin TOMLs + the ImGui-style mapping.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 16:57:38 -05:00
19e31388d9 feat(ui): light-theme contrast for floating modals
Floating (BlurFloat) modals draw no card, so in a light theme the light content
sits on a light frosted backdrop with almost no separation (washed out — the
Manage-portfolio modal being the clearest case). Add a faint elevated sheet
(translucent Surface) + a defining edge (OnSurface) behind floating content in
LIGHT themes only, gated on a new IsLightTheme() (background luminance) helper.
Dark themes are unchanged (they already read fine). Kept translucent to preserve
the airy floating feel; respects "no dark backdrop in light theme".

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 16:44:17 -05:00
10289b33b5 fix(ui): theme-aware modal backdrop + full-window lock overlay
Address two GUI-review points:
1. Light themes no longer get a dark modal backdrop: DrawFullWindowBlurBackdrop
   now tints toward the theme app background (WithAlpha(Background(), ...)) for
   both the opaque base and the frost, so a light theme gets a light frosted
   backdrop and a dark theme a dark one. Fixes all blur overlays, not just the
   lock screen.
2. The lock screen now blurs the WHOLE window (sidebar included), not just the
   content area: renderLockScreen opens a full-viewport borderless overlay window
   (##LockOverlay, same pattern as the modal overlays / portfolio, opened from
   within ##ContentArea) with an input blocker, instead of drawing in the content
   child. Removed the theme-independent dark scrim (contradicted point 1).

Auth logic untouched. Needs GUI re-verify of all auth paths + that the whole
screen (incl. sidebar) obscures on lock and unlock returns cleanly.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 15:51:57 -05:00
34ded6b441 feat(ui): shutdown-screen consistency polish (Phase 3)
The two Phase 3 "bespoke overlays" aren't content modals: the send-error is an
in-tab glass banner (already consistent — left as-is), and the shutdown screen is
a teardown screen where a blur backdrop would be semantically wrong (should read
as "closing") + risky on the teardown path. Per that finding, apply minor
consistency polish to the shutdown screen only:
- Share the overlay-scrim tone via a new material::OverlayScrimColor(opacity),
  used by both the dialog scrim and the shutdown scrim (was a slightly different
  hardcoded 0.06/0.06/0.08 → now the 0.04/0.04/0.06 dialog base).
- "Shutting Down" title → theme Warning() amber (was hardcoded gold).
- Force-Quit confirm destructive button → theme Error() (alpha-varied) instead of
  hardcoded reds.
No behavior change; the shutdown scrim stays opaque (no blur).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 14:07:36 -05:00
c6b32e86c0 feat(ui): all overlay dialogs adopt the blur backdrop (Phase 2 core)
Flip the positional BeginOverlayDialog overload's default to blurBackdrop=true, so
every app dialog (~43 call sites) now renders its card on the live-blur backdrop
instead of the opaque scrim — matching the Manage-Portfolio look. Cards stay
auto-height (small dialogs stay small = "card for small") and render opaque via
the existing sole-consumer fallback (a translucent glass card would thrash the
single-slot blur cache against the radius-64 backdrop). Nested dialogs (address
book edit) already pass a distinct idSuffix, so their blur windows/capture keys
don't collide. A dialog that wants the old scrim can pass a spec with
blurBackdrop=false. Big visual change — needs a GUI sweep; trivially reverted.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:55:40 -05:00
5caffc84b0 fix(material): make blur-overlay effect guard truly close-equivalent
The adversarial review found the ModalRenderGuard lagged the old
PortfolioEditorActive() signal by one extra frame on CLOSE (a possible 1-frame
"opaque panels" flash). Latch the overlay's open flag (already available via
spec.p_open) — "drew this frame AND still open after Esc/Close/outside handling" —
instead of just "drew last frame", so the guard drops and the acrylic re-capture
fire on the same frame the overlay closes, matching the old inline behavior on
both edges.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:46:11 -05:00
ebf01bd858 feat(market): migrate portfolio modal onto the shared blur-overlay framework (Phase 1)
Dogfood the Phase 0 framework: RenderPortfolioEditor now opens via
material::BeginOverlayDialog(BlurFloat) + EndOverlayDialog instead of its own
inline overlay scaffold, and uses the promoted material:: SegmentedControl /
RightAlignX / BeginFadeScrollChild helpers (overlay_scroll.h). Delete the
portfolio's inline backdrop/capture-once state machine + its PfEditState capture
statics + the four local helper copies. App::render's ModalRenderGuard is now
driven by the modal-agnostic AnyBlurOverlayActiveLastFrame() (timing-equivalent),
and LatchBlurOverlayActive() at frame end handles the acrylic re-capture on close.

Framework tweak: floating cards pop the card WindowPadding after BeginChild (so
nested children don't inherit it) and center button labels, keeping the net
style-var count at 2 so EndOverlayDialog is unchanged and GlassCard stays
byte-identical. RenderPortfolioEditor: 357 -> 282 lines. Needs GUI verification
that the portfolio modal is visually/behaviourally unchanged.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:33:15 -05:00
d91be3b34e feat(material): spec-driven BeginOverlayDialog (Phase 0b)
Refactor BeginOverlayDialog to run through an OverlayDialogSpec + OverlayStyle
(GlassCard vs BlurFloat) with orthogonal flags (blurBackdrop / floatingContent /
plainHeading, fixed vs auto card height). The classic look is now GlassCard with
all flags off; the existing positional overload forwards to it → all ~43 current
dialogs are byte-identical (verified line-by-line). The BlurFloat path — live-blur
backdrop with per-overlay capture-once (frame-gap arming), floating content, plain
h6 heading — is implemented but unused until Phase 1 dogfoods it on the portfolio.
Also replace the unusable open-refcount with a per-frame drawn flag + last-frame
latch that invalidates the acrylic capture on the overlay's close transition.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:13:47 -05:00
4b46d4a20c feat(material): promote blur-overlay modal primitives (Phase 0a)
First step toward unifying all app modals on the Manage-Portfolio visual style.
Add, as shared material:: helpers (additive — no caller yet, no visible change):
- blur-overlay open refcount + 1-frame latch (Push/Pop/AnyBlurOverlayActive[LastFrame],
  LatchBlurOverlayActive) so App::render's effect guard can be modal-agnostic;
- capture-frame query (IsCapturingBlurBackdrop) for scroll-fade suppression;
- RightAlignX and SegmentedControl (promoted verbatim from market_tab);
- BeginFadeScrollChild/EndFadeScrollChild in a dedicated overlay_scroll.h (kept
  out of the widely-included draw_helpers.h because effects::ScrollFadeShader's GL
  headers clash with SDL's in some TUs).
Also fix IsCurrentWindowOverlayDialog to prefix-match "##OverlayScrim" so suffixed
overlay windows are recognized by the modal-aware hover check.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-04 02:05:03 -05:00
fe18628216 feat(market): bump the portfolio backdrop blur radius (64) via sole-consumer isolation
Raising the backdrop radius alone would thrash the shared radius-keyed blur cache (the
glass panels blur at ~30). Instead, while a full-window blur overlay is active, glass
panels use their opaque fallback (IsFullWindowBlurOverlayActive) — they're covered by the
backdrop anyway — so the backdrop is the SOLE applyBlur caller and can use a strong custom
radius (64) with the cache staying coherent and frozen. The flag is toggled alongside the
theme-effect suppression by a RAII guard in App::render().

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 21:15:12 -05:00
4dc6b2ef8f perf(market): capture the portfolio backdrop once on open (+resize), not every frame
Switch the live-blur behind the modal from a per-frame capture (which flickered from
per-frame re-blur non-determinism) to capture-once-on-open, re-captured only on resize.

- Insert the acrylic live-capture callback into the overlay draw list ONLY for a few
  frames on open and whenever the viewport size changes; other frames reuse the frozen
  blurred snapshot (blurCacheValid_), so there's no per-frame capture/re-blur cost. State:
  s_pf_was_open (reset on close), s_pf_capture_frames, s_pf_capture_w/h.

Two fixes from an adversarial review of the timing/pipeline interaction:
- Do NOT override the backdrop blurRadius to 40. applyBlur caches one blurred buffer
  keyed on radius, shared with every glass panel (market cards + the modal preview card,
  radius ~30). A different radius thrashed that cache every frame and made the backdrop
  sample the wrong blur, defeating the frozen-blur guarantee. Using the theme card's
  radius keeps one blur serving all surfaces, frozen.
- Skip the blur on the overlay's first frame (allowBlur=false) — the live content isn't
  captured until that frame's render, so blurring then would show stale/background pixels
  for one frame; draw the plain opaque scrim instead.

Compiles on OpenGL (Linux) + DX11 (Windows). Needs on-device visual verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 21:05:16 -05:00
0e8816e220 feat(effects): overlay-time live-framebuffer blur behind the portfolio modal
The acrylic system only blurred a frozen snapshot of the static app background. Add
an overlay-time re-capture so the Manage-portfolio modal blurs the LIVE tab content
behind it.

- AcrylicMaterial::captureLiveFramebuffer() (GL blit of FB 0 / DX11 CopyResource of the
  backbuffer + no-backend stub) mirrors captureBackgroundDirect but bypasses the
  dirtyFrames_ gate so it refreshes every frame the overlay is open, forcing a re-blur.
- ImGuiAcrylic::GetLiveCaptureCallback() returns a draw callback for it.
- market_tab: the modal inserts that callback (+ ImDrawCallback_ResetRenderState) at the
  START of its overlay draw list, before the backdrop, so the capture holds the app UI
  drawn below and the backdrop blurs it. On close, InvalidateCapture() re-captures the
  background so other glass panels don't keep blurring the stale live capture.
- Panel theme-effects are suppressed during the market render while the modal is open
  (their foreground-draw-list borders draw above all windows and would otherwise bleed
  over the overlay). Market tab body is rendered again (no longer skipped).
- Backdrop draws the acrylic at full strength (fallbackColor.w=1) over an opaque base so
  the blurred content reads clearly; lighter dim.

Design mapped via an Explore investigation of the capture/blur pipeline; compiles on
both OpenGL (Linux) and DX11 (Windows cross-build). Needs on-device visual verification.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 20:26:11 -05:00
4aed72c73b feat(market): rework Manage-Portfolio as a full-window master-detail modal
Combine the old list/edit modes into one master-detail view rendered as a
full-window overlay instead of a floating dialog.

- Backdrop: new material::DrawFullWindowBlurBackdrop draws an opaque base first
  (so the sheet is opaque regardless of the UI-opacity slider — the acrylic path
  multiplies its alpha by GetUIOpacity), then a strong full-window blur of the
  backdrop, gated exactly like DrawGlassPanel so low-performance mode skips the
  blur entirely (just the opaque scrim).
- Frame: bespoke full-window overlay (not BeginOverlayDialog) that mirrors the
  scrim plumbing and preserves both overlay popup gotchas verbatim (no focus-steal
  and no outside-click dismiss while a combo / color picker is open). Title-bar X,
  Esc, and outside-click all dismiss.
- Left (master): selectable group list with accent bar + icon + name + value line,
  and a pinned "Add group" button.
- Right (detail): live preview + label header, then a single-open accordion
  (Appearance = color + outline opacity + icon; Price; Addresses) so only one
  control group shows at a time — far less cluttered than the old two-column form.
- Footer: Delete / Revert / Save. Editing auto-commits named groups on navigation
  and on close (preserving grid geometry); unnamed drafts are dropped.
- s_pf_editing tri-state replaced by s_pf_sel (+ accordion open-state); the Manage
  button and grid-card click open with a valid selection.

Design + adversarial review done via multi-agent workflows (understand/design, then
a 3-lens ImGui-lifecycle / state-safety / backdrop review; zero confirmed defects).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 18:46:46 -05:00
b0ded528b1 fix(dialogs): let Combo dropdowns / popups work inside overlay dialogs
BeginOverlayDialog called ImGui::SetNextWindowFocus() every frame to keep the
dialog on top. That forced the scrim back to the front each frame and closed
any popup opened from inside the dialog — so the portfolio editor's price-basis
Combo and the custom color-picker popup flashed open then vanished, and clicking
a dropdown item that overhung the card could trip the outside-click-dismiss and
close the whole dialog.

Make the overlay popup-aware: compute overlayPopupOpen (IsPopupOpen with
AnyPopupId|AnyPopupLevel) and (1) skip SetNextWindowFocus while a popup is
showing so it stays focused/on-top, and (2) skip the outside-click-dismiss while
a popup is open so clicks inside it don't close the dialog. No change when no
popup is open, so existing dialogs are unaffected.

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

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-03 01:02:18 -05:00
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
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
00ee61fe64 fix(dialogs): size the overlay glass card to its content
The overlay dialog's content child is AutoResizeY, but the glass card behind
it was drawn to a fixed viewport ratio — leaving a tall band of empty glass
below short dialogs (e.g. the key-export modal had a gap under its Close
button). Measure the rendered card height each frame and reuse it next frame
to draw the glass to the content; fall back to (and stay capped at) the ratio
so tall dialogs are unchanged and can't run off-screen.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-11 17:38:19 -05:00
d27017daeb fix(ui): scale overlay-dialog card width with the font/DPI setting (fixes modal overflow)
Every BeginOverlayDialog is passed a raw pixel card width (550, 620, …), but the fonts and
spacing inside scale with Layout::dpiScale() — which includes the user's font-size setting. At
any non-default scale the content outgrew the fixed card, so text overflowed the card edge and
elements misaligned. Scale the card width by dpiScale() (no-op at the default 1.0 scale) and clamp
it to the viewport so a large scale can't push it off-screen. Fixes all overlay dialogs at once.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 23:47:53 -05:00
9389859ee9 fix(ui): stop overlay dialogs flashing open-then-closed (BeginOverlayDialog)
BeginOverlayDialog dismisses on a click outside the card via IsMouseClicked (mouse-down). When
the dialog is opened by a button that fires on the same frame (e.g. the mining tab's
"Update miner…" button), that opening click is still registered as an outside-click, so the
dialog opens and instantly closes — it just "flashes". Skip the outside-click dismissal on the
frame the scrim window first appears (ImGui::IsWindowAppearing()); normal outside-click closing
is unaffected on every subsequent frame. Fixes all overlay dialogs, not just the xmrig updater.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-06-10 17:27:18 -05:00
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
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
4023af9466 fix scrolling bug 2026-03-11 03:15:31 -05:00
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
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
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