Signal-type popup, NC pin tagging, interactive viewer hygiene.

- Enter on a signal entry (net / explore) opens a modal popup to pick
  power / gnd / other. Recording is deduped: a sequence of toggles on
  the same signal collapses to a single `set-signal-type` line; no-op
  selections record nothing.
- Bare interactive commands (the ones that open a full-screen mode)
  are no longer recorded by `script-save`. Their inline forms still
  are. Mutating actions inside a screen record their own canonical
  line.
- Mentor importer treats signals whose name starts with `unconnected`
  as no-connect — the pin is kept on the part without a signal and
  tagged `ImportedUnconnected`.
- `drop_singleton_signals` runs at the end of `load`: any signal with
  exactly one pin is detached (singletons are NC by definition); the
  pin is tagged `DroppedSingleton`. Count is reported inline.
- `verify` gains a one-line orphan summary (imported NC / dropped
  singleton totals). Pins materialised by `FillIdentityNCs` are
  excluded via a `pin_map` filter — they are bridged to a real signal
  on the peer module and are not real NCs at system level.
- NcOrigin tag is serialized in save snapshots as an optional 4th
  field on N records (backward-compatible).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
2026-05-13 07:56:46 +02:00
parent 043fef0a31
commit 280526304d
16 changed files with 295 additions and 16 deletions

View File

@@ -1,9 +1,14 @@
#include "tui/tui.hpp"
#include "tui/tui_helpers.hpp"
#include "system/modules.hpp"
#include "system/signals.hpp"
#include "system/system.hpp"
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <exception>
#include <filesystem>
#include <fstream>
#include <ostream>
@@ -11,6 +16,55 @@
#include <system_error>
#include <thread>
void Tui::OpenSignalTypeDialog(const std::string &mod_name,
const std::string &sig_name) {
if (!sys) return;
Signal *sig = nullptr;
try {
Module *m = sys->modules()->get(mod_name);
sig = m->signals->get(sig_name);
} catch (const std::exception &) { return; }
sigtype_dialog_mod = mod_name;
sigtype_dialog_sig = sig_name;
switch (sig->type) {
case SignalType::Power: sigtype_dialog_choice = 0; break;
case SignalType::GndShield: sigtype_dialog_choice = 1; break;
default: sigtype_dialog_choice = 2; break;
}
sigtype_dialog_open = true;
}
void Tui::ApplySignalTypeChoice() {
sigtype_dialog_open = false;
if (!sys) return;
SignalType t;
switch (sigtype_dialog_choice) {
case 0: t = SignalType::Power; break;
case 1: t = SignalType::GndShield; break;
default: t = SignalType::Other; break;
}
Signal *sig = nullptr;
try {
Module *m = sys->modules()->get(sigtype_dialog_mod);
sig = m->signals->get(sigtype_dialog_sig);
} catch (const std::exception &) { return; }
if (sig->type == t) return; // no-op, no record
sig->type = t;
if (in_source) return;
// Dedup: if the immediately previous recorded line targets the same
// signal, replace it so a sequence of toggles collapses to one line.
std::string line = "set-signal-type " + sigtype_dialog_mod + " "
+ sigtype_dialog_sig + " " + signal_type_name(t);
std::string prefix = "set-signal-type " + sigtype_dialog_mod + " "
+ sigtype_dialog_sig + " ";
if (!recorded.empty() && recorded.back().rfind(prefix, 0) == 0)
recorded.back() = std::move(line);
else
recorded.push_back(std::move(line));
}
void Tui::Print(const std::string &line) {
output.push_back(line);
scroll_offset = 0; // any new line snaps the view back to the tail
@@ -136,7 +190,12 @@ void Tui::Finalize(const std::string &name,
static const std::set<std::string> no_record = {
"clear", "help", "quit", "exit", "source", "script-save",
};
if (spec.scriptable && !no_record.count(name)) recorded.push_back(canonical);
// A bare invocation of an `interactive` command opens a full-screen mode
// rather than mutating state — skip it. Any mutating action taken inside
// that screen records its own canonical line via the action callbacks.
bool opens_screen = spec.interactive && args.empty();
if (spec.scriptable && !opens_screen && !no_record.count(name))
recorded.push_back(canonical);
}
std::string Tui::ExpandVars(const std::string &s) const {