Skip to main content

par_term/
remote_shell_install_ui.rs

1//! Remote shell integration install confirmation dialog.
2//!
3//! Shows a confirmation dialog when the user selects "Install Shell Integration
4//! on Remote Host" from the Shell menu. Displays the exact curl command that will
5//! be sent to the active terminal and lets the user confirm or cancel.
6
7/// The install command URL
8const INSTALL_URL: &str = "https://paulrobello.github.io/par-term/install-shell-integration.sh";
9
10/// Action returned by the remote shell install dialog
11#[derive(Debug, Clone, PartialEq, Eq)]
12pub enum RemoteShellInstallAction {
13    /// User confirmed - send the install command to the active terminal
14    Install,
15    /// User cancelled
16    Cancel,
17    /// No action yet (dialog still showing or not visible)
18    None,
19}
20
21/// State for the remote shell integration install dialog
22pub struct RemoteShellInstallUI {
23    /// Whether the dialog is visible
24    visible: bool,
25}
26
27impl Default for RemoteShellInstallUI {
28    fn default() -> Self {
29        Self::new()
30    }
31}
32
33impl RemoteShellInstallUI {
34    /// Create a new remote shell install UI
35    pub fn new() -> Self {
36        Self { visible: false }
37    }
38
39    /// Check if the dialog is currently visible
40    pub fn is_visible(&self) -> bool {
41        self.visible
42    }
43
44    /// Show the confirmation dialog
45    pub fn show_dialog(&mut self) {
46        self.visible = true;
47    }
48
49    /// Hide the dialog
50    fn hide(&mut self) {
51        self.visible = false;
52    }
53
54    /// Get the install command string
55    pub fn install_command() -> String {
56        format!("curl -sSL {} | sh", INSTALL_URL)
57    }
58
59    /// Render the dialog and return any action
60    pub fn show(&mut self, ctx: &egui::Context) -> RemoteShellInstallAction {
61        if !self.visible {
62            return RemoteShellInstallAction::None;
63        }
64
65        let mut action = RemoteShellInstallAction::None;
66        let command = Self::install_command();
67
68        egui::Window::new("Install Shell Integration on Remote Host")
69            .collapsible(false)
70            .resizable(false)
71            .order(egui::Order::Foreground)
72            .anchor(egui::Align2::CENTER_CENTER, [0.0, 0.0])
73            .show(ctx, |ui| {
74                ui.vertical_centered(|ui| {
75                    ui.add_space(10.0);
76
77                    ui.label(
78                        egui::RichText::new("Send Install Command to Terminal")
79                            .size(16.0)
80                            .strong(),
81                    );
82                    ui.add_space(8.0);
83
84                    ui.label("This will send the following command to the active terminal:");
85                    ui.add_space(8.0);
86
87                    // Command preview in a highlighted code block
88                    egui::Frame::new()
89                        .fill(egui::Color32::from_rgba_unmultiplied(40, 40, 40, 220))
90                        .inner_margin(egui::Margin::symmetric(12, 8))
91                        .corner_radius(4.0)
92                        .show(ui, |ui| {
93                            ui.label(
94                                egui::RichText::new(&command)
95                                    .color(egui::Color32::LIGHT_GREEN)
96                                    .monospace()
97                                    .size(13.0),
98                            );
99                        });
100
101                    ui.add_space(10.0);
102
103                    // Warning
104                    ui.label(
105                        egui::RichText::new(
106                            "Only use this when SSH'd into a remote host that needs shell integration.",
107                        )
108                        .color(egui::Color32::YELLOW)
109                        .size(12.0),
110                    );
111
112                    ui.add_space(15.0);
113
114                    // Buttons
115                    ui.horizontal(|ui| {
116                        let install_button = egui::Button::new(
117                            egui::RichText::new("Install").color(egui::Color32::WHITE),
118                        )
119                        .fill(egui::Color32::from_rgb(50, 120, 50));
120
121                        if ui.add(install_button).clicked() {
122                            action = RemoteShellInstallAction::Install;
123                        }
124
125                        ui.add_space(10.0);
126
127                        if ui.button("Cancel").clicked() {
128                            action = RemoteShellInstallAction::Cancel;
129                        }
130                    });
131                    ui.add_space(10.0);
132                });
133            });
134
135        // Handle escape key to cancel
136        if ctx.input(|i| i.key_pressed(egui::Key::Escape)) {
137            action = RemoteShellInstallAction::Cancel;
138        }
139
140        // Hide dialog on any action
141        if !matches!(action, RemoteShellInstallAction::None) {
142            self.hide();
143        }
144
145        action
146    }
147}
148
149#[cfg(test)]
150mod tests {
151    use super::*;
152
153    #[test]
154    fn test_install_command_format() {
155        let cmd = RemoteShellInstallUI::install_command();
156        assert!(cmd.starts_with("curl"));
157        assert!(cmd.contains("paulrobello.github.io/par-term"));
158        assert!(cmd.contains("install-shell-integration.sh"));
159        assert!(cmd.ends_with("| sh"));
160    }
161
162    #[test]
163    fn test_dialog_initial_state() {
164        let ui = RemoteShellInstallUI::new();
165        assert!(!ui.is_visible());
166    }
167
168    #[test]
169    fn test_dialog_show_hide() {
170        let mut ui = RemoteShellInstallUI::new();
171        assert!(!ui.is_visible());
172
173        ui.show_dialog();
174        assert!(ui.is_visible());
175
176        ui.hide();
177        assert!(!ui.is_visible());
178    }
179
180    #[test]
181    fn test_default_impl() {
182        let ui = RemoteShellInstallUI::default();
183        assert!(!ui.is_visible());
184    }
185}