1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
//! # SSHUI
//!
//! A Rust framework for building interactive terminal user interfaces (TUIs) that run over SSH. Built on top of [Ratatui](https://github.com/ratatui-org/ratatui) and [russh](https://github.com/Eugeny/russh).
//!
//! ## Features
//!
//! - **SSH Server** - Host your TUI application on an SSH server
//! - **Ratatui Integration** - Build beautiful terminal UIs using the Ratatui framework
//! - **Client Isolation** - Each SSH client gets its own application instance
//! - **ANSI Rendering** - Full support for colors and styles
//! - **Terminal Resizing** - Handles dynamic terminal size changes
//! - **Customizable Auth** - Ask for a specific username and password (or not!)
//!
//! ## Installation
//!
//! Add to your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! sshui = "0.2"
//! ratatui = "0.28"
//! anyhow = "1.0"
//! ```
//!
//! ## Quick Start
//!
//! Implement the `App` trait for your TUI:
//!
//! ```rust
//! use sshui::{App, SSHUITerminal, InputEvent, KeyCode, KeyEvent};
//! use anyhow::Result;
//!
//! struct MyApp {
//! counter: i32,
//! exit: bool,
//! }
//!
//! impl App for MyApp {
//! fn render(&mut self, terminal: &mut SSHUITerminal) -> Result<Option<String>> {
//! terminal.draw(|frame| {
//! let area = frame.area();
//! // Draw your UI here
//! })?;
//!
//! Ok(if self.exit {
//! Some("Exited".to_string())
//! } else {
//! None
//! })
//! }
//!
//! fn input(&mut self, event: InputEvent) {
//! if let InputEvent::Key(KeyEvent { key, .. }) = event {
//! match key {
//! KeyCode::Char('q') => self.exit = true,
//! KeyCode::Up => self.counter += 1,
//! KeyCode::Down => self.counter -= 1,
//! _ => {}
//! }
//! }
//! }
//! }
//!
//! impl Default for MyApp {
//! fn default() -> Self {
//! Self {
//! counter: 0,
//! exit: false,
//! }
//! }
//! }
//! ```
//!
//! Start the SSH server:
//!
//! ```rust
//! #[tokio::main]
//! async fn main() -> anyhow::Result<()> {
//! let config = sshui::Config::default();
//! // also include ssh keys
//! // which you can get using the keyring feature
//! // and the sshui::get_ssh_key function
//!
//! sshui::new_server(config, ("0.0.0.0", 2222), || Box::new(MyApp::default()))
//! .await?;
//!
//! Ok(())
//! }
//! ```
//!
//! Connect via SSH:
//!
//! ```bash
//! ssh -p 2222 localhost
//! ```
//!
//! Press `Ctrl+C` to exit.
use crate::;
use Result;
use Terminal;
use Server;
use ;
use ToSocketAddrs;
pub use ;
pub type SSHUITerminal = ;
pub use ;
pub use Lobby;
pub use ratatui;
pub use Config;
pub use Duration;
pub use ;
/// The App trait that must be implemented for TUI applications served over SSH.
///
/// This trait defines the interface between the SSHUI framework and your Ratatui-based application.
/// Implement this trait to create an interactive TUI that can be accessed over SSH.
///
/// # Example
///
/// ```ignore
/// impl sshui::App for App {
/// fn render(&mut self, terminal: &mut SSHUITerminal) -> Result<Option<String>> {
/// terminal.draw(|frame| self.draw(frame))?;
///
/// Ok(if self.exit {
/// Some("Exited".to_string())
/// } else {
/// None
/// })
/// }
///
/// fn input(&mut self, event: InputEvent) {
/// let InputEvent::Key(KeyEvent { key, .. }) = event else {
/// return;
/// };
///
/// match key {
/// // Handle input events
/// _ => {}
/// }
/// }
/// }
/// ```
/// Starts an SSH server that serves your TUI application.
///
/// This function starts a russh SSH server on the specified address and begins accepting client
/// connections. Each connected client will get a fresh instance of your application created by
/// the provided factory function.
///
/// If you need to provide a config (auth, refresh_rate...), take a look at `new_server_with_config`.
///
/// # Arguments
///
/// * `server_config` - The SSH server configuration (see `russh::server::Config`)
/// * `addrs` - The address(es) to bind to (e.g., `("0.0.0.0", 2222)`)
/// * `app_factory` - A closure that creates a new instance of your App for each client connection
///
/// # Returns
///
/// * `Ok(())` if the server runs successfully
/// * `Err(e)` if the server fails to start or encounters an error
///
/// # Example
///
/// ```ignore
/// let config = sshui::Config::default();
/// sshui::new_server(config, ("0.0.0.0", 2222), || Box::new(App::default()))
/// .await
/// .unwrap();
/// ```
pub async
/// Starts an SSH server with the specified config
///
/// Same as `new_server` but a specified `SSHUIConfig` config.
///
/// # Arguments
///
/// * `server_config` - The SSH server configuration
/// * `addrs` - The address(es) to bind to
/// * `app_factory` - A closure that creates a new App instance for each client
/// * `config` - The SSHUIConfig
pub async
/// A SSHUI server configuration struct.
///
/// # Fields
///
/// - `auth` (`Arc<dyn AuthHandler>`) - The AuthHandler that will decide if the client can connect or not.
/// - `refresh_rate` (`Option<Duration>`) - Optional refresh rate to re-render the app on a regular interval.
///
/// # Example
///
/// ```ignore
/// SSHUIConfig {
/// auth: Arc::new(MyAuth),
/// ..Default::default()
/// }
/// ```