added a remove project function, and layed the framework for prompting
for user interaction.
This commit is contained in:
+181
-88
@@ -31,7 +31,7 @@ pub fn run_tui(
|
||||
cursor::Hide,
|
||||
EnableMouseCapture
|
||||
)?;
|
||||
let backend = CrosstermBackend::new(stdout);
|
||||
let backend = CrosstermBackend::new(&stdout);
|
||||
let mut terminal = Terminal::new(backend)?;
|
||||
let (event_tx, event_rx) = channel::<AppEvent>();
|
||||
let input_tx = event_tx.clone();
|
||||
@@ -142,114 +142,207 @@ pub fn run_tui(
|
||||
state.output.push(txt);
|
||||
state.output_scroll = u16::MAX;
|
||||
}
|
||||
ToolMessage::UpdateProject(index, project) => {
|
||||
if index < state.projects.len() {
|
||||
state.projects[index] = project;
|
||||
}
|
||||
}
|
||||
ToolMessage::RebuildDB => {
|
||||
let project = state.projects[state.selected_project].clone();
|
||||
if let Some(db) = project.db {
|
||||
disable_raw_mode()?;
|
||||
execute!(
|
||||
&stdout,
|
||||
LeaveAlternateScreen,
|
||||
cursor::Show,
|
||||
DisableMouseCapture
|
||||
)?;
|
||||
std::io::stdout().flush()?;
|
||||
let mut status = Command::new("distrobox");
|
||||
let template_box = state.config.get("template_box").unwrap();
|
||||
status
|
||||
.arg("create")
|
||||
.arg("--root")
|
||||
.arg("--clone")
|
||||
.arg(&template_box)
|
||||
.arg("--name")
|
||||
.arg(format!(
|
||||
"{}-{}-{}",
|
||||
template_box, project.org_name, project.name
|
||||
));
|
||||
for volume in db.volumes {
|
||||
let mut dir_name = volume.split("/").last().unwrap();
|
||||
if volume.contains("files") {
|
||||
dir_name = "/pentest";
|
||||
}
|
||||
if volume.contains("/notes") {
|
||||
dir_name = "notes";
|
||||
}
|
||||
status
|
||||
.arg("--volume")
|
||||
.arg(format!("{}:{}:rw", volume, dir_name));
|
||||
}
|
||||
status
|
||||
.stdin(Stdio::inherit())
|
||||
.stdout(Stdio::inherit())
|
||||
.stderr(Stdio::inherit());
|
||||
match status.status() {
|
||||
Ok(status) if !status.success() => {
|
||||
println!(
|
||||
"\nCommand failed with exit code {status}, press enter to return to tetanus."
|
||||
);
|
||||
let mut discard = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut discard);
|
||||
}
|
||||
Err(e) => {
|
||||
println!(
|
||||
"\nFailed to execure distrobox: {e}, press enter to return to tetanus."
|
||||
);
|
||||
let mut discard = String::new();
|
||||
let _ = std::io::stdin().read_line(&mut discard);
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
enable_raw_mode()?;
|
||||
execute!(
|
||||
&stdout,
|
||||
EnterAlternateScreen,
|
||||
DisableMouseCapture,
|
||||
cursor::Hide
|
||||
)?;
|
||||
terminal.clear()?;
|
||||
}
|
||||
}
|
||||
|
||||
_ => {}
|
||||
},
|
||||
AppEvent::Key(key) => match key.code {
|
||||
KeyCode::Esc => break,
|
||||
KeyCode::Enter => {
|
||||
let trimmed = state.curent_intput.trim().to_string();
|
||||
if !trimmed.is_empty() {
|
||||
state.history.push(trimmed.clone());
|
||||
state.output.push("\n".to_string());
|
||||
state.output.push(format!("[user input] > {}", trimmed));
|
||||
state.output.push("\n".to_string());
|
||||
let (command, args) = trimmed.split_once(' ').unwrap_or((&trimmed, ""));
|
||||
match command {
|
||||
"exit" | "quit" => break,
|
||||
"reload-modules" => {
|
||||
state.initialize_modules();
|
||||
state.output.push("Reloading module paths...".into());
|
||||
}
|
||||
"help" => {
|
||||
state.output.push("Available Modules:".into());
|
||||
for name in state.module_loader.commands.keys() {
|
||||
state.output.push(format!(" - {}", name));
|
||||
AppEvent::Key(key) => {
|
||||
match key.code {
|
||||
KeyCode::Esc => break,
|
||||
KeyCode::Enter => {
|
||||
let trimmed = state.curent_intput.trim().to_string();
|
||||
if !trimmed.is_empty() {
|
||||
state.history.push(trimmed.clone());
|
||||
state.output.push("\n".to_string());
|
||||
state.output.push(format!("[user input] > {}", trimmed));
|
||||
state.output.push("\n".to_string());
|
||||
if let Some(action) = state.prompt.action.clone() {
|
||||
match action {
|
||||
ToolMessage::RemoveProject => {
|
||||
if trimmed.to_lowercase().contains("y") {
|
||||
state.execute_command(
|
||||
"remove_project_confirm",
|
||||
None,
|
||||
)?;
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
"new_project" | "np" => {
|
||||
if args.split_once(' ').is_some() {
|
||||
let _ =
|
||||
state.execute_command(command, Some(args.to_string()));
|
||||
} else {
|
||||
state.output.push("Error: USAGE -> np <org> <name>".into());
|
||||
let (command, args) =
|
||||
trimmed.split_once(' ').unwrap_or((&trimmed, ""));
|
||||
match command {
|
||||
"exit" | "quit" => break,
|
||||
"reload-modules" => {
|
||||
state.initialize_modules();
|
||||
state.output.push("Reloading module paths...".into());
|
||||
}
|
||||
}
|
||||
"current_project" | "cp" => {
|
||||
let _ = state.execute_command(command, None);
|
||||
}
|
||||
command_name => {
|
||||
if state.module_loader.commands.contains_key(command_name) {
|
||||
state.output.push(format!(
|
||||
"[Worker] Executing script '{}'...",
|
||||
command_name
|
||||
));
|
||||
if let Err(e) = state.execute_command(command_name, None) {
|
||||
"help" => {
|
||||
state.output.push("Available Modules:".into());
|
||||
for name in state.module_loader.commands.keys() {
|
||||
state.output.push(format!(" - {}", name));
|
||||
}
|
||||
}
|
||||
"new_project" | "np" => {
|
||||
if args.split_once(' ').is_some() {
|
||||
let _ = state
|
||||
.execute_command(command, Some(args.to_string()));
|
||||
} else {
|
||||
state
|
||||
.output
|
||||
.push(format!("[Error] Pipeline fail: {}", e));
|
||||
.push("Error: USAGE -> np <org> <name>".into());
|
||||
}
|
||||
}
|
||||
command_name => {
|
||||
if state.module_loader.commands.contains_key(command_name) {
|
||||
state.output.push(format!(
|
||||
"[Worker] Executing script '{}'...",
|
||||
command_name
|
||||
));
|
||||
if let Err(e) =
|
||||
state.execute_command(command_name, None)
|
||||
{
|
||||
state
|
||||
.output
|
||||
.push(format!("[Error] Pipeline fail: {}", e));
|
||||
}
|
||||
} else {
|
||||
if args == "" {
|
||||
let _ = state.execute_command(command, None);
|
||||
} else {
|
||||
let _ = state.execute_command(
|
||||
command,
|
||||
Some(args.to_string()),
|
||||
);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
state.output.push(format!(
|
||||
"[Error] Command '{}' unknown.",
|
||||
command_name
|
||||
));
|
||||
}
|
||||
}
|
||||
state.curent_intput.clear();
|
||||
}
|
||||
state.curent_intput.clear();
|
||||
history_index = state.history.len();
|
||||
}
|
||||
history_index = state.history.len();
|
||||
}
|
||||
KeyCode::Char(c) => {
|
||||
state.curent_intput.push(c);
|
||||
}
|
||||
KeyCode::Backspace => {
|
||||
state.curent_intput.pop();
|
||||
}
|
||||
KeyCode::Up if key.modifiers.is_empty() => {
|
||||
if !state.history.is_empty() && history_index > 0 {
|
||||
history_index -= 1;
|
||||
state.curent_intput = state.history[history_index].clone();
|
||||
KeyCode::Char(c) => {
|
||||
state.curent_intput.push(c);
|
||||
}
|
||||
}
|
||||
KeyCode::Down if key.modifiers.is_empty() => {
|
||||
if history_index < state.history.len() {
|
||||
history_index += 1;
|
||||
if history_index == state.history.len() {
|
||||
state.curent_intput.clear(); // Clear back to a fresh prompt
|
||||
} else {
|
||||
KeyCode::Backspace => {
|
||||
state.curent_intput.pop();
|
||||
}
|
||||
KeyCode::Up if key.modifiers.is_empty() => {
|
||||
if !state.history.is_empty() && history_index > 0 {
|
||||
history_index -= 1;
|
||||
state.curent_intput = state.history[history_index].clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Up
|
||||
if key
|
||||
.modifiers
|
||||
.contains(crossterm::event::KeyModifiers::CONTROL) =>
|
||||
{
|
||||
if let Some(selected) = project_list_state.selected() {
|
||||
if selected > 0 {
|
||||
let new_index = selected - 1;
|
||||
project_list_state.select(Some(new_index));
|
||||
state.selected_project = new_index;
|
||||
KeyCode::Down if key.modifiers.is_empty() => {
|
||||
if history_index < state.history.len() {
|
||||
history_index += 1;
|
||||
if history_index == state.history.len() {
|
||||
state.curent_intput.clear(); // Clear back to a fresh prompt
|
||||
} else {
|
||||
state.curent_intput = state.history[history_index].clone();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Down
|
||||
if key
|
||||
.modifiers
|
||||
.contains(crossterm::event::KeyModifiers::CONTROL) =>
|
||||
{
|
||||
if let Some(selected) = project_list_state.selected() {
|
||||
if selected + 1 < state.projects.len() {
|
||||
let new_index = selected + 1;
|
||||
project_list_state.select(Some(new_index));
|
||||
state.selected_project = new_index;
|
||||
KeyCode::Up
|
||||
if key
|
||||
.modifiers
|
||||
.contains(crossterm::event::KeyModifiers::CONTROL) =>
|
||||
{
|
||||
if let Some(selected) = project_list_state.selected() {
|
||||
if selected > 0 {
|
||||
let new_index = selected - 1;
|
||||
project_list_state.select(Some(new_index));
|
||||
state.selected_project = new_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyCode::Down
|
||||
if key
|
||||
.modifiers
|
||||
.contains(crossterm::event::KeyModifiers::CONTROL) =>
|
||||
{
|
||||
if let Some(selected) = project_list_state.selected() {
|
||||
if selected + 1 < state.projects.len() {
|
||||
let new_index = selected + 1;
|
||||
project_list_state.select(Some(new_index));
|
||||
state.selected_project = new_index;
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
_ => {}
|
||||
},
|
||||
}
|
||||
AppEvent::Mouse(mouse) => {
|
||||
if mouse.kind == crossterm::event::MouseEventKind::ScrollUp {
|
||||
state.output_scroll = state.output_scroll.saturating_sub(1);
|
||||
|
||||
Reference in New Issue
Block a user