#include "livestream.h" #include "ansi.h" #include "../util/text.h" #include "../util/utf8.h" #include #include namespace detz::shell { LiveStream::LiveStream(int height, int width, OutputSink* file, bool enabled) : height_(std::max(1, height)), width_(width ? width : AnsiText::TerminalWidth(80)), file_(file), enabled_(enabled) { if (!file_) { ownedStdout_ = std::make_unique(stdout); file_ = ownedStdout_.get(); } } void LiveStream::Feed(const std::string& line) { if (!enabled_) return; // line.split("\r")[-1].rstrip("\n").expandtabs(8) std::string last = line; size_t cr = last.rfind('\r'); if (cr != std::string::npos) last = last.substr(cr + 1); while (!last.empty() && last.back() == '\n') last.pop_back(); std::string cleaned = AnsiText::Truncate(Utf8::ExpandTabs(last, 8), std::max(8, width_ - 2)); lines_.push_back(cleaned); if ((int)lines_.size() > height_) lines_.pop_front(); if (shown_ < height_) { file_->Write("\xe2\x94\x82 " + cleaned + "\n"); // │ ++shown_; } else { file_->Write("\033[" + std::to_string(height_) + "A"); for (auto& kept : lines_) file_->Write("\r\033[K\xe2\x94\x82 " + kept + "\n"); } file_->Flush(); } void LiveStream::Pause() { lines_.clear(); shown_ = 0; } void LiveStream::Close(const std::string& status) { if (!enabled_) return; if (shown_ == 0) file_->Write("\xe2\x94\x82 (no output)\n"); file_->Write("\xe2\x94\x82 " + AnsiText::Truncate(status, std::max(8, width_ - 2)) + "\n"); file_->Flush(); } std::string StreamStatus(int code, const std::string& output, double elapsed, bool timedOut) { std::string detail = std::to_string(Utf8::SplitLines(output).size()) + " lines \xc2\xb7 " + HumanSize((long)Utf8::Encode(Utf8::Decode(output)).size()); if (timedOut) return "timed out after 120s (killed) \xc2\xb7 " + detail; char buf[64]; std::snprintf(buf, sizeof buf, "%.1f", elapsed); return "exit " + std::to_string(code) + " \xc2\xb7 " + buf + "s \xc2\xb7 " + detail; } } // namespace detz::shell