From 5dd68dc9bdcb4598f2fe271e1be62fd4f4912b2c Mon Sep 17 00:00:00 2001 From: DanS Date: Tue, 14 Jul 2026 00:28:25 -0500 Subject: [PATCH] feat(settings): migrate Seed phrase backup modal to the reference design Phase 2 (second security modal), presentation-only + the review's one nit. Struct-form BlurFloat overlay (was the legacy positional overload); the phrase state's plain red warning -> material::DialogWarningHeader; StyledButtons -> TactileButtons with the Copy/Save/Close row and shared Close centered via PlaceOverlayDialogActions (the existing helper) and their separators dropped; success status now wraps. Already fully i18n'd. UNCHANGED (verified by the adversarial review): the closeAndWipe sodium_memzero of the 24-word phrase on every close path (Begin-false early return, outside-click/Esc teardown, phrase Close, shared Close), the async exportSeedPhrase fetch + its show_seed_backup_ callback guard, the atomic restricted-perms file-write + sodium_memzero of its content buffer, and copySecretToClipboard / RenderSeedWordGrid. No seed leak; Begin/EndOverlayDialog balanced on every return path. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app.cpp | 32 +++++++++++++++++++++----------- 1 file changed, 21 insertions(+), 11 deletions(-) diff --git a/src/app.cpp b/src/app.cpp index 6eaf98d..ca50401 100644 --- a/src/app.cpp +++ b/src/app.cpp @@ -3430,7 +3430,13 @@ void App::renderSeedBackupDialog() }); } - if (!ui::material::BeginOverlayDialog(TR("seed_backup_title"), &show_seed_backup_, 540.0f, 0.94f)) { + ui::material::OverlayDialogSpec ov; + ov.title = TR("seed_backup_title"); + ov.p_open = &show_seed_backup_; + ov.style = ui::material::OverlayStyle::BlurFloat; + ov.cardWidth = 560.0f; + ov.idSuffix = "seedbackup"; + if (!ui::material::BeginOverlayDialog(ov)) { if (seed_backup_fetch_started_) closeAndWipe(); return; } @@ -3453,24 +3459,26 @@ void App::renderSeedBackupDialog() } else if (seed_backup_no_mnemonic_) { ImGui::TextWrapped("%s", TR("seed_backup_none")); } else if (!seed_backup_phrase_.empty()) { - ImGui::TextWrapped("%s", TR("seed_backup_intro")); + ui::material::DialogWarningHeader(TR("seed_backup_warning")); ImGui::Spacing(); - ImGui::PushStyleColor(ImGuiCol_Text, ui::material::ReadableError()); - ImGui::TextWrapped("%s", TR("seed_backup_warning")); - ImGui::PopStyleColor(); + ImGui::TextWrapped("%s", TR("seed_backup_intro")); ImGui::Spacing(); ui::RenderSeedWordGrid(ui::SplitSeedWords(seed_backup_phrase_)); ImGui::Spacing(); if (!seed_backup_status_.empty()) { + ImGui::PushTextWrapPos(0.0f); ImGui::TextColored(ui::material::SuccessVec4(), "%s", seed_backup_status_.c_str()); + ImGui::PopTextWrapPos(); ImGui::Spacing(); } - ImGui::Separator(); - if (ui::material::StyledButton(TR("seed_backup_copy"), ImVec2(120 * dp, 0))) { + // Footer: Copy | Save | Close — centered as a group (no divider). + const float cW = 120 * dp, sW = 150 * dp; + ui::material::PlaceOverlayDialogActions(cW + sW + cW + ImGui::GetStyle().ItemSpacing.x * 2.0f); + if (ui::material::TactileButton(TR("seed_backup_copy"), ImVec2(cW, 0))) { copySecretToClipboard(seed_backup_phrase_); } ImGui::SameLine(); - if (ui::material::StyledButton(TR("seed_backup_save"), ImVec2(150 * dp, 0))) { + if (ui::material::TactileButton(TR("seed_backup_save"), ImVec2(sW, 0))) { std::string path = util::Platform::getConfigDir() + "/dragonx-seed-backup.txt"; std::string content = seed_backup_phrase_ + "\n"; const bool ok = util::Platform::writeFileAtomically(path, content, @@ -3480,7 +3488,7 @@ void App::renderSeedBackupDialog() : std::string(TR("seed_backup_save_failed"))) + path; } ImGui::SameLine(); - if (ui::material::StyledButton(TR("seed_backup_close"), ImVec2(120 * dp, 0))) { + if (ui::material::TactileButton(TR("seed_backup_close"), ImVec2(cW, 0))) { closeAndWipe(); ui::material::EndOverlayDialog(); return; @@ -3492,8 +3500,10 @@ void App::renderSeedBackupDialog() } ImGui::Spacing(); - ImGui::Separator(); - if (ui::material::StyledButton(TR("seed_backup_close"), ImVec2(120 * dp, 0))) { + // Centered Close (no divider). + const float cbW = 120 * dp; + ui::material::PlaceOverlayDialogActions(cbW); + if (ui::material::TactileButton(TR("seed_backup_close"), ImVec2(cbW, 0))) { closeAndWipe(); } ui::material::EndOverlayDialog();