refactor(console): unify lite + full-node console behind a pluggable executor
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>
This commit is contained in:
@@ -6,6 +6,7 @@
|
||||
// tab completion, daemon log display, and color-coded output.
|
||||
|
||||
#include "console_tab.h"
|
||||
#include "console_command_executor.h"
|
||||
#include "console_command_reference.h"
|
||||
#include "console_input_model.h"
|
||||
#include "console_output_model.h"
|
||||
@@ -23,6 +24,7 @@
|
||||
#include "../../util/i18n.h"
|
||||
|
||||
#include <imgui.h>
|
||||
#include <cctype>
|
||||
#include <cstring>
|
||||
#include <sstream>
|
||||
#include <algorithm>
|
||||
@@ -143,7 +145,7 @@ ConsoleTab::~ConsoleTab()
|
||||
if (s_rpc_trace_console == this) s_rpc_trace_console = nullptr;
|
||||
}
|
||||
|
||||
void ConsoleTab::render(daemon::EmbeddedDaemon* daemon, rpc::RPCClient* rpc, rpc::RPCWorker* worker, daemon::XmrigManager* xmrig)
|
||||
void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
||||
{
|
||||
using namespace material;
|
||||
|
||||
@@ -173,103 +175,25 @@ void ConsoleTab::render(daemon::EmbeddedDaemon* daemon, rpc::RPCClient* rpc, rpc
|
||||
}
|
||||
}
|
||||
|
||||
// Check for daemon state changes
|
||||
if (daemon) {
|
||||
auto current_state = daemon->getState();
|
||||
|
||||
// Show message when daemon starts
|
||||
if (current_state == daemon::EmbeddedDaemon::State::Starting &&
|
||||
last_daemon_state_ == daemon::EmbeddedDaemon::State::Stopped) {
|
||||
addLine("", COLOR_RESULT);
|
||||
addLine(TR("console_starting_node"), COLOR_INFO);
|
||||
addLine(TR("console_capturing_output"), COLOR_INFO);
|
||||
addLine("", COLOR_RESULT);
|
||||
shown_startup_message_ = true;
|
||||
}
|
||||
else if (current_state == daemon::EmbeddedDaemon::State::Running &&
|
||||
last_daemon_state_ != daemon::EmbeddedDaemon::State::Running) {
|
||||
addLine(TR("console_daemon_started"), COLOR_INFO);
|
||||
}
|
||||
else if (current_state == daemon::EmbeddedDaemon::State::Stopped &&
|
||||
last_daemon_state_ == daemon::EmbeddedDaemon::State::Running) {
|
||||
addLine("", COLOR_RESULT);
|
||||
addLine(TR("console_daemon_stopped"), COLOR_INFO);
|
||||
}
|
||||
else if (current_state == daemon::EmbeddedDaemon::State::Error) {
|
||||
addLine(std::string(TR("console_daemon_error")) + daemon->getLastError() + " ===", COLOR_ERROR);
|
||||
}
|
||||
|
||||
last_daemon_state_ = current_state;
|
||||
// Pull passive log lines (daemon/xmrig output, or the lite diagnostics ring) and any
|
||||
// completed command results from the backend executor.
|
||||
exec.pollLogLines([this](const std::string& l, ImU32 c) { addLine(l, c); });
|
||||
{
|
||||
std::string result;
|
||||
bool isError = false;
|
||||
while (exec.pollResult(result, isError)) addFormattedResult(result, isError);
|
||||
}
|
||||
|
||||
// Track RPC connection state and show a message when connected
|
||||
if (rpc) {
|
||||
bool connected_now = rpc->isConnected();
|
||||
if (connected_now && !last_rpc_connected_) {
|
||||
addLine(TR("console_connected"), COLOR_INFO);
|
||||
} else if (!connected_now && last_rpc_connected_) {
|
||||
addLine(TR("console_disconnected"), COLOR_ERROR);
|
||||
}
|
||||
last_rpc_connected_ = connected_now;
|
||||
}
|
||||
|
||||
// Check for new daemon output — always capture so toggle works as a live filter
|
||||
if (daemon) {
|
||||
std::string new_output = daemon->getOutputSince(last_daemon_output_size_);
|
||||
if (!new_output.empty()) {
|
||||
// Split by newlines and add each line
|
||||
std::istringstream stream(new_output);
|
||||
std::string line;
|
||||
while (std::getline(stream, line)) {
|
||||
if (!line.empty()) {
|
||||
// Color based on content: [ERROR] -> red, [WARN] -> warning color
|
||||
ImU32 lineColor = COLOR_DAEMON;
|
||||
if (line.find("[ERROR]") != std::string::npos ||
|
||||
line.find("error:") != std::string::npos ||
|
||||
line.find("Error:") != std::string::npos) {
|
||||
lineColor = COLOR_ERROR;
|
||||
}
|
||||
addLine("[daemon] " + line, lineColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check for new xmrig output (pool mining)
|
||||
if (xmrig && xmrig->isRunning()) {
|
||||
std::string new_output = xmrig->getOutputSince(last_xmrig_output_size_);
|
||||
if (!new_output.empty()) {
|
||||
std::istringstream stream(new_output);
|
||||
std::string line;
|
||||
while (std::getline(stream, line)) {
|
||||
if (!line.empty()) {
|
||||
// Color xmrig output - errors in red, accepted shares in green
|
||||
ImU32 lineColor = COLOR_DAEMON;
|
||||
if (line.find("error") != std::string::npos ||
|
||||
line.find("ERROR") != std::string::npos ||
|
||||
line.find("failed") != std::string::npos) {
|
||||
lineColor = COLOR_ERROR;
|
||||
} else if (line.find("accepted") != std::string::npos) {
|
||||
lineColor = COLOR_INFO;
|
||||
}
|
||||
addLine("[xmrig] " + line, lineColor);
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (!xmrig || !xmrig->isRunning()) {
|
||||
// Reset offset when xmrig stops so we get fresh output next time
|
||||
if (last_xmrig_output_size_ != 0) {
|
||||
last_xmrig_output_size_ = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// Main console layout
|
||||
ImGui::BeginChild("ConsoleContainer", ImVec2(0, 0), false,
|
||||
ImGuiWindowFlags_NoBackground | ImGuiWindowFlags_NoScrollbar);
|
||||
|
||||
// Optional status header (lite: sync + last error) above the toolbar.
|
||||
renderStatusHeader(exec);
|
||||
|
||||
// Toolbar
|
||||
renderToolbar(daemon);
|
||||
|
||||
renderToolbar(exec);
|
||||
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Output area (scrollable) — glass panel background
|
||||
@@ -384,8 +308,8 @@ void ConsoleTab::render(daemon::EmbeddedDaemon* daemon, rpc::RPCClient* rpc, rpc
|
||||
ImGui::Dummy(ImVec2(0, Layout::spacingSm()));
|
||||
|
||||
// Input area
|
||||
renderInput(rpc, worker);
|
||||
|
||||
renderInput(exec);
|
||||
|
||||
ImGui::EndChild();
|
||||
}
|
||||
|
||||
@@ -397,7 +321,7 @@ void ConsoleTab::renderCommandsPopupModal()
|
||||
renderCommandsPopup();
|
||||
}
|
||||
|
||||
void ConsoleTab::renderToolbar(daemon::EmbeddedDaemon* daemon)
|
||||
void ConsoleTab::renderToolbar(ConsoleCommandExecutor& exec)
|
||||
{
|
||||
using namespace material;
|
||||
ImDrawList* dl = ImGui::GetWindowDrawList();
|
||||
@@ -414,56 +338,29 @@ void ConsoleTab::renderToolbar(daemon::EmbeddedDaemon* daemon)
|
||||
ImGui::SetCursorPosY(ImGui::GetCursorPosY() + (toolbarH - ImGui::GetFrameHeight()) * 0.5f);
|
||||
ImGui::SetCursorPosX(ImGui::GetCursorPosX() + Layout::spacingMd());
|
||||
|
||||
// Daemon status with colored dot
|
||||
if (daemon) {
|
||||
auto state = daemon->getState();
|
||||
const char* status_text = TR("console_status_unknown");
|
||||
ImU32 dotCol = IM_COL32(150, 150, 150, 255);
|
||||
bool pulse = false;
|
||||
// Backend status with colored dot (daemon state / lite connection).
|
||||
{
|
||||
ConsoleStatusLine st = exec.toolbarStatus();
|
||||
if (!st.text.empty()) {
|
||||
ImVec2 cp = ImGui::GetCursorScreenPos();
|
||||
float dotR = schema::UI().drawElement("tabs.console", "status-dot-radius-base").size + schema::UI().drawElement("tabs.console", "status-dot-radius-scale").size * Layout::hScale();
|
||||
float dotY = cp.y + ImGui::GetTextLineHeight() * 0.5f;
|
||||
float dotX = cp.x + dotR + 2;
|
||||
|
||||
switch (state) {
|
||||
case daemon::EmbeddedDaemon::State::Stopped:
|
||||
status_text = TR("console_status_stopped");
|
||||
dotCol = IM_COL32(150, 150, 150, 255);
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Starting:
|
||||
status_text = TR("console_status_starting");
|
||||
dotCol = Warning();
|
||||
pulse = true;
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Running:
|
||||
status_text = TR("console_status_running");
|
||||
dotCol = Success();
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Stopping:
|
||||
status_text = TR("console_status_stopping");
|
||||
dotCol = Warning();
|
||||
pulse = true;
|
||||
break;
|
||||
case daemon::EmbeddedDaemon::State::Error:
|
||||
status_text = TR("console_status_error");
|
||||
dotCol = Error();
|
||||
break;
|
||||
}
|
||||
if (st.pulse) {
|
||||
float a = schema::UI().drawElement("animations", "pulse-base-glow").size + schema::UI().drawElement("animations", "pulse-amp-glow").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-fast").size);
|
||||
ImU32 pCol = (st.color & 0x00FFFFFF) | ((ImU32)(255 * a) << 24);
|
||||
dl->AddCircleFilled(ImVec2(dotX, dotY), dotR, pCol);
|
||||
} else {
|
||||
dl->AddCircleFilled(ImVec2(dotX, dotY), dotR, st.color);
|
||||
}
|
||||
|
||||
ImVec2 cp = ImGui::GetCursorScreenPos();
|
||||
float dotR = schema::UI().drawElement("tabs.console", "status-dot-radius-base").size + schema::UI().drawElement("tabs.console", "status-dot-radius-scale").size * Layout::hScale();
|
||||
float dotY = cp.y + ImGui::GetTextLineHeight() * 0.5f;
|
||||
float dotX = cp.x + dotR + 2;
|
||||
|
||||
if (pulse) {
|
||||
float a = schema::UI().drawElement("animations", "pulse-base-glow").size + schema::UI().drawElement("animations", "pulse-amp-glow").size * (float)std::sin((double)ImGui::GetTime() * schema::UI().drawElement("animations", "pulse-speed-fast").size);
|
||||
ImU32 pCol = (dotCol & 0x00FFFFFF) | ((ImU32)(255 * a) << 24);
|
||||
dl->AddCircleFilled(ImVec2(dotX, dotY), dotR, pCol);
|
||||
ImGui::Dummy(ImVec2(dotR * 2 + 6, 0));
|
||||
ImGui::SameLine();
|
||||
Type().textColored(TypeStyle::Caption, st.color, st.text.c_str());
|
||||
} else {
|
||||
dl->AddCircleFilled(ImVec2(dotX, dotY), dotR, dotCol);
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("console_no_daemon"));
|
||||
}
|
||||
|
||||
ImGui::Dummy(ImVec2(dotR * 2 + 6, 0));
|
||||
ImGui::SameLine();
|
||||
Type().textColored(TypeStyle::Caption, dotCol, status_text);
|
||||
} else {
|
||||
Type().textColored(TypeStyle::Caption, OnSurfaceDisabled(), TR("console_no_daemon"));
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
@@ -482,6 +379,8 @@ void ConsoleTab::renderToolbar(daemon::EmbeddedDaemon* daemon)
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
|
||||
// Log filters (daemon/errors/rpc-trace/app) apply only to a daemon log source.
|
||||
if (exec.hasLogFilters()) {
|
||||
// Daemon messages toggle
|
||||
{
|
||||
static bool s_prev_daemon_enabled = true;
|
||||
@@ -554,6 +453,8 @@ void ConsoleTab::renderToolbar(daemon::EmbeddedDaemon* daemon)
|
||||
ImGui::Spacing();
|
||||
ImGui::SameLine();
|
||||
|
||||
} // exec.hasLogFilters()
|
||||
|
||||
// Clear button
|
||||
if (TactileButton(TR("console_clear"), ImVec2(0, 0), schema::UI().resolveFont("button"))) {
|
||||
clear();
|
||||
@@ -587,15 +488,16 @@ void ConsoleTab::renderToolbar(daemon::EmbeddedDaemon* daemon)
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Commands reference button
|
||||
if (TactileButton(TR("console_commands"), ImVec2(0, 0), schema::UI().resolveFont("button"))) {
|
||||
show_commands_popup_ = true;
|
||||
// Commands reference button (full-node RPC reference only)
|
||||
if (exec.hasRpcReference()) {
|
||||
if (TactileButton(TR("console_commands"), ImVec2(0, 0), schema::UI().resolveFont("button"))) {
|
||||
show_commands_popup_ = true;
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
material::Tooltip("%s", TR("console_show_rpc_ref"));
|
||||
}
|
||||
ImGui::SameLine();
|
||||
}
|
||||
if (ImGui::IsItemHovered()) {
|
||||
material::Tooltip("%s", TR("console_show_rpc_ref"));
|
||||
}
|
||||
|
||||
ImGui::SameLine();
|
||||
|
||||
// Line count
|
||||
{
|
||||
@@ -1192,7 +1094,7 @@ void ConsoleTab::clearSelection()
|
||||
sel_end_ = {-1, 0};
|
||||
}
|
||||
|
||||
void ConsoleTab::renderInput(rpc::RPCClient* rpc, rpc::RPCWorker* worker)
|
||||
void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
||||
{
|
||||
using namespace material;
|
||||
|
||||
@@ -1269,15 +1171,49 @@ void ConsoleTab::renderInput(rpc::RPCClient* rpc, rpc::RPCWorker* worker)
|
||||
return 0;
|
||||
};
|
||||
|
||||
const bool busy = exec.busy();
|
||||
if (busy) ImGui::BeginDisabled();
|
||||
if (ImGui::InputText("##ConsoleInput", input_buffer_, sizeof(input_buffer_), flags, callback, this)) {
|
||||
std::string cmd(input_buffer_);
|
||||
// Trim surrounding whitespace.
|
||||
size_t tb = cmd.find_first_not_of(" \t");
|
||||
if (tb == std::string::npos) cmd.clear(); else cmd = cmd.substr(tb);
|
||||
while (!cmd.empty() && (cmd.back() == ' ' || cmd.back() == '\t')) cmd.pop_back();
|
||||
|
||||
if (!cmd.empty()) {
|
||||
executeCommand(cmd, rpc, worker);
|
||||
addLine("> " + cmd, COLOR_COMMAND);
|
||||
AppendConsoleHistory(command_history_, cmd, 100);
|
||||
history_index_ = -1;
|
||||
|
||||
// First token, lowercased, for built-in interception.
|
||||
std::string first;
|
||||
{
|
||||
size_t fb = cmd.find_first_not_of(" \t");
|
||||
size_t fe = cmd.find_first_of(" \t", fb);
|
||||
first = cmd.substr(fb, fe == std::string::npos ? std::string::npos : fe - fb);
|
||||
std::transform(first.begin(), first.end(), first.begin(),
|
||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||
}
|
||||
auto add = [this](const std::string& l, ImU32 c) { addLine(l, c); };
|
||||
if (first == "clear" || first == "cls") {
|
||||
// View-only clear — NEVER forwarded (the lite backend's `clear` wipes tx history).
|
||||
clear();
|
||||
clearSelection();
|
||||
} else if (first == "help") {
|
||||
exec.printHelp(add);
|
||||
} else if (first == "quit" || first == "exit") {
|
||||
addLine(TR("console_quit_note"), COLOR_INFO);
|
||||
} else if (!exec.isReady()) {
|
||||
addLine(TR("console_not_connected"), COLOR_ERROR);
|
||||
} else {
|
||||
exec.submit(cmd);
|
||||
}
|
||||
input_buffer_[0] = '\0';
|
||||
reclaim_focus = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (busy) ImGui::EndDisabled();
|
||||
|
||||
ImGui::PopItemWidth();
|
||||
|
||||
// Auto-focus on input
|
||||
@@ -1480,106 +1416,37 @@ void ConsoleTab::renderCommandsPopup()
|
||||
material::EndOverlayDialog();
|
||||
}
|
||||
|
||||
void ConsoleTab::executeCommand(const std::string& cmd, rpc::RPCClient* rpc, rpc::RPCWorker* worker)
|
||||
void ConsoleTab::addFormattedResult(const std::string& result, bool is_error)
|
||||
{
|
||||
using namespace material;
|
||||
// Add to history (avoid duplicates)
|
||||
AppendConsoleHistory(command_history_, cmd, 100);
|
||||
history_index_ = -1;
|
||||
|
||||
// Echo command
|
||||
addLine("> " + cmd, COLOR_COMMAND);
|
||||
|
||||
// Handle built-in commands
|
||||
if (cmd == "clear") {
|
||||
clear();
|
||||
return;
|
||||
}
|
||||
|
||||
if (cmd == "help") {
|
||||
addLine(TR("console_available_commands"), COLOR_INFO);
|
||||
addLine(TR("console_help_clear"), COLOR_RESULT);
|
||||
addLine(TR("console_help_help"), COLOR_RESULT);
|
||||
addLine("", COLOR_RESULT);
|
||||
addLine(TR("console_common_rpc"), COLOR_INFO);
|
||||
addLine(TR("console_help_getinfo"), COLOR_RESULT);
|
||||
addLine(TR("console_help_getbalance"), COLOR_RESULT);
|
||||
addLine(TR("console_help_gettotalbalance"), COLOR_RESULT);
|
||||
addLine(TR("console_help_getblockcount"), COLOR_RESULT);
|
||||
addLine(TR("console_help_getpeerinfo"), COLOR_RESULT);
|
||||
addLine(TR("console_help_setgenerate"), COLOR_RESULT);
|
||||
addLine(TR("console_help_getmininginfo"), COLOR_RESULT);
|
||||
addLine(TR("console_help_stop"), COLOR_RESULT);
|
||||
addLine("", COLOR_RESULT);
|
||||
addLine(TR("console_click_commands"), COLOR_INFO);
|
||||
addLine(TR("console_tab_completion"), COLOR_INFO);
|
||||
return;
|
||||
}
|
||||
|
||||
// Execute RPC command
|
||||
if (!rpc || !rpc->isConnected()) {
|
||||
addLine(TR("console_not_connected"), COLOR_ERROR);
|
||||
return;
|
||||
}
|
||||
|
||||
auto call = BuildConsoleRpcCall(cmd);
|
||||
if (!call.valid) return;
|
||||
ImU32 json_key_col = WithAlpha(Secondary(), 255);
|
||||
ImU32 json_str_col = WithAlpha(Success(), 255);
|
||||
ImU32 json_num_col = WithAlpha(Warning(), 255);
|
||||
ImU32 json_brace_col = IM_COL32(200, 200, 200, 150);
|
||||
|
||||
std::string method = call.method;
|
||||
nlohmann::json params = call.params;
|
||||
|
||||
// Execute RPC call on worker thread to avoid blocking UI
|
||||
if (worker) {
|
||||
// Capture 'this' for addLine calls in MainCb (runs on main thread, ConsoleTab outlives callbacks)
|
||||
auto self = this;
|
||||
worker->post([rpc, method, params, self]() -> rpc::RPCWorker::MainCb {
|
||||
std::string result_str;
|
||||
bool is_error = false;
|
||||
try {
|
||||
rpc::RPCClient::TraceScope trace("Console tab / User command");
|
||||
result_str = rpc->callRaw(method, params);
|
||||
} catch (const std::exception& e) {
|
||||
result_str = e.what();
|
||||
is_error = true;
|
||||
}
|
||||
return [result_str, is_error, self]() {
|
||||
// Process results on main thread where ImGui colors are available
|
||||
using namespace material;
|
||||
ImU32 json_key_col = WithAlpha(Secondary(), 255);
|
||||
ImU32 json_str_col = WithAlpha(Success(), 255);
|
||||
ImU32 json_num_col = WithAlpha(Warning(), 255);
|
||||
ImU32 json_brace_col = IM_COL32(200, 200, 200, 150);
|
||||
|
||||
for (const auto& resultLine : FormatConsoleRpcResultLines(result_str, is_error)) {
|
||||
ImU32 lineCol = COLOR_RESULT;
|
||||
switch (resultLine.role) {
|
||||
case ConsoleResultLineRole::Error: lineCol = COLOR_ERROR; break;
|
||||
case ConsoleResultLineRole::JsonKey: lineCol = json_key_col; break;
|
||||
case ConsoleResultLineRole::JsonString: lineCol = json_str_col; break;
|
||||
case ConsoleResultLineRole::JsonNumber: lineCol = json_num_col; break;
|
||||
case ConsoleResultLineRole::JsonBrace: lineCol = json_brace_col; break;
|
||||
case ConsoleResultLineRole::Result: break;
|
||||
}
|
||||
self->addLine(resultLine.text, lineCol);
|
||||
}
|
||||
};
|
||||
});
|
||||
} else {
|
||||
// Fallback: synchronous execution if no worker available
|
||||
try {
|
||||
rpc::RPCClient::TraceScope trace("Console tab / User command");
|
||||
std::string result_str = rpc->callRaw(method, params);
|
||||
for (const auto& resultLine : FormatConsoleRpcResultLines(result_str, false)) {
|
||||
addLine(resultLine.text, COLOR_RESULT);
|
||||
}
|
||||
} catch (const std::exception& e) {
|
||||
for (const auto& resultLine : FormatConsoleRpcResultLines(e.what(), true)) {
|
||||
addLine(resultLine.text, COLOR_ERROR);
|
||||
}
|
||||
for (const auto& resultLine : FormatConsoleRpcResultLines(result, is_error)) {
|
||||
ImU32 lineCol = COLOR_RESULT;
|
||||
switch (resultLine.role) {
|
||||
case ConsoleResultLineRole::Error: lineCol = COLOR_ERROR; break;
|
||||
case ConsoleResultLineRole::JsonKey: lineCol = json_key_col; break;
|
||||
case ConsoleResultLineRole::JsonString: lineCol = json_str_col; break;
|
||||
case ConsoleResultLineRole::JsonNumber: lineCol = json_num_col; break;
|
||||
case ConsoleResultLineRole::JsonBrace: lineCol = json_brace_col; break;
|
||||
case ConsoleResultLineRole::Result: break;
|
||||
}
|
||||
addLine(resultLine.text, lineCol);
|
||||
}
|
||||
}
|
||||
|
||||
void ConsoleTab::renderStatusHeader(ConsoleCommandExecutor& exec)
|
||||
{
|
||||
using namespace material;
|
||||
auto lines = exec.statusLines();
|
||||
for (const auto& sl : lines)
|
||||
Type().textColored(TypeStyle::Caption, sl.color, sl.text.c_str());
|
||||
if (!lines.empty()) ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||
}
|
||||
|
||||
void ConsoleTab::addLine(const std::string& line, ImU32 color)
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lines_mutex_);
|
||||
@@ -1635,10 +1502,9 @@ void ConsoleTab::clear()
|
||||
{
|
||||
std::lock_guard<std::mutex> lock(lines_mutex_);
|
||||
lines_.clear();
|
||||
last_daemon_output_size_ = 0;
|
||||
last_xmrig_output_size_ = 0;
|
||||
}
|
||||
// addLine() takes the lock itself, so call it outside the locked scope
|
||||
// The executor keeps its own log cursors, so a view-clear stays cleared (new output
|
||||
// still appends). addLine() takes the lock itself, so call it outside the locked scope.
|
||||
addLine(TR("console_cleared"), COLOR_INFO);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user