The two consoles were separate implementations (the rich full-node ConsoleTab and a
minimal RenderLiteConsoleTab). Introduce ConsoleCommandExecutor to abstract the only
real differences — command execution + log source — so both variants share the one rich
terminal (the lite console now gains selection, zoom, copy, and JSON-colored output).
- console_command_executor.{h,cpp}: interface + FullNodeConsoleExecutor (dragonxd log
ingestion + RPC command execution, result queue) and LiteConsoleExecutor (lite
diagnostics ring + backend runConsoleCommand/takeConsoleResult, connection/sync status).
- ConsoleTab::render() now takes a ConsoleCommandExecutor&; log ingestion, command
submission, and command results flow through it. The RPC command-reference popup and
the daemon/errors/rpc-trace/app filter toggles are gated on the executor's capabilities;
the toolbar status dot + a status header come from the executor.
- Safety preserved: `clear`/`cls` is intercepted as a view-only clear in the shared UI so
it is NEVER forwarded (the lite backend's `clear` wipes tx history); command output is
not persisted to diagnostics.
- App owns the executor (created per variant) and routes both NavPages to console_tab_;
lite_console_tab.{h,cpp} deleted.
Needs runtime verification of BOTH consoles (full-node RPC + lite: clear, seed/export
secrecy, sync status, command results).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
175 lines
5.9 KiB
C++
175 lines
5.9 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#pragma once
|
|
|
|
#include "../layout.h"
|
|
#include "../../daemon/embedded_daemon.h"
|
|
#include "../../daemon/xmrig_manager.h"
|
|
#include "../../rpc/rpc_client.h"
|
|
#include "../../rpc/rpc_worker.h"
|
|
|
|
#include <string>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <mutex>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class ConsoleCommandExecutor;
|
|
|
|
/**
|
|
* @brief Console tab — a rich terminal shared by the full-node and lite variants.
|
|
*
|
|
* The command execution + log sources are provided by a ConsoleCommandExecutor, so this
|
|
* one widget serves both dragonxd (RPC) and the lite backend.
|
|
*/
|
|
class ConsoleTab {
|
|
public:
|
|
ConsoleTab();
|
|
~ConsoleTab();
|
|
|
|
/**
|
|
* @brief Render the console tab against a backend executor.
|
|
*/
|
|
void render(ConsoleCommandExecutor& exec);
|
|
|
|
/**
|
|
* @brief Render the RPC Command Reference popup at top-level scope.
|
|
* Must be called outside any child window so the modal blocks all input.
|
|
*/
|
|
void renderCommandsPopupModal();
|
|
|
|
/**
|
|
* @brief Add a line to the console output
|
|
*/
|
|
void addLine(const std::string& line, ImU32 color = IM_COL32(200, 200, 200, 255));
|
|
void addRpcTraceLine(const std::string& source, const std::string& method);
|
|
|
|
/**
|
|
* @brief Clear console output
|
|
*/
|
|
void clear();
|
|
|
|
/**
|
|
* @brief Check if auto-scroll is enabled
|
|
*/
|
|
bool isAutoScrollEnabled() const { return auto_scroll_; }
|
|
|
|
// Scanline effect toggle (set from settings)
|
|
static bool s_scanline_enabled;
|
|
|
|
// Console output zoom factor (1.0 = default caption font size)
|
|
static float s_console_zoom;
|
|
|
|
// Show/hide daemon output messages
|
|
static bool s_daemon_messages_enabled;
|
|
|
|
// Show only error messages (filter toggle)
|
|
static bool s_errors_only_enabled;
|
|
|
|
// Show app RPC calls made through RPCClient (method/source only)
|
|
static bool s_rpc_trace_enabled;
|
|
|
|
// Show "[app] ..." wallet log lines
|
|
static bool s_app_messages_enabled;
|
|
|
|
/// Refresh console text colors for current theme (call after theme switch)
|
|
static void refreshColors();
|
|
|
|
// Colors — follow active theme (public for use by notification forwarding)
|
|
static ImU32 COLOR_COMMAND;
|
|
static ImU32 COLOR_RESULT;
|
|
static ImU32 COLOR_ERROR;
|
|
static ImU32 COLOR_DAEMON;
|
|
static ImU32 COLOR_INFO;
|
|
static ImU32 COLOR_RPC;
|
|
|
|
private:
|
|
struct ConsoleLine {
|
|
std::string text;
|
|
ImU32 color;
|
|
};
|
|
|
|
void addCommandResult(const std::string& cmd, const std::string& result, bool is_error = false);
|
|
// Format + color a completed command result (JSON-aware) into console lines.
|
|
void addFormattedResult(const std::string& result, bool is_error);
|
|
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
|
void renderToolbar(ConsoleCommandExecutor& exec);
|
|
void renderOutput();
|
|
void renderInput(ConsoleCommandExecutor& exec);
|
|
void renderCommandsPopup();
|
|
|
|
// Selection helpers
|
|
void handleSelection();
|
|
std::string getSelectedText() const;
|
|
void clearSelection();
|
|
|
|
// Convert screen position to line/column
|
|
struct TextPos {
|
|
int line = -1;
|
|
int col = 0;
|
|
};
|
|
TextPos screenToTextPos(ImVec2 screen_pos, float line_height) const;
|
|
bool isPosBeforeOrEqual(const TextPos& a, const TextPos& b) const;
|
|
TextPos selectionStart() const; // Returns the earlier of sel_anchor_ and sel_end_
|
|
TextPos selectionEnd() const; // Returns the later of sel_anchor_ and sel_end_
|
|
|
|
std::deque<ConsoleLine> lines_;
|
|
std::vector<std::string> command_history_;
|
|
int history_index_ = -1;
|
|
char input_buffer_[4096] = {0};
|
|
bool auto_scroll_ = true;
|
|
bool scroll_to_bottom_ = false;
|
|
float scroll_up_cooldown_ = 0.0f; // seconds to wait before re-enabling auto-scroll
|
|
int new_lines_since_scroll_ = 0; // new lines while scrolled up (for indicator)
|
|
// (log-ingestion cursors + result queue moved to the ConsoleCommandExecutor)
|
|
|
|
// Text selection state
|
|
bool is_selecting_ = false;
|
|
bool has_selection_ = false;
|
|
TextPos sel_anchor_; // Where the mouse was first pressed
|
|
TextPos sel_end_; // Where the mouse currently is / was released
|
|
float output_scroll_y_ = 0.0f; // Track scroll position for selection
|
|
ImVec2 output_origin_ = {0, 0}; // Top-left of output area
|
|
float output_line_height_ = 0.0f; // Text line height (for scanline alignment)
|
|
|
|
struct ScanlineRow {
|
|
float yTop = 0.0f;
|
|
float yBot = 0.0f;
|
|
int rowIndex = 0;
|
|
};
|
|
std::vector<ScanlineRow> scanline_rows_;
|
|
|
|
std::mutex lines_mutex_;
|
|
|
|
// Output filter
|
|
char filter_text_[128] = {0};
|
|
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
|
|
|
// Wrapped line height caching (for variable-height text wrapping)
|
|
mutable std::vector<float> wrapped_heights_; // Height of each visible line (accounts for wrapping)
|
|
mutable std::vector<float> cumulative_y_offsets_; // Cumulative Y offset for each visible line
|
|
mutable float total_wrapped_height_ = 0.0f; // Total height of all visible lines
|
|
mutable float cached_wrap_width_ = 0.0f; // Wrap width used for cached heights
|
|
|
|
// Sub-row layout: each visible line is split into wrap segments so
|
|
// selection and hit-testing know the exact screen position of every
|
|
// character.
|
|
struct WrapSegment {
|
|
int byteStart; // byte offset into ConsoleLine::text
|
|
int byteEnd; // byte offset past last char in this segment
|
|
float yOffset; // Y offset of this segment relative to the line's top
|
|
float height; // visual height of this segment
|
|
};
|
|
mutable std::vector<std::vector<WrapSegment>> visible_wrap_segments_; // [vi] -> segments
|
|
|
|
// Commands popup
|
|
bool show_commands_popup_ = false;
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|