fix(ui): trim verbose startup notice; show daemon output on shutdown for external daemons

- Loading "taking longer than expected" notice: shorten the body + hint so the
  startup screen reads less wordy (same info, ~half the text).
- Shutdown screen: when the wallet attached to an EXTERNAL daemon (no captured
  stdout — debug_log_path_ is only set when we spawn it), the "dragonxd output"
  panel was always empty, leaving just a spinner. Fall back to tailing the
  daemon's debug.log so the user can watch the node flush the block index and
  exit. Adds App::tailDaemonDebugLog() (best-effort, reads only the file tail).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-08-30 23:09:17 -05:00
co-authored by Claude Opus 4.8
parent 870793433b
commit 29274c2f48
3 changed files with 46 additions and 2 deletions
+37
View File
@@ -5742,6 +5742,39 @@ void App::beginShutdown()
});
}
std::vector<std::string> App::tailDaemonDebugLog(int maxLines) const
{
std::vector<std::string> out;
if (maxLines <= 0) return out;
const std::string path = util::Platform::getDataDir() + "debug.log";
std::error_code ec;
const auto sz = std::filesystem::file_size(path, ec);
if (ec || sz == 0) return out;
std::ifstream f(path, std::ios::binary);
if (!f) return out;
// Read only the last ~16 KB — plenty for a handful of lines, cheap even for a multi-GB log.
const std::uintmax_t kTailBytes = 16 * 1024;
const std::uintmax_t start = sz > kTailBytes ? sz - kTailBytes : 0;
f.seekg(static_cast<std::streamoff>(start), std::ios::beg);
std::string chunk(static_cast<std::size_t>(sz - start), '\0');
f.read(&chunk[0], static_cast<std::streamsize>(chunk.size()));
chunk.resize(static_cast<std::size_t>(f.gcount()));
std::vector<std::string> lines;
std::string cur;
for (char c : chunk) {
if (c == '\n') { if (!cur.empty()) lines.push_back(cur); cur.clear(); }
else if (c != '\r') cur.push_back(c);
}
if (!cur.empty()) lines.push_back(cur);
// When we seeked into the middle of the file the first line is a fragment — drop it.
if (start > 0 && !lines.empty()) lines.erase(lines.begin());
if (static_cast<int>(lines.size()) > maxLines)
lines.erase(lines.begin(), lines.end() - static_cast<std::ptrdiff_t>(maxLines));
return lines;
}
void App::renderShutdownScreen()
{
using namespace ui::material;
@@ -5974,6 +6007,10 @@ void App::renderShutdownScreen()
// -------------------------------------------------------------------
if (daemon_controller_) {
auto lines = daemon_controller_->recentLines(8);
// External daemon (attached, not spawned) has no captured stdout — tail its debug.log directly
// so the user can still watch the node flush the block index and exit.
if (lines.empty())
lines = tailDaemonDebugLog(8);
if (!lines.empty()) {
float panelW = vp_size.x * shutElem("panel-width-fraction", 0.70f);
float panelX = cx - panelW * 0.5f;