use crate::section::{collapsing_section, collapsing_section_with_state};
use crate::settings_ui::SettingsUI;
use par_term_config::automation::RestartPolicy;
use std::collections::HashSet;
use super::editor::show_script_edit_form;
pub(super) fn show_scripts_section(
ui: &mut egui::Ui,
settings: &mut SettingsUI,
changes_this_frame: &mut bool,
collapsed: &mut HashSet<String>,
) {
collapsing_section_with_state(
ui,
"Observer Scripts",
"scripts_list",
true,
collapsed,
|ui, collapsed_inner| {
ui.label(
"Define external scripts that receive terminal events and can send commands back.",
);
ui.add_space(4.0);
let mut delete_index: Option<usize> = None;
let mut start_edit_index: Option<usize> = None;
let mut toggle_index: Option<usize> = None;
let script_count = settings.config.scripts.len();
for i in 0..script_count {
let is_editing =
settings.editing_script_index == Some(i) && !settings.adding_new_script;
if is_editing {
show_script_edit_form(ui, settings, changes_this_frame, Some(i));
} else {
show_script_row(
ui,
settings,
i,
&mut delete_index,
&mut start_edit_index,
&mut toggle_index,
collapsed_inner,
);
}
}
if let Some(i) = toggle_index {
settings.config.scripts[i].enabled = !settings.config.scripts[i].enabled;
settings.has_changes = true;
*changes_this_frame = true;
}
if let Some(i) = delete_index {
settings.config.scripts.remove(i);
settings.has_changes = true;
*changes_this_frame = true;
if settings.editing_script_index == Some(i) {
settings.editing_script_index = None;
}
}
if let Some(i) = start_edit_index {
let script = &settings.config.scripts[i];
settings.editing_script_index = Some(i);
settings.adding_new_script = false;
settings.temp_script_name = script.name.clone();
settings.temp_script_path = script.script_path.clone();
settings.temp_script_args = script.args.join(" ");
settings.temp_script_auto_start = script.auto_start;
settings.temp_script_enabled = script.enabled;
settings.temp_script_restart_policy = script.restart_policy;
settings.temp_script_restart_delay_ms = script.restart_delay_ms;
settings.temp_script_subscriptions = script.subscriptions.join(", ");
settings.temp_script_allow_write_text = script.allow_write_text;
settings.temp_script_allow_run_command = script.allow_run_command;
settings.temp_script_allow_change_config = script.allow_change_config;
settings.temp_script_write_text_rate_limit = script.write_text_rate_limit;
settings.temp_script_run_command_rate_limit = script.run_command_rate_limit;
}
ui.add_space(4.0);
if settings.adding_new_script {
ui.separator();
ui.label(egui::RichText::new("New Script").strong());
show_script_edit_form(ui, settings, changes_this_frame, None);
} else if settings.editing_script_index.is_none()
&& ui
.button("+ Add Script")
.on_hover_text("Add a new observer script definition")
.clicked()
{
settings.adding_new_script = true;
settings.editing_script_index = None;
settings.temp_script_name = String::new();
settings.temp_script_path = String::new();
settings.temp_script_args = String::new();
settings.temp_script_auto_start = false;
settings.temp_script_enabled = true;
settings.temp_script_restart_policy = RestartPolicy::Never;
settings.temp_script_restart_delay_ms = 0;
settings.temp_script_subscriptions = String::new();
settings.temp_script_allow_write_text = false;
settings.temp_script_allow_run_command = false;
settings.temp_script_allow_change_config = false;
settings.temp_script_write_text_rate_limit = 0;
settings.temp_script_run_command_rate_limit = 0;
}
},
);
}
fn show_script_row(
ui: &mut egui::Ui,
settings: &mut SettingsUI,
i: usize,
delete_index: &mut Option<usize>,
start_edit_index: &mut Option<usize>,
toggle_index: &mut Option<usize>,
collapsed: &mut HashSet<String>,
) {
let script = &settings.config.scripts[i];
let is_running = settings.script_running.get(i).copied().unwrap_or(false);
let has_error = settings.script_errors.get(i).is_some_and(|e| !e.is_empty());
ui.horizontal(|ui| {
if is_running {
ui.label(egui::RichText::new("\u{25cf}").color(egui::Color32::from_rgb(100, 200, 100)));
} else if has_error {
ui.label(egui::RichText::new("\u{25cf}").color(egui::Color32::from_rgb(220, 80, 80)));
} else if script.auto_start {
ui.label(
egui::RichText::new("[auto]")
.color(egui::Color32::from_rgb(100, 200, 100))
.small(),
);
} else {
ui.label(egui::RichText::new("\u{25cb}").color(egui::Color32::GRAY));
}
let mut enabled = script.enabled;
if ui.checkbox(&mut enabled, "").changed() {
*toggle_index = Some(i);
}
ui.label(egui::RichText::new(&script.name).strong());
ui.with_layout(egui::Layout::right_to_left(egui::Align::Center), |ui| {
if ui
.small_button(
egui::RichText::new("Delete").color(egui::Color32::from_rgb(200, 80, 80)),
)
.clicked()
{
*delete_index = Some(i);
}
if ui.small_button("Edit").clicked() {
*start_edit_index = Some(i);
}
if is_running {
if ui
.small_button(
egui::RichText::new("Stop").color(egui::Color32::from_rgb(220, 160, 50)),
)
.clicked()
{
settings.pending_script_actions.push((i, false));
}
} else if ui.small_button("Start").clicked() {
log::debug!("Script Start button clicked for index {}", i);
if let Some(err) = settings.script_errors.get_mut(i) {
err.clear();
}
settings.pending_script_actions.push((i, true));
}
});
});
let script = &settings.config.scripts[i];
let path_display = if script.args.is_empty() {
script.script_path.clone()
} else {
format!("{} {}", script.script_path, script.args.join(" "))
};
ui.indent(format!("script_path_{}", i), |ui| {
ui.horizontal(|ui| {
ui.label(
egui::RichText::new(&path_display)
.monospace()
.small()
.color(egui::Color32::from_rgb(150, 150, 200)),
);
if script.restart_policy != RestartPolicy::Never {
let restart_text = if script.restart_delay_ms > 0 {
format!(
"[restart: {}, delay: {}ms]",
script.restart_policy.display_name(),
script.restart_delay_ms
)
} else {
format!("[restart: {}]", script.restart_policy.display_name())
};
ui.label(
egui::RichText::new(restart_text)
.small()
.color(egui::Color32::from_rgb(180, 180, 100)),
);
}
if !script.subscriptions.is_empty() {
ui.label(
egui::RichText::new(format!("[{}]", script.subscriptions.join(", ")))
.small()
.color(egui::Color32::from_rgb(140, 180, 220)),
);
}
});
});
if has_error && !is_running {
let err_text = &settings.script_errors[i];
ui.indent(format!("script_err_{}", i), |ui| {
ui.label(
egui::RichText::new(format!("Error: {}", err_text))
.small()
.color(egui::Color32::from_rgb(220, 80, 80)),
);
});
}
let has_output = settings
.script_output
.get(i)
.is_some_and(|lines| !lines.is_empty());
if has_output {
let is_expanded = settings
.script_output_expanded
.get(i)
.copied()
.unwrap_or(false);
let line_count = settings.script_output[i].len();
ui.indent(format!("script_out_{}", i), |ui| {
let toggle_text = if is_expanded {
format!("\u{25bc} Output ({} lines)", line_count)
} else {
format!("\u{25b6} Output ({} lines)", line_count)
};
if ui
.small_button(
egui::RichText::new(&toggle_text)
.small()
.color(egui::Color32::from_rgb(140, 180, 140)),
)
.clicked()
&& let Some(expanded) = settings.script_output_expanded.get_mut(i)
{
*expanded = !*expanded;
}
if is_expanded {
let output_text = settings.script_output[i].join("\n");
egui::ScrollArea::vertical()
.id_salt(format!("script_output_scroll_{}", i))
.max_height(150.0)
.stick_to_bottom(true)
.show(ui, |ui| {
ui.label(
egui::RichText::new(&output_text)
.monospace()
.small()
.color(egui::Color32::from_rgb(180, 180, 180)),
);
});
if ui.small_button("Clear").clicked() {
settings.script_output[i].clear();
}
}
});
}
if let Some(Some((title, content))) = settings.script_panels.get(i) {
let panel_title = format!("Panel: {}", title);
let panel_id = format!("script_panel_{}", i);
let panel_scroll_id = format!("script_panel_scroll_{}", i);
ui.indent(&panel_id, |ui| {
collapsing_section(ui, &panel_title, &panel_id, false, collapsed, |ui| {
egui::ScrollArea::vertical()
.id_salt(&panel_scroll_id)
.max_height(200.0)
.show(ui, |ui| {
ui.label(
egui::RichText::new(content)
.monospace()
.small()
.color(egui::Color32::from_rgb(200, 200, 200)),
);
});
});
});
}
ui.add_space(2.0);
}