Export command (CSV + ODS), file dialog, error modal, path persistence.
New user-facing features:
- `export connections <file>` writes a tabular dump of every wire pair:
connection, transform, left/right module/part/pin/signal/type/suspect,
mixed-types flag. Dispatch on extension: `.csv` (flat file) or `.ods`
(one sheet per connection). Any other extension shows an error and
writes nothing.
- Bare `export` (or dashboard `[x]`, or palette `export`) opens an
interactive file-picker dialog with a CSV/ODS toggle at the top.
Picking a filter rewrites the filename's extension. Last-used
directory and filename are remembered per-call-site.
- Two new CLI flags on the binary: `--source FILE` to run a script at
boot, `--restore FILE` to restore a snapshot at boot. Combinable.
Reusable infrastructure:
- `OdsWriter` (`src/imports/ods_writer.{hpp,cpp}`): minimal .ods writer
using libzip + pugixml (already in the build for the importer).
Multi-sheet workbook of string cells. ~180 lines, no new dep.
- Generic file-picker dialog (`screen_filedialog.cpp`): one Modal
reused for any "pick a path" interaction via
`OpenFileDialog(title, persist_key, default_filename, filters, cb)`.
Validates the picked extension against the filter whitelist;
unknown ones stay in the dialog with a status message. Persists
(dir, filename) per `persist_key`.
- Generic error modal (`screen_error.cpp`, `ShowError(msg)`): centred
red-titled popup, dismissable with Esc/Enter. Used by the export
failures (open-for-write, ODS save, unknown extension/kind);
ready for adoption elsewhere.
- Per-key path persistence (`SaveLastUsed`/`LoadLastUsed` in
`shell.cpp`): two-line file per key under the user-data dir.
- `UserDataDir()` extracted from the history path helper so the new
per-key persistence shares the same XDG/AppData logic.
- New help-screen topic "Export"; user-facing `doc/user/analysis.md`
gains an "Exporting" section; `DESIGN.md` gains a generics
section covering the dialog / error modal / persistence / ODS
writer; `DumpCommandsMd` now respects the `hidden` flag (the
`connect` alias no longer appears in the auto-gen reference).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -84,8 +84,13 @@ class Tui {
|
||||
std::string explore_child_filter;
|
||||
std::string explore_detail_filter;
|
||||
std::vector<std::string> explore_detail;
|
||||
std::vector<std::string> explore_detail_sig; ///< parallel: signal name per detail line (empty = no signal)
|
||||
std::vector<std::string> explore_detail_sig; ///< parallel: `module\tsignal` per row (empty = no action)
|
||||
int explore_detail_idx;
|
||||
// Second detail sub-panel — populated only when the children pane is on
|
||||
// the signals tab (top = local pins, bottom = net members).
|
||||
std::vector<std::string> explore_detail2;
|
||||
std::vector<std::string> explore_detail2_sig;
|
||||
int explore_detail2_idx = 0;
|
||||
std::string explore_header;
|
||||
int explore_focus_idx;
|
||||
|
||||
@@ -103,6 +108,45 @@ class Tui {
|
||||
// ---- Dashboard scroll state (0 = top; grows as the user scrolls down) ----
|
||||
int dashboard_scroll_offset = 0;
|
||||
|
||||
// ---- Generic error-notification dialog (modal) ----
|
||||
bool error_open = false;
|
||||
std::string error_message;
|
||||
|
||||
// ---- Generic file-picker dialog (modal) ----
|
||||
// Reused for any "pick a path" interaction (export, save, restore, …).
|
||||
// `persist_key` ties the dir + filename to a key persisted under the
|
||||
// user-data directory so the next invocation under the same key
|
||||
// re-opens on the same location. `on_confirm` runs the actual action
|
||||
// when the user accepts a path.
|
||||
//
|
||||
// Optional `filters`: a horizontal selector (Toggle) at the top of
|
||||
// the dialog with (label, extension) pairs. Picking a filter rewrites
|
||||
// the filename's extension to match. The action then dispatches on
|
||||
// extension (the dialog stays format-agnostic).
|
||||
public:
|
||||
struct FilenameFilter { std::string label; std::string extension; };
|
||||
private:
|
||||
struct FileDialogState {
|
||||
bool open = false;
|
||||
std::string title;
|
||||
std::string persist_key;
|
||||
std::string dir;
|
||||
std::string filename;
|
||||
std::vector<std::string> entries; ///< rebuilt every frame
|
||||
std::vector<bool> entries_is_dir;
|
||||
int entry_idx = 0;
|
||||
// Focus map (variable: 0=filters [if any], else +1 each next slot):
|
||||
// filters present → 0=filters 1=entries 2=filename 3=button (4 slots)
|
||||
// no filters → 0=entries 1=filename 2=button (3 slots)
|
||||
int focus_idx = 0;
|
||||
std::string status;
|
||||
std::vector<FilenameFilter> filters;
|
||||
std::vector<std::string> filter_labels; ///< parallel to `filters`
|
||||
int filter_idx = 0;
|
||||
std::function<void(const std::string &)> on_confirm;
|
||||
};
|
||||
FileDialogState file_dialog;
|
||||
|
||||
// ---- Help screen state ----
|
||||
int help_topic_idx = 0;
|
||||
std::vector<std::string> help_topic_names; ///< populated by BuildHelpScreen
|
||||
@@ -149,6 +193,11 @@ public:
|
||||
void Run();
|
||||
void DumpCommandsMd(std::ostream &out) const;
|
||||
|
||||
// Boot-time hook: dispatch a single command exactly as if the user
|
||||
// typed it (e.g. `restore foo.essim` or `source bring-up.essim`).
|
||||
// Call before `Run()` to seed the system before the event loop starts.
|
||||
void BootDispatch(const std::string &raw);
|
||||
|
||||
private:
|
||||
// Lifecycle (commands.cpp)
|
||||
void RegisterCommands();
|
||||
@@ -197,6 +246,22 @@ private:
|
||||
ftxui::Component BuildDashboardScreen();
|
||||
ftxui::Component BuildAnalyzeScreen();
|
||||
ftxui::Component BuildHelpScreen();
|
||||
ftxui::Component BuildFileDialog();
|
||||
ftxui::Component BuildErrorModal();
|
||||
// Pop a centred modal with `msg` and an OK button. Esc / Enter close
|
||||
// it. Use for actionable failures the user must see (write errors,
|
||||
// bad inputs, etc.) — for normal feedback keep `Print()`.
|
||||
void ShowError(const std::string &msg);
|
||||
// Open the picker modal. `persist_key` controls where the last-used
|
||||
// dir + filename are stored (one tiny file per key under the user
|
||||
// data directory). `on_confirm` runs when the user presses Enter on
|
||||
// the action button — it receives the absolute path the user picked.
|
||||
void OpenFileDialog(std::string title,
|
||||
std::string persist_key,
|
||||
std::string default_filename,
|
||||
std::vector<FilenameFilter> filters,
|
||||
std::function<void(const std::string &)> on_confirm);
|
||||
void ConfirmFileDialog();
|
||||
ftxui::Component BuildSignalTypeModal();
|
||||
ftxui::Component BuildPaletteModal();
|
||||
// Open palette (resets query/index, builds initial list).
|
||||
|
||||
Reference in New Issue
Block a user