Add mine-when-idle, default banlist, and console parsing improvements

Mine-when-idle:
- Auto-start/stop mining based on system idle time detection
- Platform::getSystemIdleSeconds() via XScreenSaver (Linux) / GetLastInputInfo (Win)
- Settings: mine_when_idle toggle + configurable delay (30s–10m)
- Settings page UI with checkbox and delay combo

Console tab:
- Shell-like argument parsing with quote and JSON bracket support
- Pass JSON objects/arrays directly as RPC params
- Fix selection indices when lines are evicted from buffer

Connection & status bar:
- Reduce RPC connect timeout to 1s for localhost fast-fail
- Fast retry timer on daemon startup and external daemon detection
- Show pool mining hashrate in status bar; sidebar badge reflects pool state

UI polish:
- Add logo to About card in settings; expose logo dimensions on App
- Header title offset-y support; adjust content-area margins
- Fix banned peers row cursor position (rawRowPosB.x)

Branding:
- Update copyright to "DragonX Developers" in RC and About section
- Replace logo/icon assets with updated versions

Misc:
- setup.sh: checkout dragonx branch before pulling
- Remove stale prebuilt-binaries/xmrig/.gitkeep
This commit is contained in:
dan_s
2026-03-07 13:42:31 -06:00
parent 653a90de62
commit cc617dd5be
22 changed files with 431 additions and 41 deletions

View File

@@ -1630,12 +1630,39 @@ void ConsoleTab::executeCommand(const std::string& cmd, rpc::RPCClient* rpc, rpc
return;
}
// Parse command and arguments
// Parse command and arguments (shell-like: handles quotes and JSON brackets)
std::vector<std::string> args;
std::istringstream iss(cmd);
std::string token;
while (iss >> token) {
args.push_back(token);
{
size_t i = 0;
size_t len = cmd.size();
while (i < len) {
// Skip whitespace
while (i < len && (cmd[i] == ' ' || cmd[i] == '\t')) i++;
if (i >= len) break;
std::string tok;
if (cmd[i] == '"' || cmd[i] == '\'') {
// Quoted string — collect until matching close quote
char quote = cmd[i++];
while (i < len && cmd[i] != quote) tok += cmd[i++];
if (i < len) i++; // skip closing quote
} else if (cmd[i] == '[' || cmd[i] == '{') {
// JSON array/object — collect until matching bracket
char open = cmd[i];
char close = (open == '[') ? ']' : '}';
int depth = 0;
while (i < len) {
if (cmd[i] == open) depth++;
else if (cmd[i] == close) depth--;
tok += cmd[i++];
if (depth == 0) break;
}
} else {
// Unquoted token — collect until whitespace
while (i < len && cmd[i] != ' ' && cmd[i] != '\t') tok += cmd[i++];
}
if (!tok.empty()) args.push_back(tok);
}
}
if (args.empty()) return;
@@ -1647,24 +1674,31 @@ void ConsoleTab::executeCommand(const std::string& cmd, rpc::RPCClient* rpc, rpc
for (size_t i = 1; i < args.size(); i++) {
const std::string& arg = args[i];
// Try to parse as number
try {
if (arg.find('.') != std::string::npos) {
params.push_back(std::stod(arg));
} else {
// Check for bool
if (arg == "true") {
params.push_back(true);
} else if (arg == "false") {
params.push_back(false);
// Try to parse as JSON first (handles objects, arrays, etc.)
if (!arg.empty() && (arg[0] == '{' || arg[0] == '[')) {
auto parsed = nlohmann::json::parse(arg, nullptr, false);
if (!parsed.is_discarded()) {
params.push_back(parsed);
continue;
}
}
// Try to parse as number or bool
if (arg == "true") {
params.push_back(true);
} else if (arg == "false") {
params.push_back(false);
} else {
try {
if (arg.find('.') != std::string::npos) {
params.push_back(std::stod(arg));
} else {
// Try as integer
params.push_back(std::stoll(arg));
}
} catch (...) {
// Keep as string
params.push_back(arg);
}
} catch (...) {
// Keep as string
params.push_back(arg);
}
}
@@ -1757,9 +1791,24 @@ void ConsoleTab::addLine(const std::string& line, ImU32 color)
lines_.push_back({line, color});
// Limit buffer size
// Limit buffer size — adjust selection indices when lines are removed
// from the front so the highlight stays on the text the user selected.
int popped = 0;
while (lines_.size() > 10000) {
lines_.pop_front();
popped++;
}
if (popped > 0 && has_selection_) {
sel_anchor_.line -= popped;
sel_end_.line -= popped;
if (sel_anchor_.line < 0 && sel_end_.line < 0) {
// Entire selection was in the removed range
has_selection_ = false;
is_selecting_ = false;
} else {
if (sel_anchor_.line < 0) { sel_anchor_.line = 0; sel_anchor_.col = 0; }
if (sel_end_.line < 0) { sel_end_.line = 0; sel_end_.col = 0; }
}
}
// Track new output while user is scrolled up

View File

@@ -907,7 +907,7 @@ void RenderPeersTab(App* app)
effects::ImGuiAcrylic::EndAcrylicPopup();
}
ImGui::SetCursorScreenPos(ImVec2(rowPos.x, rowEnd.y));
ImGui::SetCursorScreenPos(ImVec2(rawRowPosB.x, rowEnd.y));
if (i < state.bannedPeers.size() - 1) {
ImVec2 divStart = ImGui::GetCursorScreenPos();