feat(lite): network-tab polish, reworked key-export/QR dialogs

Network tab: glow only the active node, drop the left accent bar. Key-export
dialog: fix the lite-wallet "Not connected" failure by exporting the key
locally via the SDXL backend when there's no daemon; rework the layout to
wrapping click-to-copy fields with a side QR (empty placeholder when hidden),
85% modal width, HRP-preserving key chunking, and a centered, emphasized
warning. QR popup matched to the same sizing and click-to-copy address. Shared
field rendering extracted to widgets/copy_field.h so both dialogs stay in sync.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-27 21:27:25 -05:00
parent 4473e7e00a
commit b20e7efb16
4 changed files with 289 additions and 122 deletions

View File

@@ -6,8 +6,10 @@
#include "../../app.h"
#include "../../util/i18n.h"
#include "../widgets/qr_code.h"
#include "../widgets/copy_field.h"
#include "../schema/ui_schema.h"
#include "../material/draw_helpers.h"
#include "../layout.h"
#include "../theme.h"
#include "imgui.h"
@@ -59,12 +61,13 @@ void QRPopupDialog::render(App* app)
if (!s_open) return;
auto& S = schema::UI();
auto win = S.window("dialogs.qr-popup");
auto qr = S.drawElement("dialogs.qr-popup", "qr-code");
auto addrInput = S.input("dialogs.qr-popup", "address-input");
auto actionBtn = S.button("dialogs.qr-popup", "action-button");
if (material::BeginOverlayDialog(TR("qr_title"), &s_open, win.width, 0.94f)) {
// Match the key-export modal: 85% of the window width (divide out the dpiScale that
// BeginOverlayDialog re-applies, so the final card is exactly 85% at any font scale).
const float cardW = (0.85f * ImGui::GetMainViewport()->Size.x) / Layout::dpiScale();
if (material::BeginOverlayDialog(TR("qr_title"), &s_open, cardW, 0.94f)) {
// Label if present
if (!s_label.empty()) {
@@ -74,10 +77,14 @@ void QRPopupDialog::render(App* app)
ImGui::Spacing();
}
// Center the QR code
float qr_size = qr.size > 0 ? qr.size : 280;
// Center the QR code (responsive — larger, to suit the wider modal).
float window_width = ImGui::GetWindowWidth();
float qr_size = qr.size > 0 ? (float)qr.size : 280.0f;
const float responsive = window_width * 0.5f;
if (responsive > qr_size) qr_size = responsive;
if (qr_size > 420.0f) qr_size = 420.0f;
float padding = (window_width - qr_size) / 2.0f;
if (padding < 0.0f) padding = 0.0f;
ImGui::SetCursorPosX(padding);
@@ -95,39 +102,29 @@ void QRPopupDialog::render(App* app)
ImGui::Separator();
ImGui::Spacing();
// Address display
// Address — click-to-copy field (same formatting as the key-export dialog).
ImGui::Text("%s", TR("address_label"));
// Use multiline for z-addresses
if (s_address.length() > 50) {
char addr_buf[512];
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
addr_buf[sizeof(addr_buf) - 1] = '\0';
ImGui::InputTextMultiline("##QRAddress", addr_buf, sizeof(addr_buf),
ImVec2(-1, addrInput.height > 0 ? addrInput.height : 60), ImGuiInputTextFlags_ReadOnly);
} else {
char addr_buf[128];
strncpy(addr_buf, s_address.c_str(), sizeof(addr_buf) - 1);
addr_buf[sizeof(addr_buf) - 1] = '\0';
ImGui::SetNextItemWidth(-1);
ImGui::InputText("##QRAddress", addr_buf, sizeof(addr_buf), ImGuiInputTextFlags_ReadOnly);
}
widgets::AddressCopyField("##QRAddress", s_address);
ImGui::Spacing();
// Buttons
float button_width = actionBtn.width;
float total_width = button_width * 2 + ImGui::GetStyle().ItemSpacing.x;
float start_x = (window_width - total_width) / 2.0f;
ImGui::SetCursorPosX(start_x);
if (material::StyledButton(TR("copy_address"), ImVec2(button_width, 0), S.resolveFont(actionBtn.font))) {
// Buttons — size each to its label (so "Copy address" never clips), then center the pair.
ImFont* btnFont = S.resolveFont(actionBtn.font);
ImGui::PushFont(btnFont);
const float btnPad = ImGui::GetStyle().FramePadding.x * 2.0f + 24.0f;
float w_copy = ImGui::CalcTextSize(TR("copy_address")).x + btnPad;
float w_close = ImGui::CalcTextSize(TR("close")).x + btnPad;
ImGui::PopFont();
if (w_copy < actionBtn.width) w_copy = actionBtn.width;
if (w_close < actionBtn.width) w_close = actionBtn.width;
const float total_width = w_copy + w_close + ImGui::GetStyle().ItemSpacing.x;
ImGui::SetCursorPosX((window_width - total_width) / 2.0f);
if (material::StyledButton(TR("copy_address"), ImVec2(w_copy, 0), btnFont)) {
ImGui::SetClipboardText(s_address.c_str());
}
ImGui::SameLine();
if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(actionBtn.font))) {
if (material::StyledButton(TR("close"), ImVec2(w_close, 0), btnFont)) {
close();
}
material::EndOverlayDialog();