// DragonX Wallet - ImGui Edition // Copyright 2024-2026 The Hush Developers // Released under the GPLv3 // // ConsoleCommandExecutor — the pluggable backend behind the shared ConsoleTab UI. It // abstracts the only real differences between the full-node console (dragonxd log + RPC // command execution) and the lite console (lite diagnostics ring + backend command), // so both variants use one rich terminal widget instead of two implementations. #pragma once #include "console_channel.h" #include "imgui.h" #include #include #include #include #include #include #include #include namespace dragonx { class App; namespace ui { using ConsoleAddLineFn = std::function; struct ConsoleStatusLine { std::string text; ImU32 color = IM_COL32(200, 200, 200, 255); bool pulse = false; // animate the toolbar dot (starting/connecting) }; // Which log-filter toggles the toolbar should show for a given backend. The full node has a // daemon/xmrig log + an RPC trace; the lite backend has neither (its diagnostics ring maps to // the App/Error channels), so it only wants the errors-only + app-messages toggles. struct ConsoleLogFilterCaps { bool daemon = false; // daemon + xmrig output toggle bool errorsOnly = false; // errors-only toggle bool rpcTrace = false; // RPC method/source trace toggle bool appMessages = false; // app / diagnostics log toggle bool any() const { return daemon || errorsOnly || rpcTrace || appMessages; } }; class ConsoleCommandExecutor { public: virtual ~ConsoleCommandExecutor() = default; // True when commands can be run (RPC connected / lite wallet open). virtual bool isReady() const = 0; // A command is currently in flight (input is disabled while true). virtual bool busy() const { return false; } // Submit a user command for async execution. The UI has already echoed "> cmd" and // handled the built-ins (clear/help/quit). Results arrive later via pollResult(). virtual void submit(const std::string& cmd) = 0; // Pop one completed command result (raw text + error flag) if available; the UI // formats/colors it via FormatConsoleRpcResultLines. Returns false if none pending. virtual bool pollResult(std::string& result, bool& isError) = 0; // Pull any new passive log lines (daemon/xmrig output, or the lite diagnostics ring) // and hand each to `add`. Called once per frame. virtual void pollLogLines(const ConsoleAddLineFn& add) { (void)add; } // UI-chrome capabilities. virtual bool hasRpcReference() const { return false; } // show the RPC command-reference popup // Which log-filter toggles the toolbar should show (default: none). virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; } // Print the backend-appropriate `help` output via `add`. virtual void printHelp(const ConsoleAddLineFn& add) = 0; // Extra status lines drawn above the toolbar (lite shows sync + last error). virtual std::vector statusLines() const { return {}; } // Short status shown in the toolbar (daemon state / lite connection). Empty text => // the toolbar shows its generic "no daemon" label. virtual ConsoleStatusLine toolbarStatus() const { return {}; } }; // ── Full-node: dragonxd log ingestion + RPC command execution ──────────────── class FullNodeConsoleExecutor : public ConsoleCommandExecutor { public: explicit FullNodeConsoleExecutor(App* app) : app_(app) {} bool isReady() const override; void submit(const std::string& cmd) override; bool pollResult(std::string& result, bool& isError) override; void pollLogLines(const ConsoleAddLineFn& add) override; bool hasRpcReference() const override { return true; } // Full node: daemon/xmrig log, errors-only, RPC trace, and app messages. ConsoleLogFilterCaps logFilterCaps() const override { return {true, true, true, true}; } void printHelp(const ConsoleAddLineFn& add) override; ConsoleStatusLine toolbarStatus() const override; // A command is in flight on the RPC worker — lets the UI disable the input (no queued pile-up). bool busy() const override { return in_flight_.load() > 0; } private: App* app_; std::atomic in_flight_{0}; size_t last_daemon_output_size_ = 0; size_t last_xmrig_output_size_ = 0; int last_daemon_state_ = -1; // daemon::EmbeddedDaemon::State as int bool last_rpc_connected_ = false; std::deque> results_; // {text, isError} std::mutex results_mutex_; }; // ── Lite: diagnostics ring + backend console command ───────────────────────── class LiteConsoleExecutor : public ConsoleCommandExecutor { public: explicit LiteConsoleExecutor(App* app) : app_(app) {} bool isReady() const override; bool busy() const override; void submit(const std::string& cmd) override; bool pollResult(std::string& result, bool& isError) override; void pollLogLines(const ConsoleAddLineFn& add) override; // Lite: no daemon log / RPC trace — its diagnostics ring maps to the App + Error // channels, so offer errors-only + app-messages (plus the always-shown text filter). ConsoleLogFilterCaps logFilterCaps() const override { return {false, true, false, true}; } void printHelp(const ConsoleAddLineFn& add) override; std::vector statusLines() const override; ConsoleStatusLine toolbarStatus() const override; private: App* app_; std::uint64_t diag_gen_ = static_cast(-1); // last consumed diagnostics generation }; } // namespace ui } // namespace dragonx