fix(lite): always-populated Console (live status) + single-instance log
The Console could look empty if the wallet produced few events. Make it useful in every state and remove a cross-platform footgun: - Add a live status header read straight from the controller (connected / connecting / disconnected, sync %, and the last open error) — independent of the diagnostics event log, so the Console always shows the current connection + wallet-open state even when the log is sparse. - Move LiteDiagnostics::instance() into a single .cpp so there is exactly one instance across the binary, rather than relying on the linker folding an inline-function static across translation units (a known fragility, especially on mingw/Windows — the most likely cause of a stuck-empty event log there). Verified the writer and reader share one instance on Linux; builds clean for full-node, lite, and Windows cross-compile; tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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 <cstdint>
|
||||
#include <cstdio>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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";
|
||||
|
||||
19
src/wallet/lite_diagnostics.cpp
Normal file
19
src/wallet/lite_diagnostics.cpp
Normal file
@@ -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
|
||||
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user