The console redacted secret command ARGS but showed the OUTPUT of key-revealing commands (dumpprivkey / z_exportkey / z_exportviewingkey / z_exportmnemonic / dumpwallet / z_exportwallet, and the lite seed/export verbs) in full — visible in the scrollback, Copy All, Ctrl+C and the filter. Now, when the submitted command is key-revealing, its result is held ONLY in a member buffer (never in the scrollback model, so Copy-All etc. can't leak it) and a masked placeholder is shown. An explicit "Copy secret" button routes through the auto-clearing clipboard; the buffer auto-wipes after 60s, on Clear, and on wallet lock (via wipeDisplayedSecrets). The command string passed to the executor is byte-identical. New pure helper ConsoleCommandRevealsSecret is unit-testable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
244 lines
12 KiB
C++
244 lines
12 KiB
C++
// DragonX Wallet - ImGui Edition
|
|
// Copyright 2024-2026 The Hush Developers
|
|
// Released under the GPLv3
|
|
|
|
#pragma once
|
|
|
|
#include "console_channel.h"
|
|
#include "console_model.h"
|
|
#include "console_scroll_controller.h"
|
|
#include "console_selection_controller.h"
|
|
#include "console_text_layout.h"
|
|
#include "../layout.h"
|
|
#include "../../daemon/embedded_daemon.h"
|
|
#include "../../daemon/xmrig_manager.h"
|
|
#include "../../rpc/rpc_client.h"
|
|
#include "../../rpc/rpc_worker.h"
|
|
|
|
#include <cstdint>
|
|
#include <string>
|
|
#include <vector>
|
|
#include <deque>
|
|
#include <mutex>
|
|
#include <functional>
|
|
#include <chrono>
|
|
|
|
namespace dragonx {
|
|
namespace ui {
|
|
|
|
class ConsoleCommandExecutor;
|
|
struct ConsoleLogFilterCaps;
|
|
struct ConsoleCommandEntry;
|
|
|
|
/**
|
|
* @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 command-reference popup at top-level scope.
|
|
* Must be called outside any child window so the modal blocks all input. `exec` supplies the
|
|
* reference table (full-node RPC vs lite backend verbs); may be null (no console yet) -> no-op.
|
|
*/
|
|
void renderCommandsPopupModal(ConsoleCommandExecutor* exec);
|
|
|
|
// Debug/UI-sweep hook: force the RPC command-reference popup open/closed so the full UI sweep
|
|
// can capture it (the popup is otherwise opened only by a toolbar button).
|
|
void sweepSetCommandsPopup(bool open) { show_commands_popup_ = open; }
|
|
|
|
/**
|
|
* @brief Add a line to the console output
|
|
*/
|
|
void addLine(const std::string& line, ConsoleChannel channel = ConsoleChannel::None);
|
|
void addRpcTraceLine(const std::string& source, const std::string& method);
|
|
|
|
/**
|
|
* @brief Clear console output
|
|
*/
|
|
void clear();
|
|
|
|
// Masked secret-result support: the plaintext of a key-revealing command's output is held ONLY here
|
|
// (never in the scrollback model), so Copy All / Ctrl+C / the filter can't leak it. An explicit
|
|
// "Copy secret" action routes through this handler (the app wires it to the auto-clearing clipboard).
|
|
void setCopySecretHandler(std::function<void(const std::string&)> f) { copy_secret_cb_ = std::move(f); }
|
|
bool hasSecretResult() const { return !secret_result_.empty(); }
|
|
void wipeSecretResult(); // zero + drop the buffer, disarm, reset the deadline (call on lock/clear)
|
|
|
|
// Ask the console to place the keyboard focus in the command input on the next render (consumed once).
|
|
// Called when the user switches to the Console tab, gated by the console_auto_focus setting.
|
|
void requestInputFocus() { focus_input_pending_ = true; }
|
|
|
|
// 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;
|
|
|
|
// Draw the per-line left color accent bars (channel-colored). Toggled from the toolbar.
|
|
static bool s_line_accents_enabled;
|
|
|
|
// Color output text per channel. When false the console is monochrome text. Toolbar-toggled.
|
|
static bool s_line_text_color_enabled;
|
|
|
|
// 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:
|
|
// Text color for a channel, resolved from the current theme (COLOR_* + material). Each
|
|
// line stores only its semantic ConsoleChannel (in ConsoleModel); the text color and
|
|
// the left accent-bar color are both derived from it at draw time.
|
|
ImU32 channelTextColor(ConsoleChannel channel) const;
|
|
// Saturated accent color for a channel — the left accent-bar color, also used to tint the
|
|
// matching toolbar filter toggle. 0 for channels that get no accent (result/JSON/None).
|
|
static ImU32 channelAccentColor(ConsoleChannel channel);
|
|
|
|
// Drain the thread-safe ingest queue into the visible model (once per frame, main
|
|
// thread), applying the line cap + selection/scroll bookkeeping for the new lines.
|
|
void drainModel();
|
|
|
|
// Format a completed command result (JSON role -> channel) into console lines.
|
|
void addFormattedResult(const std::string& result, bool is_error);
|
|
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
|
void renderCommandsPopup(ConsoleCommandExecutor& exec);
|
|
// right pane; jsonArgs=true (full-node RPC) auto-quotes string args, false (lite) inserts bare
|
|
void renderCommandDetail(const ConsoleCommandEntry& cmd, const char* catLabel, bool jsonArgs);
|
|
void insertCommandToInput(const ConsoleCommandEntry& cmd); // fill input + close the modal
|
|
|
|
// renderToolbar() draws the top bar; these are its sub-steps:
|
|
void renderToolbar(ConsoleCommandExecutor& exec);
|
|
void drawToolbarStatus(ConsoleCommandExecutor& exec); // backend status dot + label
|
|
void drawLogFilterToggles(const ConsoleLogFilterCaps& caps); // color-coded filter checkboxes
|
|
void drawFilterInput(); // text filter + match count
|
|
void drawZoomControls(); // zoom -/+ buttons
|
|
|
|
// renderInput() draws the command line; dispatch is factored out:
|
|
void renderInput(ConsoleCommandExecutor& exec);
|
|
// Echo + handle a submitted command line (built-ins + submit). True if non-empty/handled.
|
|
bool submitConsoleCommand(ConsoleCommandExecutor& exec, const std::string& cmd);
|
|
|
|
// renderOutput() orchestrates the scrollable output area; these are its sub-steps:
|
|
void renderOutput();
|
|
void computeVisibleLines(bool& hasTextFilter, std::string& filterLower); // -> visible_indices_
|
|
void handleOutputInteraction(ImVec2 mousePos, bool mouseInOutput); // wheel/selection/keys
|
|
void drawVisibleLines(float padX, float lineHeight, bool hasTextFilter,
|
|
const std::string& filterLower); // bars/guides/highlight/text
|
|
void drawNewOutputIndicator(); // jump-to-bottom pill
|
|
void drawOutputContextMenu(); // right-click menu
|
|
|
|
// Map a screen point to a raw {line, col} position via this frame's wrapped layout.
|
|
// (View-side because it needs layout_ + output_origin_; feeds the selection controller.)
|
|
ConsoleTextPos screenToTextPos(ImVec2 screen_pos) const;
|
|
|
|
// The current selection's text (via selection_ over the visible model). "" if inactive.
|
|
std::string selectedText() const;
|
|
|
|
// Thread-safe line store: producers on any thread call model_.ingest(); the main
|
|
// thread drains it once per frame in render(). The visible deque is main-thread-only.
|
|
ConsoleModel model_;
|
|
std::vector<std::string> command_history_;
|
|
int history_index_ = -1;
|
|
char input_buffer_[4096] = {0};
|
|
bool stop_confirm_pending_ = false; // 'stop' typed once, awaiting a confirming second 'stop'
|
|
std::string destructive_confirm_cmd_; // a destructive command typed once, awaiting an identical re-entry
|
|
bool secret_result_armed_ = false; // the just-submitted command reveals key material in its result
|
|
std::string secret_result_; // plaintext of the masked result (member-only; never in model_)
|
|
std::chrono::steady_clock::time_point secret_result_deadline_{}; // auto-wipe the buffer at this time
|
|
std::function<void(const std::string&)> copy_secret_cb_; // routes "Copy secret" to the app
|
|
bool focus_input_pending_ = false; // one-shot: focus the command input next render (tab-open auto-focus)
|
|
// (log-ingestion cursors + result queue moved to the ConsoleCommandExecutor)
|
|
|
|
// Auto-scroll state machine (pin-to-bottom, wheel-up cooldown, new-line backlog count).
|
|
ConsoleScrollController scroll_;
|
|
|
|
// Text-selection state + logic (anchor/caret, ordering, extraction, eviction shift).
|
|
ConsoleSelectionController selection_;
|
|
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_;
|
|
|
|
// Output filter
|
|
char filter_text_[128] = {0};
|
|
mutable int filter_match_count_ = 0; // lines matching the text filter (for the toolbar)
|
|
std::string context_token_; // hash/address under the cursor at right-click
|
|
mutable std::vector<int> visible_indices_; // Cached for selection mapping
|
|
bool folding_active_ = true; // fold UI shown only in the unfiltered (foldable) view
|
|
bool has_text_filter_ = false; // computed once per frame (before the toolbar draws it)
|
|
std::string filter_lower_; // lowercased filter needle for match highlighting
|
|
// Flat (filtered) view bookkeeping for the incremental computeVisibleLines path: the exact filter and the
|
|
// absolute model window [vis_flat_first_, vis_flat_end_) the current visible_indices_ were computed for.
|
|
bool vis_flat_valid_ = false;
|
|
int vis_flat_flags_ = 0;
|
|
std::string vis_flat_filter_;
|
|
std::uint64_t vis_flat_first_ = 0;
|
|
std::uint64_t vis_flat_end_ = 0;
|
|
|
|
// Memoization keys so the two expensive per-frame passes rebuild only on change (not every frame):
|
|
// computeVisibleLines (filter scan over the whole model) is keyed on the model revision + filter
|
|
// state; BuildConsoleLayout (glyph-by-glyph text shaping of every visible line) is keyed on the
|
|
// resulting visible-set generation + wrap width + line height/zoom.
|
|
std::uint64_t vis_key_ = ~0ull; // key of the last computeVisibleLines
|
|
std::uint64_t vis_generation_ = 0; // bumped whenever visible_indices_ is rebuilt
|
|
std::uint64_t layout_key_ = ~0ull; // key of the last layout assembly
|
|
|
|
// Per-raw-line wrap cache (index == model_ index after sync). layout_.segments point INTO it, so it is only
|
|
// mutated by sync() immediately followed by AssembleConsoleLayout (renderOutput), or by reset() together
|
|
// with clearing layout_ (clear()). Touch it nowhere else.
|
|
ConsoleWrapCache wrap_cache_;
|
|
|
|
// Wrap layout for the visible lines (segments + per-line heights + cumulative Y), re-assembled from
|
|
// wrap_cache_ when the visible set / geometry changes, and consumed by the renderer + hit-testing.
|
|
mutable ConsoleLayout layout_;
|
|
|
|
// Commands popup (command explorer) — indexes into the active executor's commandReference()
|
|
// table (full-node RPC commands or the lite backend verbs), not always the full-node one.
|
|
bool show_commands_popup_ = false;
|
|
char command_search_[128] = {0}; // reference search filter (cleared when the modal opens)
|
|
int cmd_sel_cat_ = -1; // detail-pane selection: category index into that table
|
|
int cmd_sel_idx_ = -1; // detail-pane selection: command index within that category
|
|
std::string pending_submit_; // command to run next frame (deferred so the modal needs no executor)
|
|
const ConsoleCommandEntry* run_confirm_cmd_ = nullptr; // destructive "Insert & run" awaiting confirmation
|
|
char cmd_param_bufs_[6][256] = {{0}}; // parameter-builder input fields
|
|
const ConsoleCommandEntry* cmd_param_owner_ = nullptr; // command the param buffers currently hold
|
|
};
|
|
|
|
} // namespace ui
|
|
} // namespace dragonx
|