feat(console): terminal polish — lite/color-coded toggles, dark bg, monospace font
Work through the console items in todo.md: - Lite filter toggles (#5): replace the single hasLogFilters() bool with a ConsoleLogFilterCaps struct so each backend advertises which toggles apply. Full node = daemon/errors/rpc-trace/app; lite = errors-only + app (its diagnostics ring maps to the App/Error channels; the text filter is always shown). Lite previously showed no toggles at all. - Color-coded toggles (#6): each filter checkbox is tinted with its channel's accent color (daemon=blue, errors=red, rpc=secondary, app=teal) via a new channelAccentColor() that also drives the output's left accent bar — one source of truth for channel color. - Darker terminal look (#7): drop the light glass rectangle on the input; both output and input now get a terminal-dark overlay (tabs.console.bg-darken-alpha, default 110) and the input field blends into it (transparent frame bg). - Monospace font (#8): bundle Ubuntu Mono (res/fonts/UbuntuMono-R.ttf, Ubuntu Font License — same family as the existing Ubuntu fonts) via INCBIN, load it at caption size as Type().mono(), and render console output + input in it so pretty-printed JSON columns and terminal text align. Falls back to the proportional caption font if unavailable. Full-node + lite build clean; ctest green; source hygiene clean; sandboxed startup smoke confirms the mono font loads + atlas builds without crashing. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -689,6 +689,7 @@ set_source_files_properties(
|
|||||||
"${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf;\
|
"${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-R.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Light.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Light.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Medium.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/Ubuntu-Medium.ttf;\
|
||||||
|
${CMAKE_SOURCE_DIR}/res/fonts/UbuntuMono-R.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/MaterialIcons-Regular.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
|
${CMAKE_SOURCE_DIR}/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf;\
|
||||||
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf"
|
${CMAKE_SOURCE_DIR}/res/fonts/NotoSansCJK-Subset.ttf"
|
||||||
|
|||||||
BIN
res/fonts/UbuntuMono-R.ttf
Normal file
BIN
res/fonts/UbuntuMono-R.ttf
Normal file
Binary file not shown.
@@ -908,6 +908,7 @@ scroll-fade-zone = { size = 24.0 }
|
|||||||
|
|
||||||
[tabs.console]
|
[tabs.console]
|
||||||
input-area-padding = 8.0
|
input-area-padding = 8.0
|
||||||
|
bg-darken-alpha = { size = 110.0 } # black overlay alpha (0-255) for the terminal-dark output + input
|
||||||
output-line-spacing = 2.0
|
output-line-spacing = 2.0
|
||||||
output = { line-spacing = 2 }
|
output = { line-spacing = 2 }
|
||||||
scroll-multiplier = { size = 3.0 }
|
scroll-multiplier = { size = 3.0 }
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
INCBIN(ubuntu_regular, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-R.ttf");
|
INCBIN(ubuntu_regular, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-R.ttf");
|
||||||
INCBIN(ubuntu_light, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-Light.ttf");
|
INCBIN(ubuntu_light, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-Light.ttf");
|
||||||
INCBIN(ubuntu_medium, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-Medium.ttf");
|
INCBIN(ubuntu_medium, "@CMAKE_SOURCE_DIR@/res/fonts/Ubuntu-Medium.ttf");
|
||||||
|
INCBIN(ubuntu_mono, "@CMAKE_SOURCE_DIR@/res/fonts/UbuntuMono-R.ttf");
|
||||||
INCBIN(material_icons, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialIcons-Regular.ttf");
|
INCBIN(material_icons, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialIcons-Regular.ttf");
|
||||||
INCBIN(mdi_pickaxe_subset, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf");
|
INCBIN(mdi_pickaxe_subset, "@CMAKE_SOURCE_DIR@/res/fonts/MaterialDesignIcons-Pickaxe-Subset.ttf");
|
||||||
INCBIN(noto_cjk_subset, "@CMAKE_SOURCE_DIR@/res/fonts/NotoSansCJK-Subset.ttf");
|
INCBIN(noto_cjk_subset, "@CMAKE_SOURCE_DIR@/res/fonts/NotoSansCJK-Subset.ttf");
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ extern "C" {
|
|||||||
extern const unsigned char g_ubuntu_medium_data[];
|
extern const unsigned char g_ubuntu_medium_data[];
|
||||||
extern const unsigned int g_ubuntu_medium_size;
|
extern const unsigned int g_ubuntu_medium_size;
|
||||||
|
|
||||||
|
extern const unsigned char g_ubuntu_mono_data[];
|
||||||
|
extern const unsigned int g_ubuntu_mono_size;
|
||||||
|
|
||||||
extern const unsigned char g_material_icons_data[];
|
extern const unsigned char g_material_icons_data[];
|
||||||
extern const unsigned int g_material_icons_size;
|
extern const unsigned int g_material_icons_size;
|
||||||
|
|
||||||
|
|||||||
@@ -184,6 +184,10 @@ bool Typography::load(ImGuiIO& io, float dpiScale)
|
|||||||
// ButtonLg: Medium
|
// ButtonLg: Medium
|
||||||
fonts_[14] = loadFont(io, kWeightMedium, Layout::kFontButtonLg() * scale, "ButtonLg");
|
fonts_[14] = loadFont(io, kWeightMedium, Layout::kFontButtonLg() * scale, "ButtonLg");
|
||||||
|
|
||||||
|
// Monospace (Ubuntu Mono) at caption size — used by the console so terminal text +
|
||||||
|
// pretty-printed JSON columns align. Same size as Caption to keep line heights matched.
|
||||||
|
mono_ = loadFont(io, kWeightMono, Layout::kFontCaption() * scale, "Mono");
|
||||||
|
|
||||||
// --- Icon fonts at multiple sizes ---
|
// --- Icon fonts at multiple sizes ---
|
||||||
// These are standalone fonts (not merged) so we can PushFont(iconXxx) for icon rendering.
|
// These are standalone fonts (not merged) so we can PushFont(iconXxx) for icon rendering.
|
||||||
iconFonts_[0] = loadIconFont(io, 14.0f * scale, "IconSmall");
|
iconFonts_[0] = loadIconFont(io, 14.0f * scale, "IconSmall");
|
||||||
@@ -236,6 +240,10 @@ ImFont* Typography::loadFont(ImGuiIO& io, int weight, float size, const char* na
|
|||||||
fontData = g_ubuntu_medium_data;
|
fontData = g_ubuntu_medium_data;
|
||||||
fontDataLen = g_ubuntu_medium_size;
|
fontDataLen = g_ubuntu_medium_size;
|
||||||
break;
|
break;
|
||||||
|
case kWeightMono:
|
||||||
|
fontData = g_ubuntu_mono_data;
|
||||||
|
fontDataLen = g_ubuntu_mono_size;
|
||||||
|
break;
|
||||||
case kWeightRegular:
|
case kWeightRegular:
|
||||||
default:
|
default:
|
||||||
fontData = g_ubuntu_regular_data;
|
fontData = g_ubuntu_regular_data;
|
||||||
@@ -271,10 +279,10 @@ ImFont* Typography::loadFont(ImGuiIO& io, int weight, float size, const char* na
|
|||||||
cfg.GlyphRanges = glyphRanges;
|
cfg.GlyphRanges = glyphRanges;
|
||||||
|
|
||||||
// Create a unique name for this font variant
|
// Create a unique name for this font variant
|
||||||
snprintf(cfg.Name, sizeof(cfg.Name), "Ubuntu %s %.0fpx",
|
const char* weightName = weight == kWeightLight ? "Light" :
|
||||||
weight == kWeightLight ? "Light" :
|
weight == kWeightMedium ? "Medium" :
|
||||||
weight == kWeightMedium ? "Medium" : "Regular",
|
weight == kWeightMono ? "Mono" : "Regular";
|
||||||
size);
|
snprintf(cfg.Name, sizeof(cfg.Name), "Ubuntu %s %.0fpx", weightName, size);
|
||||||
|
|
||||||
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, fontDataLen, size, &cfg);
|
ImFont* font = io.Fonts->AddFontFromMemoryTTF(fontDataCopy, fontDataLen, size, &cfg);
|
||||||
|
|
||||||
|
|||||||
@@ -168,6 +168,10 @@ public:
|
|||||||
ImFont* caption() const { return getFont(TypeStyle::Caption); }
|
ImFont* caption() const { return getFont(TypeStyle::Caption); }
|
||||||
ImFont* overline() const { return getFont(TypeStyle::Overline); }
|
ImFont* overline() const { return getFont(TypeStyle::Overline); }
|
||||||
|
|
||||||
|
// Monospace (Ubuntu Mono) at caption size — for the console, so JSON/columns align
|
||||||
|
// like a real terminal. Falls back to the proportional caption font if not loaded.
|
||||||
|
ImFont* mono() const { return mono_ ? mono_ : caption(); }
|
||||||
|
|
||||||
// Icon fonts — Material Design Icons merged at specific pixel sizes
|
// Icon fonts — Material Design Icons merged at specific pixel sizes
|
||||||
// Use these when you need to render an icon at a specific size via AddText/ImGui::Text.
|
// Use these when you need to render an icon at a specific size via AddText/ImGui::Text.
|
||||||
// Common sizes: iconSmall (14px), iconMed (18px), iconLarge (24px).
|
// Common sizes: iconSmall (14px), iconMed (18px), iconLarge (24px).
|
||||||
@@ -253,6 +257,7 @@ private:
|
|||||||
static constexpr int kWeightLight = 300;
|
static constexpr int kWeightLight = 300;
|
||||||
static constexpr int kWeightRegular = 400;
|
static constexpr int kWeightRegular = 400;
|
||||||
static constexpr int kWeightMedium = 500;
|
static constexpr int kWeightMedium = 500;
|
||||||
|
static constexpr int kWeightMono = 401; // selects Ubuntu Mono (monospace, regular)
|
||||||
|
|
||||||
bool loaded_ = false;
|
bool loaded_ = false;
|
||||||
float dpiScale_ = 1.0f;
|
float dpiScale_ = 1.0f;
|
||||||
@@ -260,6 +265,9 @@ private:
|
|||||||
// Fonts for each type style
|
// Fonts for each type style
|
||||||
ImFont* fonts_[15] = {};
|
ImFont* fonts_[15] = {};
|
||||||
|
|
||||||
|
// Monospace font (Ubuntu Mono) at caption size — console/terminal text.
|
||||||
|
ImFont* mono_ = nullptr;
|
||||||
|
|
||||||
// Icon fonts at different sizes: [0]=small(14), [1]=med(18), [2]=large(24), [3]=xl(40)
|
// Icon fonts at different sizes: [0]=small(14), [1]=med(18), [2]=large(24), [3]=xl(40)
|
||||||
ImFont* iconFonts_[4] = {};
|
ImFont* iconFonts_[4] = {};
|
||||||
ImFont* pickaxeFonts_[4] = {};
|
ImFont* pickaxeFonts_[4] = {};
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ struct ConsoleStatusLine {
|
|||||||
bool pulse = false; // animate the toolbar dot (starting/connecting)
|
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 {
|
class ConsoleCommandExecutor {
|
||||||
public:
|
public:
|
||||||
virtual ~ConsoleCommandExecutor() = default;
|
virtual ~ConsoleCommandExecutor() = default;
|
||||||
@@ -53,7 +64,8 @@ public:
|
|||||||
|
|
||||||
// UI-chrome capabilities.
|
// UI-chrome capabilities.
|
||||||
virtual bool hasRpcReference() const { return false; } // show the RPC command-reference popup
|
virtual bool hasRpcReference() const { return false; } // show the RPC command-reference popup
|
||||||
virtual bool hasLogFilters() const { return false; } // show daemon/errors/rpc-trace/app filters
|
// Which log-filter toggles the toolbar should show (default: none).
|
||||||
|
virtual ConsoleLogFilterCaps logFilterCaps() const { return {}; }
|
||||||
|
|
||||||
// Print the backend-appropriate `help` output via `add`.
|
// Print the backend-appropriate `help` output via `add`.
|
||||||
virtual void printHelp(const ConsoleAddLineFn& add) = 0;
|
virtual void printHelp(const ConsoleAddLineFn& add) = 0;
|
||||||
@@ -75,7 +87,8 @@ public:
|
|||||||
bool pollResult(std::string& result, bool& isError) override;
|
bool pollResult(std::string& result, bool& isError) override;
|
||||||
void pollLogLines(const ConsoleAddLineFn& add) override;
|
void pollLogLines(const ConsoleAddLineFn& add) override;
|
||||||
bool hasRpcReference() const override { return true; }
|
bool hasRpcReference() const override { return true; }
|
||||||
bool hasLogFilters() 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;
|
void printHelp(const ConsoleAddLineFn& add) override;
|
||||||
ConsoleStatusLine toolbarStatus() const override;
|
ConsoleStatusLine toolbarStatus() const override;
|
||||||
|
|
||||||
@@ -99,6 +112,9 @@ public:
|
|||||||
void submit(const std::string& cmd) override;
|
void submit(const std::string& cmd) override;
|
||||||
bool pollResult(std::string& result, bool& isError) override;
|
bool pollResult(std::string& result, bool& isError) override;
|
||||||
void pollLogLines(const ConsoleAddLineFn& add) 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;
|
void printHelp(const ConsoleAddLineFn& add) override;
|
||||||
std::vector<ConsoleStatusLine> statusLines() const override;
|
std::vector<ConsoleStatusLine> statusLines() const override;
|
||||||
ConsoleStatusLine toolbarStatus() const override;
|
ConsoleStatusLine toolbarStatus() const override;
|
||||||
|
|||||||
@@ -151,6 +151,23 @@ ImU32 ConsoleTab::channelTextColor(ConsoleChannel channel) const
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ImU32 ConsoleTab::channelAccentColor(ConsoleChannel channel)
|
||||||
|
{
|
||||||
|
using namespace material;
|
||||||
|
switch (channel) {
|
||||||
|
case ConsoleChannel::Command: return Primary();
|
||||||
|
case ConsoleChannel::Error: return Error();
|
||||||
|
case ConsoleChannel::Warning: return Warning();
|
||||||
|
case ConsoleChannel::Success: return Success();
|
||||||
|
case ConsoleChannel::Rpc: return Secondary();
|
||||||
|
case ConsoleChannel::Daemon: return IM_COL32(90, 130, 190, 210); // node log — blue
|
||||||
|
case ConsoleChannel::Xmrig: return Warning(); // mining — amber
|
||||||
|
case ConsoleChannel::App: return IM_COL32(120, 190, 160, 210); // app — teal
|
||||||
|
case ConsoleChannel::Info: return OnSurfaceDisabled();
|
||||||
|
default: return 0; // result / JSON roles get no accent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
ConsoleTab::ConsoleTab()
|
ConsoleTab::ConsoleTab()
|
||||||
{
|
{
|
||||||
{
|
{
|
||||||
@@ -246,6 +263,11 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
outGlass.rounding = Layout::glassRounding();
|
outGlass.rounding = Layout::glassRounding();
|
||||||
outGlass.fillAlpha = 12;
|
outGlass.fillAlpha = 12;
|
||||||
DrawGlassPanel(dlOut, outPanelMin, outPanelMax, outGlass);
|
DrawGlassPanel(dlOut, outPanelMin, outPanelMax, outGlass);
|
||||||
|
// Terminal-dark overlay: dim the glass so console text reads like a real terminal.
|
||||||
|
{
|
||||||
|
int darkA = static_cast<int>(schema::UI().drawElement("tabs.console", "bg-darken-alpha").sizeOr(110.0f));
|
||||||
|
dlOut->AddRectFilled(outPanelMin, outPanelMax, IM_COL32(0, 0, 0, darkA), outGlass.rounding);
|
||||||
|
}
|
||||||
|
|
||||||
int consoleParentVtx = dlOut->VtxBuffer.Size;
|
int consoleParentVtx = dlOut->VtxBuffer.Size;
|
||||||
|
|
||||||
@@ -256,8 +278,8 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
int consoleChildVtx = consoleChildDL->VtxBuffer.Size;
|
int consoleChildVtx = consoleChildDL->VtxBuffer.Size;
|
||||||
float consoleScrollY = ImGui::GetScrollY();
|
float consoleScrollY = ImGui::GetScrollY();
|
||||||
float consoleScrollMaxY = ImGui::GetScrollMaxY();
|
float consoleScrollMaxY = ImGui::GetScrollMaxY();
|
||||||
// Use smaller font for console output
|
// Console output uses the monospace font so terminal text + JSON columns align.
|
||||||
ImGui::PushFont(Type().caption());
|
ImGui::PushFont(Type().mono());
|
||||||
ImGui::SetWindowFontScale(s_console_zoom);
|
ImGui::SetWindowFontScale(s_console_zoom);
|
||||||
renderOutput();
|
renderOutput();
|
||||||
ImGui::SetWindowFontScale(1.0f);
|
ImGui::SetWindowFontScale(1.0f);
|
||||||
@@ -270,7 +292,7 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
// away from the bottom before we check position again.
|
// away from the bottom before we check position again.
|
||||||
scroll_.tickCooldown(ImGui::GetIO().DeltaTime);
|
scroll_.tickCooldown(ImGui::GetIO().DeltaTime);
|
||||||
{
|
{
|
||||||
float tolerance = Type().caption()->LegacySize * 1.5f;
|
float tolerance = Type().mono()->LegacySize * 1.5f;
|
||||||
bool atBottom = consoleScrollMaxY > 0.0f &&
|
bool atBottom = consoleScrollMaxY > 0.0f &&
|
||||||
consoleScrollY >= consoleScrollMaxY - tolerance;
|
consoleScrollY >= consoleScrollMaxY - tolerance;
|
||||||
scroll_.considerReenable(atBottom);
|
scroll_.considerReenable(atBottom);
|
||||||
@@ -280,7 +302,7 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
// When auto-scroll is off, force bottom fade to always show by
|
// When auto-scroll is off, force bottom fade to always show by
|
||||||
// inflating scrollMax so the mask thinks there's content below.
|
// inflating scrollMax so the mask thinks there's content below.
|
||||||
{
|
{
|
||||||
float fadeZone = std::min(Type().caption()->LegacySize * 3.0f, outputH * 0.18f);
|
float fadeZone = std::min(Type().mono()->LegacySize * 3.0f, outputH * 0.18f);
|
||||||
float effectiveScrollMax = scroll_.autoScroll() ? consoleScrollMaxY
|
float effectiveScrollMax = scroll_.autoScroll() ? consoleScrollMaxY
|
||||||
: std::max(consoleScrollMaxY, consoleScrollY + 10.0f);
|
: std::max(consoleScrollMaxY, consoleScrollY + 10.0f);
|
||||||
ApplyScrollEdgeMask(dlOut, consoleParentVtx, consoleChildDL, consoleChildVtx,
|
ApplyScrollEdgeMask(dlOut, consoleParentVtx, consoleChildDL, consoleChildVtx,
|
||||||
@@ -290,7 +312,7 @@ void ConsoleTab::render(ConsoleCommandExecutor& exec)
|
|||||||
// CRT scanline effect over output area — aligned to text lines
|
// CRT scanline effect over output area — aligned to text lines
|
||||||
if (s_scanline_enabled) {
|
if (s_scanline_enabled) {
|
||||||
float textLineH = output_line_height_;
|
float textLineH = output_line_height_;
|
||||||
if (textLineH <= 1.0f) textLineH = Type().caption()->LegacySize * s_console_zoom + 2.0f;
|
if (textLineH <= 1.0f) textLineH = Type().mono()->LegacySize * s_console_zoom + 2.0f;
|
||||||
|
|
||||||
int lightAlpha = std::clamp((int)schema::UI().drawElement("tabs.console", "scanline-line-alpha").sizeOr(10.0f), 0, 255);
|
int lightAlpha = std::clamp((int)schema::UI().drawElement("tabs.console", "scanline-line-alpha").sizeOr(10.0f), 0, 255);
|
||||||
int darkAlpha = std::clamp(lightAlpha + 10, 0, 255);
|
int darkAlpha = std::clamp(lightAlpha + 10, 0, 255);
|
||||||
@@ -382,9 +404,11 @@ void ConsoleTab::renderToolbar(ConsoleCommandExecutor& exec)
|
|||||||
ImGui::Spacing();
|
ImGui::Spacing();
|
||||||
ImGui::SameLine();
|
ImGui::SameLine();
|
||||||
|
|
||||||
// Log filters (daemon/errors/rpc-trace/app) apply only to a daemon log source.
|
// Log-filter toggles — only the ones the backend supports (full node = all; lite =
|
||||||
if (exec.hasLogFilters()) {
|
// errors-only + app), each tinted with its channel color.
|
||||||
drawLogFilterToggles();
|
ConsoleLogFilterCaps filterCaps = exec.logFilterCaps();
|
||||||
|
if (filterCaps.any()) {
|
||||||
|
drawLogFilterToggles(filterCaps);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Clear button
|
// Clear button
|
||||||
@@ -474,49 +498,51 @@ void ConsoleTab::drawToolbarStatus(ConsoleCommandExecutor& exec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleTab::drawLogFilterToggles()
|
void ConsoleTab::drawLogFilterToggles(const ConsoleLogFilterCaps& caps)
|
||||||
{
|
{
|
||||||
// Daemon messages toggle
|
// Each toggle is tinted with its channel's accent color (the same color as that channel's
|
||||||
ImGui::Checkbox(TR("console_daemon"), &s_daemon_messages_enabled);
|
// left accent bar in the output), so the filter maps visually to the lines it controls.
|
||||||
if (ImGui::IsItemHovered()) {
|
// Only the toggles the backend supports (caps) are shown — lite has no daemon/rpc-trace.
|
||||||
material::Tooltip("%s", TR("console_show_daemon_output"));
|
bool drawnAny = false;
|
||||||
|
auto separate = [&drawnAny]() {
|
||||||
|
if (drawnAny) { ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); }
|
||||||
|
drawnAny = true;
|
||||||
|
};
|
||||||
|
auto colored = [](ConsoleChannel ch, const char* label, bool* v, const char* tip) -> bool {
|
||||||
|
ImVec4 c = ImGui::ColorConvertU32ToFloat4(channelAccentColor(ch));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_CheckMark, c);
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_Text, c);
|
||||||
|
bool changed = ImGui::Checkbox(label, v);
|
||||||
|
ImGui::PopStyleColor(2);
|
||||||
|
if (ImGui::IsItemHovered()) material::Tooltip("%s", tip);
|
||||||
|
return changed;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (caps.daemon) {
|
||||||
|
separate();
|
||||||
|
colored(ConsoleChannel::Daemon, TR("console_daemon"), &s_daemon_messages_enabled,
|
||||||
|
TR("console_show_daemon_output"));
|
||||||
|
}
|
||||||
|
if (caps.errorsOnly) {
|
||||||
|
separate();
|
||||||
|
colored(ConsoleChannel::Error, TR("console_errors"), &s_errors_only_enabled,
|
||||||
|
TR("console_show_errors_only"));
|
||||||
|
}
|
||||||
|
if (caps.rpcTrace) {
|
||||||
|
separate();
|
||||||
|
// Captures method/source only, never results or params.
|
||||||
|
if (colored(ConsoleChannel::Rpc, TR("console_rpc_trace"), &s_rpc_trace_enabled,
|
||||||
|
TR("console_show_rpc_trace")))
|
||||||
|
rpc::RPCClient::setTraceEnabled(s_rpc_trace_enabled);
|
||||||
|
}
|
||||||
|
if (caps.appMessages) {
|
||||||
|
separate();
|
||||||
|
colored(ConsoleChannel::App, TR("console_app"), &s_app_messages_enabled,
|
||||||
|
TR("console_show_app_output"));
|
||||||
}
|
}
|
||||||
|
|
||||||
ImGui::SameLine();
|
// Trailing spacer so the next toolbar item (Clear) stays on the same line.
|
||||||
ImGui::Spacing();
|
if (drawnAny) { ImGui::SameLine(); ImGui::Spacing(); ImGui::SameLine(); }
|
||||||
ImGui::SameLine();
|
|
||||||
|
|
||||||
// Errors-only toggle
|
|
||||||
ImGui::Checkbox(TR("console_errors"), &s_errors_only_enabled);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
material::Tooltip("%s", TR("console_show_errors_only"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::SameLine();
|
|
||||||
|
|
||||||
// App RPC trace toggle — captures method/source only, never results or params
|
|
||||||
if (ImGui::Checkbox(TR("console_rpc_trace"), &s_rpc_trace_enabled)) {
|
|
||||||
rpc::RPCClient::setTraceEnabled(s_rpc_trace_enabled);
|
|
||||||
}
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
material::Tooltip("%s", TR("console_show_rpc_trace"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::SameLine();
|
|
||||||
|
|
||||||
// App messages toggle — "[app] ..." wallet log lines
|
|
||||||
ImGui::Checkbox(TR("console_app"), &s_app_messages_enabled);
|
|
||||||
if (ImGui::IsItemHovered()) {
|
|
||||||
material::Tooltip("%s", TR("console_show_app_output"));
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Spacing();
|
|
||||||
ImGui::SameLine();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void ConsoleTab::drawFilterInput()
|
void ConsoleTab::drawFilterInput()
|
||||||
@@ -657,7 +683,7 @@ void ConsoleTab::renderOutput()
|
|||||||
// toggles — otherwise the geometry shift clamps the user back to
|
// toggles — otherwise the geometry shift clamps the user back to
|
||||||
// bottom and a single scroll-up tick can't escape.
|
// bottom and a single scroll-up tick can't escape.
|
||||||
{
|
{
|
||||||
float fadeZone = std::min(Type().caption()->LegacySize * 3.0f,
|
float fadeZone = std::min(Type().mono()->LegacySize * 3.0f,
|
||||||
ImGui::GetWindowHeight() * 0.18f);
|
ImGui::GetWindowHeight() * 0.18f);
|
||||||
ImGui::Dummy(ImVec2(0, fadeZone));
|
ImGui::Dummy(ImVec2(0, fadeZone));
|
||||||
}
|
}
|
||||||
@@ -825,21 +851,9 @@ void ConsoleTab::drawVisibleLines(float padX, float lineHeight, bool hasTextFilt
|
|||||||
float totalH = layout_.heights[vi];
|
float totalH = layout_.heights[vi];
|
||||||
|
|
||||||
// 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. Same color that tints the toolbar toggle.
|
||||||
if (line.channel != ConsoleChannel::None) {
|
if (line.channel != ConsoleChannel::None) {
|
||||||
ImU32 barCol = 0;
|
ImU32 barCol = channelAccentColor(line.channel);
|
||||||
switch (line.channel) {
|
|
||||||
case ConsoleChannel::Command: barCol = Primary(); break;
|
|
||||||
case ConsoleChannel::Error: barCol = Error(); break;
|
|
||||||
case ConsoleChannel::Warning: barCol = Warning(); break;
|
|
||||||
case ConsoleChannel::Success: barCol = Success(); break;
|
|
||||||
case ConsoleChannel::Rpc: barCol = Secondary(); break;
|
|
||||||
case ConsoleChannel::Daemon: barCol = IM_COL32(90, 130, 190, 210); break; // node log — blue
|
|
||||||
case ConsoleChannel::Xmrig: barCol = Warning(); break; // mining — amber
|
|
||||||
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();
|
||||||
float barX = output_origin_.x - padX + 3.0f * Layout::hScale();
|
float barX = output_origin_.x - padX + 3.0f * Layout::hScale();
|
||||||
@@ -1109,10 +1123,12 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
float inputPanelH = ImGui::GetFrameHeightWithSpacing() + Layout::spacingSm() + Layout::spacingXs();
|
float inputPanelH = ImGui::GetFrameHeightWithSpacing() + Layout::spacingSm() + Layout::spacingXs();
|
||||||
ImVec2 inMin = ImGui::GetCursorScreenPos();
|
ImVec2 inMin = ImGui::GetCursorScreenPos();
|
||||||
ImVec2 inMax(inMin.x + ImGui::GetContentRegionAvail().x, inMin.y + inputPanelH);
|
ImVec2 inMax(inMin.x + ImGui::GetContentRegionAvail().x, inMin.y + inputPanelH);
|
||||||
GlassPanelSpec inGlass;
|
// No light glass rectangle on the input — a flat terminal-dark bar instead, matching
|
||||||
inGlass.rounding = Layout::glassRounding();
|
// the darkened output panel above.
|
||||||
inGlass.fillAlpha = 12;
|
{
|
||||||
material::DrawGlassPanel(dlIn, inMin, inMax, inGlass);
|
int darkA = static_cast<int>(schema::UI().drawElement("tabs.console", "bg-darken-alpha").sizeOr(110.0f));
|
||||||
|
dlIn->AddRectFilled(inMin, inMax, IM_COL32(0, 0, 0, darkA), Layout::glassRounding());
|
||||||
|
}
|
||||||
|
|
||||||
// Center content vertically within glass panel
|
// Center content vertically within glass panel
|
||||||
float inputFrameH = ImGui::GetFrameHeight();
|
float inputFrameH = ImGui::GetFrameHeight();
|
||||||
@@ -1121,7 +1137,8 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
// Input field
|
// Input field — monospace to match the terminal-style output.
|
||||||
|
ImGui::PushFont(Type().mono());
|
||||||
ImGui::PushItemWidth(-Layout::spacingMd());
|
ImGui::PushItemWidth(-Layout::spacingMd());
|
||||||
|
|
||||||
ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue |
|
ImGuiInputTextFlags flags = ImGuiInputTextFlags_EnterReturnsTrue |
|
||||||
@@ -1177,6 +1194,11 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
return 0;
|
return 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Blend the field into the dark terminal bar: transparent frame, subtle hover/active.
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBg, IM_COL32(0, 0, 0, 0));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgHovered, IM_COL32(255, 255, 255, 12));
|
||||||
|
ImGui::PushStyleColor(ImGuiCol_FrameBgActive, IM_COL32(255, 255, 255, 18));
|
||||||
|
|
||||||
const bool busy = exec.busy();
|
const bool busy = exec.busy();
|
||||||
if (busy) ImGui::BeginDisabled();
|
if (busy) ImGui::BeginDisabled();
|
||||||
if (ImGui::InputText("##ConsoleInput", input_buffer_, sizeof(input_buffer_), flags, callback, this)) {
|
if (ImGui::InputText("##ConsoleInput", input_buffer_, sizeof(input_buffer_), flags, callback, this)) {
|
||||||
@@ -1192,8 +1214,10 @@ void ConsoleTab::renderInput(ConsoleCommandExecutor& exec)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (busy) ImGui::EndDisabled();
|
if (busy) ImGui::EndDisabled();
|
||||||
|
ImGui::PopStyleColor(3);
|
||||||
|
|
||||||
ImGui::PopItemWidth();
|
ImGui::PopItemWidth();
|
||||||
|
ImGui::PopFont();
|
||||||
|
|
||||||
// Auto-focus on input
|
// Auto-focus on input
|
||||||
if (reclaim_focus) {
|
if (reclaim_focus) {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ namespace dragonx {
|
|||||||
namespace ui {
|
namespace ui {
|
||||||
|
|
||||||
class ConsoleCommandExecutor;
|
class ConsoleCommandExecutor;
|
||||||
|
struct ConsoleLogFilterCaps;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Console tab — a rich terminal shared by the full-node and lite variants.
|
* @brief Console tab — a rich terminal shared by the full-node and lite variants.
|
||||||
@@ -92,6 +93,9 @@ private:
|
|||||||
// line stores only its semantic ConsoleChannel (in ConsoleModel); the text color and
|
// 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.
|
// the left accent-bar color are both derived from it at draw time.
|
||||||
ImU32 channelTextColor(ConsoleChannel channel) const;
|
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
|
// 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.
|
// thread), applying the line cap + selection/scroll bookkeeping for the new lines.
|
||||||
@@ -105,7 +109,7 @@ private:
|
|||||||
// renderToolbar() draws the top bar; these are its sub-steps:
|
// renderToolbar() draws the top bar; these are its sub-steps:
|
||||||
void renderToolbar(ConsoleCommandExecutor& exec);
|
void renderToolbar(ConsoleCommandExecutor& exec);
|
||||||
void drawToolbarStatus(ConsoleCommandExecutor& exec); // backend status dot + label
|
void drawToolbarStatus(ConsoleCommandExecutor& exec); // backend status dot + label
|
||||||
void drawLogFilterToggles(); // daemon/errors/rpc/app checkboxes
|
void drawLogFilterToggles(const ConsoleLogFilterCaps& caps); // color-coded filter checkboxes
|
||||||
void drawFilterInput(); // text filter + match count
|
void drawFilterInput(); // text filter + match count
|
||||||
void drawZoomControls(); // zoom -/+ buttons
|
void drawZoomControls(); // zoom -/+ buttons
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user