[Feat] Split Daemon & TUI

This commit is contained in:
Alex Emmet 2026-07-20 23:40:33 +02:00
commit 36a70e82a0
35 changed files with 970 additions and 239 deletions

View file

@ -1,10 +1,17 @@
use crossterm::event::{KeyCode, KeyEvent};
use iota_logger::{log, log_command, log_cv};
#[cfg(feature = "legacy-commands")]
use iota_logger::{log, log_cv};
#[cfg(feature = "legacy-commands")]
use iota_state::{ACTIVE_TASKS, RELOAD, SHUTDOWN};
#[cfg(feature = "legacy-commands")]
use iota_storage::users::{user_manager, user_profile::UserProfile};
#[cfg(feature = "legacy-commands")]
use iota_storage::util::config_util::modify_config;
#[cfg(feature = "legacy-commands")]
use iota_util::file_util;
#[cfg(feature = "legacy-commands")]
use mtp::codec::{CommunicationType, CommunicationValue};
#[cfg(feature = "legacy-commands")]
use omikron_connector::omikron_connection::OMIKRON_CONNECTION;
use ratatui::{
Frame,
@ -13,7 +20,6 @@ use ratatui::{
text::{Line, Span},
widgets::{Block, Borders, Paragraph},
};
use uuid::Uuid;
use std::{
any::Any,
@ -25,11 +31,12 @@ use tokio::time::Instant;
use crate::{
elements::elements::{Element, InteractableElement, JoinableElement},
interaction_result::InteractionResult,
ui::FPS,
ipc_client::IpcClient,
util::borders::draw_block_joins,
};
pub struct ConsoleCard {
ipc: Arc<IpcClient>,
focused: bool,
pub title: String,
pub content: String,
@ -44,8 +51,9 @@ pub struct ConsoleCard {
}
impl ConsoleCard {
pub fn new(title: &str, content: &str) -> Self {
pub fn new(title: &str, content: &str, ipc: Arc<IpcClient>) -> Self {
ConsoleCard {
ipc,
focused: false,
title: title.to_string(),
content: content.to_string(),
@ -290,22 +298,17 @@ impl InteractableElement for ConsoleCard {
match key.code {
KeyCode::Enter => {
if self.content.is_empty() {
log!("");
return InteractionResult::Handled;
}
let command = self.content.clone();
let id = Uuid::new_v4();
let id = id.to_string();
let id = id.split_at(8).0;
let task_id = format!("command_{}_{}", command, id);
ACTIVE_TASKS.insert(task_id.clone());
log_command!("{}", command);
let ipc = self.ipc.clone();
let seq = std::time::SystemTime::now()
.duration_since(std::time::UNIX_EPOCH)
.unwrap_or_default()
.as_millis() as u64;
tokio::spawn(async move {
run_command(&command).await;
ACTIVE_TASKS.remove(&task_id);
let _ = ipc.send_command(seq, command).await;
});
self.content.clear();
@ -379,6 +382,7 @@ impl InteractableElement for ConsoleCard {
}
}
#[cfg(feature = "legacy-commands")]
pub async fn run_command(command: &str) {
let parts = command.split(" ").collect::<Vec<&str>>();
@ -503,6 +507,7 @@ pub async fn run_command(command: &str) {
}
}
#[cfg(feature = "legacy-commands")]
pub async fn ping(time: u64) {
let conn = OMIKRON_CONNECTION.clone();

View file

@ -1,7 +1,7 @@
use std::{any::Any, sync::Arc};
use crossterm::event::KeyEvent;
use iota_state::APP_STATE;
use iota_state::ClientState;
use ratatui::{
Frame,
layout::Rect,
@ -34,11 +34,29 @@ impl GRAPHS {
}
}
pub fn get_graph(&self) -> Vec<(f64, f64)> {
pub fn get_graph(&self, state: &ClientState) -> Vec<(f64, f64)> {
match self {
GRAPHS::Ram => APP_STATE.lock().unwrap().with_width(28).ram.clone(),
GRAPHS::Cpu => APP_STATE.lock().unwrap().with_width(28).cpu.clone(),
GRAPHS::Ping => APP_STATE.lock().unwrap().with_width(28).ping.clone(),
GRAPHS::Ram => state
.app
.lock()
.unwrap_or_else(|error| error.into_inner())
.with_width(28)
.ram
.clone(),
GRAPHS::Cpu => state
.app
.lock()
.unwrap_or_else(|error| error.into_inner())
.with_width(28)
.cpu
.clone(),
GRAPHS::Ping => state
.app
.lock()
.unwrap_or_else(|error| error.into_inner())
.with_width(28)
.ping
.clone(),
}
}
@ -54,6 +72,7 @@ impl GRAPHS {
#[allow(unused)]
pub struct GraphCard {
ui: Arc<UI>,
state: ClientState,
graph_type: GRAPHS,
focused: bool,
@ -66,9 +85,10 @@ pub struct GraphCard {
}
impl GraphCard {
pub fn new(ui: Arc<UI>, graph_type: GRAPHS, title: String) -> Self {
pub fn new(ui: Arc<UI>, state: ClientState, graph_type: GRAPHS, title: String) -> Self {
Self {
ui,
state,
graph_type,
focused: false,
title,
@ -93,7 +113,7 @@ impl Element for GraphCard {
fn render(&self, f: &mut Frame, r: Rect) {
if self.open {
let graph = self.graph_type.get_graph();
let graph = self.graph_type.get_graph(&self.state);
let unit = self.graph_type.get_unit();
let min_x = graph.first().map(|(x, _)| *x).unwrap_or(0.0);
let max_x = graph.last().map(|(x, _)| *x).unwrap_or(100.0);

View file

@ -1,9 +1,8 @@
use crate::app_state::APP_STATE;
use crate::elements::elements::{Element, InteractableElement, JoinableElement};
use crate::interaction_result::InteractionResult;
use crate::util::borders::draw_block_joins;
use crossterm::event::{KeyCode, KeyEvent};
use iota_logger::PrintType;
use iota_state::{ClientState, UiLogEntry};
use ratatui::{
Frame,
layout::Rect,
@ -12,60 +11,9 @@ use ratatui::{
widgets::{Block, Borders, Paragraph},
};
use std::any::Any;
use std::time::{SystemTime, UNIX_EPOCH};
#[derive(Clone, Debug)]
pub struct UiLogEntry {
pub timestamp_ms: u128,
pub sender: PrintType,
pub message: String,
pub is_error: bool,
}
impl UiLogEntry {
pub fn format_timestamp(&self) -> String {
let secs = (self.timestamp_ms / 1000) as i64;
let hours = (secs / 3600) % 24;
let minutes = (secs / 60) % 60;
let seconds = secs % 60;
format!("{:02}:{:02}:{:02}", hours, minutes, seconds)
}
}
impl From<LogEntry> for UiLogEntry {
fn from(entry: LogEntry) -> Self {
Self {
timestamp_ms: entry.timestamp_ms,
sender: entry.sender,
message: entry.message,
is_error: entry.is_error,
}
}
}
#[derive(Clone, Debug)]
pub struct LogEntry {
pub timestamp_ms: u128,
pub sender: PrintType,
pub message: String,
pub is_error: bool,
}
impl LogEntry {
pub fn new(sender: PrintType, message: String, is_error: bool) -> Self {
Self {
timestamp_ms: SystemTime::now()
.duration_since(UNIX_EPOCH)
.unwrap()
.as_millis(),
sender,
message,
is_error,
}
}
}
pub struct LogCard {
state: ClientState,
focused: bool,
selected: bool,
scroll_offset: usize,
@ -76,8 +24,9 @@ pub struct LogCard {
}
impl LogCard {
pub fn new() -> Self {
pub fn new(state: ClientState) -> Self {
Self {
state,
focused: false,
selected: false,
scroll_offset: 0,
@ -89,21 +38,17 @@ impl LogCard {
}
fn get_logs(&self) -> Vec<UiLogEntry> {
let state = APP_STATE.lock().unwrap();
let state = self
.state
.app
.lock()
.unwrap_or_else(|error| error.into_inner());
state
.get_logs()
.iter()
.map(|e| UiLogEntry {
timestamp_ms: e.timestamp_ms,
sender: match e.sender.as_str() {
"Call" => PrintType::Call,
"Client" => PrintType::Client,
"Iota" => PrintType::Iota,
"Omikron" => PrintType::Omikron,
"Omega" => PrintType::Omega,
"Command" => PrintType::Command,
_ => PrintType::General,
},
sender: e.sender.clone(),
message: e.message.clone(),
is_error: e.is_error,
})
@ -196,12 +141,24 @@ impl LogCard {
line.push_str(&timestamp);
}
result.push((line, entry.sender.prefix_color(), entry.is_error));
result.push((line, Self::sender_color(&entry.sender), entry.is_error));
}
result
}
fn sender_color(sender: &str) -> Color {
match sender {
"Call" => Color::Magenta,
"Client" => Color::Green,
"Iota" => Color::Yellow,
"Omikron" => Color::Blue,
"Omega" => Color::Cyan,
"Command" => Color::LightGreen,
_ => Color::LightCyan,
}
}
fn build_all_lines(
&self,
entries: Vec<UiLogEntry>,