23template<concepts::signal_br
idge Br
idge>
struct processor
36 const std::shared_ptr<Bridge> &bridge,
39 : node_id_(std::move(node_id)), mode_(std::move(mode)), bridge_(bridge), command_queue_(command_queue),
40 display_queue_(display_queue)
59 print_message(
"Radix Relay - Interactive Mode");
60 print_message(
"Node: " + node_id_);
61 print_message(
"Mode: " + mode_);
63 print_message(
"Type '/help' for available commands, '/quit' to exit");
68 message_poller_ = std::thread([
this] {
69 while (running_.load()) {
70 while (auto msg = display_queue_->try_pop()) { print_message(msg->message); }
71 std::this_thread::sleep_for(std::chrono::milliseconds(message_poll_interval_ms));
75 while (running_.load()) {
76 const char *input = rx_.input(prompt_);
78 if (input ==
nullptr) {
79 running_.store(
false);
83 std::string command(input);
85 command.erase(0, command.find_first_not_of(
" \t\r\n"));
86 command.erase(command.find_last_not_of(
" \t\r\n") + 1);
88 if (command.empty()) {
continue; }
90 if (command ==
"/quit" or command ==
"/exit" or command ==
"/q") {
91 print_message(
"Goodbye!");
92 running_.store(
false);
96 rx_.history_add(command);
97 process_command(command);
109 if (running_.exchange(
false)) {
110 if (message_poller_.joinable()) { message_poller_.join(); }
119 [[nodiscard]]
auto get_mode() const -> const std::
string & {
return mode_; }
125 auto setup_replxx() ->
void
127 prompt_ = std::string(GREEN) +
"[⇌] " + RESET;
129 rx_.history_load(HISTORY_FILE);
131 rx_.set_max_history_size(MAX_HISTORY_SIZE);
132 rx_.set_max_hint_rows(MAX_HINT_ROWS);
134 rx_.set_completion_callback([](
const std::string &input,
int & ) {
135 replxx::Replxx::completions_t completions;
137 std::vector<std::string> commands = {
"/connect",
150 std::copy_if(commands.begin(), commands.end(), std::back_inserter(completions), [&input](
const std::string &cmd) {
151 return cmd.starts_with(input);
163 auto print_message(
const std::string &message) ->
void
166 if (not msg.empty() and msg.back() ==
'\n') { msg.pop_back(); }
167 auto formatted = std::string(GREEN) + msg + RESET +
"\n";
168 rx_.write(formatted.c_str(),
static_cast<int>(formatted.size()));
176 auto process_command(
const std::string &input) ->
void
178 constexpr auto mode_cmd =
"/mode ";
179 if (input.starts_with(mode_cmd)) {
180 const auto new_mode = input.substr(std::string_view(mode_cmd).length());
181 if (new_mode ==
"internet" or new_mode ==
"mesh" or new_mode ==
"hybrid") {
182 mode_ = std::string(new_mode);
183 print_message(
"Switched to " + std::string(new_mode) +
" mode");
185 print_message(
"Invalid mode. Use: internet, mesh, or hybrid");
190 command_queue_->push(core::events::raw_command{ .input = std::string(input) });
196 auto save_history() ->
void { rx_.history_save(HISTORY_FILE); }
198 static constexpr const char *GREEN =
"\033[32m";
199 static constexpr const char *RESET =
"\033[0m";
200 static constexpr const char *HISTORY_FILE =
".radix_relay_history";
201 static constexpr auto message_poll_interval_ms = 16;
202 static constexpr auto MAX_HISTORY_SIZE = 1000;
203 static constexpr auto MAX_HINT_ROWS = 3;
205 std::string node_id_;
207 std::shared_ptr<Bridge> bridge_;
208 std::shared_ptr<async::async_queue<core::events::raw_command>> command_queue_;
209 std::shared_ptr<async::async_queue<core::events::display_message>> display_queue_;
211 std::atomic<bool> running_{
false };
212 std::thread message_poller_;