#ifndef _TUI_HPP_ #define _TUI_HPP_ #include #include #include #include #include #include #include #include #include #include class System; class Tui { enum class Completion { None, Path, Command }; struct Prompt { std::string question; std::function on_answer; Completion completion = Completion::None; }; struct CommandSpec { struct Param { std::string name; Completion completion = Completion::None; }; std::vector params; std::function &)> action; bool prompt_for_missing = true; std::string description; bool scriptable = true; bool interactive = false; ///< opens a full-screen mode when called bare bool hidden = false; ///< don't list in `help` (used by aliases) }; // ---- Shell state ---- std::vector history; std::vector recorded; // commands since the last 'new', for script-save std::vector output; std::string input; int cursor_pos; int history_idx; int scroll_offset; ///< Lines scrolled up from the tail; 0 = follow newest output. bool quit; bool in_source; std::unique_ptr sys; std::deque pending; std::map commands; std::map vars; ///< $var-style substitution table. // ---- Screen orchestration ---- int screen_idx; // Where Esc should send the user *next* if they came via an inter-screen // jump (e.g. Enter on a part in `explore` → set-connector-type). −1 means // "no back-link, Esc goes to the dashboard like usual". Always reset // after consumption to avoid stale links across unrelated navigation. int screen_back_idx = -1; // ---- Connect screen state ---- std::vector connect_modules; int connect_m1_idx; int connect_m2_idx; std::string connect_p1_filter; std::string connect_p2_filter; std::vector connect_p1_list; std::vector connect_p2_list; int connect_p1_idx; int connect_p2_idx; int connect_focus_idx; // ---- Explore screen state ---- std::vector explore_modules; int explore_module_idx; std::vector explore_types; int explore_type_idx; std::vector explore_children; int explore_child_idx; std::string explore_child_filter; std::string explore_detail_filter; std::vector explore_detail; std::vector 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 explore_detail2; std::vector explore_detail2_sig; int explore_detail2_idx = 0; std::string explore_header; int explore_focus_idx; // ---- Source-file loading (event-driven, one line per tick) ---- std::atomic loading; ///< true while a script is being processed; read by tick thread. std::atomic tick_in_flight; ///< main thread acks each tick by clearing this; ticker waits. std::string loading_filename; std::vector loading_lines; size_t loading_idx; int loading_executed; int loading_lineno; bool loading_prev_in_source; ftxui::ScreenInteractive *screen_ptr; ///< set in Run() so Source() can post events. // ---- 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 Yes/No confirmation dialog (modal) ---- bool confirm_open = false; std::string confirm_message; std::function confirm_on_yes; // ---- 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 entries; ///< rebuilt every frame std::vector 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 filters; std::vector filter_labels; ///< parallel to `filters` int filter_idx = 0; std::function on_confirm; }; FileDialogState file_dialog; // ---- Help screen state ---- int help_topic_idx = 0; std::vector help_topic_names; ///< populated by BuildHelpScreen // ---- Analyze screen state (unified verify + analyze) ---- int analyze_focus_idx = 0; ///< 0=issues 1=groups 2=types std::vector analyze_issues; std::vector analyze_groups; std::vector analyze_types; int analyze_issue_idx = 0; int analyze_group_idx = 0; int analyze_type_idx = 0; // ---- Command palette (global, fuzzy-find over commands + objects) ---- bool palette_open = false; std::string palette_query; int palette_idx = 0; // Rebuilt every frame from : label shown to the user. std::vector palette_labels; // Parallel kind/payload for each label. kind: 'c'=command, 'm'=module, // 's'=signal. payload: command name / module name / "module\tsignal". std::vector> palette_items; // ---- Signal-type popup (shared between net + explore screens) ---- bool sigtype_dialog_open = false; std::string sigtype_dialog_mod; std::string sigtype_dialog_sig; std::vector sigtype_dialog_entries; ///< ["power","gnd","other"] int sigtype_dialog_choice = 0; // ---- Set-type screen state ---- std::vector settype_modules; int settype_m_idx; std::string settype_p_filter; std::vector settype_p_list; int settype_p_idx; std::string settype_type; std::string settype_status; int settype_focus_idx; public: Tui(); ~Tui(); void Run(); void DumpCommandsMd(std::ostream &out) const; // Write the accumulated console output to `out`. Used by batch mode to // surface a script's output without starting the TUI. void DumpOutput(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(); // Per-file command-group registrators. Each adds entries to the // `commands` map. Called from RegisterCommands(). void RegisterExportCommands(); // commands_export.cpp // Shell (shell.cpp) void Print(const std::string &line); void Submit(); void Dispatch(const std::string &raw); void Finalize(const std::string &name, const CommandSpec &spec, const std::vector &args); void HistoryUp(); void HistoryDown(); void CancelPending(); void LoadHistory(); void AppendHistory(const std::string &cmd); void Source(const std::string &filename); void ProcessNextSourceLine(); std::string ExpandVars(const std::string &s) const; // Completion (completion.cpp) void CompleteCommand(size_t start = 0); void CompletePath(size_t start = 0); void CompleteInline(); // Open the signal-type popup for / (no-op if names don't resolve). void OpenSignalTypeDialog(const std::string &mod_name, const std::string &sig_name); // Apply the selected type to the targeted signal, record a // `set-signal-type` line (deduping consecutive edits of the same signal), // and close the popup. void ApplySignalTypeChoice(); // Filtered part list rebuild (used by connect & set-connector-type screens) void RefreshFilteredPartList(const std::vector &modules, int m_idx, const std::string &filter, std::vector &out, int &sel_idx); // Screen builders (screen_*.cpp) ftxui::Component BuildMainScreen(ftxui::ScreenInteractive &screen); ftxui::Component BuildConnectScreen(); ftxui::Component BuildSettypeScreen(); ftxui::Component BuildExploreScreen(); ftxui::Component BuildDashboardScreen(); ftxui::Component BuildAnalyzeScreen(); ftxui::Component BuildHelpScreen(); ftxui::Component BuildFileDialog(); ftxui::Component BuildErrorModal(); ftxui::Component BuildConfirmModal(); // 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); // Pop a centred Yes/No modal. `on_yes` runs only if the user picks // Yes (Esc and No cancel). Use for destructive confirmations // (overwrite a file, delete a snapshot, …). void ShowConfirm(const std::string &msg, std::function on_yes); // 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 filters, std::function on_confirm); void ConfirmFileDialog(); ftxui::Component BuildSignalTypeModal(); ftxui::Component BuildPaletteModal(); // Open palette (resets query/index, builds initial list). void OpenPalette(); // Execute the currently-highlighted palette entry. void ActivatePaletteEntry(); }; #endif // _TUI_HPP_