From 88e10f3e065f86ecdf8e8f7f1d686484b71b576c Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 14 Jul 2026 18:23:14 -0500 Subject: [PATCH] feat(overview): migrate the Export-Key modal to the reference design MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Presentation-only migration of KeyExportDialog::render (reveals a private or viewing key) — a secret-handling modal: - struct-form BeginOverlayDialog(OverlayDialogSpec{BlurFloat, cardWidth cardW, idSuffix keyexport}) replacing the legacy positional overload - replace the bespoke red-bordered "WARNING" box with material::DialogWarningHeader (user-approved) so the key-export warning matches the import-key reference; passes the correct per-key-type body key (private vs viewing) - remove the two dividers (after the warning, above the Close footer) - 4 StyledButton -> TactileButton (retry / reveal / show-hide / close) The secret path is byte-for-byte unchanged: the z_exportkey/dumpprivkey/ z_exportviewingkey (and lite exportPrivateKeys) fetch + their secureWipeLiteSecret calls, the "*"-masking when hidden, the Show/Hide toggle, copySecretToClipboard (auto-clearing), the QR generate/cache/releaseQr, and the secret-clear on BOTH exit paths (Close button + the post-EndOverlayDialog dismiss teardown). Verified: build + ctest + hygiene clean; a 2-lens adversarial review (secret-hygiene / presentation-fund) returned zero findings, proving every secret-handling path diffs byte-for-byte identical to HEAD. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/ui/windows/key_export_dialog.cpp | 65 +++++----------------------- 1 file changed, 11 insertions(+), 54 deletions(-) diff --git a/src/ui/windows/key_export_dialog.cpp b/src/ui/windows/key_export_dialog.cpp index e38def3..706bf6b 100644 --- a/src/ui/windows/key_export_dialog.cpp +++ b/src/ui/windows/key_export_dialog.cpp @@ -66,7 +66,6 @@ void KeyExportDialog::render(App* app) TR("export_private_key") : TR("export_viewing_key"); auto& S = schema::UI(); - auto warningBox = S.drawElement("dialogs.key-export", "warning-box"); auto addrInput = S.input("dialogs.key-export", "address-input"); auto revealBtn = S.button("dialogs.key-export", "reveal-button"); auto keyDisplay = S.drawElement("dialogs.key-export", "key-display"); @@ -79,54 +78,14 @@ void KeyExportDialog::render(App* app) // Modal scales to 85% of the window width. BeginOverlayDialog multiplies the width by // Layout::dpiScale(); divide it out here 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(title, &s_open, cardW, 0.94f)) { - ImGui::Spacing(); + material::OverlayDialogSpec ov; + ov.title = title; ov.p_open = &s_open; + ov.style = material::OverlayStyle::BlurFloat; + ov.cardWidth = cardW; ov.idSuffix = "keyexport"; + if (material::BeginOverlayDialog(ov)) { + material::DialogWarningHeader((s_key_type == KeyType::Private) + ? TR("key_export_private_warning") : TR("key_export_viewing_warning")); - // Warning section with colored background - ImGui::PushStyleColor(ImGuiCol_ChildBg, ImVec4(0.6f, 0.2f, 0.2f, 0.3f)); - // Auto-size the warning box to its (wrapping, font-scaled) text so it never clips it. - (void)warningBox; - ImGui::BeginChild("WarningBox", ImVec2(-1, 0), - ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders); - - // Heading — warning icon (drawn with the icon font, not the text font, so it isn't tofu) + - // larger h6 text, centered, for emphasis. - { - ImFont* iconF = material::Type().iconMed(); - ImFont* textF = material::Type().h6(); - const char* icon = ICON_MD_WARNING; - const char* txt = TR("warning_upper"); - ImGui::PushFont(iconF); const float iw = ImGui::CalcTextSize(icon).x; ImGui::PopFont(); - ImGui::PushFont(textF); const float tw = ImGui::CalcTextSize(txt).x; ImGui::PopFont(); - const float sp = ImGui::GetStyle().ItemSpacing.x; - const float wa = ImGui::GetContentRegionAvail().x; - const float total = iw + sp + tw; - ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (wa > total ? (wa - total) * 0.5f : 0.0f)); - const ImVec4 red(1.0f, 0.42f, 0.42f, 1.0f); - ImGui::PushFont(iconF); ImGui::TextColored(red, "%s", icon); ImGui::PopFont(); - ImGui::SameLine(0, sp); - ImGui::PushFont(textF); ImGui::TextColored(red, "%s", txt); ImGui::PopFont(); - } - ImGui::Spacing(); - - // Body — centered wrapped text. - { - const char* body = (s_key_type == KeyType::Private) - ? TR("key_export_private_warning") : TR("key_export_viewing_warning"); - ImDrawList* wdl = ImGui::GetWindowDrawList(); - ImFont* wf = ImGui::GetFont(); - const float wfs = ImGui::GetFontSize(); - const float wa = ImGui::GetContentRegionAvail().x; - const ImVec2 wp = ImGui::GetCursorScreenPos(); - const float bh = widgets::DrawCenteredWrapped(wdl, wf, wfs, wp, wa, material::OnSurface(), body); - ImGui::Dummy(ImVec2(wa, bh)); - } - - ImGui::EndChild(); - ImGui::PopStyleColor(); - - ImGui::Spacing(); - ImGui::Separator(); ImGui::Spacing(); // Address — click-to-copy field (chunked, width-clamped, centered). @@ -145,7 +104,7 @@ void KeyExportDialog::render(App* app) ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), TR("error_format"), s_error.c_str()); // Not a dead-end: clearing the error falls back through to the Reveal button next frame. ImGui::Spacing(); - if (material::StyledButton(TR("retry"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) { + if (material::TactileButton(TR("retry"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) { s_error.clear(); } } else if (s_key.empty() && s_address.length() < 26) { @@ -154,7 +113,7 @@ void KeyExportDialog::render(App* app) ImGui::TextColored(ImVec4(1.0f, 0.3f, 0.3f, 1.0f), "%s", TR("invalid_address")); } else if (s_key.empty()) { // Show button to fetch key - if (material::StyledButton(TR("key_export_reveal"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) { + if (material::TactileButton(TR("key_export_reveal"), ImVec2(revealBtn.width, 0), S.resolveFont(revealBtn.font))) { s_fetching = true; // Check if z-address or t-address @@ -292,7 +251,7 @@ void KeyExportDialog::render(App* app) app->copySecretToClipboard(s_key); // auto-clearing clipboard } ImGui::Spacing(); - if (material::StyledButton(s_show_key ? TR("hide") : TR("show"), ImVec2(0, 0), + if (material::TactileButton(s_show_key ? TR("hide") : TR("show"), ImVec2(0, 0), material::Type().subtitle1())) { s_show_key = !s_show_key; } @@ -328,8 +287,6 @@ void KeyExportDialog::render(App* app) } } - ImGui::Spacing(); - ImGui::Separator(); ImGui::Spacing(); // Close button @@ -337,7 +294,7 @@ void KeyExportDialog::render(App* app) float avail_width = ImGui::GetContentRegionAvail().x; ImGui::SetCursorPosX(ImGui::GetCursorPosX() + (avail_width - button_width) / 2.0f); - if (material::StyledButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) { + if (material::TactileButton(TR("close"), ImVec2(button_width, 0), S.resolveFont(closeBtn.font))) { s_open = false; // Clear sensitive data s_key.clear();