feat(wizard): modernize the first-run wizard to the current design language

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) <noreply@anthropic.com>
This commit is contained in:
2026-07-11 22:48:22 -05:00
parent d189ee5ddb
commit 758ea06ab7

View File

@@ -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);