feat(ux): wizard handoff note, daemon restart reminder, settings polish (code)
The code changes intended with the settings_window removal in a90aa29 (a bad
git-add pathspec aborted staging them there, leaving that commit with only the
file deletions and CMakeLists still referencing them):
- First-run handoff info toast at the completion point.
- Daemon-updater "restart pending" reminder (Settings → Daemon Binary) + the
Done screen's warning-level "restart required" headline.
- Debug restart-note raised to a readable color with the restart prompt beneath
it; DEBUG OPTIONS header shows a lock + explanatory caption (auth gate
unchanged); a few hardcoded literals → material tokens / dpiScale.
- Drop the legacy settings_window from the build (CMakeLists + the dev-only
references) to match the file deletions.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -904,7 +904,7 @@ void RenderSettingsPage(App* app) {
|
||||
float cellGap = Layout::spacingLg();
|
||||
bool twoCol = contentW >= 460.0f * dp; // drop to a single column when too narrow (high font scale)
|
||||
int cols = twoCol ? 2 : 1;
|
||||
float colW = std::max(160.0f, twoCol ? (contentW - cellGap) * 0.5f : contentW);
|
||||
float colW = std::max(160.0f * dp, twoCol ? (contentW - cellGap) * 0.5f : contentW);
|
||||
float baseX = ImGui::GetCursorScreenPos().x;
|
||||
|
||||
const char* cellLabels[4] = { TR("theme"), TR("balance_layout"), TR("language"), TR("clock_format") };
|
||||
@@ -1346,7 +1346,7 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::SetCursorScreenPos(ImVec2(cx, last + gp));
|
||||
ImGui::PushTextWrapPos((cx + cw) - ImGui::GetWindowPos().x); // wrap to the card content width
|
||||
if (app->daemonAutoShieldActive()) {
|
||||
ImGui::TextColored(ImVec4(0.40f, 0.78f, 0.40f, 1.0f), "%s", TR("autoshield_by_node"));
|
||||
ImGui::TextColored(material::SuccessVec4(), "%s", TR("autoshield_by_node"));
|
||||
if (!app->daemonAutoShieldAddress().empty())
|
||||
ImGui::TextDisabled(" %s", app->daemonAutoShieldAddress().c_str());
|
||||
} else {
|
||||
@@ -2595,6 +2595,21 @@ void RenderSettingsPage(App* app) {
|
||||
if (ui::DaemonUpdateDialog::consumeInstalled())
|
||||
s_settingsState.daemon_info_loaded = false;
|
||||
|
||||
// Persistent reminder: a newer daemon binary is installed but the running node is still
|
||||
// the old one until a restart (easy to miss on the updater's Done screen).
|
||||
if (app->daemonRestartPending()) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + contentW);
|
||||
Type().textColored(TypeStyle::Caption, material::Warning(), TR("daemon_restart_pending"));
|
||||
ImGui::PopTextWrapPos();
|
||||
if (app->isUsingEmbeddedDaemon()) {
|
||||
if (material::ActionButton("##drestartpend", TR("daemon_update_restart_now"),
|
||||
ICON_MD_RESTART_ALT, material::ActionTier::Primary))
|
||||
app->restartDaemon();
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_daemon_restart_pending"));
|
||||
}
|
||||
}
|
||||
|
||||
// --- UPDATES ---
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("daemon_updates_label"));
|
||||
@@ -3001,14 +3016,23 @@ void RenderSettingsPage(App* app) {
|
||||
s_settingsState.debug_gate_verifying = false;
|
||||
}
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", s_settingsState.debug_expanded ? TR("tt_debug_collapse") : TR("tt_debug_expand"));
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", s_settingsState.debug_expanded ? TR("tt_debug_collapse")
|
||||
: (s_settingsState.debug_gate_passed ? TR("tt_debug_expand") : TR("tt_debug_expand_gated")));
|
||||
ImGui::PopStyleColor(3);
|
||||
|
||||
// Draw overline label + arrow on top of the invisible button
|
||||
{
|
||||
ImFont* ovFont = Type().overline();
|
||||
float textY = headerPos.y + (ImGui::GetFrameHeight() - ovFont->LegacySize) * 0.5f;
|
||||
dl->AddText(ovFont, ovFont->LegacySize, ImVec2(headerPos.x, textY), OnSurfaceMedium(), TR("debug_logging"));
|
||||
float labelX = headerPos.x;
|
||||
// Show a lock beside the label while the session gate hasn't been passed, so it's clear the
|
||||
// section is gated rather than just a bare collapsible header.
|
||||
if (!s_settingsState.debug_gate_passed) {
|
||||
ImFont* lockFont = Type().iconSmall(); if (!lockFont) lockFont = ovFont;
|
||||
dl->AddText(lockFont, lockFont->LegacySize, ImVec2(labelX, textY), OnSurfaceMedium(), ICON_MD_LOCK);
|
||||
labelX += lockFont->CalcTextSizeA(lockFont->LegacySize, FLT_MAX, 0, ICON_MD_LOCK).x + Layout::spacingXs();
|
||||
}
|
||||
dl->AddText(ovFont, ovFont->LegacySize, ImVec2(labelX, textY), OnSurfaceMedium(), TR("debug_logging"));
|
||||
// Use the icon font for the expand/collapse arrow
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (!iconFont) iconFont = ovFont;
|
||||
@@ -3016,6 +3040,14 @@ void RenderSettingsPage(App* app) {
|
||||
dl->AddText(iconFont, iconFont->LegacySize, ImVec2(headerPos.x + availWidth - arrowW - pad, textY), OnSurfaceMedium(), arrow);
|
||||
}
|
||||
|
||||
// One-line explanation under the collapsed header (what it is + that it's gated).
|
||||
if (!s_settingsState.debug_expanded) {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(),
|
||||
s_settingsState.debug_gate_passed ? TR("debug_options_caption_unlocked")
|
||||
: TR("debug_options_caption_gated"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
}
|
||||
|
||||
if (s_settingsState.debug_expanded) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
|
||||
@@ -3059,9 +3091,33 @@ void RenderSettingsPage(App* app) {
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
#endif
|
||||
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.5f), "%s", TR("settings_debug_select"));
|
||||
ImGui::TextColored(ImVec4(1,1,1,0.35f), "%s", TR("settings_debug_restart_note"));
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::OnSurfaceMedium());
|
||||
ImGui::TextUnformatted(TR("settings_debug_select"));
|
||||
ImGui::TextWrapped("%s", TR("settings_debug_restart_note")); // was near-invisible 35% white
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
// Restart prompt moved directly under the note so the required action is prominent (appears the
|
||||
// frame after a category toggles, which sets debug_cats_dirty while rendering the grid below).
|
||||
if (s_settingsState.debug_cats_dirty && app->supportsFullNodeLifecycleActions()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, material::Warning());
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (iconFont) {
|
||||
ImGui::PushFont(iconFont);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(ICON_MD_INFO);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(0, Layout::spacingXs());
|
||||
}
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(TR("settings_debug_changed"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("settings_restart_daemon"), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
s_settingsState.confirm_restart_daemon = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_restart_daemon"));
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
}
|
||||
|
||||
// The 22 dragonxd debug categories
|
||||
static const char* debugCats[] = {
|
||||
@@ -3119,27 +3175,7 @@ void RenderSettingsPage(App* app) {
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// "Restart daemon" button — only active when categories changed
|
||||
if (s_settingsState.debug_cats_dirty && app->supportsFullNodeLifecycleActions()) {
|
||||
ImGui::PushStyleColor(ImGuiCol_Text, IM_COL32(255, 218, 0, 255));
|
||||
ImFont* iconFont = Type().iconSmall();
|
||||
if (iconFont) {
|
||||
ImGui::PushFont(iconFont);
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(ICON_MD_INFO);
|
||||
ImGui::PopFont();
|
||||
ImGui::SameLine(0, Layout::spacingXs());
|
||||
}
|
||||
ImGui::AlignTextToFramePadding();
|
||||
ImGui::TextUnformatted(TR("settings_debug_changed"));
|
||||
ImGui::PopStyleColor();
|
||||
ImGui::SameLine();
|
||||
if (TactileButton(TR("settings_restart_daemon"), ImVec2(0, 0), S.resolveFont("button"))) {
|
||||
s_settingsState.confirm_restart_daemon = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) material::Tooltip("%s", TR("tt_restart_daemon"));
|
||||
}
|
||||
// (The "restart daemon" prompt was moved up to directly under the restart note above.)
|
||||
}
|
||||
|
||||
// ---- Debug-options gate: confirmation + warning (+ re-auth if a PIN/passphrase is set) ----
|
||||
|
||||
@@ -445,11 +445,20 @@ private:
|
||||
|
||||
static void renderDone(float, const Progress& p) {
|
||||
using namespace material;
|
||||
if (!s_notified) { s_installed_flag = true; s_notified = true; }
|
||||
if (!s_notified) {
|
||||
s_installed_flag = true;
|
||||
s_notified = true;
|
||||
// Record a persistent "restart pending" reminder so it isn't lost if the user clicks Later.
|
||||
if (s_app) s_app->markDaemonRestartPending();
|
||||
}
|
||||
Type().textColored(TypeStyle::Subtitle2, Success(), TR("daemon_update_installed_ok"));
|
||||
ImGui::Spacing();
|
||||
ImGui::Text("%s %s", TR("daemon_update_version"), p.latest_tag.c_str());
|
||||
ImGui::Spacing();
|
||||
// Lead with a warning-level headline that a restart is required, then the detail note.
|
||||
ImGui::PushTextWrapPos(ImGui::GetCursorPosX() + ImGui::GetContentRegionAvail().x);
|
||||
Type().textColored(TypeStyle::Subtitle2, Warning(), TR("daemon_update_restart_needed"));
|
||||
ImGui::PopTextWrapPos();
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("daemon_update_restart_note"));
|
||||
ImGui::Spacing();
|
||||
if (s_app->isUsingEmbeddedDaemon()) {
|
||||
@@ -459,6 +468,7 @@ private:
|
||||
}
|
||||
ImGui::Spacing();
|
||||
if (TactileButton(TR("daemon_update_later"), ImVec2(fullW(), 0))) s_open = false;
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("daemon_update_later_hint"));
|
||||
} else {
|
||||
if (TactileButton(TR("close"), ImVec2(fullW(), 0))) s_open = false;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user