From 758ea06ab785ef2489637368c47dc263655deb0f Mon Sep 17 00:00:00 2001 From: DanS Date: Sat, 11 Jul 2026 22:48:22 -0500 Subject: [PATCH] feat(wizard): modernize the first-run wizard to the current design language MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The first-run wizard predated the modal-unification pass and was the least on-brand surface left — a flat Surface() slab with a 3-card masonry whose inactive steps were heavily greyed (alpha 165) and whose focused card used a hard 2px border + a hand-drawn offset shadow. Keeping the working phase logic and the "see all steps at once" masonry: - Reveal the skin backdrop: the app already paints its marble / gradient / acrylic backdrop behind every window (drawWindowBackdrop), so drop the opaque Surface() fill (transparent WindowBg) and lay a gentle theme-tinted scrim over it. The wizard now sits on the same rich backdrop as the rest of the app. - Focused card lifts with the shared DrawCardDropShadow (the uniform, non- clipping card shadow) instead of a bespoke offset rect, and a softer 1.5px accent ring. - Inactive cards get lighter veils (upcoming 165->115, completed 110->70) so they read as "waiting" / "done" rather than disabled-grey. - Warmer header: a one-line subtitle under the welcome title. Verified on the headless sweep across all four phases (appearance / bootstrap / encrypt / pin) on marble + obsidian at 1.0 and 1.5x DPI — backdrop shows, cards float cleanly, geometry scales, nothing clipped. Co-Authored-By: Claude Opus 4.8 (1M context) --- src/app_wizard.cpp | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) diff --git a/src/app_wizard.cpp b/src/app_wizard.cpp index b2993e5..9fcba07 100644 --- a/src/app_wizard.cpp +++ b/src/app_wizard.cpp @@ -121,16 +121,21 @@ void App::renderFirstRunWizard() { ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse; ImGui::PushStyleVar(ImGuiStyleVar_WindowPadding, ImVec2(0, 0)); + ImGui::PushStyleColor(ImGuiCol_WindowBg, ImVec4(0, 0, 0, 0)); // reveal the skin backdrop behind ImGui::Begin("##FirstRunWizard", nullptr, flags); + ImGui::PopStyleColor(); ImGui::PopStyleVar(); ImDrawList* dl = ImGui::GetWindowDrawList(); ImVec2 winPos = ImGui::GetWindowPos(); ImVec2 winSize = ImGui::GetWindowSize(); - // Background fill - ImU32 bgCol = ui::material::Surface(); - dl->AddRectFilled(winPos, ImVec2(winPos.x + winSize.x, winPos.y + winSize.y), bgCol); + // The app's skin backdrop (marble / gradient / acrylic) is already painted behind every window by + // drawWindowBackdrop(); reveal it here instead of a flat Surface() slab, under a gentle theme-tinted + // scrim so the wizard cards and text keep their contrast on busy skins. + ImU32 bgCol = ui::material::Surface(); // still used by the completed / not-reached card overlays + dl->AddRectFilled(winPos, ImVec2(winPos.x + winSize.x, winPos.y + winSize.y), + ui::material::WithAlpha(ui::material::Background(), 120)); // --- Determine which of the 3 masonry sections is focused --- // 0 = Appearance, 1 = Bootstrap, 2 = Encrypt + PIN @@ -188,7 +193,14 @@ void App::renderFirstRunWizard() { ImVec2 wts = titleFont->CalcTextSizeA(titleFont->LegacySize, FLT_MAX, 0, welcomeTitle); dl->AddText(titleFont, titleFont->LegacySize, ImVec2(winPos.x + (winSize.x - wts.x) * 0.5f, headerCy), textCol, welcomeTitle); - headerCy += wts.y + 16.0f * dp; + headerCy += wts.y + 6.0f * dp; + + // Warmer, less-sparse header: a one-line subtitle under the welcome (dimmed body). + const char* welcomeSub = "A few quick choices and your full node is ready."; + ImVec2 sts = bodyFont->CalcTextSizeA(bodyFont->LegacySize, FLT_MAX, 0, welcomeSub); + dl->AddText(bodyFont, bodyFont->LegacySize, + ImVec2(winPos.x + (winSize.x - sts.x) * 0.5f, headerCy), dimCol, welcomeSub); + headerCy += sts.y + 16.0f * dp; } // --- Masonry: 2 columns --- @@ -223,11 +235,8 @@ void App::renderFirstRunWizard() { // Background (channel 0) dl->ChannelsSetCurrent(0); if (state == 1) { - // Focused card: subtle drop shadow - float shadowOff = 3.0f * dp; - dl->AddRectFilled( - ImVec2(cMin.x + shadowOff, cMin.y + shadowOff), ImVec2(cMax.x + shadowOff, cMax.y + shadowOff), - IM_COL32(0, 0, 0, 35), cardRound); + // Focused card lifts off the backdrop with the app's uniform card shadow (not a hard offset). + ui::material::DrawCardDropShadow(dl, cMin, cMax, cardRound); } // Use DrawGlassPanel for proper acrylic/opacity/noise/theme effects ui::material::GlassPanelSpec glass; @@ -237,14 +246,14 @@ void App::renderFirstRunWizard() { // Overlays & borders (channel 2) dl->ChannelsSetCurrent(2); if (state == 1) { - // Focused: accent border - dl->AddRect(cMin, cMax, ui::material::Primary(), cardRound, 0, 2.0f * dp); + // Focused: soft accent ring + dl->AddRect(cMin, cMax, ui::material::Primary(), cardRound, 0, 1.5f * dp); } else if (state == 2) { - // Completed: dim overlay (preserves color) - dl->AddRectFilled(cMin, cMax, (bgCol & 0x00FFFFFF) | IM_COL32(0, 0, 0, 110), cardRound); + // Completed: a light veil — reads as "done", not disabled. + dl->AddRectFilled(cMin, cMax, (bgCol & 0x00FFFFFF) | IM_COL32(0, 0, 0, 70), cardRound); } else { - // Not reached: heavy overlay (creates greyscale look) - dl->AddRectFilled(cMin, cMax, (bgCol & 0x00FFFFFF) | IM_COL32(0, 0, 0, 165), cardRound); + // Upcoming: a gentle veil — reads as "waiting", not greyed-out. + dl->AddRectFilled(cMin, cMax, (bgCol & 0x00FFFFFF) | IM_COL32(0, 0, 0, 115), cardRound); } dl->ChannelsSetCurrent(1);