Shell: only its own replies, Apps by their names, and rm as Unix has it (#67)
CI / build (pull_request) Successful in 1m38s
Site / build (pull_request) Successful in 10s

The Shell shows the replies to its own commands and nothing else. The
console knows who each line is printed for (Console::As, Console::origin):
a command run from the Shell prints as the Shell's, and what answers it
later from another task carries that along (ls, tasks, du, cp, update
check, sd list, screenshot, gemini get). Ctrl+b shows everything instead.
This replaces the ten-second window, which was a guess.

An App's name with a capital opens it (Notes, Irc, Wifi, Gnss, Gemini, Lora,
Storage, Shell, System, Settings), from the Shell and from the consoles.

rm needs -r for a folder, here and over the consoles. In the Shell a file,
or a folder with something in it, is asked about unless -f; an empty folder
with -r goes without a word.

The Shell now hands its line to the main loop to run: run from inside the
key handler, rm on a folder overflowed the loop's stack and crashed the
device. `info` says which App is in front.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EhqxQ49eCju4CzKYNjZzwT
This commit is contained in:
2026-10-07 11:35:29 +02:00
co-authored by Claude Opus 5.5
parent 3863d28593
commit 7b5df713ad
24 changed files with 414 additions and 124 deletions
+5 -9
View File
@@ -4,12 +4,6 @@
namespace roro {
bool ShellLog::wanted(const std::string& line, uint32_t nowMs) const {
if (line.rfind("status: heap ", 0) == 0) return false;
if (background_) return true;
return ranOne_ && nowMs - lastCommandMs_ < kMineMs; // unsigned: right across the clock's wrap
}
void ShellLog::push(const std::string& line) {
lines_.push_back(line);
bytes_ += line.size() + 1;
@@ -22,7 +16,7 @@ void ShellLog::push(const std::string& line) {
void ShellLog::add(const std::string& line) { push(line); }
void ShellLog::feed(const char* data, size_t len, uint32_t nowMs) {
void ShellLog::feed(const char* data, size_t len) {
for (size_t i = 0; i < len; i++) {
char c = data[i];
if (c == '\r') continue;
@@ -30,7 +24,7 @@ void ShellLog::feed(const char* data, size_t len, uint32_t nowMs) {
if (partial_.size() < 512) partial_ += c; // a line that never ends doesn't take the heap
continue;
}
if (wanted(partial_, nowMs)) push(partial_);
if (partial_.rfind("status: heap ", 0) != 0) push(partial_);
partial_.clear();
}
}
@@ -61,7 +55,9 @@ std::vector<std::string> commandWords(const char* helpText) {
if (start != std::string::npos) {
size_t end = one.find(' ', start);
std::string word = one.substr(start, end == std::string::npos ? std::string::npos : end - start);
bool plain = !word.empty() && std::all_of(word.begin(), word.end(), [](char ch) { return ch >= 'a' && ch <= 'z'; });
// Small letters, and for an App's name a capital first.
bool plain = !word.empty() && std::all_of(word.begin() + 1, word.end(), [](char ch) { return ch >= 'a' && ch <= 'z'; }) &&
((word[0] >= 'a' && word[0] <= 'z') || (word[0] >= 'A' && word[0] <= 'Z'));
if (plain && std::find(words.begin(), words.end(), word) == words.end()) words.push_back(word);
}
if (bar == std::string::npos) break;
+8 -21
View File
@@ -8,43 +8,30 @@
namespace roro {
// What the Shell App shows (issue #67): the lines the console printed, with the oldest dropped past
// a size. Everything the firmware prints passes through; the Shell keeps what's worth a place on a
// ten-line screen (Q206).
// What the Shell App shows (issue #67): the lines it was given, with the oldest dropped past a size.
// Which lines it is given is the console's business: by default, only what is printed for the
// Shell's own commands (Console::Origin).
class ShellLog {
public:
static constexpr size_t kMaxBytes = 4096;
static constexpr uint32_t kMineMs = 10000; // how long after a command its output is taken to be
// Bytes as the console printed them: lines may arrive in pieces.
void feed(const char* data, size_t len, uint32_t nowMs);
// A line the Shell adds itself: the command typed, an answer of its own.
// Bytes as the console printed them: lines may arrive in pieces. The line with the free heap
// every ten seconds is never kept: it would push everything else off a ten-line screen.
void feed(const char* data, size_t len);
// A line the Shell adds itself.
void add(const std::string& line);
// A command was run now: what follows for a while is "its".
void commandRun(uint32_t nowMs) {
lastCommandMs_ = nowMs;
ranOne_ = true;
}
// All, or mine only: with background lines hidden, only what's printed in the ten seconds after
// a command is kept. A reply can't be told from other output any better: `ls` and `tasks` answer
// later, from other tasks. The line with the free heap every ten seconds is never kept.
void showBackground(bool show) { background_ = show; }
bool showsBackground() const { return background_; }
const std::deque<std::string>& lines() const { return lines_; }
void clear();
uint32_t revision() const { return revision_; } // changes when the lines do
private:
bool wanted(const std::string& line, uint32_t nowMs) const;
void push(const std::string& line);
std::deque<std::string> lines_;
std::string partial_;
size_t bytes_ = 0;
bool background_ = true, ranOne_ = false;
uint32_t lastCommandMs_ = 0, revision_ = 0;
uint32_t revision_ = 0;
};
// The commands `help` lists: the first word of each (`ls [folder] | du <path>` gives ls and du),