diff --git a/CMakeLists.txt b/CMakeLists.txt index 2b45c8f..d72ce98 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -409,6 +409,7 @@ set(APP_SOURCES src/wallet/lite_rollout_policy.cpp src/wallet/lite_client_bridge.cpp src/wallet/lite_connection_service.cpp + src/wallet/lite_diagnostics.cpp src/wallet/lite_wallet_controller.cpp src/wallet/lite_result_parsers.cpp src/wallet/lite_sync_service.cpp @@ -962,6 +963,7 @@ if(BUILD_TESTING) src/wallet/lite_rollout_policy.cpp src/wallet/lite_client_bridge.cpp src/wallet/lite_connection_service.cpp + src/wallet/lite_diagnostics.cpp src/wallet/lite_wallet_controller.cpp src/wallet/lite_result_parsers.cpp src/wallet/lite_sync_service.cpp diff --git a/src/ui/windows/lite_console_tab.cpp b/src/ui/windows/lite_console_tab.cpp index 742bcb6..77cdb15 100644 --- a/src/ui/windows/lite_console_tab.cpp +++ b/src/ui/windows/lite_console_tab.cpp @@ -5,14 +5,17 @@ #include "lite_console_tab.h" #include "../../app.h" +#include "../../data/wallet_state.h" #include "../../util/i18n.h" #include "../../wallet/lite_diagnostics.h" +#include "../../wallet/lite_wallet_controller.h" #include "../layout.h" #include "../material/type.h" #include "../material/colors.h" #include "imgui.h" #include +#include #include #include @@ -45,7 +48,59 @@ ImU32 lineColor(const std::string& line) void RenderLiteConsoleTab(App* app) { - (void)app; + if (!app) return; + + // ── Header ────────────────────────────────────────────────────────────────── + Type().text(TypeStyle::H6, TR("lite_console_title")); + Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("lite_console_intro")); + ImGui::Spacing(); + + // ── Live status (read straight from the controller — always meaningful, even if the + // event log below is empty) ───────────────────────────────────────────────── + { + const wallet::LiteWalletController* lw = app->liteWallet(); + const char* connText; + ImU32 connCol; + if (!lw) { + connText = "Lite backend unavailable"; + connCol = Error(); + } else if (lw->walletOpen()) { + connText = "Connected"; + connCol = Success(); + } else if (lw->openInProgress()) { + connText = "Connecting\xE2\x80\xA6"; // ellipsis + connCol = Warning(); + } else { + connText = "Disconnected"; + connCol = Error(); + } + Type().textColored(TypeStyle::Overline, OnSurfaceMedium(), TR("lite_console_status")); + ImGui::SameLine(); + Type().textColored(TypeStyle::Body2, connCol, connText); + + if (lw && lw->walletOpen()) { + const SyncInfo& sync = app->state().sync; + char buf[96]; + if (sync.syncing && !sync.isSynced()) { + double vp = sync.verification_progress; + if (vp < 0.0) vp = 0.0; else if (vp > 1.0) vp = 1.0; + std::snprintf(buf, sizeof(buf), "Syncing %.1f%% (block %d / %d)", + vp * 100.0, sync.blocks, sync.headers); + } else { + std::snprintf(buf, sizeof(buf), "Synced (block %d)", sync.blocks); + } + Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), buf); + } + // Surface the last open failure (e.g. server unreachable) prominently. + const std::string& openErr = app->liteOpenError(); + if (!openErr.empty() && (!lw || !lw->walletOpen())) { + Type().textColored(TypeStyle::Caption, Error(), + (std::string("Last error: ") + openErr).c_str()); + } + } + ImGui::Spacing(); + + // Snapshot the event log (only when it changed). auto& diag = wallet::LiteDiagnostics::instance(); const std::uint64_t gen = diag.generation(); if (gen != s_cachedGeneration) { @@ -53,11 +108,6 @@ void RenderLiteConsoleTab(App* app) s_cachedGeneration = gen; } - // ── Header ────────────────────────────────────────────────────────────────── - Type().text(TypeStyle::H6, TR("lite_console_title")); - Type().textColored(TypeStyle::Caption, OnSurfaceMedium(), TR("lite_console_intro")); - ImGui::Spacing(); - // ── Toolbar ───────────────────────────────────────────────────────────────── if (ImGui::Button(TR("lite_console_clear"))) diag.clear(); ImGui::SameLine(); @@ -70,7 +120,7 @@ void RenderLiteConsoleTab(App* app) ImGui::Checkbox(TR("lite_console_autoscroll"), &s_autoScroll); ImGui::Spacing(); - // ── Log (terminal-styled scroll region) ───────────────────────────────────── + // ── Event log (terminal-styled scroll region) ─────────────────────────────── ImGui::PushStyleColor(ImGuiCol_ChildBg, IM_COL32(0, 0, 0, 90)); ImGui::BeginChild("##LiteConsoleLog", ImVec2(0, 0), true, ImGuiWindowFlags_HorizontalScrollbar); diff --git a/src/util/i18n.cpp b/src/util/i18n.cpp index 16d7515..ea1db68 100644 --- a/src/util/i18n.cpp +++ b/src/util/i18n.cpp @@ -1109,6 +1109,7 @@ void I18n::loadBuiltinEnglish() // --- Lite Network tab (server browser) --- strings_["lite_console_title"] = "Console"; strings_["lite_console_intro"] = "Diagnostic log: server connections, wallet open/create, and sync events."; + strings_["lite_console_status"] = "Status:"; strings_["lite_console_clear"] = "Clear"; strings_["lite_console_copy"] = "Copy"; strings_["lite_console_autoscroll"] = "Auto-scroll"; diff --git a/src/wallet/lite_diagnostics.cpp b/src/wallet/lite_diagnostics.cpp new file mode 100644 index 0000000..e9743fb --- /dev/null +++ b/src/wallet/lite_diagnostics.cpp @@ -0,0 +1,19 @@ +// DragonX Wallet - ImGui Edition +// Copyright 2024-2026 The Hush Developers +// Released under the GPLv3 + +#include "lite_diagnostics.h" + +namespace dragonx { +namespace wallet { + +// Single definition in one translation unit guarantees exactly one shared instance across the +// binary (writers in the controller/App, reader in the lite Console tab all see the same log). +LiteDiagnostics& LiteDiagnostics::instance() +{ + static LiteDiagnostics inst; + return inst; +} + +} // namespace wallet +} // namespace dragonx diff --git a/src/wallet/lite_diagnostics.h b/src/wallet/lite_diagnostics.h index def6af4..5f9caf6 100644 --- a/src/wallet/lite_diagnostics.h +++ b/src/wallet/lite_diagnostics.h @@ -24,10 +24,9 @@ namespace wallet { class LiteDiagnostics { public: - static LiteDiagnostics& instance() { - static LiteDiagnostics inst; - return inst; - } + // Defined in lite_diagnostics.cpp (a single TU) so there is exactly one instance across the + // whole binary — no reliance on the linker folding an inline-function static across TUs. + static LiteDiagnostics& instance(); // Append a timestamped line (thread-safe). Safe to call from background threads. void log(const std::string& message) {