25template<concepts::signal_br
idge Br
idge>
struct processor
38 const std::shared_ptr<Bridge> &bridge,
41 : node_id_(std::move(node_id)), mode_(std::move(mode)), bridge_(bridge), command_queue_(command_queue),
42 ui_event_queue_(ui_event_queue), prompt_(std::string(GREEN) +
"[⇌] " + RESET)
61 print_message(
"Radix Relay - Interactive Mode");
62 print_message(
"Node: " + node_id_);
63 print_message(
"Mode: " + mode_);
65 print_message(
"Type '/help' for available commands, '/quit' to exit");
70 while (running_.load()) {
72 while (
auto event = ui_event_queue_->try_pop()) { process_ui_event(*event); }
74 const char *input = rx_.input(prompt_);
76 if (input ==
nullptr) {
77 running_.store(
false);
81 std::string command(input);
83 command.erase(0, command.find_first_not_of(
" \t\r\n"));
84 command.erase(command.find_last_not_of(
" \t\r\n") + 1);
86 if (command.empty()) {
continue; }
88 if (command ==
"/quit" or command ==
"/exit" or command ==
"/q") {
89 print_message(
"Goodbye!");
90 running_.store(
false);
94 rx_.history_add(command);
95 process_command(command);
98 constexpr auto max_wait_ms = 100;
99 constexpr auto poll_interval_ms = 5;
100 for (
int waited = 0; waited < max_wait_ms; waited += poll_interval_ms) {
101 if (
auto event = ui_event_queue_->try_pop()) {
103 process_ui_event(*event);
104 while (
auto next_event = ui_event_queue_->try_pop()) { process_ui_event(*next_event); }
107 std::this_thread::sleep_for(std::chrono::milliseconds(poll_interval_ms));
120 if (running_.exchange(
false)) {
121 if (message_poller_.joinable()) { message_poller_.join(); }
130 [[nodiscard]]
auto get_mode() const -> const std::
string & {
return mode_; }
139 active_chat_context_ = std::move(contact_name);
148 active_chat_context_.reset();
157 [[nodiscard]]
auto get_chat_context() const -> std::optional<std::
string> {
return active_chat_context_; }
164 [[nodiscard]]
auto get_prompt() const -> const std::
string & {
return prompt_; }
170 auto setup_replxx() ->
void
172 prompt_ = std::string(GREEN) +
"[⇌] " + RESET;
174 rx_.history_load(HISTORY_FILE);
176 rx_.set_max_history_size(MAX_HISTORY_SIZE);
177 rx_.set_max_hint_rows(MAX_HINT_ROWS);
179 rx_.set_completion_callback([](
const std::string &input,
int & ) {
180 replxx::Replxx::completions_t completions;
182 std::vector<std::string> commands = {
"/connect",
195 std::copy_if(commands.begin(), commands.end(), std::back_inserter(completions), [&input](
const std::string &cmd) {
196 return cmd.starts_with(input);
208 auto print_message(
const std::string &message) ->
void
211 if (not msg.empty() and msg.back() ==
'\n') { msg.pop_back(); }
212 auto formatted = std::string(GREEN) + msg + RESET +
"\n";
213 rx_.write(formatted.c_str(),
static_cast<int>(formatted.size()));
221 auto process_command(
const std::string &input) ->
void
223 constexpr auto mode_cmd =
"/mode ";
224 if (input.starts_with(mode_cmd)) {
225 const auto new_mode = input.substr(std::string_view(mode_cmd).length());
226 if (new_mode ==
"internet" or new_mode ==
"mesh" or new_mode ==
"hybrid") {
227 mode_ = std::string(new_mode);
228 print_message(
"Switched to " + std::string(new_mode) +
" mode");
230 print_message(
"Invalid mode. Use: internet, mesh, or hybrid");
235 command_queue_->push(core::events::raw_command{ .input = std::string(input) });
241 auto save_history() ->
void { rx_.history_save(HISTORY_FILE); }
246 auto update_prompt() ->
void
248 if (active_chat_context_.has_value()) {
249 prompt_ = std::string(GREEN) +
"[⇌ @" + active_chat_context_.value() +
"] " + RESET;
251 prompt_ = std::string(GREEN) +
"[⇌] " + RESET;
262 std::visit(core::overload{ [
this](
const core::events::display_message &evt) { print_message(evt.message); },
263 [
this](
const core::events::enter_chat_mode &evt) {
update_chat_context(evt.display_name); },
268 static constexpr const char *GREEN =
"\033[32m";
269 static constexpr const char *RESET =
"\033[0m";
270 static constexpr const char *HISTORY_FILE =
".radix_relay_history";
271 static constexpr auto message_poll_interval_ms = 16;
272 static constexpr auto MAX_HISTORY_SIZE = 1000;
273 static constexpr auto MAX_HINT_ROWS = 3;
275 std::string node_id_;
277 std::shared_ptr<Bridge> bridge_;
278 std::shared_ptr<async::async_queue<core::events::raw_command>> command_queue_;
279 std::shared_ptr<async::async_queue<core::events::ui_event_t>> ui_event_queue_;
281 std::atomic<bool> running_{
false };
282 std::thread message_poller_;
285 std::optional<std::string> active_chat_context_;