Interactive net screen, main + per-screen title bars, focus highlight, help split.
- New `net` full-screen layout (`screen_net.cpp`, `screen_idx = 5`): three columns (module menu / signal filter + menu / live BFS result). Bare `net` opens the screen; `net <m> <s>` keeps the inline path. - Main screen grows a title bar: " essim — system digital twin " (bold + dim) on the left, live "N module(s), M connection(s)" on the right. - Every interactive screen now renders the same breadcrumb at the top: " essim → <name> — <short description> ", followed by a separator. - `tui_helpers.hpp` exports `FocusLabel(elem, focused)`. Every interactive screen wraps its field labels with it so the active field's label flips to inverted video. Buttons (Connect, Apply) invert as a whole. - `CommandSpec` gains a `bool interactive`. `help` (no args) splits the listing into "Interactive (open a full-screen mode)" and "Other". `help <name>` tags interactive entries with [interactive] and explains the bare-vs-inline duality. - `DESIGN.md` (renamed from `CLAUDE.md`): refreshed Layout, TUI, and screen-recipe sections to cover the new field, the title idiom, FocusLabel, the `net` screen, and the event-paced `Computing…` modal during `source`. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -25,15 +25,23 @@ void Tui::RegisterCommands() {
|
||||
{{"command name (optional)", Completion::Command}},
|
||||
[this](const std::vector<std::string> &args) {
|
||||
if (args.empty()) {
|
||||
Print("Commands — type `help <name>` for details.");
|
||||
size_t maxw = 0;
|
||||
for (const auto &kv : commands) maxw = std::max(maxw, kv.first.size());
|
||||
for (const auto &kv : commands) {
|
||||
Print(" " + kv.first
|
||||
+ std::string(maxw - kv.first.size() + 2, ' ')
|
||||
+ kv.second.description);
|
||||
}
|
||||
Print("Keys: Esc cancels a multi-step prompt; Tab completes commands or paths;");
|
||||
auto print_group = [&](const std::string &title, bool want_interactive) {
|
||||
bool printed_any = false;
|
||||
for (const auto &kv : commands) {
|
||||
if (kv.second.interactive != want_interactive) continue;
|
||||
if (!printed_any) { Print(title); printed_any = true; }
|
||||
Print(" " + kv.first
|
||||
+ std::string(maxw - kv.first.size() + 2, ' ')
|
||||
+ kv.second.description);
|
||||
}
|
||||
};
|
||||
Print("Commands — type `help <name>` for details.");
|
||||
print_group("Interactive (open a full-screen mode):", true);
|
||||
print_group("Other:", false);
|
||||
Print("Keys: Esc cancels a multi-step prompt or leaves an interactive screen;");
|
||||
Print(" Tab completes commands/paths or cycles focus in interactive screens;");
|
||||
Print(" PageUp/PageDown scroll output (10 lines), Home/End jump to top/bottom.");
|
||||
return;
|
||||
}
|
||||
@@ -41,7 +49,8 @@ void Tui::RegisterCommands() {
|
||||
auto it = commands.find(name);
|
||||
if (it == commands.end()) { Print("unknown command: " + name); return; }
|
||||
const auto &spec = it->second;
|
||||
Print(name + " — " + spec.description);
|
||||
std::string tag = spec.interactive ? " [interactive]" : "";
|
||||
Print(name + tag + " — " + spec.description);
|
||||
if (spec.params.empty()) {
|
||||
Print(" no arguments.");
|
||||
} else {
|
||||
@@ -49,8 +58,11 @@ void Tui::RegisterCommands() {
|
||||
Print(" arg " + std::to_string(i + 1) + ": " + spec.params[i].name);
|
||||
}
|
||||
}
|
||||
if (!spec.prompt_for_missing) {
|
||||
Print(" run with no args for the interactive form.");
|
||||
if (spec.interactive) {
|
||||
Print(" run with no args to open the interactive screen,");
|
||||
Print(" or with all args for inline (scriptable) execution.");
|
||||
} else if (!spec.prompt_for_missing) {
|
||||
Print(" no per-arg prompt — provide all args inline (or use the bare form).");
|
||||
} else if (!spec.params.empty()) {
|
||||
Print(" missing args trigger a prompt for each one.");
|
||||
}
|
||||
@@ -245,6 +257,25 @@ void Tui::RegisterCommands() {
|
||||
{"signal name", Completion::None}},
|
||||
[this](const std::vector<std::string> &args) {
|
||||
if (!sys) { Print("no system: run 'new' first."); return; }
|
||||
|
||||
if (args.empty()) {
|
||||
net_modules.clear();
|
||||
for (auto &m : *sys->modules()) net_modules.push_back(m.first);
|
||||
std::sort(net_modules.begin(), net_modules.end(), NaturalLess);
|
||||
if (net_modules.empty()) { Print("no modules loaded."); return; }
|
||||
net_module_idx = 0;
|
||||
net_sig_filter.clear();
|
||||
net_sig_idx = 0;
|
||||
net_focus_idx = 0;
|
||||
screen_idx = 5;
|
||||
return;
|
||||
}
|
||||
|
||||
if (args.size() != 2) {
|
||||
Print("usage: net <module> <signal> (or no args for interactive)");
|
||||
return;
|
||||
}
|
||||
|
||||
Module *mod;
|
||||
try { mod = sys->modules()->get(args[0]); }
|
||||
catch (const std::exception &) {
|
||||
@@ -267,8 +298,11 @@ void Tui::RegisterCommands() {
|
||||
+ " (" + signal_type_name(mp.second->type) + ")");
|
||||
}
|
||||
},
|
||||
/*prompt_for_missing=*/ true,
|
||||
"show all signals reachable from <module>/<signal> through connections",
|
||||
/*prompt_for_missing=*/ false,
|
||||
"show all signals reachable from <module>/<signal> through connections "
|
||||
"(interactive screen if no args)",
|
||||
/*scriptable=*/ true,
|
||||
/*interactive=*/ true,
|
||||
};
|
||||
|
||||
commands["set-signal-type"] = {
|
||||
@@ -364,6 +398,8 @@ void Tui::RegisterCommands() {
|
||||
},
|
||||
/*prompt_for_missing=*/ false,
|
||||
"tag a part's connector type for transform lookup",
|
||||
/*scriptable=*/ true,
|
||||
/*interactive=*/ true,
|
||||
};
|
||||
|
||||
commands["connect"] = {
|
||||
@@ -498,6 +534,8 @@ void Tui::RegisterCommands() {
|
||||
},
|
||||
/*prompt_for_missing=*/ false,
|
||||
"connect a part across two modules (interactive screen if no args)",
|
||||
/*scriptable=*/ true,
|
||||
/*interactive=*/ true,
|
||||
};
|
||||
|
||||
commands["explore"] = { {}, [this](auto &) {
|
||||
@@ -515,7 +553,8 @@ void Tui::RegisterCommands() {
|
||||
explore_focus_idx = 0;
|
||||
screen_idx = 4;
|
||||
}, true, "browse modules → parts/signals/connections → details (interactive)",
|
||||
/*scriptable=*/ false };
|
||||
/*scriptable=*/ false,
|
||||
/*interactive=*/ true };
|
||||
|
||||
commands["search"] = {
|
||||
{{"module", Completion::None},
|
||||
@@ -575,6 +614,8 @@ void Tui::RegisterCommands() {
|
||||
},
|
||||
/*prompt_for_missing=*/ false,
|
||||
"list parts/signals matching a pattern (interactive screen if no args)",
|
||||
/*scriptable=*/ true,
|
||||
/*interactive=*/ true,
|
||||
};
|
||||
|
||||
commands["duplicate"] = {
|
||||
|
||||
Reference in New Issue
Block a user