refactor(console): phase 2 — channel-as-semantic key; decouple executor from UI colors
Make ConsoleChannel the canonical semantic classification of every console
line. Producers (command executor, app log forwarders, result formatter) tag
each line with a channel; the UI derives both the text color and the left
accent-bar color from it at draw time, and the output filter keys off it.
This replaces the prior scheme of storing an ImU32 color per line and then
reverse-engineering the source from color-equality plus a "[daemon] "/"[xmrig] "
/"[app] "/"[rpc] " text prefix. Consequences:
- ConsoleLine now stores {text, ConsoleChannel} — no per-line color.
- addLine()/addRpcTraceLine() take a channel; the redundant text prefixes are
gone (the accent bar carries the origin).
- The executor's ConsoleAddLineFn hands a channel, not an ImU32, so the backend
no longer depends on ConsoleTab::COLOR_* — FullNode/Lite executors emit clean
text + channel.
- The theme-remap loop that rewrote stored colors on light/dark flip is deleted;
colors are resolved per-channel each frame (refreshColors on flip only).
- ConsoleOutputFilter drops its color fields; consoleLinePassesFilter() keys off
the channel. Test updated to the channel-based API.
- Added a Warning channel (amber severity peer of Error/Success) so the
notification + logger warning forwarders keep their color.
Full-node + lite build clean; ctest green; source hygiene clean.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
24
src/app.cpp
24
src/app.cpp
@@ -396,37 +396,39 @@ bool App::init()
|
|||||||
worker_->start();
|
worker_->start();
|
||||||
network_refresh_.markDue(services::NetworkRefreshService::Timer::Price);
|
network_refresh_.markDue(services::NetworkRefreshService::Timer::Price);
|
||||||
|
|
||||||
// Forward error/warning notifications to the console tab
|
// Forward error/warning notifications to the console tab. The channel is the semantic
|
||||||
// Use ConsoleTab colors so the Errors filter works correctly
|
// key — the Errors filter keys off ConsoleChannel::Error, and the color/accent bar are
|
||||||
|
// derived from the channel at draw time.
|
||||||
ui::Notifications::instance().setConsoleCallback(
|
ui::Notifications::instance().setConsoleCallback(
|
||||||
[this](const std::string& msg, bool is_error) {
|
[this](const std::string& msg, bool is_error) {
|
||||||
ImU32 color = is_error ? ui::ConsoleTab::COLOR_ERROR : ui::material::Warning();
|
console_tab_.addLine(msg, is_error ? ui::ConsoleChannel::Error
|
||||||
console_tab_.addLine(msg, color);
|
: ui::ConsoleChannel::Warning);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Forward all app-level log messages (DEBUG_LOGF, LOGF, etc.) to the
|
// Forward all app-level log messages (DEBUG_LOGF, LOGF, etc.) to the
|
||||||
// console tab so they are visible in the UI, not just in the log file.
|
// console tab so they are visible in the UI, not just in the log file.
|
||||||
util::Logger::instance().setCallback(
|
util::Logger::instance().setCallback(
|
||||||
[this](const std::string& msg) {
|
[this](const std::string& msg) {
|
||||||
// Classify by content: errors in red, warnings in warning color,
|
// Classify by content into a severity channel: errors, warnings, else the App
|
||||||
// everything else in the default info color.
|
// channel (the routine wallet log stream, controlled by the App messages filter).
|
||||||
ImU32 color = ui::ConsoleTab::COLOR_INFO;
|
ui::ConsoleChannel channel = ui::ConsoleChannel::App;
|
||||||
if (msg.find("[ERROR]") != std::string::npos ||
|
if (msg.find("[ERROR]") != std::string::npos ||
|
||||||
msg.find("error") != std::string::npos ||
|
msg.find("error") != std::string::npos ||
|
||||||
msg.find("Error") != std::string::npos ||
|
msg.find("Error") != std::string::npos ||
|
||||||
msg.find("failed") != std::string::npos ||
|
msg.find("failed") != std::string::npos ||
|
||||||
msg.find("Failed") != std::string::npos) {
|
msg.find("Failed") != std::string::npos) {
|
||||||
color = ui::ConsoleTab::COLOR_ERROR;
|
channel = ui::ConsoleChannel::Error;
|
||||||
} else if (msg.find("[WARN]") != std::string::npos ||
|
} else if (msg.find("[WARN]") != std::string::npos ||
|
||||||
msg.find("warn") != std::string::npos) {
|
msg.find("warn") != std::string::npos) {
|
||||||
color = ui::material::Warning();
|
channel = ui::ConsoleChannel::Warning;
|
||||||
}
|
}
|
||||||
// Strip trailing newline so console tab lines look clean
|
// Strip trailing newline so console tab lines look clean. The App channel
|
||||||
|
// supplies the accent bar; no "[app] " text prefix needed.
|
||||||
std::string trimmed = msg;
|
std::string trimmed = msg;
|
||||||
while (!trimmed.empty() && (trimmed.back() == '\n' || trimmed.back() == '\r'))
|
while (!trimmed.empty() && (trimmed.back() == '\n' || trimmed.back() == '\r'))
|
||||||
trimmed.pop_back();
|
trimmed.pop_back();
|
||||||
if (!trimmed.empty())
|
if (!trimmed.empty())
|
||||||
console_tab_.addLine("[app] " + trimmed, color);
|
console_tab_.addLine(trimmed, channel);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Check for first-run wizard — also re-run if blockchain data is missing
|
// Check for first-run wizard — also re-run if blockchain data is missing
|
||||||
|
|||||||
35
src/ui/windows/console_channel.h
Normal file
35
src/ui/windows/console_channel.h
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
// DragonX Wallet - ImGui Edition
|
||||||
|
// Copyright 2024-2026 The Hush Developers
|
||||||
|
// Released under the GPLv3
|
||||||
|
//
|
||||||
|
// ConsoleChannel — the semantic classification of a console line. It is the canonical
|
||||||
|
// key: producers (the command executor, app log forwarders, the result formatter) tag
|
||||||
|
// each line with a channel, the UI derives both the text color and the left accent-bar
|
||||||
|
// color from it at draw time, and the output filter keys off it. This replaces the older
|
||||||
|
// approach of storing an ImU32 color and reverse-engineering the source from color
|
||||||
|
// equality + a "[daemon] " text prefix.
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
namespace dragonx {
|
||||||
|
namespace ui {
|
||||||
|
|
||||||
|
enum class ConsoleChannel {
|
||||||
|
None = 0, // plain result / default text
|
||||||
|
Command, // the "> cmd" echo
|
||||||
|
Info, // informational / help text
|
||||||
|
Success, // positive events (connected, accepted, ready)
|
||||||
|
Warning, // non-fatal warnings (amber)
|
||||||
|
Error, // errors
|
||||||
|
Rpc, // RPC trace lines
|
||||||
|
Daemon, // dragonxd log output
|
||||||
|
Xmrig, // miner log output
|
||||||
|
App, // app / wallet / lite-diagnostics log
|
||||||
|
JsonKey, // result JSON: object key
|
||||||
|
JsonString, // result JSON: string value
|
||||||
|
JsonNumber, // result JSON: number / bool / null
|
||||||
|
JsonBrace, // result JSON: braces / brackets
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace ui
|
||||||
|
} // namespace dragonx
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
// Released under the GPLv3
|
// Released under the GPLv3
|
||||||
|
|
||||||
#include "console_command_executor.h"
|
#include "console_command_executor.h"
|
||||||
#include "console_tab.h" // ConsoleTab::COLOR_* statics
|
#include "console_channel.h"
|
||||||
#include "console_input_model.h" // BuildConsoleRpcCall
|
#include "console_input_model.h" // BuildConsoleRpcCall
|
||||||
|
|
||||||
#include "../../app.h"
|
#include "../../app.h"
|
||||||
@@ -103,17 +103,17 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
const DState cur = d->getState();
|
const DState cur = d->getState();
|
||||||
const int prev = last_daemon_state_;
|
const int prev = last_daemon_state_;
|
||||||
if (cur == DState::Starting && prev == static_cast<int>(DState::Stopped)) {
|
if (cur == DState::Starting && prev == static_cast<int>(DState::Stopped)) {
|
||||||
add("", ConsoleTab::COLOR_RESULT);
|
add("", ConsoleChannel::None);
|
||||||
add(TR("console_starting_node"), ConsoleTab::COLOR_INFO);
|
add(TR("console_starting_node"), ConsoleChannel::Info);
|
||||||
add(TR("console_capturing_output"), ConsoleTab::COLOR_INFO);
|
add(TR("console_capturing_output"), ConsoleChannel::Info);
|
||||||
add("", ConsoleTab::COLOR_RESULT);
|
add("", ConsoleChannel::None);
|
||||||
} else if (cur == DState::Running && prev != static_cast<int>(DState::Running)) {
|
} else if (cur == DState::Running && prev != static_cast<int>(DState::Running)) {
|
||||||
add(TR("console_daemon_started"), ConsoleTab::COLOR_INFO);
|
add(TR("console_daemon_started"), ConsoleChannel::Info);
|
||||||
} else if (cur == DState::Stopped && prev == static_cast<int>(DState::Running)) {
|
} else if (cur == DState::Stopped && prev == static_cast<int>(DState::Running)) {
|
||||||
add("", ConsoleTab::COLOR_RESULT);
|
add("", ConsoleChannel::None);
|
||||||
add(TR("console_daemon_stopped"), ConsoleTab::COLOR_INFO);
|
add(TR("console_daemon_stopped"), ConsoleChannel::Info);
|
||||||
} else if (cur == DState::Error) {
|
} else if (cur == DState::Error) {
|
||||||
add(std::string(TR("console_daemon_error")) + d->getLastError() + " ===", ConsoleTab::COLOR_ERROR);
|
add(std::string(TR("console_daemon_error")) + d->getLastError() + " ===", ConsoleChannel::Error);
|
||||||
}
|
}
|
||||||
last_daemon_state_ = static_cast<int>(cur);
|
last_daemon_state_ = static_cast<int>(cur);
|
||||||
}
|
}
|
||||||
@@ -121,8 +121,8 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
rpc::RPCClient* rpc = app_->consoleRpc();
|
rpc::RPCClient* rpc = app_->consoleRpc();
|
||||||
if (rpc) {
|
if (rpc) {
|
||||||
const bool now = rpc->isConnected();
|
const bool now = rpc->isConnected();
|
||||||
if (now && !last_rpc_connected_) add(TR("console_connected"), ConsoleTab::COLOR_INFO);
|
if (now && !last_rpc_connected_) add(TR("console_connected"), ConsoleChannel::Info);
|
||||||
else if (!now && last_rpc_connected_) add(TR("console_disconnected"), ConsoleTab::COLOR_ERROR);
|
else if (!now && last_rpc_connected_) add(TR("console_disconnected"), ConsoleChannel::Error);
|
||||||
last_rpc_connected_ = now;
|
last_rpc_connected_ = now;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -133,12 +133,12 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ss, line)) {
|
while (std::getline(ss, line)) {
|
||||||
if (line.empty()) continue;
|
if (line.empty()) continue;
|
||||||
ImU32 c = ConsoleTab::COLOR_DAEMON;
|
ConsoleChannel c = ConsoleChannel::Daemon;
|
||||||
if (line.find("[ERROR]") != std::string::npos ||
|
if (line.find("[ERROR]") != std::string::npos ||
|
||||||
line.find("error:") != std::string::npos ||
|
line.find("error:") != std::string::npos ||
|
||||||
line.find("Error:") != std::string::npos)
|
line.find("Error:") != std::string::npos)
|
||||||
c = ConsoleTab::COLOR_ERROR;
|
c = ConsoleChannel::Error;
|
||||||
add("[daemon] " + line, c);
|
add(line, c); // channel conveys the source; no "[daemon] " prefix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -151,14 +151,14 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
std::string line;
|
std::string line;
|
||||||
while (std::getline(ss, line)) {
|
while (std::getline(ss, line)) {
|
||||||
if (line.empty()) continue;
|
if (line.empty()) continue;
|
||||||
ImU32 c = ConsoleTab::COLOR_DAEMON;
|
ConsoleChannel c = ConsoleChannel::Xmrig;
|
||||||
if (line.find("error") != std::string::npos ||
|
if (line.find("error") != std::string::npos ||
|
||||||
line.find("ERROR") != std::string::npos ||
|
line.find("ERROR") != std::string::npos ||
|
||||||
line.find("failed") != std::string::npos)
|
line.find("failed") != std::string::npos)
|
||||||
c = ConsoleTab::COLOR_ERROR;
|
c = ConsoleChannel::Error;
|
||||||
else if (line.find("accepted") != std::string::npos)
|
else if (line.find("accepted") != std::string::npos)
|
||||||
c = ConsoleTab::COLOR_INFO;
|
c = ConsoleChannel::Success;
|
||||||
add("[xmrig] " + line, c);
|
add(line, c); // channel conveys the source; no "[xmrig] " prefix
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -168,22 +168,22 @@ void FullNodeConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
|
|
||||||
void FullNodeConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
void FullNodeConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
||||||
{
|
{
|
||||||
add(TR("console_available_commands"), ConsoleTab::COLOR_INFO);
|
add(TR("console_available_commands"), ConsoleChannel::Info);
|
||||||
add(TR("console_help_clear"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_clear"), ConsoleChannel::None);
|
||||||
add(TR("console_help_help"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_help"), ConsoleChannel::None);
|
||||||
add("", ConsoleTab::COLOR_RESULT);
|
add("", ConsoleChannel::None);
|
||||||
add(TR("console_common_rpc"), ConsoleTab::COLOR_INFO);
|
add(TR("console_common_rpc"), ConsoleChannel::Info);
|
||||||
add(TR("console_help_getinfo"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_getinfo"), ConsoleChannel::None);
|
||||||
add(TR("console_help_getbalance"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_getbalance"), ConsoleChannel::None);
|
||||||
add(TR("console_help_gettotalbalance"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_gettotalbalance"), ConsoleChannel::None);
|
||||||
add(TR("console_help_getblockcount"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_getblockcount"), ConsoleChannel::None);
|
||||||
add(TR("console_help_getpeerinfo"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_getpeerinfo"), ConsoleChannel::None);
|
||||||
add(TR("console_help_setgenerate"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_setgenerate"), ConsoleChannel::None);
|
||||||
add(TR("console_help_getmininginfo"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_getmininginfo"), ConsoleChannel::None);
|
||||||
add(TR("console_help_stop"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_stop"), ConsoleChannel::None);
|
||||||
add("", ConsoleTab::COLOR_RESULT);
|
add("", ConsoleChannel::None);
|
||||||
add(TR("console_click_commands"), ConsoleTab::COLOR_INFO);
|
add(TR("console_click_commands"), ConsoleChannel::Info);
|
||||||
add(TR("console_tab_completion"), ConsoleTab::COLOR_INFO);
|
add(TR("console_tab_completion"), ConsoleChannel::Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleStatusLine FullNodeConsoleExecutor::toolbarStatus() const
|
ConsoleStatusLine FullNodeConsoleExecutor::toolbarStatus() const
|
||||||
@@ -207,16 +207,16 @@ ConsoleStatusLine FullNodeConsoleExecutor::toolbarStatus() const
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
// Colour error/success lines from the lite diagnostics stream for at-a-glance scanning.
|
// Classify a lite diagnostics line into a channel for at-a-glance scanning.
|
||||||
ImU32 liteLogColor(const std::string& line)
|
ConsoleChannel liteLogChannel(const std::string& line)
|
||||||
{
|
{
|
||||||
const auto has = [&line](const char* s) { return line.find(s) != std::string::npos; };
|
const auto has = [&line](const char* s) { return line.find(s) != std::string::npos; };
|
||||||
if (has("failed") || has(" unreachable") || has("blocked") || has("could not") ||
|
if (has("failed") || has(" unreachable") || has("blocked") || has("could not") ||
|
||||||
has("Error") || has("error"))
|
has("Error") || has("error"))
|
||||||
return Error();
|
return ConsoleChannel::Error;
|
||||||
if (has(": connected") || has("opened") || has("wallet ready") || has("Ready"))
|
if (has(": connected") || has("opened") || has("wallet ready") || has("Ready"))
|
||||||
return Success();
|
return ConsoleChannel::Success;
|
||||||
return OnSurfaceMedium();
|
return ConsoleChannel::App; // lite diagnostics / wallet log
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
@@ -263,16 +263,16 @@ void LiteConsoleExecutor::pollLogLines(const ConsoleAddLineFn& add)
|
|||||||
startIdx = (added >= snap.size()) ? 0 : snap.size() - static_cast<std::size_t>(added);
|
startIdx = (added >= snap.size()) ? 0 : snap.size() - static_cast<std::size_t>(added);
|
||||||
}
|
}
|
||||||
for (std::size_t i = startIdx; i < snap.size(); ++i)
|
for (std::size_t i = startIdx; i < snap.size(); ++i)
|
||||||
add(snap[i], liteLogColor(snap[i]));
|
add(snap[i], liteLogChannel(snap[i]));
|
||||||
diag_gen_ = gen;
|
diag_gen_ = gen;
|
||||||
}
|
}
|
||||||
|
|
||||||
void LiteConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
void LiteConsoleExecutor::printHelp(const ConsoleAddLineFn& add)
|
||||||
{
|
{
|
||||||
add(TR("console_available_commands"), ConsoleTab::COLOR_INFO);
|
add(TR("console_available_commands"), ConsoleChannel::Info);
|
||||||
add(TR("console_help_clear"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_clear"), ConsoleChannel::None);
|
||||||
add(TR("console_help_help"), ConsoleTab::COLOR_RESULT);
|
add(TR("console_help_help"), ConsoleChannel::None);
|
||||||
add(TR("lite_console_help_passthrough"), ConsoleTab::COLOR_INFO);
|
add(TR("lite_console_help_passthrough"), ConsoleChannel::Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<ConsoleStatusLine> LiteConsoleExecutor::statusLines() const
|
std::vector<ConsoleStatusLine> LiteConsoleExecutor::statusLines() const
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "console_channel.h"
|
||||||
#include "imgui.h"
|
#include "imgui.h"
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
@@ -23,7 +24,7 @@ namespace dragonx {
|
|||||||
class App;
|
class App;
|
||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
using ConsoleAddLineFn = std::function<void(const std::string&, ImU32)>;
|
using ConsoleAddLineFn = std::function<void(const std::string&, ConsoleChannel)>;
|
||||||
|
|
||||||
struct ConsoleStatusLine {
|
struct ConsoleStatusLine {
|
||||||
std::string text;
|
std::string text;
|
||||||
|
|||||||
@@ -16,14 +16,14 @@ std::string lowerCopy(std::string value)
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool consoleLinePassesFilter(const std::string& lineText,
|
bool consoleLinePassesFilter(const std::string& lineText,
|
||||||
ImU32 lineColor,
|
ConsoleChannel channel,
|
||||||
const ConsoleOutputFilter& filter)
|
const ConsoleOutputFilter& filter)
|
||||||
{
|
{
|
||||||
if (!filter.daemonMessagesEnabled && lineColor == filter.daemonColor) return false;
|
if (!filter.daemonMessagesEnabled &&
|
||||||
if (!filter.rpcTraceEnabled && lineColor == filter.rpcTraceColor) return false;
|
(channel == ConsoleChannel::Daemon || channel == ConsoleChannel::Xmrig)) return false;
|
||||||
// "[app] ..." lines share COLOR_INFO with other info text, so match them by prefix.
|
if (!filter.rpcTraceEnabled && channel == ConsoleChannel::Rpc) return false;
|
||||||
if (!filter.appMessagesEnabled && lineText.rfind("[app] ", 0) == 0) return false;
|
if (!filter.appMessagesEnabled && channel == ConsoleChannel::App) return false;
|
||||||
if (filter.errorsOnly && lineColor != filter.errorColor) return false;
|
if (filter.errorsOnly && channel != ConsoleChannel::Error) return false;
|
||||||
if (!filter.text.empty()) {
|
if (!filter.text.empty()) {
|
||||||
std::string needle = lowerCopy(filter.text);
|
std::string needle = lowerCopy(filter.text);
|
||||||
std::string haystack = lowerCopy(lineText);
|
std::string haystack = lowerCopy(lineText);
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include "imgui.h"
|
#include "console_channel.h"
|
||||||
|
|
||||||
#include <string>
|
#include <string>
|
||||||
|
|
||||||
@@ -9,17 +9,15 @@ namespace ui {
|
|||||||
|
|
||||||
struct ConsoleOutputFilter {
|
struct ConsoleOutputFilter {
|
||||||
std::string text;
|
std::string text;
|
||||||
bool daemonMessagesEnabled = true;
|
bool daemonMessagesEnabled = true; // Daemon + Xmrig (node log) channels
|
||||||
bool errorsOnly = false;
|
bool errorsOnly = false; // keep only the Error channel
|
||||||
bool rpcTraceEnabled = false;
|
bool rpcTraceEnabled = false; // Rpc channel
|
||||||
bool appMessagesEnabled = true; // "[app] ..." wallet log lines (matched by prefix, not color)
|
bool appMessagesEnabled = true; // App channel
|
||||||
ImU32 daemonColor = 0;
|
|
||||||
ImU32 errorColor = 0;
|
|
||||||
ImU32 rpcTraceColor = 0;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Whether a line with the given channel + text passes the active filter.
|
||||||
bool consoleLinePassesFilter(const std::string& lineText,
|
bool consoleLinePassesFilter(const std::string& lineText,
|
||||||
ImU32 lineColor,
|
ConsoleChannel channel,
|
||||||
const ConsoleOutputFilter& filter);
|
const ConsoleOutputFilter& filter);
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|||||||
@@ -72,6 +72,22 @@ std::string rpcTraceTimestamp()
|
|||||||
return buffer;
|
return buffer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Result-body channels (plain command output + JSON syntax roles) — the lines that carry
|
||||||
|
// no left accent bar and get the JSON indent guides.
|
||||||
|
bool isResultBodyChannel(ConsoleChannel ch)
|
||||||
|
{
|
||||||
|
switch (ch) {
|
||||||
|
case ConsoleChannel::None:
|
||||||
|
case ConsoleChannel::JsonKey:
|
||||||
|
case ConsoleChannel::JsonString:
|
||||||
|
case ConsoleChannel::JsonNumber:
|
||||||
|
case ConsoleChannel::JsonBrace:
|
||||||
|
return true;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
void ConsoleTab::refreshColors()
|
void ConsoleTab::refreshColors()
|
||||||
@@ -112,6 +128,29 @@ void ConsoleTab::refreshColors()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImU32 ConsoleTab::channelTextColor(ConsoleChannel channel) const
|
||||||
|
{
|
||||||
|
using namespace material;
|
||||||
|
switch (channel) {
|
||||||
|
case ConsoleChannel::Command: return COLOR_COMMAND;
|
||||||
|
case ConsoleChannel::Info: return COLOR_INFO;
|
||||||
|
case ConsoleChannel::Success: return WithAlpha(Success(), 255);
|
||||||
|
case ConsoleChannel::Warning: return Warning();
|
||||||
|
case ConsoleChannel::Error: return COLOR_ERROR;
|
||||||
|
case ConsoleChannel::Rpc: return COLOR_RPC;
|
||||||
|
case ConsoleChannel::Daemon: return COLOR_DAEMON;
|
||||||
|
case ConsoleChannel::Xmrig: return COLOR_DAEMON;
|
||||||
|
case ConsoleChannel::App: return COLOR_INFO;
|
||||||
|
// JSON syntax roles — highlight against the plain-result body.
|
||||||
|
case ConsoleChannel::JsonKey: return WithAlpha(Secondary(), 255);
|
||||||
|
case ConsoleChannel::JsonString: return WithAlpha(Success(), 255);
|
||||||
|
case ConsoleChannel::JsonNumber: return WithAlpha(Warning(), 255);
|
||||||
|
case ConsoleChannel::JsonBrace: return IM_COL32(200, 200, 200, 150);
|
||||||
|
case ConsoleChannel::None:
|
||||||
|
default: return COLOR_RESULT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleTab::ConsoleTab()
|
ConsoleTab::ConsoleTab()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -132,9 +171,9 @@ ConsoleTab::ConsoleTab()
|
|||||||
refreshColors();
|
refreshColors();
|
||||||
|
|
||||||
// Add welcome message
|
// Add welcome message
|
||||||
addLine(TR("console_welcome"), COLOR_INFO);
|
addLine(TR("console_welcome"), ConsoleChannel::Info);
|
||||||
addLine(TR("console_type_help"), COLOR_INFO);
|
addLine(TR("console_type_help"), ConsoleChannel::Info);
|
||||||
addLine("", COLOR_RESULT);
|
addLine("", ConsoleChannel::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
ConsoleTab::~ConsoleTab()
|
ConsoleTab::~ConsoleTab()
|
||||||
@@ -149,35 +188,20 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
{
|
{
|
||||||
using namespace material;
|
using namespace material;
|
||||||
|
|
||||||
// Refresh console colors when dark/light theme changes
|
// Refresh the console theme colors on a dark/light switch. Line colors are derived
|
||||||
|
// from each line's channel at draw time, so no per-line remap is needed.
|
||||||
{
|
{
|
||||||
static bool s_lastDark = IsDarkTheme();
|
static bool s_lastDark = IsDarkTheme();
|
||||||
bool nowDark = IsDarkTheme();
|
bool nowDark = IsDarkTheme();
|
||||||
if (nowDark != s_lastDark) {
|
if (nowDark != s_lastDark) {
|
||||||
// Save old colors to remap existing lines
|
|
||||||
ImU32 oldCmd = COLOR_COMMAND, oldRes = COLOR_RESULT;
|
|
||||||
ImU32 oldErr = COLOR_ERROR, oldDmn = COLOR_DAEMON;
|
|
||||||
ImU32 oldInf = COLOR_INFO, oldRpc = COLOR_RPC;
|
|
||||||
refreshColors();
|
refreshColors();
|
||||||
// Remap stored line colors from old to new
|
|
||||||
{
|
|
||||||
std::lock_guard<std::mutex> lock(lines_mutex_);
|
|
||||||
for (auto& line : lines_) {
|
|
||||||
if (line.color == oldCmd) line.color = COLOR_COMMAND;
|
|
||||||
else if (line.color == oldRes) line.color = COLOR_RESULT;
|
|
||||||
else if (line.color == oldErr) line.color = COLOR_ERROR;
|
|
||||||
else if (line.color == oldDmn) line.color = COLOR_DAEMON;
|
|
||||||
else if (line.color == oldInf) line.color = COLOR_INFO;
|
|
||||||
else if (line.color == oldRpc) line.color = COLOR_RPC;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
s_lastDark = nowDark;
|
s_lastDark = nowDark;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Pull passive log lines (daemon/xmrig output, or the lite diagnostics ring) and any
|
// Pull passive log lines (daemon/xmrig output, or the lite diagnostics ring) and any
|
||||||
// completed command results from the backend executor.
|
// completed command results from the backend executor.
|
||||||
exec.pollLogLines([this](const std::string& l, ImU32 c) { addLine(l, c); });
|
exec.pollLogLines([this](const std::string& l, ConsoleChannel c) { addLine(l, c); });
|
||||||
{
|
{
|
||||||
std::string result;
|
std::string result;
|
||||||
bool isError = false;
|
bool isError = false;
|
||||||
@@ -579,8 +603,7 @@ void ConsoleTab::renderOutput()
|
|||||||
// Build filtered line index list BEFORE mouse handling (so screenToTextPos works)
|
// Build filtered line index list BEFORE mouse handling (so screenToTextPos works)
|
||||||
ConsoleOutputFilter outputFilter{filter_text_, s_daemon_messages_enabled,
|
ConsoleOutputFilter outputFilter{filter_text_, s_daemon_messages_enabled,
|
||||||
s_errors_only_enabled, s_rpc_trace_enabled,
|
s_errors_only_enabled, s_rpc_trace_enabled,
|
||||||
s_app_messages_enabled,
|
s_app_messages_enabled};
|
||||||
COLOR_DAEMON, COLOR_ERROR, COLOR_RPC};
|
|
||||||
bool has_text_filter = !outputFilter.text.empty();
|
bool has_text_filter = !outputFilter.text.empty();
|
||||||
// Lowercased needle for in-line match highlighting.
|
// Lowercased needle for in-line match highlighting.
|
||||||
std::string filter_lower;
|
std::string filter_lower;
|
||||||
@@ -595,7 +618,7 @@ void ConsoleTab::renderOutput()
|
|||||||
visible_indices_.clear();
|
visible_indices_.clear();
|
||||||
if (has_filter) {
|
if (has_filter) {
|
||||||
for (int i = 0; i < static_cast<int>(lines_.size()); i++) {
|
for (int i = 0; i < static_cast<int>(lines_.size()); i++) {
|
||||||
if (!consoleLinePassesFilter(lines_[i].text, lines_[i].color, outputFilter)) continue;
|
if (!consoleLinePassesFilter(lines_[i].text, lines_[i].channel, outputFilter)) continue;
|
||||||
visible_indices_.push_back(i);
|
visible_indices_.push_back(i);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -745,17 +768,19 @@ void ConsoleTab::renderOutput()
|
|||||||
|
|
||||||
// Left-edge channel accent bar, drawn in the padX margin so it never overlaps
|
// Left-edge channel accent bar, drawn in the padX margin so it never overlaps
|
||||||
// the text or the selection highlight.
|
// the text or the selection highlight.
|
||||||
if (line.channel != CH_NONE) {
|
if (line.channel != ConsoleChannel::None) {
|
||||||
ImU32 barCol = 0;
|
ImU32 barCol = 0;
|
||||||
switch (line.channel) {
|
switch (line.channel) {
|
||||||
case CH_COMMAND: barCol = Primary(); break;
|
case ConsoleChannel::Command: barCol = Primary(); break;
|
||||||
case CH_ERROR: barCol = Error(); break;
|
case ConsoleChannel::Error: barCol = Error(); break;
|
||||||
case CH_RPC: barCol = Secondary(); break;
|
case ConsoleChannel::Warning: barCol = Warning(); break;
|
||||||
case CH_DAEMON: barCol = IM_COL32(90, 130, 190, 210); break; // node log — blue
|
case ConsoleChannel::Success: barCol = Success(); break;
|
||||||
case CH_XMRIG: barCol = Warning(); break; // mining — amber
|
case ConsoleChannel::Rpc: barCol = Secondary(); break;
|
||||||
case CH_APP: barCol = IM_COL32(120, 190, 160, 210); break; // app — teal
|
case ConsoleChannel::Daemon: barCol = IM_COL32(90, 130, 190, 210); break; // node log — blue
|
||||||
case CH_INFO: barCol = OnSurfaceDisabled(); break;
|
case ConsoleChannel::Xmrig: barCol = Warning(); break; // mining — amber
|
||||||
default: break;
|
case ConsoleChannel::App: barCol = IM_COL32(120, 190, 160, 210); break; // app — teal
|
||||||
|
case ConsoleChannel::Info: barCol = OnSurfaceDisabled(); break;
|
||||||
|
default: break; // result / JSON roles get no bar
|
||||||
}
|
}
|
||||||
if (barCol != 0) {
|
if (barCol != 0) {
|
||||||
float barW = 3.0f * Layout::hScale();
|
float barW = 3.0f * Layout::hScale();
|
||||||
@@ -767,8 +792,8 @@ void ConsoleTab::renderOutput()
|
|||||||
}
|
}
|
||||||
|
|
||||||
// JSON indent guides — faint vertical lines per 2-space nesting level, on result
|
// JSON indent guides — faint vertical lines per 2-space nesting level, on result
|
||||||
// (non-channel) lines only. Draw-only, like the channel bar.
|
// body lines only (plain result + JSON syntax roles). Draw-only, like the bar.
|
||||||
if (line.channel == CH_NONE) {
|
if (isResultBodyChannel(line.channel)) {
|
||||||
size_t leading = 0;
|
size_t leading = 0;
|
||||||
while (leading < line.text.size() && line.text[leading] == ' ') ++leading;
|
while (leading < line.text.size() && line.text[leading] == ' ') ++leading;
|
||||||
if (leading >= 2) {
|
if (leading >= 2) {
|
||||||
@@ -846,7 +871,7 @@ void ConsoleTab::renderOutput()
|
|||||||
if (seg.byteStart < seg.byteEnd) {
|
if (seg.byteStart < seg.byteEnd) {
|
||||||
dl->AddText(font, fontSize,
|
dl->AddText(font, fontSize,
|
||||||
ImVec2(lineOrigin.x, rowY),
|
ImVec2(lineOrigin.x, rowY),
|
||||||
line.color, segStart, segEnd);
|
channelTextColor(line.channel), segStart, segEnd);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1094,9 +1119,9 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
data->InsertChars(0, completion.matches.front().c_str());
|
data->InsertChars(0, completion.matches.front().c_str());
|
||||||
} else if (completion.matches.size() > 1) {
|
} else if (completion.matches.size() > 1) {
|
||||||
// Multiple matches — show list in console and complete common prefix
|
// Multiple matches — show list in console and complete common prefix
|
||||||
console->addLine(TR("console_completions"), ConsoleTab::COLOR_INFO);
|
console->addLine(TR("console_completions"), ConsoleChannel::Info);
|
||||||
for (const auto& line : FormatConsoleCompletionLines(completion.matches)) {
|
for (const auto& line : FormatConsoleCompletionLines(completion.matches)) {
|
||||||
console->addLine(line, ConsoleTab::COLOR_RESULT);
|
console->addLine(line, ConsoleChannel::None);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Complete to longest common prefix
|
// Complete to longest common prefix
|
||||||
@@ -1121,7 +1146,7 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
while (!cmd.empty() && (cmd.back() == ' ' || cmd.back() == '\t')) cmd.pop_back();
|
while (!cmd.empty() && (cmd.back() == ' ' || cmd.back() == '\t')) cmd.pop_back();
|
||||||
|
|
||||||
if (!cmd.empty()) {
|
if (!cmd.empty()) {
|
||||||
addLine("> " + cmd, COLOR_COMMAND);
|
addLine("> " + cmd, ConsoleChannel::Command);
|
||||||
AppendConsoleHistory(command_history_, cmd, 100);
|
AppendConsoleHistory(command_history_, cmd, 100);
|
||||||
history_index_ = -1;
|
history_index_ = -1;
|
||||||
|
|
||||||
@@ -1134,7 +1159,7 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
std::transform(first.begin(), first.end(), first.begin(),
|
std::transform(first.begin(), first.end(), first.begin(),
|
||||||
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
[](unsigned char c) { return static_cast<char>(std::tolower(c)); });
|
||||||
}
|
}
|
||||||
auto add = [this](const std::string& l, ImU32 c) { addLine(l, c); };
|
auto add = [this](const std::string& l, ConsoleChannel c) { addLine(l, c); };
|
||||||
if (first == "clear" || first == "cls") {
|
if (first == "clear" || first == "cls") {
|
||||||
// View-only clear — NEVER forwarded (the lite backend's `clear` wipes tx history).
|
// View-only clear — NEVER forwarded (the lite backend's `clear` wipes tx history).
|
||||||
clear();
|
clear();
|
||||||
@@ -1142,9 +1167,9 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
} else if (first == "help") {
|
} else if (first == "help") {
|
||||||
exec.printHelp(add);
|
exec.printHelp(add);
|
||||||
} else if (first == "quit" || first == "exit") {
|
} else if (first == "quit" || first == "exit") {
|
||||||
addLine(TR("console_quit_note"), COLOR_INFO);
|
addLine(TR("console_quit_note"), ConsoleChannel::Info);
|
||||||
} else if (!exec.isReady()) {
|
} else if (!exec.isReady()) {
|
||||||
addLine(TR("console_not_connected"), COLOR_ERROR);
|
addLine(TR("console_not_connected"), ConsoleChannel::Error);
|
||||||
} else {
|
} else {
|
||||||
exec.submit(cmd);
|
exec.submit(cmd);
|
||||||
}
|
}
|
||||||
@@ -1358,23 +1383,17 @@ void ConsoleTab::renderCommandsPopup()
|
|||||||
|
|
||||||
void ConsoleTab::addFormattedResult(const std::string& result, bool is_error)
|
void ConsoleTab::addFormattedResult(const std::string& result, bool is_error)
|
||||||
{
|
{
|
||||||
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, is_error)) {
|
for (const auto& resultLine : FormatConsoleRpcResultLines(result, is_error)) {
|
||||||
ImU32 lineCol = COLOR_RESULT;
|
ConsoleChannel channel = ConsoleChannel::None;
|
||||||
switch (resultLine.role) {
|
switch (resultLine.role) {
|
||||||
case ConsoleResultLineRole::Error: lineCol = COLOR_ERROR; break;
|
case ConsoleResultLineRole::Error: channel = ConsoleChannel::Error; break;
|
||||||
case ConsoleResultLineRole::JsonKey: lineCol = json_key_col; break;
|
case ConsoleResultLineRole::JsonKey: channel = ConsoleChannel::JsonKey; break;
|
||||||
case ConsoleResultLineRole::JsonString: lineCol = json_str_col; break;
|
case ConsoleResultLineRole::JsonString: channel = ConsoleChannel::JsonString; break;
|
||||||
case ConsoleResultLineRole::JsonNumber: lineCol = json_num_col; break;
|
case ConsoleResultLineRole::JsonNumber: channel = ConsoleChannel::JsonNumber; break;
|
||||||
case ConsoleResultLineRole::JsonBrace: lineCol = json_brace_col; break;
|
case ConsoleResultLineRole::JsonBrace: channel = ConsoleChannel::JsonBrace; break;
|
||||||
case ConsoleResultLineRole::Result: break;
|
case ConsoleResultLineRole::Result: break;
|
||||||
}
|
}
|
||||||
addLine(resultLine.text, lineCol);
|
addLine(resultLine.text, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1387,36 +1406,15 @@ void ConsoleTab::renderStatusHeader(ConsoleCommandExecutor& exec)
|
|||||||
if (!lines.empty()) ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
if (!lines.empty()) ImGui::Dummy(ImVec2(0, Layout::spacingXs()));
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleTab::addLine(const std::string& line, ImU32 color)
|
void ConsoleTab::addLine(const std::string& line, ConsoleChannel channel)
|
||||||
{
|
{
|
||||||
std::lock_guard<std::mutex> lock(lines_mutex_);
|
std::lock_guard<std::mutex> lock(lines_mutex_);
|
||||||
|
|
||||||
// Infer the channel (for the left accent bar) from the color, then strip the now
|
// The channel is the canonical semantic key — text color and the left accent bar are
|
||||||
// redundant "[daemon]"/"[xmrig]"/"[app]" text prefix so the bar carries the source.
|
// both derived from it at draw time (channelTextColor / the bar switch). Sources emit
|
||||||
int channel = CH_NONE;
|
// clean text (no "[daemon]"/"[xmrig]"/"[app]"/"[rpc]" prefix); the bar carries origin.
|
||||||
if (color == COLOR_ERROR) channel = CH_ERROR;
|
lines_.push_back({line, channel});
|
||||||
else if (color == COLOR_RPC) channel = CH_RPC;
|
|
||||||
else if (color == COLOR_COMMAND) channel = CH_COMMAND;
|
|
||||||
else if (color == COLOR_DAEMON) channel = CH_DAEMON;
|
|
||||||
else if (color == COLOR_INFO) channel = CH_INFO;
|
|
||||||
|
|
||||||
std::string text = line;
|
|
||||||
auto stripPrefix = [&text, &channel](const char* p, int ch) {
|
|
||||||
const size_t n = std::strlen(p);
|
|
||||||
if (text.rfind(p, 0) == 0) {
|
|
||||||
text.erase(0, n);
|
|
||||||
// Keep an error channel (red accent) for e.g. a "[daemon] error:" line so the
|
|
||||||
// bar matches the red text, rather than being downgraded to the source channel.
|
|
||||||
if (channel != CH_ERROR) channel = ch;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
stripPrefix("[daemon] ", CH_DAEMON);
|
|
||||||
stripPrefix("[xmrig] ", CH_XMRIG);
|
|
||||||
stripPrefix("[app] ", CH_APP);
|
|
||||||
stripPrefix("[rpc] ", CH_RPC);
|
|
||||||
|
|
||||||
lines_.push_back({text, color, channel});
|
|
||||||
|
|
||||||
// Limit buffer size — adjust selection indices when lines are removed
|
// Limit buffer size — adjust selection indices when lines are removed
|
||||||
// from the front so the highlight stays on the text the user selected.
|
// from the front so the highlight stays on the text the user selected.
|
||||||
int popped = 0;
|
int popped = 0;
|
||||||
@@ -1445,7 +1443,8 @@ void ConsoleTab::addLine(const std::string& line, ImU32 color)
|
|||||||
|
|
||||||
void ConsoleTab::addRpcTraceLine(const std::string& source, const std::string& method)
|
void ConsoleTab::addRpcTraceLine(const std::string& source, const std::string& method)
|
||||||
{
|
{
|
||||||
addLine("[rpc] [" + rpcTraceTimestamp() + "] [" + source + "] " + method, COLOR_RPC);
|
// The Rpc channel supplies the accent bar + color; no "[rpc] " text prefix needed.
|
||||||
|
addLine("[" + rpcTraceTimestamp() + "] [" + source + "] " + method, ConsoleChannel::Rpc);
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleTab::clear()
|
void ConsoleTab::clear()
|
||||||
@@ -1456,7 +1455,7 @@ void ConsoleTab::clear()
|
|||||||
}
|
}
|
||||||
// The executor keeps its own log cursors, so a view-clear stays cleared (new output
|
// 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.
|
// still appends). addLine() takes the lock itself, so call it outside the locked scope.
|
||||||
addLine(TR("console_cleared"), COLOR_INFO);
|
addLine(TR("console_cleared"), ConsoleChannel::Info);
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace ui
|
} // namespace ui
|
||||||
|
|||||||
@@ -4,6 +4,7 @@
|
|||||||
|
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include "console_channel.h"
|
||||||
#include "console_text_layout.h"
|
#include "console_text_layout.h"
|
||||||
#include "../layout.h"
|
#include "../layout.h"
|
||||||
#include "../../daemon/embedded_daemon.h"
|
#include "../../daemon/embedded_daemon.h"
|
||||||
@@ -46,7 +47,7 @@ public:
|
|||||||
/**
|
/**
|
||||||
* @brief Add a line to the console output
|
* @brief Add a line to the console output
|
||||||
*/
|
*/
|
||||||
void addLine(const std::string& line, ImU32 color = IM_COL32(200, 200, 200, 255));
|
void addLine(const std::string& line, ConsoleChannel channel = ConsoleChannel::None);
|
||||||
void addRpcTraceLine(const std::string& source, const std::string& method);
|
void addRpcTraceLine(const std::string& source, const std::string& method);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -84,17 +85,17 @@ public:
|
|||||||
static ImU32 COLOR_RPC;
|
static ImU32 COLOR_RPC;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Line source, for the left-edge channel accent bar. Inferred in addLine() from the
|
// Each line stores its semantic ConsoleChannel; the text color and left accent-bar
|
||||||
// color + any "[daemon]"/"[xmrig]"/"[app]" prefix (which is then stripped).
|
// color are both derived from it at draw time (channelTextColor / channelBarColor).
|
||||||
enum Channel { CH_NONE = 0, CH_COMMAND, CH_INFO, CH_ERROR, CH_RPC, CH_DAEMON, CH_XMRIG, CH_APP };
|
|
||||||
|
|
||||||
struct ConsoleLine {
|
struct ConsoleLine {
|
||||||
std::string text;
|
std::string text;
|
||||||
ImU32 color;
|
ConsoleChannel channel = ConsoleChannel::None;
|
||||||
int channel = CH_NONE;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
// Format + color a completed command result (JSON-aware) into console lines.
|
// Text color for a channel, resolved from the current theme (COLOR_* + material).
|
||||||
|
ImU32 channelTextColor(ConsoleChannel channel) const;
|
||||||
|
|
||||||
|
// Format a completed command result (JSON role -> channel) into console lines.
|
||||||
void addFormattedResult(const std::string& result, bool is_error);
|
void addFormattedResult(const std::string& result, bool is_error);
|
||||||
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
void renderStatusHeader(ConsoleCommandExecutor& exec);
|
||||||
void renderToolbar(ConsoleCommandExecutor& exec);
|
void renderToolbar(ConsoleCommandExecutor& exec);
|
||||||
|
|||||||
@@ -2354,26 +2354,34 @@ void testRendererHelpers()
|
|||||||
EXPECT_FALSE(dragonx::ui::IsPoolMiningActive(true, false, true));
|
EXPECT_FALSE(dragonx::ui::IsPoolMiningActive(true, false, true));
|
||||||
EXPECT_TRUE(dragonx::ui::IsPoolMiningActive(false, false, true));
|
EXPECT_TRUE(dragonx::ui::IsPoolMiningActive(false, false, true));
|
||||||
|
|
||||||
dragonx::ui::ConsoleOutputFilter filter{"error", false, false, false, true, 10, 20, 30};
|
// The output filter keys off the line's semantic ConsoleChannel (not a color / prefix).
|
||||||
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("RPC Error", 20, filter));
|
using CC = dragonx::ui::ConsoleChannel;
|
||||||
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("daemon line", 10, filter));
|
// {text, daemonMessagesEnabled, errorsOnly, rpcTraceEnabled, appMessagesEnabled}
|
||||||
|
dragonx::ui::ConsoleOutputFilter filter{"error", false, false, false, true};
|
||||||
|
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("RPC Error", CC::Error, filter)); // substring "error"
|
||||||
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("Daemon line", CC::Daemon, filter)); // no "error" substring
|
||||||
filter.text.clear();
|
filter.text.clear();
|
||||||
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("[rpc] History -> listtransactions", 30, filter));
|
// Daemon/Xmrig channels hidden while daemonMessagesEnabled=false.
|
||||||
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("node log", CC::Daemon, filter));
|
||||||
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("hashrate 1000", CC::Xmrig, filter));
|
||||||
|
// Rpc channel hidden while rpcTraceEnabled=false, shown once enabled.
|
||||||
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("History -> listtransactions", CC::Rpc, filter));
|
||||||
filter.rpcTraceEnabled = true;
|
filter.rpcTraceEnabled = true;
|
||||||
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("[rpc] History -> listtransactions", 30, filter));
|
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("History -> listtransactions", CC::Rpc, filter));
|
||||||
filter.daemonMessagesEnabled = true;
|
filter.daemonMessagesEnabled = true;
|
||||||
|
// Errors-only keeps only the Error channel.
|
||||||
filter.errorsOnly = true;
|
filter.errorsOnly = true;
|
||||||
filter.text.clear();
|
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("error line", CC::Error, filter));
|
||||||
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("error line", 20, filter));
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("info line", CC::Info, filter));
|
||||||
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("info line", 30, filter));
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("node log", CC::Daemon, filter));
|
||||||
|
|
||||||
// "[app] ..." lines are filtered by prefix (they share the info color), not by color.
|
// App channel is toggled by appMessagesEnabled; other channels are unaffected by it.
|
||||||
filter.errorsOnly = false;
|
filter.errorsOnly = false;
|
||||||
filter.appMessagesEnabled = false;
|
filter.appMessagesEnabled = false;
|
||||||
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("[app] Price updated", 30, filter));
|
EXPECT_FALSE(dragonx::ui::consoleLinePassesFilter("Price updated", CC::App, filter));
|
||||||
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("[rpc] trace", 30, filter));
|
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("trace", CC::Rpc, filter));
|
||||||
filter.appMessagesEnabled = true;
|
filter.appMessagesEnabled = true;
|
||||||
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("[app] Price updated", 30, filter));
|
EXPECT_TRUE(dragonx::ui::consoleLinePassesFilter("Price updated", CC::App, filter));
|
||||||
|
|
||||||
std::vector<dragonx::AddressInfo> poolAddresses;
|
std::vector<dragonx::AddressInfo> poolAddresses;
|
||||||
poolAddresses.push_back({"R-transparent", 0.0, "transparent", true});
|
poolAddresses.push_back({"R-transparent", 0.0, "transparent", true});
|
||||||
|
|||||||
Reference in New Issue
Block a user