1use crate::home::default_layout_dir;
2use crate::input::actions::{Action, RunCommandAction};
3use crate::input::config::{ConversionError, KdlError};
4use crate::input::keybinds::Keybinds;
5use crate::input::layout::{
6 Layout, PercentOrFixed, Run, RunPlugin, RunPluginLocation, RunPluginOrAlias,
7};
8pub use crate::input::options::PaneFrameStyle;
9use crate::pane_size::PaneGeom;
10use crate::position::Position;
11use crate::shared::{colors as default_colors, eightbit_to_rgb};
12use clap::ValueEnum;
13use serde::{Deserialize, Serialize};
14use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
15use std::fmt;
16use std::fs::Metadata;
17use std::hash::{Hash, Hasher};
18use std::net::IpAddr;
19use std::path::{Path, PathBuf};
20use std::str::{self, FromStr};
21use std::time::Duration;
22use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString};
23use unicode_width::UnicodeWidthChar;
24
25#[cfg(not(target_family = "wasm"))]
26use crate::vendored::termwiz::{
27 input::KittyKeyboardFlags,
28 input::{KeyCode, KeyCodeEncodeModes, KeyboardEncoding, Modifiers},
29};
30
31pub type ClientId = u16; #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
34pub enum UnblockCondition {
35 OnExitSuccess,
37 OnExitFailure,
39 OnAnyExit,
41}
42
43impl UnblockCondition {
44 pub fn is_met(&self, exit_status: i32) -> bool {
46 match self {
47 UnblockCondition::OnExitSuccess => exit_status == 0,
48 UnblockCondition::OnExitFailure => exit_status != 0,
49 UnblockCondition::OnAnyExit => true,
50 }
51 }
52}
53
54#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
55pub enum CommandOrPlugin {
56 Command(RunCommandAction),
57 Plugin(RunPluginOrAlias),
58 File(FileToOpen), }
60
61impl CommandOrPlugin {
62 pub fn new_command(command: Vec<String>) -> Self {
63 CommandOrPlugin::Command(RunCommandAction::new(command))
64 }
65}
66
67pub fn client_id_to_colors(
68 client_id: ClientId,
69 colors: MultiplayerColors,
70) -> Option<(PaletteColor, PaletteColor)> {
71 let black = PaletteColor::EightBit(default_colors::BLACK);
73 match client_id {
74 1 => Some((colors.player_1, black)),
75 2 => Some((colors.player_2, black)),
76 3 => Some((colors.player_3, black)),
77 4 => Some((colors.player_4, black)),
78 5 => Some((colors.player_5, black)),
79 6 => Some((colors.player_6, black)),
80 7 => Some((colors.player_7, black)),
81 8 => Some((colors.player_8, black)),
82 9 => Some((colors.player_9, black)),
83 10 => Some((colors.player_10, black)),
84 _ => None,
85 }
86}
87
88pub fn single_client_color(colors: Palette) -> (PaletteColor, PaletteColor) {
89 (colors.green, colors.black)
90}
91
92impl FromStr for KeyWithModifier {
93 type Err = Box<dyn std::error::Error>;
94 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
95 let mut key_string_parts: Vec<&str> = key_str.split_ascii_whitespace().collect();
96 let bare_key: BareKey = BareKey::from_str(key_string_parts.pop().ok_or("empty key")?)?;
97 let mut key_modifiers: BTreeSet<KeyModifier> = BTreeSet::new();
98 for stringified_modifier in key_string_parts {
99 key_modifiers.insert(KeyModifier::from_str(stringified_modifier)?);
100 }
101 Ok(KeyWithModifier {
102 bare_key,
103 key_modifiers,
104 })
105 }
106}
107
108#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialOrd, Ord)]
109pub struct KeyWithModifier {
110 pub bare_key: BareKey,
111 pub key_modifiers: BTreeSet<KeyModifier>,
112}
113
114impl PartialEq for KeyWithModifier {
115 fn eq(&self, other: &Self) -> bool {
116 match (self.bare_key, other.bare_key) {
117 (BareKey::Char(self_char), BareKey::Char(other_char))
118 if self_char.to_ascii_lowercase() == other_char.to_ascii_lowercase() =>
119 {
120 let mut self_cloned = self.clone();
121 let mut other_cloned = other.clone();
122 if self_char.is_ascii_uppercase() {
123 self_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
124 self_cloned.key_modifiers.insert(KeyModifier::Shift);
125 }
126 if other_char.is_ascii_uppercase() {
127 other_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
128 other_cloned.key_modifiers.insert(KeyModifier::Shift);
129 }
130 self_cloned.bare_key == other_cloned.bare_key
131 && self_cloned.key_modifiers == other_cloned.key_modifiers
132 },
133 _ => self.bare_key == other.bare_key && self.key_modifiers == other.key_modifiers,
134 }
135 }
136}
137
138impl Hash for KeyWithModifier {
139 fn hash<H: Hasher>(&self, state: &mut H) {
140 match self.bare_key {
141 BareKey::Char(character) if character.is_ascii_uppercase() => {
142 let mut to_hash = self.clone();
143 to_hash.bare_key = BareKey::Char(character.to_ascii_lowercase());
144 to_hash.key_modifiers.insert(KeyModifier::Shift);
145 to_hash.bare_key.hash(state);
146 to_hash.key_modifiers.hash(state);
147 },
148 _ => {
149 self.bare_key.hash(state);
150 self.key_modifiers.hash(state);
151 },
152 }
153 }
154}
155
156impl fmt::Display for KeyWithModifier {
157 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
158 if self.key_modifiers.is_empty() {
159 write!(f, "{}", self.bare_key)
160 } else {
161 write!(
162 f,
163 "{} {}",
164 self.key_modifiers
165 .iter()
166 .map(|m| m.to_string())
167 .collect::<Vec<_>>()
168 .join(" "),
169 self.bare_key
170 )
171 }
172 }
173}
174
175#[cfg(not(target_family = "wasm"))]
176impl Into<Modifiers> for &KeyModifier {
177 fn into(self) -> Modifiers {
178 match self {
179 KeyModifier::Shift => Modifiers::SHIFT,
180 KeyModifier::Alt => Modifiers::ALT,
181 KeyModifier::Ctrl => Modifiers::CTRL,
182 KeyModifier::Super => Modifiers::SUPER,
183 }
184 }
185}
186
187#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
188pub enum BareKey {
189 PageDown,
190 PageUp,
191 Left,
192 Down,
193 Up,
194 Right,
195 Home,
196 End,
197 Backspace,
198 Delete,
199 Insert,
200 F(u8),
201 Char(char),
202 Tab,
203 Esc,
204 Enter,
205 CapsLock,
206 ScrollLock,
207 NumLock,
208 PrintScreen,
209 Pause,
210 Menu,
211}
212
213impl fmt::Display for BareKey {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 match self {
216 BareKey::PageDown => write!(f, "PgDn"),
217 BareKey::PageUp => write!(f, "PgUp"),
218 BareKey::Left => write!(f, "←"),
219 BareKey::Down => write!(f, "↓"),
220 BareKey::Up => write!(f, "↑"),
221 BareKey::Right => write!(f, "→"),
222 BareKey::Home => write!(f, "HOME"),
223 BareKey::End => write!(f, "END"),
224 BareKey::Backspace => write!(f, "BACKSPACE"),
225 BareKey::Delete => write!(f, "DEL"),
226 BareKey::Insert => write!(f, "INS"),
227 BareKey::F(index) => write!(f, "F{}", index),
228 BareKey::Char(' ') => write!(f, "SPACE"),
229 BareKey::Char(character) => write!(f, "{}", character),
230 BareKey::Tab => write!(f, "TAB"),
231 BareKey::Esc => write!(f, "ESC"),
232 BareKey::Enter => write!(f, "ENTER"),
233 BareKey::CapsLock => write!(f, "CAPSlOCK"),
234 BareKey::ScrollLock => write!(f, "SCROLLlOCK"),
235 BareKey::NumLock => write!(f, "NUMLOCK"),
236 BareKey::PrintScreen => write!(f, "PRINTSCREEN"),
237 BareKey::Pause => write!(f, "PAUSE"),
238 BareKey::Menu => write!(f, "MENU"),
239 }
240 }
241}
242
243impl FromStr for BareKey {
244 type Err = Box<dyn std::error::Error>;
245 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
246 match key_str.to_ascii_lowercase().as_str() {
247 "pagedown" => Ok(BareKey::PageDown),
248 "pageup" => Ok(BareKey::PageUp),
249 "left" => Ok(BareKey::Left),
250 "down" => Ok(BareKey::Down),
251 "up" => Ok(BareKey::Up),
252 "right" => Ok(BareKey::Right),
253 "home" => Ok(BareKey::Home),
254 "end" => Ok(BareKey::End),
255 "backspace" => Ok(BareKey::Backspace),
256 "delete" => Ok(BareKey::Delete),
257 "del" => Ok(BareKey::Delete),
258 "insert" => Ok(BareKey::Insert),
259 "f1" => Ok(BareKey::F(1)),
260 "f2" => Ok(BareKey::F(2)),
261 "f3" => Ok(BareKey::F(3)),
262 "f4" => Ok(BareKey::F(4)),
263 "f5" => Ok(BareKey::F(5)),
264 "f6" => Ok(BareKey::F(6)),
265 "f7" => Ok(BareKey::F(7)),
266 "f8" => Ok(BareKey::F(8)),
267 "f9" => Ok(BareKey::F(9)),
268 "f10" => Ok(BareKey::F(10)),
269 "f11" => Ok(BareKey::F(11)),
270 "f12" => Ok(BareKey::F(12)),
271 "tab" => Ok(BareKey::Tab),
272 "esc" => Ok(BareKey::Esc),
273 "enter" => Ok(BareKey::Enter),
274 "capslock" => Ok(BareKey::CapsLock),
275 "scrolllock" => Ok(BareKey::ScrollLock),
276 "numlock" => Ok(BareKey::NumLock),
277 "printscreen" => Ok(BareKey::PrintScreen),
278 "pause" => Ok(BareKey::Pause),
279 "menu" => Ok(BareKey::Menu),
280 "space" => Ok(BareKey::Char(' ')),
281 _ => {
282 if key_str.chars().count() == 1 {
283 if let Some(character) = key_str.chars().next() {
284 return Ok(BareKey::Char(character));
285 }
286 }
287 Err("unsupported key".into())
288 },
289 }
290 }
291}
292
293#[derive(
294 Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord, Display,
295)]
296pub enum KeyModifier {
297 Ctrl,
298 Alt,
299 Shift,
300 Super,
301}
302
303impl FromStr for KeyModifier {
304 type Err = Box<dyn std::error::Error>;
305 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
306 match key_str.to_ascii_lowercase().as_str() {
307 "shift" => Ok(KeyModifier::Shift),
308 "alt" => Ok(KeyModifier::Alt),
309 "ctrl" => Ok(KeyModifier::Ctrl),
310 "super" => Ok(KeyModifier::Super),
311 _ => Err("unsupported modifier".into()),
312 }
313 }
314}
315
316impl BareKey {
317 pub fn from_bytes_with_u(bytes: &[u8]) -> Option<Self> {
318 match str::from_utf8(bytes) {
319 Ok("27") => Some(BareKey::Esc),
320 Ok("13") => Some(BareKey::Enter),
321 Ok("9") => Some(BareKey::Tab),
322 Ok("127") => Some(BareKey::Backspace),
323 Ok("57358") => Some(BareKey::CapsLock),
324 Ok("57359") => Some(BareKey::ScrollLock),
325 Ok("57360") => Some(BareKey::NumLock),
326 Ok("57361") => Some(BareKey::PrintScreen),
327 Ok("57362") => Some(BareKey::Pause),
328 Ok("57363") => Some(BareKey::Menu),
329 Ok("57399") => Some(BareKey::Char('0')),
330 Ok("57400") => Some(BareKey::Char('1')),
331 Ok("57401") => Some(BareKey::Char('2')),
332 Ok("57402") => Some(BareKey::Char('3')),
333 Ok("57403") => Some(BareKey::Char('4')),
334 Ok("57404") => Some(BareKey::Char('5')),
335 Ok("57405") => Some(BareKey::Char('6')),
336 Ok("57406") => Some(BareKey::Char('7')),
337 Ok("57407") => Some(BareKey::Char('8')),
338 Ok("57408") => Some(BareKey::Char('9')),
339 Ok("57409") => Some(BareKey::Char('.')),
340 Ok("57410") => Some(BareKey::Char('/')),
341 Ok("57411") => Some(BareKey::Char('*')),
342 Ok("57412") => Some(BareKey::Char('-')),
343 Ok("57413") => Some(BareKey::Char('+')),
344 Ok("57414") => Some(BareKey::Enter),
345 Ok("57415") => Some(BareKey::Char('=')),
346 Ok("57417") => Some(BareKey::Left),
347 Ok("57418") => Some(BareKey::Right),
348 Ok("57419") => Some(BareKey::Up),
349 Ok("57420") => Some(BareKey::Down),
350 Ok("57421") => Some(BareKey::PageUp),
351 Ok("57422") => Some(BareKey::PageDown),
352 Ok("57423") => Some(BareKey::Home),
353 Ok("57424") => Some(BareKey::End),
354 Ok("57425") => Some(BareKey::Insert),
355 Ok("57426") => Some(BareKey::Delete),
356 Ok(num) => u32::from_str_radix(num, 10)
357 .ok()
358 .and_then(char::from_u32)
359 .map(BareKey::Char),
360 _ => None,
361 }
362 }
363 pub fn from_bytes_with_tilde(bytes: &[u8]) -> Option<Self> {
364 match str::from_utf8(bytes) {
365 Ok("2") => Some(BareKey::Insert),
366 Ok("3") => Some(BareKey::Delete),
367 Ok("5") => Some(BareKey::PageUp),
368 Ok("6") => Some(BareKey::PageDown),
369 Ok("7") => Some(BareKey::Home),
370 Ok("8") => Some(BareKey::End),
371 Ok("11") => Some(BareKey::F(1)),
372 Ok("12") => Some(BareKey::F(2)),
373 Ok("13") => Some(BareKey::F(3)),
374 Ok("14") => Some(BareKey::F(4)),
375 Ok("15") => Some(BareKey::F(5)),
376 Ok("17") => Some(BareKey::F(6)),
377 Ok("18") => Some(BareKey::F(7)),
378 Ok("19") => Some(BareKey::F(8)),
379 Ok("20") => Some(BareKey::F(9)),
380 Ok("21") => Some(BareKey::F(10)),
381 Ok("23") => Some(BareKey::F(11)),
382 Ok("24") => Some(BareKey::F(12)),
383 _ => None,
384 }
385 }
386 pub fn from_bytes_with_no_ending_byte(bytes: &[u8]) -> Option<Self> {
387 match str::from_utf8(bytes) {
388 Ok("1D") | Ok("D") => Some(BareKey::Left),
389 Ok("1C") | Ok("C") => Some(BareKey::Right),
390 Ok("1A") | Ok("A") => Some(BareKey::Up),
391 Ok("1B") | Ok("B") => Some(BareKey::Down),
392 Ok("1H") | Ok("H") => Some(BareKey::Home),
393 Ok("1F") | Ok("F") => Some(BareKey::End),
394 Ok("1P") | Ok("P") => Some(BareKey::F(1)),
395 Ok("1Q") | Ok("Q") => Some(BareKey::F(2)),
396 Ok("1S") | Ok("S") => Some(BareKey::F(4)),
397 _ => None,
398 }
399 }
400}
401
402bitflags::bitflags! {
403 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
404 struct ModifierFlags: u8 {
405 const SHIFT = 0b0000_0001;
406 const ALT = 0b0000_0010;
407 const CONTROL = 0b0000_0100;
408 const SUPER = 0b0000_1000;
409 const HYPER = 0b0001_0000;
412 const META = 0b0010_0000;
413 const CAPS_LOCK = 0b0100_0000;
414 const NUM_LOCK = 0b1000_0000;
415 }
416}
417
418impl KeyModifier {
419 pub fn from_bytes(bytes: &[u8]) -> BTreeSet<KeyModifier> {
420 let modifier_flags = str::from_utf8(bytes)
421 .ok() .and_then(|s| u8::from_str_radix(&s, 10).ok()) .map(|s| s.saturating_sub(1)) .and_then(|b| ModifierFlags::from_bits(b)); let mut key_modifiers = BTreeSet::new();
426 if let Some(modifier_flags) = modifier_flags {
427 for name in modifier_flags.iter() {
428 match name {
429 ModifierFlags::SHIFT => key_modifiers.insert(KeyModifier::Shift),
430 ModifierFlags::ALT => key_modifiers.insert(KeyModifier::Alt),
431 ModifierFlags::CONTROL => key_modifiers.insert(KeyModifier::Ctrl),
432 ModifierFlags::SUPER => key_modifiers.insert(KeyModifier::Super),
433 _ => false,
434 };
435 }
436 }
437 key_modifiers
438 }
439}
440
441impl KeyWithModifier {
442 pub fn new(bare_key: BareKey) -> Self {
443 KeyWithModifier {
444 bare_key,
445 key_modifiers: BTreeSet::new(),
446 }
447 }
448 pub fn new_with_modifiers(bare_key: BareKey, key_modifiers: BTreeSet<KeyModifier>) -> Self {
449 KeyWithModifier {
450 bare_key,
451 key_modifiers,
452 }
453 }
454 pub fn with_shift_modifier(mut self) -> Self {
455 self.key_modifiers.insert(KeyModifier::Shift);
456 self
457 }
458 pub fn with_alt_modifier(mut self) -> Self {
459 self.key_modifiers.insert(KeyModifier::Alt);
460 self
461 }
462 pub fn with_ctrl_modifier(mut self) -> Self {
463 self.key_modifiers.insert(KeyModifier::Ctrl);
464 self
465 }
466 pub fn with_super_modifier(mut self) -> Self {
467 self.key_modifiers.insert(KeyModifier::Super);
468 self
469 }
470 pub fn from_bytes_with_u(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
471 let bare_key = BareKey::from_bytes_with_u(number_bytes);
473 match bare_key {
474 Some(bare_key) => {
475 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
476 Some(KeyWithModifier {
477 bare_key,
478 key_modifiers,
479 })
480 },
481 _ => None,
482 }
483 }
484 pub fn from_bytes_with_tilde(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
485 let bare_key = BareKey::from_bytes_with_tilde(number_bytes);
487 match bare_key {
488 Some(bare_key) => {
489 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
490 Some(KeyWithModifier {
491 bare_key,
492 key_modifiers,
493 })
494 },
495 _ => None,
496 }
497 }
498 pub fn from_bytes_with_no_ending_byte(
499 number_bytes: &[u8],
500 modifier_bytes: &[u8],
501 ) -> Option<Self> {
502 let bare_key = BareKey::from_bytes_with_no_ending_byte(number_bytes);
504 match bare_key {
505 Some(bare_key) => {
506 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
507 Some(KeyWithModifier {
508 bare_key,
509 key_modifiers,
510 })
511 },
512 _ => None,
513 }
514 }
515 pub fn strip_common_modifiers(&self, common_modifiers: &Vec<KeyModifier>) -> Self {
516 let common_modifiers: BTreeSet<&KeyModifier> = common_modifiers.into_iter().collect();
517 KeyWithModifier {
518 bare_key: self.bare_key.clone(),
519 key_modifiers: self
520 .key_modifiers
521 .iter()
522 .filter(|m| !common_modifiers.contains(m))
523 .cloned()
524 .collect(),
525 }
526 }
527 pub fn is_key_without_modifier(&self, key: BareKey) -> bool {
528 self.bare_key == key && self.key_modifiers.is_empty()
529 }
530 pub fn is_key_with_ctrl_modifier(&self, key: BareKey) -> bool {
531 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Ctrl)
532 }
533 pub fn is_key_with_alt_modifier(&self, key: BareKey) -> bool {
534 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Alt)
535 }
536 pub fn is_key_with_shift_modifier(&self, key: BareKey) -> bool {
537 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Shift)
538 }
539 pub fn is_key_with_super_modifier(&self, key: BareKey) -> bool {
540 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Super)
541 }
542 pub fn is_cancel_key(&self) -> bool {
543 self.bare_key == BareKey::Esc
545 }
546 #[cfg(not(target_family = "wasm"))]
547 pub fn to_termwiz_modifiers(&self) -> Modifiers {
548 let mut modifiers = Modifiers::empty();
549 for modifier in &self.key_modifiers {
550 modifiers.set(modifier.into(), true);
551 }
552 modifiers
553 }
554 #[cfg(not(target_family = "wasm"))]
555 pub fn to_termwiz_keycode(&self) -> KeyCode {
556 match self.bare_key {
557 BareKey::PageDown => KeyCode::PageDown,
558 BareKey::PageUp => KeyCode::PageUp,
559 BareKey::Left => KeyCode::LeftArrow,
560 BareKey::Down => KeyCode::DownArrow,
561 BareKey::Up => KeyCode::UpArrow,
562 BareKey::Right => KeyCode::RightArrow,
563 BareKey::Home => KeyCode::Home,
564 BareKey::End => KeyCode::End,
565 BareKey::Backspace => KeyCode::Backspace,
566 BareKey::Delete => KeyCode::Delete,
567 BareKey::Insert => KeyCode::Insert,
568 BareKey::F(index) => KeyCode::Function(index),
569 BareKey::Char(character) => KeyCode::Char(character),
570 BareKey::Tab => KeyCode::Tab,
571 BareKey::Esc => KeyCode::Escape,
572 BareKey::Enter => KeyCode::Enter,
573 BareKey::CapsLock => KeyCode::CapsLock,
574 BareKey::ScrollLock => KeyCode::ScrollLock,
575 BareKey::NumLock => KeyCode::NumLock,
576 BareKey::PrintScreen => KeyCode::PrintScreen,
577 BareKey::Pause => KeyCode::Pause,
578 BareKey::Menu => KeyCode::Menu,
579 }
580 }
581 #[cfg(not(target_family = "wasm"))]
582 pub fn serialize_non_kitty(&self) -> Option<String> {
583 let modifiers = self.to_termwiz_modifiers();
584 let key_code_encode_modes = KeyCodeEncodeModes {
585 encoding: KeyboardEncoding::Xterm,
586 application_cursor_keys: false,
589 newline_mode: false,
590 modify_other_keys: None,
591 };
592 self.to_termwiz_keycode()
593 .encode(modifiers, key_code_encode_modes, true)
594 .ok()
595 }
596 #[cfg(not(target_family = "wasm"))]
597 pub fn serialize_kitty(&self) -> Option<String> {
598 let modifiers = self.to_termwiz_modifiers();
599 let key_code_encode_modes = KeyCodeEncodeModes {
600 encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
601 application_cursor_keys: false,
604 newline_mode: false,
605 modify_other_keys: None,
606 };
607 self.to_termwiz_keycode()
608 .encode(modifiers, key_code_encode_modes, true)
609 .ok()
610 }
611 pub fn has_no_modifiers(&self) -> bool {
612 self.key_modifiers.is_empty()
613 }
614 pub fn has_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
615 for modifier in modifiers {
616 if !self.key_modifiers.contains(modifier) {
617 return false;
618 }
619 }
620 true
621 }
622 pub fn has_only_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
623 for modifier in modifiers {
624 if !self.key_modifiers.contains(modifier) {
625 return false;
626 }
627 }
628 if self.key_modifiers.len() != modifiers.len() {
629 return false;
630 }
631 true
632 }
633}
634
635#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
636pub enum Direction {
637 Left,
638 Right,
639 Up,
640 Down,
641}
642
643impl Default for Direction {
644 fn default() -> Self {
645 Direction::Left
646 }
647}
648
649impl Direction {
650 pub fn invert(&self) -> Direction {
651 match *self {
652 Direction::Left => Direction::Right,
653 Direction::Down => Direction::Up,
654 Direction::Up => Direction::Down,
655 Direction::Right => Direction::Left,
656 }
657 }
658
659 pub fn is_horizontal(&self) -> bool {
660 matches!(self, Direction::Left | Direction::Right)
661 }
662
663 pub fn is_vertical(&self) -> bool {
664 matches!(self, Direction::Down | Direction::Up)
665 }
666}
667
668impl fmt::Display for Direction {
669 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
670 match self {
671 Direction::Left => write!(f, "←"),
672 Direction::Right => write!(f, "→"),
673 Direction::Up => write!(f, "↑"),
674 Direction::Down => write!(f, "↓"),
675 }
676 }
677}
678
679impl FromStr for Direction {
680 type Err = String;
681 fn from_str(s: &str) -> Result<Self, Self::Err> {
682 match s {
683 "Left" | "left" => Ok(Direction::Left),
684 "Right" | "right" => Ok(Direction::Right),
685 "Up" | "up" => Ok(Direction::Up),
686 "Down" | "down" => Ok(Direction::Down),
687 _ => Err(format!(
688 "Failed to parse Direction. Unknown Direction: {}",
689 s
690 )),
691 }
692 }
693}
694
695#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
697pub enum Resize {
698 Increase,
699 Decrease,
700}
701
702impl Default for Resize {
703 fn default() -> Self {
704 Resize::Increase
705 }
706}
707
708impl Resize {
709 pub fn invert(&self) -> Self {
710 match self {
711 Resize::Increase => Resize::Decrease,
712 Resize::Decrease => Resize::Increase,
713 }
714 }
715}
716
717impl fmt::Display for Resize {
718 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
719 match self {
720 Resize::Increase => write!(f, "+"),
721 Resize::Decrease => write!(f, "-"),
722 }
723 }
724}
725
726impl FromStr for Resize {
727 type Err = String;
728 fn from_str(s: &str) -> Result<Self, Self::Err> {
729 match s {
730 "Increase" | "increase" | "+" => Ok(Resize::Increase),
731 "Decrease" | "decrease" | "-" => Ok(Resize::Decrease),
732 _ => Err(format!(
733 "failed to parse resize type. Unknown specifier '{}'",
734 s
735 )),
736 }
737 }
738}
739
740#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
750pub struct ResizeStrategy {
751 pub resize: Resize,
753 pub direction: Option<Direction>,
755 pub invert_on_boundaries: bool,
772}
773
774impl From<Direction> for ResizeStrategy {
775 fn from(direction: Direction) -> Self {
776 ResizeStrategy::new(Resize::Increase, Some(direction))
777 }
778}
779
780impl From<Resize> for ResizeStrategy {
781 fn from(resize: Resize) -> Self {
782 ResizeStrategy::new(resize, None)
783 }
784}
785
786impl ResizeStrategy {
787 pub fn new(resize: Resize, direction: Option<Direction>) -> Self {
788 ResizeStrategy {
789 resize,
790 direction,
791 invert_on_boundaries: true,
792 }
793 }
794
795 pub fn invert(&self) -> ResizeStrategy {
796 let resize = match self.resize {
797 Resize::Increase => Resize::Decrease,
798 Resize::Decrease => Resize::Increase,
799 };
800 let direction = match self.direction {
801 Some(direction) => Some(direction.invert()),
802 None => None,
803 };
804
805 ResizeStrategy::new(resize, direction)
806 }
807
808 pub fn resize_type(&self) -> Resize {
809 self.resize
810 }
811
812 pub fn direction(&self) -> Option<Direction> {
813 self.direction
814 }
815
816 pub fn direction_horizontal(&self) -> bool {
817 matches!(
818 self.direction,
819 Some(Direction::Left) | Some(Direction::Right)
820 )
821 }
822
823 pub fn direction_vertical(&self) -> bool {
824 matches!(self.direction, Some(Direction::Up) | Some(Direction::Down))
825 }
826
827 pub fn resize_increase(&self) -> bool {
828 self.resize == Resize::Increase
829 }
830
831 pub fn resize_decrease(&self) -> bool {
832 self.resize == Resize::Decrease
833 }
834
835 pub fn move_left_border_left(&self) -> bool {
836 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Left))
837 }
838
839 pub fn move_left_border_right(&self) -> bool {
840 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Left))
841 }
842
843 pub fn move_lower_border_down(&self) -> bool {
844 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Down))
845 }
846
847 pub fn move_lower_border_up(&self) -> bool {
848 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Down))
849 }
850
851 pub fn move_upper_border_up(&self) -> bool {
852 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Up))
853 }
854
855 pub fn move_upper_border_down(&self) -> bool {
856 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Up))
857 }
858
859 pub fn move_right_border_right(&self) -> bool {
860 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Right))
861 }
862
863 pub fn move_right_border_left(&self) -> bool {
864 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Right))
865 }
866
867 pub fn move_all_borders_out(&self) -> bool {
868 (self.resize == Resize::Increase) && (self.direction == None)
869 }
870
871 pub fn move_all_borders_in(&self) -> bool {
872 (self.resize == Resize::Decrease) && (self.direction == None)
873 }
874}
875
876impl fmt::Display for ResizeStrategy {
877 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
878 let resize = match self.resize {
879 Resize::Increase => "increase",
880 Resize::Decrease => "decrease",
881 };
882 let border = match self.direction {
883 Some(Direction::Left) => "left",
884 Some(Direction::Down) => "bottom",
885 Some(Direction::Up) => "top",
886 Some(Direction::Right) => "right",
887 None => "every",
888 };
889
890 write!(f, "{} size on {} border", resize, border)
891 }
892}
893
894#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
895pub enum Mouse {
899 ScrollUp(usize), ScrollDown(usize), ScrollLeft(usize),
902 ScrollRight(usize),
903 LeftClick(isize, usize), RightClick(isize, usize), Hold(isize, usize), Release(isize, usize), Hover(isize, usize), }
909
910impl Mouse {
911 pub fn position(&self) -> Option<(usize, usize)> {
912 match self {
914 Mouse::LeftClick(line, column) => Some((*line as usize, *column as usize)),
915 Mouse::RightClick(line, column) => Some((*line as usize, *column as usize)),
916 Mouse::Hold(line, column) => Some((*line as usize, *column as usize)),
917 Mouse::Release(line, column) => Some((*line as usize, *column as usize)),
918 Mouse::Hover(line, column) => Some((*line as usize, *column as usize)),
919 _ => None,
920 }
921 }
922}
923
924#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
925pub struct FileMetadata {
926 pub is_dir: bool,
927 pub is_file: bool,
928 pub is_symlink: bool,
929 pub len: u64,
930}
931
932impl From<Metadata> for FileMetadata {
933 fn from(metadata: Metadata) -> Self {
934 FileMetadata {
935 is_dir: metadata.is_dir(),
936 is_file: metadata.is_file(),
937 is_symlink: metadata.is_symlink(),
938 len: metadata.len(),
939 }
940 }
941}
942
943#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
944pub struct StyledText {
945 pub text: String,
946 pub indices: Vec<Vec<usize>>,
947}
948
949#[derive(Debug, Clone, PartialEq, EnumDiscriminants, Display, Serialize, Deserialize)]
952#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
953#[strum_discriminants(name(EventType))]
954#[non_exhaustive]
955pub enum Event {
956 ModeUpdate(ModeInfo),
957 TabUpdate(Vec<TabInfo>),
958 PaneUpdate(PaneManifest),
959 Key(KeyWithModifier),
961 Mouse(Mouse),
963 Timer(f64),
965 CopyToClipboard(CopyDestination),
967 SystemClipboardFailure,
969 InputReceived,
971 Visible(bool),
973 CustomMessage(
975 String, String, ),
978 FileSystemCreate(Vec<(PathBuf, Option<FileMetadata>)>),
980 FileSystemRead(Vec<(PathBuf, Option<FileMetadata>)>),
982 FileSystemUpdate(Vec<(PathBuf, Option<FileMetadata>)>),
984 FileSystemDelete(Vec<(PathBuf, Option<FileMetadata>)>),
986 PermissionRequestResult(PermissionStatus),
988 SessionUpdate(
989 Vec<SessionInfo>,
990 Vec<(String, Duration)>, ),
992 RunCommandResult(Option<i32>, Vec<u8>, Vec<u8>, BTreeMap<String, String>), WebRequestResult(
995 u16,
996 BTreeMap<String, String>,
997 Vec<u8>,
998 BTreeMap<String, String>,
999 ), CommandPaneOpened(u32, Context), CommandPaneExited(u32, Option<i32>, Context), PaneClosed(PaneId),
1007 EditPaneOpened(u32, Context), EditPaneExited(u32, Option<i32>, Context), CommandPaneReRun(u32, Context), FailedToWriteConfigToDisk(Option<String>), ListClients(Vec<ClientInfo>),
1012 HostFolderChanged(PathBuf), FailedToChangeHostFolder(Option<String>), PastedText(String),
1015 ConfigWasWrittenToDisk,
1016 WebServerStatus(WebServerStatus),
1017 FailedToStartWebServer(String),
1018 BeforeClose,
1019 InterceptedKeyPress(KeyWithModifier),
1020 UserAction(Action, ClientId, Option<u32>, Option<ClientId>), PaneRenderReport(HashMap<PaneId, PaneContents>),
1023 ActionComplete(Action, Option<PaneId>, BTreeMap<String, String>), CwdChanged(PaneId, PathBuf, Vec<ClientId>), CommandChanged(PaneId, Vec<String>, bool, Vec<ClientId>), AvailableLayoutInfo(Vec<LayoutInfo>, Vec<LayoutWithError>),
1027 PluginConfigurationChanged(BTreeMap<String, String>),
1028 HighlightClicked {
1029 pane_id: PaneId,
1030 pattern: String,
1031 matched_string: String,
1032 context: BTreeMap<String, String>,
1033 },
1034 InitialKeybinds(KeybindsVec),
1038 HostTerminalThemeChanged(HostTerminalThemeMode),
1040 SoftKeyboardVisibilityChanged(bool),
1041 HintText(BTreeMap<usize, StyledText>),
1042 ActivePaneScroll(Option<(usize, usize)>),
1043}
1044
1045#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Serialize, Deserialize)]
1046pub enum HostTerminalThemeMode {
1047 Dark,
1048 Light,
1049}
1050
1051#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants, Display, Serialize, Deserialize)]
1052pub enum WebServerStatus {
1053 Online(String), Offline,
1055 DifferentVersion(String), }
1057
1058#[derive(
1059 Debug,
1060 PartialEq,
1061 Eq,
1062 Hash,
1063 Copy,
1064 Clone,
1065 EnumDiscriminants,
1066 Display,
1067 Serialize,
1068 Deserialize,
1069 PartialOrd,
1070 Ord,
1071)]
1072#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize, Display, PartialOrd, Ord))]
1073#[strum_discriminants(name(PermissionType))]
1074#[non_exhaustive]
1075pub enum Permission {
1076 ReadApplicationState,
1077 ChangeApplicationState,
1078 OpenFiles,
1079 RunCommands,
1080 OpenTerminalsOrPlugins,
1081 WriteToStdin,
1082 WebAccess,
1083 ReadCliPipes,
1084 MessageAndLaunchOtherPlugins,
1085 Reconfigure,
1086 FullHdAccess,
1087 StartWebServer,
1088 InterceptInput,
1089 ReadPaneContents,
1090 RunActionsAsUser,
1091 WriteToClipboard,
1092 ReadSessionEnvironmentVariables,
1093}
1094
1095impl PermissionType {
1096 pub fn display_name(&self) -> String {
1097 match self {
1098 PermissionType::ReadApplicationState => {
1099 "Access Zellij state (Panes, Tabs and UI)".to_owned()
1100 },
1101 PermissionType::ChangeApplicationState => {
1102 "Change Zellij state (Panes, Tabs and UI) and run commands".to_owned()
1103 },
1104 PermissionType::OpenFiles => "Open files (eg. for editing)".to_owned(),
1105 PermissionType::RunCommands => "Run commands".to_owned(),
1106 PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
1107 PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
1108 PermissionType::WebAccess => "Make web requests".to_owned(),
1109 PermissionType::ReadCliPipes => "Control command line pipes and output".to_owned(),
1110 PermissionType::MessageAndLaunchOtherPlugins => {
1111 "Send messages to and launch other plugins".to_owned()
1112 },
1113 PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
1114 PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
1115 PermissionType::StartWebServer => {
1116 "Start a local web server to serve Zellij sessions".to_owned()
1117 },
1118 PermissionType::InterceptInput => "Intercept Input (keyboard & mouse)".to_owned(),
1119 PermissionType::ReadPaneContents => {
1120 "Read pane contents (viewport and selection)".to_owned()
1121 },
1122 PermissionType::RunActionsAsUser => "Execute actions as the user".to_owned(),
1123 PermissionType::WriteToClipboard => "Write to clipboard".to_owned(),
1124 PermissionType::ReadSessionEnvironmentVariables => {
1125 "Read environment variables present upon session creation".to_owned()
1126 },
1127 }
1128 }
1129}
1130
1131#[derive(Debug, Clone)]
1132pub struct PluginPermission {
1133 pub name: String,
1134 pub permissions: Vec<PermissionType>,
1135}
1136
1137impl PluginPermission {
1138 pub fn new(name: String, permissions: Vec<PermissionType>) -> Self {
1139 PluginPermission { name, permissions }
1140 }
1141}
1142
1143#[derive(
1145 Debug,
1146 PartialEq,
1147 Eq,
1148 Hash,
1149 Copy,
1150 Clone,
1151 EnumIter,
1152 Serialize,
1153 Deserialize,
1154 ValueEnum,
1155 PartialOrd,
1156 Ord,
1157)]
1158pub enum InputMode {
1159 #[serde(alias = "normal")]
1162 Normal,
1163 #[serde(alias = "locked")]
1166 Locked,
1167 #[serde(alias = "resize")]
1169 Resize,
1170 #[serde(alias = "pane")]
1172 Pane,
1173 #[serde(alias = "tab")]
1175 Tab,
1176 #[serde(alias = "scroll")]
1178 Scroll,
1179 #[serde(alias = "entersearch")]
1181 EnterSearch,
1182 #[serde(alias = "search")]
1184 Search,
1185 #[serde(alias = "renametab")]
1187 RenameTab,
1188 #[serde(alias = "renamepane")]
1190 RenamePane,
1191 #[serde(alias = "session")]
1193 Session,
1194 #[serde(alias = "move")]
1196 Move,
1197 #[serde(alias = "prompt")]
1199 Prompt,
1200 #[serde(alias = "tmux")]
1202 Tmux,
1203}
1204
1205impl Default for InputMode {
1206 fn default() -> InputMode {
1207 InputMode::Normal
1208 }
1209}
1210
1211#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, ValueEnum)]
1212pub enum ThemeHue {
1213 #[serde(alias = "light")]
1214 Light,
1215 #[serde(alias = "dark")]
1216 Dark,
1217}
1218impl Default for ThemeHue {
1219 fn default() -> ThemeHue {
1220 ThemeHue::Dark
1221 }
1222}
1223
1224impl FromStr for ThemeHue {
1225 type Err = String;
1226 fn from_str(s: &str) -> Result<Self, Self::Err> {
1227 match s.trim().to_lowercase().as_str() {
1228 "light" => Ok(ThemeHue::Light),
1229 "dark" => Ok(ThemeHue::Dark),
1230 e => Err(format!(
1231 "Unknown theme hue: '{}' (expected 'dark' or 'light')",
1232 e
1233 )),
1234 }
1235 }
1236}
1237
1238impl fmt::Display for ThemeHue {
1239 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1240 match self {
1241 ThemeHue::Light => write!(f, "light"),
1242 ThemeHue::Dark => write!(f, "dark"),
1243 }
1244 }
1245}
1246
1247impl From<ThemeHue> for HostTerminalThemeMode {
1248 fn from(hue: ThemeHue) -> Self {
1249 match hue {
1250 ThemeHue::Light => HostTerminalThemeMode::Light,
1251 ThemeHue::Dark => HostTerminalThemeMode::Dark,
1252 }
1253 }
1254}
1255
1256impl From<HostTerminalThemeMode> for ThemeHue {
1257 fn from(mode: HostTerminalThemeMode) -> Self {
1258 match mode {
1259 HostTerminalThemeMode::Light => ThemeHue::Light,
1260 HostTerminalThemeMode::Dark => ThemeHue::Dark,
1261 }
1262 }
1263}
1264
1265#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1266pub enum PaletteColor {
1267 Rgb((u8, u8, u8)),
1268 EightBit(u8),
1269}
1270impl Default for PaletteColor {
1271 fn default() -> PaletteColor {
1272 PaletteColor::EightBit(0)
1273 }
1274}
1275
1276#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
1281pub enum HighlightLayer {
1282 Hint, Tool, ActionFeedback, }
1286
1287impl Default for HighlightLayer {
1288 fn default() -> Self {
1289 HighlightLayer::Hint
1290 }
1291}
1292
1293#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1296pub enum HighlightStyle {
1297 None, Emphasis0, Emphasis1, Emphasis2, Emphasis3, BackgroundEmphasis0, BackgroundEmphasis1, BackgroundEmphasis2, BackgroundEmphasis3, CustomRgb {
1307 fg: Option<(u8, u8, u8)>,
1308 bg: Option<(u8, u8, u8)>,
1309 },
1310 CustomIndex {
1311 fg: Option<u8>,
1312 bg: Option<u8>,
1313 },
1314}
1315
1316#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1318pub struct RegexHighlight {
1319 pub pattern: String, pub style: HighlightStyle,
1321 pub layer: HighlightLayer,
1322 pub context: BTreeMap<String, String>, pub on_hover: bool, pub bold: bool,
1325 pub italic: bool,
1326 pub underline: bool,
1327 pub tooltip_text: Option<String>, }
1329
1330impl PaletteColor {
1332 pub fn as_rgb_str(&self) -> String {
1333 let (r, g, b) = match *self {
1334 Self::Rgb((r, g, b)) => (r, g, b),
1335 Self::EightBit(c) => eightbit_to_rgb(c),
1336 };
1337 format!("rgb({}, {}, {})", r, g, b)
1338 }
1339 pub fn from_rgb_str(rgb_str: &str) -> Self {
1340 let trimmed = rgb_str.trim();
1341
1342 if !trimmed.starts_with("rgb(") || !trimmed.ends_with(')') {
1343 return Self::default();
1344 }
1345
1346 let inner = trimmed
1347 .strip_prefix("rgb(")
1348 .and_then(|s| s.strip_suffix(')'))
1349 .unwrap_or("");
1350
1351 let parts: Vec<&str> = inner.split(',').collect();
1352
1353 if parts.len() != 3 {
1354 return Self::default();
1355 }
1356
1357 let mut rgb_values = [0u8; 3];
1358 for (i, part) in parts.iter().enumerate() {
1359 if let Some(rgb_val) = rgb_values.get_mut(i) {
1360 if let Ok(parsed) = part.trim().parse::<u8>() {
1361 *rgb_val = parsed;
1362 } else {
1363 return Self::default();
1364 }
1365 }
1366 }
1367
1368 Self::Rgb((rgb_values[0], rgb_values[1], rgb_values[2]))
1369 }
1370}
1371
1372impl FromStr for InputMode {
1373 type Err = ConversionError;
1374
1375 fn from_str(s: &str) -> Result<Self, ConversionError> {
1376 match s {
1377 "normal" | "Normal" => Ok(InputMode::Normal),
1378 "locked" | "Locked" => Ok(InputMode::Locked),
1379 "resize" | "Resize" => Ok(InputMode::Resize),
1380 "pane" | "Pane" => Ok(InputMode::Pane),
1381 "tab" | "Tab" => Ok(InputMode::Tab),
1382 "search" | "Search" => Ok(InputMode::Search),
1383 "scroll" | "Scroll" => Ok(InputMode::Scroll),
1384 "renametab" | "RenameTab" => Ok(InputMode::RenameTab),
1385 "renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
1386 "session" | "Session" => Ok(InputMode::Session),
1387 "move" | "Move" => Ok(InputMode::Move),
1388 "prompt" | "Prompt" => Ok(InputMode::Prompt),
1389 "tmux" | "Tmux" => Ok(InputMode::Tmux),
1390 "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch),
1391 e => Err(ConversionError::UnknownInputMode(e.into())),
1392 }
1393 }
1394}
1395
1396#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1397pub enum PaletteSource {
1398 Default,
1399 Xresources,
1400}
1401impl Default for PaletteSource {
1402 fn default() -> PaletteSource {
1403 PaletteSource::Default
1404 }
1405}
1406#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
1407pub struct Palette {
1408 pub source: PaletteSource,
1409 pub theme_hue: ThemeHue,
1410 pub fg: PaletteColor,
1411 pub bg: PaletteColor,
1412 pub black: PaletteColor,
1413 pub red: PaletteColor,
1414 pub green: PaletteColor,
1415 pub yellow: PaletteColor,
1416 pub blue: PaletteColor,
1417 pub magenta: PaletteColor,
1418 pub cyan: PaletteColor,
1419 pub white: PaletteColor,
1420 pub orange: PaletteColor,
1421 pub gray: PaletteColor,
1422 pub purple: PaletteColor,
1423 pub gold: PaletteColor,
1424 pub silver: PaletteColor,
1425 pub pink: PaletteColor,
1426 pub brown: PaletteColor,
1427}
1428
1429#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1430pub struct Style {
1431 pub colors: Styling,
1432 pub rounded_corners: bool,
1433 pub hide_session_name: bool,
1434}
1435
1436#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1437pub enum Coloration {
1438 NoStyling,
1439 Styled(StyleDeclaration),
1440}
1441
1442impl Coloration {
1443 pub fn with_fallback(&self, fallback: StyleDeclaration) -> StyleDeclaration {
1444 match &self {
1445 Coloration::NoStyling => fallback,
1446 Coloration::Styled(style) => *style,
1447 }
1448 }
1449}
1450
1451#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1452pub struct Styling {
1453 pub text_unselected: StyleDeclaration,
1454 pub text_selected: StyleDeclaration,
1455 pub ribbon_unselected: StyleDeclaration,
1456 pub ribbon_selected: StyleDeclaration,
1457 pub table_title: StyleDeclaration,
1458 pub table_cell_unselected: StyleDeclaration,
1459 pub table_cell_selected: StyleDeclaration,
1460 pub list_unselected: StyleDeclaration,
1461 pub list_selected: StyleDeclaration,
1462 pub frame_unselected: Option<StyleDeclaration>,
1463 pub frame_selected: StyleDeclaration,
1464 pub frame_highlight: StyleDeclaration,
1465 pub exit_code_success: StyleDeclaration,
1466 pub exit_code_error: StyleDeclaration,
1467 pub multiplayer_user_colors: MultiplayerColors,
1468}
1469
1470#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1471pub struct StyleDeclaration {
1472 pub base: PaletteColor,
1473 pub background: PaletteColor,
1474 pub emphasis_0: PaletteColor,
1475 pub emphasis_1: PaletteColor,
1476 pub emphasis_2: PaletteColor,
1477 pub emphasis_3: PaletteColor,
1478}
1479
1480#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1481pub struct MultiplayerColors {
1482 pub player_1: PaletteColor,
1483 pub player_2: PaletteColor,
1484 pub player_3: PaletteColor,
1485 pub player_4: PaletteColor,
1486 pub player_5: PaletteColor,
1487 pub player_6: PaletteColor,
1488 pub player_7: PaletteColor,
1489 pub player_8: PaletteColor,
1490 pub player_9: PaletteColor,
1491 pub player_10: PaletteColor,
1492}
1493
1494pub const DEFAULT_STYLES: Styling = Styling {
1495 text_unselected: StyleDeclaration {
1496 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1497 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1498 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1499 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1500 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1501 background: PaletteColor::EightBit(default_colors::GRAY),
1502 },
1503 text_selected: StyleDeclaration {
1504 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1505 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1506 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1507 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1508 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1509 background: PaletteColor::EightBit(default_colors::GRAY),
1510 },
1511 ribbon_unselected: StyleDeclaration {
1512 base: PaletteColor::EightBit(default_colors::BLACK),
1513 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1514 emphasis_1: PaletteColor::EightBit(default_colors::WHITE),
1515 emphasis_2: PaletteColor::EightBit(default_colors::BLUE),
1516 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1517 background: PaletteColor::EightBit(default_colors::GRAY),
1518 },
1519 ribbon_selected: StyleDeclaration {
1520 base: PaletteColor::EightBit(default_colors::BLACK),
1521 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1522 emphasis_1: PaletteColor::EightBit(default_colors::ORANGE),
1523 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1524 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1525 background: PaletteColor::EightBit(default_colors::GREEN),
1526 },
1527 exit_code_success: StyleDeclaration {
1528 base: PaletteColor::EightBit(default_colors::GREEN),
1529 emphasis_0: PaletteColor::EightBit(default_colors::CYAN),
1530 emphasis_1: PaletteColor::EightBit(default_colors::BLACK),
1531 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1532 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1533 background: PaletteColor::EightBit(default_colors::GRAY),
1534 },
1535 exit_code_error: StyleDeclaration {
1536 base: PaletteColor::EightBit(default_colors::RED),
1537 emphasis_0: PaletteColor::EightBit(default_colors::YELLOW),
1538 emphasis_1: PaletteColor::EightBit(default_colors::GOLD),
1539 emphasis_2: PaletteColor::EightBit(default_colors::SILVER),
1540 emphasis_3: PaletteColor::EightBit(default_colors::PURPLE),
1541 background: PaletteColor::EightBit(default_colors::GRAY),
1542 },
1543 frame_unselected: None,
1544 frame_selected: StyleDeclaration {
1545 base: PaletteColor::EightBit(default_colors::GREEN),
1546 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1547 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1548 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1549 emphasis_3: PaletteColor::EightBit(default_colors::BROWN),
1550 background: PaletteColor::EightBit(default_colors::GRAY),
1551 },
1552 frame_highlight: StyleDeclaration {
1553 base: PaletteColor::EightBit(default_colors::ORANGE),
1554 emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA),
1555 emphasis_1: PaletteColor::EightBit(default_colors::PURPLE),
1556 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1557 emphasis_3: PaletteColor::EightBit(default_colors::GREEN),
1558 background: PaletteColor::EightBit(default_colors::GREEN),
1559 },
1560 table_title: StyleDeclaration {
1561 base: PaletteColor::EightBit(default_colors::GREEN),
1562 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1563 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1564 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1565 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1566 background: PaletteColor::EightBit(default_colors::GRAY),
1567 },
1568 table_cell_unselected: StyleDeclaration {
1569 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1570 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1571 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1572 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1573 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1574 background: PaletteColor::EightBit(default_colors::GRAY),
1575 },
1576 table_cell_selected: StyleDeclaration {
1577 base: PaletteColor::EightBit(default_colors::GREEN),
1578 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1579 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1580 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1581 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1582 background: PaletteColor::EightBit(default_colors::GRAY),
1583 },
1584 list_unselected: StyleDeclaration {
1585 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1586 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1587 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1588 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1589 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1590 background: PaletteColor::EightBit(default_colors::GRAY),
1591 },
1592 list_selected: StyleDeclaration {
1593 base: PaletteColor::EightBit(default_colors::GREEN),
1594 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1595 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1596 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1597 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1598 background: PaletteColor::EightBit(default_colors::GRAY),
1599 },
1600 multiplayer_user_colors: MultiplayerColors {
1601 player_1: PaletteColor::EightBit(default_colors::MAGENTA),
1602 player_2: PaletteColor::EightBit(default_colors::BLUE),
1603 player_3: PaletteColor::EightBit(default_colors::PURPLE),
1604 player_4: PaletteColor::EightBit(default_colors::YELLOW),
1605 player_5: PaletteColor::EightBit(default_colors::CYAN),
1606 player_6: PaletteColor::EightBit(default_colors::GOLD),
1607 player_7: PaletteColor::EightBit(default_colors::RED),
1608 player_8: PaletteColor::EightBit(default_colors::SILVER),
1609 player_9: PaletteColor::EightBit(default_colors::PINK),
1610 player_10: PaletteColor::EightBit(default_colors::BROWN),
1611 },
1612};
1613
1614impl Default for Styling {
1615 fn default() -> Self {
1616 DEFAULT_STYLES
1617 }
1618}
1619
1620impl From<Styling> for Palette {
1621 fn from(styling: Styling) -> Self {
1622 Palette {
1623 theme_hue: ThemeHue::Dark,
1624 source: PaletteSource::Default,
1625 fg: styling.ribbon_unselected.background,
1626 bg: styling.text_unselected.background,
1627 red: styling.exit_code_error.base,
1628 green: styling.text_unselected.emphasis_2,
1629 yellow: styling.exit_code_error.emphasis_0,
1630 blue: styling.ribbon_unselected.emphasis_2,
1631 magenta: styling.text_unselected.emphasis_3,
1632 orange: styling.text_unselected.emphasis_0,
1633 cyan: styling.text_unselected.emphasis_1,
1634 black: styling.ribbon_unselected.base,
1635 white: styling.ribbon_unselected.emphasis_1,
1636 gray: styling.list_unselected.background,
1637 purple: styling.multiplayer_user_colors.player_3,
1638 gold: styling.multiplayer_user_colors.player_6,
1639 silver: styling.multiplayer_user_colors.player_8,
1640 pink: styling.multiplayer_user_colors.player_9,
1641 brown: styling.multiplayer_user_colors.player_10,
1642 }
1643 }
1644}
1645
1646impl From<Palette> for Styling {
1647 fn from(palette: Palette) -> Self {
1648 let (fg, bg) = match palette.theme_hue {
1649 ThemeHue::Light => (palette.black, palette.white),
1650 ThemeHue::Dark => (palette.white, palette.black),
1651 };
1652 Styling {
1653 text_unselected: StyleDeclaration {
1654 base: fg,
1655 emphasis_0: palette.orange,
1656 emphasis_1: palette.cyan,
1657 emphasis_2: palette.green,
1658 emphasis_3: palette.magenta,
1659 background: bg,
1660 },
1661 text_selected: StyleDeclaration {
1662 base: fg,
1663 emphasis_0: palette.orange,
1664 emphasis_1: palette.cyan,
1665 emphasis_2: palette.green,
1666 emphasis_3: palette.magenta,
1667 background: palette.bg,
1668 },
1669 ribbon_unselected: StyleDeclaration {
1670 base: palette.black,
1671 emphasis_0: palette.red,
1672 emphasis_1: palette.white,
1673 emphasis_2: palette.blue,
1674 emphasis_3: palette.magenta,
1675 background: palette.fg,
1676 },
1677 ribbon_selected: StyleDeclaration {
1678 base: palette.black,
1679 emphasis_0: palette.red,
1680 emphasis_1: palette.orange,
1681 emphasis_2: palette.magenta,
1682 emphasis_3: palette.blue,
1683 background: palette.green,
1684 },
1685 exit_code_success: StyleDeclaration {
1686 base: palette.green,
1687 emphasis_0: palette.cyan,
1688 emphasis_1: palette.black,
1689 emphasis_2: palette.magenta,
1690 emphasis_3: palette.blue,
1691 background: Default::default(),
1692 },
1693 exit_code_error: StyleDeclaration {
1694 base: palette.red,
1695 emphasis_0: palette.yellow,
1696 emphasis_1: palette.gold,
1697 emphasis_2: palette.silver,
1698 emphasis_3: palette.purple,
1699 background: Default::default(),
1700 },
1701 frame_unselected: None,
1702 frame_selected: StyleDeclaration {
1703 base: palette.green,
1704 emphasis_0: palette.orange,
1705 emphasis_1: palette.cyan,
1706 emphasis_2: palette.magenta,
1707 emphasis_3: palette.brown,
1708 background: Default::default(),
1709 },
1710 frame_highlight: StyleDeclaration {
1711 base: palette.orange,
1712 emphasis_0: palette.magenta,
1713 emphasis_1: palette.purple,
1714 emphasis_2: palette.orange,
1715 emphasis_3: palette.orange,
1716 background: Default::default(),
1717 },
1718 table_title: StyleDeclaration {
1719 base: palette.green,
1720 emphasis_0: palette.orange,
1721 emphasis_1: palette.cyan,
1722 emphasis_2: palette.green,
1723 emphasis_3: palette.magenta,
1724 background: palette.gray,
1725 },
1726 table_cell_unselected: StyleDeclaration {
1727 base: fg,
1728 emphasis_0: palette.orange,
1729 emphasis_1: palette.cyan,
1730 emphasis_2: palette.green,
1731 emphasis_3: palette.magenta,
1732 background: palette.black,
1733 },
1734 table_cell_selected: StyleDeclaration {
1735 base: fg,
1736 emphasis_0: palette.orange,
1737 emphasis_1: palette.cyan,
1738 emphasis_2: palette.green,
1739 emphasis_3: palette.magenta,
1740 background: palette.bg,
1741 },
1742 list_unselected: StyleDeclaration {
1743 base: palette.white,
1744 emphasis_0: palette.orange,
1745 emphasis_1: palette.cyan,
1746 emphasis_2: palette.green,
1747 emphasis_3: palette.magenta,
1748 background: palette.black,
1749 },
1750 list_selected: StyleDeclaration {
1751 base: palette.white,
1752 emphasis_0: palette.orange,
1753 emphasis_1: palette.cyan,
1754 emphasis_2: palette.green,
1755 emphasis_3: palette.magenta,
1756 background: palette.bg,
1757 },
1758 multiplayer_user_colors: MultiplayerColors {
1759 player_1: palette.magenta,
1760 player_2: palette.blue,
1761 player_3: palette.purple,
1762 player_4: palette.yellow,
1763 player_5: palette.cyan,
1764 player_6: palette.gold,
1765 player_7: palette.red,
1766 player_8: palette.silver,
1767 player_9: palette.pink,
1768 player_10: palette.brown,
1769 },
1770 }
1771 }
1772}
1773
1774pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
1776
1777#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1779pub struct ModeInfo {
1780 pub mode: InputMode,
1781 pub base_mode: Option<InputMode>,
1782 pub keybinds: KeybindsVec,
1783 pub style: Style,
1784 pub capabilities: PluginCapabilities,
1785 pub session_name: Option<String>,
1786 pub editor: Option<PathBuf>,
1787 pub shell: Option<PathBuf>,
1788 pub web_clients_allowed: Option<bool>,
1789 pub web_sharing: Option<WebSharing>,
1790 pub currently_marking_pane_group: Option<bool>,
1791 pub is_web_client: Option<bool>,
1792 pub web_server_ip: Option<IpAddr>,
1794 pub web_server_port: Option<u16>,
1795 pub web_server_capability: Option<bool>,
1796 pub pane_frame_style: Option<PaneFrameStyle>,
1797 pub session_dimmed: Option<bool>,
1798 pub session_ancestry: Vec<String>,
1799 pub host_fullscreen: Option<bool>,
1800 pub nested_ascend_keys: Vec<KeyWithModifier>,
1801 pub session_ascended: Option<bool>,
1802 pub nested_descend_keys: Vec<KeyWithModifier>,
1803}
1804
1805impl ModeInfo {
1806 pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
1807 self.get_keybinds_for_mode(self.mode)
1808 }
1809
1810 pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
1811 for (vec_mode, map) in &self.keybinds {
1812 if mode == *vec_mode {
1813 return map.to_vec();
1814 }
1815 }
1816 vec![]
1817 }
1818 pub fn update_keybinds(&mut self, keybinds: Keybinds) {
1819 self.keybinds = keybinds.to_keybinds_vec();
1820 }
1821 pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
1822 self.base_mode = Some(new_default_mode);
1823 }
1824 pub fn update_theme(&mut self, theme: Styling) {
1825 self.style.colors = theme.into();
1826 }
1827 pub fn update_rounded_corners(&mut self, rounded_corners: bool) {
1828 self.style.rounded_corners = rounded_corners;
1829 }
1830 pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) {
1831 self.capabilities.arrow_fonts = !should_support_arrow_fonts;
1834 }
1835 pub fn update_hide_session_name(&mut self, hide_session_name: bool) {
1836 self.style.hide_session_name = hide_session_name;
1837 }
1838 pub fn change_to_default_mode(&mut self) {
1839 if let Some(base_mode) = self.base_mode {
1840 self.mode = base_mode;
1841 }
1842 }
1843}
1844
1845#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1846pub struct SessionInfo {
1847 pub name: String,
1848 pub tabs: Vec<TabInfo>,
1849 pub panes: PaneManifest,
1850 pub connected_clients: usize,
1851 pub is_current_session: bool,
1852 pub available_layouts: Vec<LayoutInfo>,
1853 pub plugins: BTreeMap<u32, PluginInfo>,
1854 pub web_clients_allowed: bool,
1855 pub web_client_count: usize,
1856 pub tab_history: BTreeMap<ClientId, Vec<usize>>,
1857 pub pane_history: BTreeMap<ClientId, Vec<PaneId>>,
1858 pub creation_time: Duration,
1859}
1860
1861#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1862pub struct PluginInfo {
1863 pub location: String,
1864 pub configuration: BTreeMap<String, String>,
1865}
1866
1867impl From<RunPlugin> for PluginInfo {
1868 fn from(run_plugin: RunPlugin) -> Self {
1869 PluginInfo {
1870 location: run_plugin.location.display(),
1871 configuration: run_plugin.configuration.inner().clone(),
1872 }
1873 }
1874}
1875
1876#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1877pub enum LayoutInfo {
1878 BuiltIn(String),
1879 File(String, LayoutMetadata),
1880 Url(String),
1881 Stringified(String),
1882}
1883
1884#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1885pub struct LayoutWithError {
1886 pub layout_name: String,
1887 pub error: LayoutParsingError,
1888}
1889
1890#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1891pub enum LayoutParsingError {
1892 KdlError {
1893 kdl_error: KdlError,
1894 file_name: String,
1895 source_code: String,
1896 },
1897 SyntaxError,
1898}
1899
1900impl AsRef<LayoutInfo> for LayoutInfo {
1901 fn as_ref(&self) -> &LayoutInfo {
1902 self
1903 }
1904}
1905
1906#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1907pub struct LayoutMetadata {
1908 pub tabs: Vec<TabMetadata>,
1909 pub creation_time: String,
1910 pub update_time: String,
1911}
1912
1913impl From<&PathBuf> for LayoutMetadata {
1914 fn from(path: &PathBuf) -> LayoutMetadata {
1915 match Layout::stringified_from_path(path) {
1916 Ok((path_str, stringified_layout, _swap_layouts)) => {
1917 match Layout::from_kdl(&stringified_layout, Some(path_str), None, None) {
1918 Ok(layout) => {
1919 let layout_tabs = layout.tabs();
1920 let tabs = if layout_tabs.is_empty() {
1921 let (tiled_pane_layout, floating_pane_layout) = layout.new_tab();
1922 vec![TabMetadata::from(&(
1923 None,
1924 tiled_pane_layout,
1925 floating_pane_layout,
1926 ))]
1927 } else {
1928 layout
1929 .tabs()
1930 .into_iter()
1931 .map(|tab| TabMetadata::from(&tab))
1932 .collect()
1933 };
1934
1935 let (creation_time, update_time) =
1937 LayoutMetadata::creation_and_update_times(&path);
1938
1939 LayoutMetadata {
1940 tabs,
1941 creation_time,
1942 update_time,
1943 }
1944 },
1945 Err(e) => {
1946 log::error!("Failed to parse layout: {}", e);
1947 LayoutMetadata::default()
1948 },
1949 }
1950 },
1951 Err(e) => {
1952 log::error!("Failed to read layout file: {}", e);
1953 LayoutMetadata::default()
1954 },
1955 }
1956 }
1957}
1958
1959impl LayoutMetadata {
1960 fn creation_and_update_times(path: &PathBuf) -> (String, String) {
1961 match std::fs::metadata(path) {
1963 Ok(metadata) => {
1964 let creation_time = metadata
1965 .created()
1966 .ok()
1967 .and_then(|t| {
1968 t.duration_since(std::time::UNIX_EPOCH)
1969 .ok()
1970 .map(|d| d.as_secs().to_string())
1971 })
1972 .unwrap_or_default();
1973
1974 let update_time = metadata
1975 .modified()
1976 .ok()
1977 .and_then(|t| {
1978 t.duration_since(std::time::UNIX_EPOCH)
1979 .ok()
1980 .map(|d| d.as_secs().to_string())
1981 })
1982 .unwrap_or_default();
1983
1984 (creation_time, update_time)
1985 },
1986 Err(_) => (String::new(), String::new()),
1987 }
1988 }
1989}
1990
1991#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1992pub struct TabMetadata {
1993 pub panes: Vec<PaneMetadata>,
1994 pub name: Option<String>,
1995}
1996
1997impl
1998 From<&(
1999 Option<String>,
2000 crate::input::layout::TiledPaneLayout,
2001 Vec<crate::input::layout::FloatingPaneLayout>,
2002 )> for TabMetadata
2003{
2004 fn from(
2005 tab: &(
2006 Option<String>,
2007 crate::input::layout::TiledPaneLayout,
2008 Vec<crate::input::layout::FloatingPaneLayout>,
2009 ),
2010 ) -> Self {
2011 let (tab_name, tiled_pane_layout, floating_panes) = tab;
2012
2013 let mut panes = Vec::new();
2015 collect_leaf_panes(&tiled_pane_layout, &mut panes);
2016
2017 for floating_pane in floating_panes {
2019 panes.push(PaneMetadata::from(floating_pane));
2020 }
2021
2022 TabMetadata {
2023 panes,
2024 name: tab_name.clone(),
2025 }
2026 }
2027}
2028
2029#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
2030pub struct PaneMetadata {
2031 pub name: Option<String>,
2032 pub is_plugin: bool,
2033 pub is_builtin_plugin: bool,
2034}
2035
2036impl From<&crate::input::layout::TiledPaneLayout> for PaneMetadata {
2037 fn from(pane: &crate::input::layout::TiledPaneLayout) -> Self {
2038 let mut is_plugin = false;
2039 let mut is_builtin_plugin = false;
2040
2041 let name = if let Some(ref name) = pane.name {
2043 Some(name.clone())
2044 } else if let Some(ref run) = pane.run {
2045 match run {
2047 Run::Command(cmd) => {
2048 Some(cmd.command.to_string_lossy().to_string())
2050 },
2051 Run::EditFile(path, _line, _cwd) => {
2052 path.file_name().map(|n| n.to_string_lossy().to_string())
2054 },
2055 Run::Plugin(plugin) => {
2056 is_plugin = true;
2057 is_builtin_plugin = plugin.is_builtin_plugin();
2058 Some(plugin.location_string())
2059 },
2060 Run::Cwd(_) => None,
2061 }
2062 } else {
2063 None
2064 };
2065
2066 PaneMetadata {
2067 name,
2068 is_plugin,
2069 is_builtin_plugin,
2070 }
2071 }
2072}
2073
2074impl From<&crate::input::layout::FloatingPaneLayout> for PaneMetadata {
2075 fn from(pane: &crate::input::layout::FloatingPaneLayout) -> Self {
2076 let mut is_plugin = false;
2077 let mut is_builtin_plugin = false;
2078
2079 let name = if let Some(ref name) = pane.name {
2081 Some(name.clone())
2082 } else if let Some(ref run) = pane.run {
2083 match run {
2085 Run::Command(cmd) => {
2086 Some(cmd.command.to_string_lossy().to_string())
2088 },
2089 Run::EditFile(path, _line, _cwd) => {
2090 path.file_name().map(|n| n.to_string_lossy().to_string())
2092 },
2093 Run::Plugin(plugin) => {
2094 is_plugin = true;
2095 is_builtin_plugin = match plugin {
2096 crate::input::layout::RunPluginOrAlias::RunPlugin(run_plugin) => {
2097 matches!(run_plugin.location, RunPluginLocation::Zellij(_))
2098 },
2099 crate::input::layout::RunPluginOrAlias::Alias(_) => false,
2100 };
2101 Some(plugin.location_string())
2103 },
2104 Run::Cwd(_) => None,
2105 }
2106 } else {
2107 None
2108 };
2109
2110 PaneMetadata {
2111 name,
2112 is_plugin,
2113 is_builtin_plugin,
2114 }
2115 }
2116}
2117
2118fn collect_leaf_panes(
2120 pane: &crate::input::layout::TiledPaneLayout,
2121 result: &mut Vec<PaneMetadata>,
2122) {
2123 if pane.children.is_empty() {
2124 result.push(PaneMetadata::from(pane));
2126 } else {
2127 for child in &pane.children {
2129 collect_leaf_panes(child, result);
2130 }
2131 }
2132}
2133
2134impl LayoutInfo {
2135 pub fn name(&self) -> &str {
2136 match self {
2137 LayoutInfo::BuiltIn(name) => &name,
2138 LayoutInfo::File(name, _) => &name,
2139 LayoutInfo::Url(url) => &url,
2140 LayoutInfo::Stringified(layout) => &layout,
2141 }
2142 }
2143 pub fn is_builtin(&self) -> bool {
2144 match self {
2145 LayoutInfo::BuiltIn(_name) => true,
2146 LayoutInfo::File(_name, _) => false,
2147 LayoutInfo::Url(_url) => false,
2148 LayoutInfo::Stringified(_stringified) => false,
2149 }
2150 }
2151 pub fn from_cli(
2152 layout_dir: &Option<PathBuf>,
2153 maybe_layout_path: &Option<PathBuf>,
2154 cwd: PathBuf,
2155 ) -> Option<Self> {
2156 let layout_path = maybe_layout_path
2163 .clone()
2164 .unwrap_or(PathBuf::from("default"));
2165
2166 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2167 Some(LayoutInfo::Url(layout_path.display().to_string()))
2168 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2169 let layout_dir = cwd;
2170 let file_path = layout_dir.join(layout_path);
2171 Some(LayoutInfo::File(
2172 file_path.display().to_string(),
2174 LayoutMetadata::from(&file_path),
2175 ))
2176 } else {
2177 if let Some(layout_dir) = layout_dir
2181 .as_ref()
2182 .map(|l| l.clone())
2183 .or_else(default_layout_dir)
2184 {
2185 let file_path = layout_dir.join(&layout_path);
2186 if file_path.exists() {
2187 return Some(LayoutInfo::File(
2188 file_path.display().to_string(),
2189 LayoutMetadata::from(&file_path),
2190 ));
2191 }
2192 let file_path_with_ext = file_path.with_extension("kdl");
2193 if file_path_with_ext.exists() {
2194 return Some(LayoutInfo::File(
2195 file_path_with_ext.display().to_string(),
2196 LayoutMetadata::from(&file_path_with_ext),
2197 ));
2198 }
2199 }
2200 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2202 }
2203 }
2204 pub fn from_config(
2205 layout_dir: &Option<PathBuf>,
2206 maybe_layout_path: &Option<PathBuf>,
2207 ) -> Option<Self> {
2208 let layout_path = maybe_layout_path
2215 .clone()
2216 .unwrap_or(PathBuf::from("default"));
2217
2218 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2219 Some(LayoutInfo::Url(layout_path.display().to_string()))
2220 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2221 let Some(layout_dir) = layout_dir
2222 .as_ref()
2223 .map(|l| l.clone())
2224 .or_else(default_layout_dir)
2225 else {
2226 return None;
2227 };
2228 let file_path = layout_dir.join(layout_path);
2229 Some(LayoutInfo::File(
2230 file_path.display().to_string(),
2232 LayoutMetadata::from(&file_path),
2233 ))
2234 } else {
2235 if let Some(layout_dir) = layout_dir
2239 .as_ref()
2240 .map(|l| l.clone())
2241 .or_else(default_layout_dir)
2242 {
2243 let file_path = layout_dir.join(&layout_path);
2244 if file_path.exists() {
2245 return Some(LayoutInfo::File(
2246 file_path.display().to_string(),
2247 LayoutMetadata::from(&file_path),
2248 ));
2249 }
2250 let file_path_with_ext = file_path.with_extension("kdl");
2251 if file_path_with_ext.exists() {
2252 return Some(LayoutInfo::File(
2253 file_path_with_ext.display().to_string(),
2254 LayoutMetadata::from(&file_path_with_ext),
2255 ));
2256 }
2257 }
2258 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2260 }
2261 }
2262}
2263
2264#[allow(clippy::derive_hash_xor_eq)]
2265impl Hash for SessionInfo {
2266 fn hash<H: Hasher>(&self, state: &mut H) {
2267 self.name.hash(state);
2268 }
2269}
2270
2271impl SessionInfo {
2272 pub fn new(name: String) -> Self {
2273 SessionInfo {
2274 name,
2275 ..Default::default()
2276 }
2277 }
2278 pub fn update_tab_info(&mut self, new_tab_info: Vec<TabInfo>) {
2279 self.tabs = new_tab_info;
2280 }
2281 pub fn update_pane_info(&mut self, new_pane_info: PaneManifest) {
2282 self.panes = new_pane_info;
2283 }
2284 pub fn update_connected_clients(&mut self, new_connected_clients: usize) {
2285 self.connected_clients = new_connected_clients;
2286 }
2287 pub fn populate_plugin_list(&mut self, plugins: BTreeMap<u32, RunPlugin>) {
2288 let mut plugin_list = BTreeMap::new();
2290 for (plugin_id, run_plugin) in plugins {
2291 plugin_list.insert(plugin_id, run_plugin.into());
2292 }
2293 self.plugins = plugin_list;
2294 }
2295}
2296
2297#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2299pub struct TabInfo {
2300 pub position: usize,
2302 pub name: String,
2304 pub active: bool,
2306 pub panes_to_hide: usize,
2308 pub is_fullscreen_active: bool,
2310 pub is_sync_panes_active: bool,
2312 pub are_floating_panes_visible: bool,
2313 pub other_focused_clients: Vec<ClientId>,
2314 pub active_swap_layout_name: Option<String>,
2315 pub is_swap_layout_dirty: bool,
2317 pub viewport_rows: usize,
2319 pub viewport_columns: usize,
2321 pub display_area_rows: usize,
2324 pub display_area_columns: usize,
2327 pub selectable_tiled_panes_count: usize,
2329 pub selectable_floating_panes_count: usize,
2331 pub tab_id: usize,
2333 pub has_bell_notification: bool,
2335 pub is_flashing_bell: bool,
2337}
2338
2339#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
2343pub struct PaneManifest {
2344 pub panes: HashMap<usize, Vec<PaneInfo>>, }
2346
2347#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2358pub struct PaneInfo {
2359 pub id: u32,
2361 pub is_plugin: bool,
2364 pub is_focused: bool,
2366 pub is_fullscreen: bool,
2367 pub is_floating: bool,
2369 pub is_suppressed: bool,
2372 pub title: String,
2374 pub exited: bool,
2377 pub exit_status: Option<i32>,
2379 pub is_held: bool,
2382 pub pane_x: usize,
2383 pub pane_content_x: usize,
2384 pub pane_y: usize,
2385 pub pane_content_y: usize,
2386 pub pane_rows: usize,
2387 pub pane_content_rows: usize,
2388 pub pane_columns: usize,
2389 pub pane_content_columns: usize,
2390 pub cursor_coordinates_in_pane: Option<(usize, usize)>, pub terminal_command: Option<String>,
2396 pub plugin_url: Option<String>,
2399 pub is_selectable: bool,
2402 pub index_in_pane_group: BTreeMap<ClientId, usize>,
2405 pub default_fg: Option<String>,
2407 pub default_bg: Option<String>,
2409}
2410
2411#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
2412pub struct PaneListEntry {
2413 #[serde(flatten)]
2414 pub pane_info: PaneInfo,
2415 pub tab_id: usize,
2416 pub tab_position: usize,
2417 pub tab_name: String,
2418 #[serde(skip_serializing_if = "Option::is_none")]
2419 pub pane_command: Option<String>,
2420 #[serde(skip_serializing_if = "Option::is_none")]
2421 pub pane_cwd: Option<String>,
2422}
2423
2424pub type ListPanesResponse = Vec<PaneListEntry>;
2425pub type ListTabsResponse = Vec<TabInfo>;
2426
2427#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
2428pub struct ClientInfo {
2429 pub client_id: ClientId,
2430 pub pane_id: PaneId,
2431 pub running_command: String,
2432 pub is_current_client: bool,
2433}
2434
2435impl ClientInfo {
2436 pub fn new(
2437 client_id: ClientId,
2438 pane_id: PaneId,
2439 running_command: String,
2440 is_current_client: bool,
2441 ) -> Self {
2442 ClientInfo {
2443 client_id,
2444 pane_id,
2445 running_command,
2446 is_current_client,
2447 }
2448 }
2449}
2450
2451#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2452pub struct PaneRenderReport {
2453 pub all_pane_contents: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2454 pub all_pane_contents_with_ansi: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2455}
2456
2457impl PaneRenderReport {
2458 pub fn add_pane_contents(
2459 &mut self,
2460 client_ids: &[ClientId],
2461 pane_id: PaneId,
2462 pane_contents: PaneContents,
2463 ) {
2464 for client_id in client_ids {
2465 let p = self
2466 .all_pane_contents
2467 .entry(*client_id)
2468 .or_insert_with(|| HashMap::new());
2469 p.insert(pane_id, pane_contents.clone());
2470 }
2471 }
2472 pub fn add_pane_contents_with_ansi(
2473 &mut self,
2474 client_ids: &[ClientId],
2475 pane_id: PaneId,
2476 pane_contents: PaneContents,
2477 ) {
2478 for client_id in client_ids {
2479 let p = self
2480 .all_pane_contents_with_ansi
2481 .entry(*client_id)
2482 .or_insert_with(|| HashMap::new());
2483 p.insert(pane_id, pane_contents.clone());
2484 }
2485 }
2486}
2487
2488#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2489pub struct PaneContents {
2490 pub lines_above_viewport: Vec<String>,
2494 pub lines_below_viewport: Vec<String>,
2495 pub viewport: Vec<String>,
2496 pub selected_text: Option<SelectedText>,
2497 pub cursor: Option<(usize, usize)>,
2498}
2499
2500fn extract_text_by_columns(line: &str, start_col: usize, end_col: usize) -> String {
2502 let mut current_col = 0;
2503 let mut result = String::new();
2504 let mut capturing = false;
2505
2506 for ch in line.chars() {
2507 let char_width = ch.width().unwrap_or(0);
2508
2509 if current_col >= start_col && !capturing {
2511 capturing = true;
2512 }
2513
2514 if current_col >= end_col {
2516 break;
2517 }
2518
2519 if capturing {
2521 result.push(ch);
2522 }
2523
2524 current_col += char_width;
2525 }
2526
2527 result
2528}
2529
2530fn extract_text_from_column(line: &str, start_col: usize) -> String {
2532 let mut current_col = 0;
2533 let mut result = String::new();
2534 let mut capturing = false;
2535
2536 for ch in line.chars() {
2537 let char_width = ch.width().unwrap_or(0);
2538
2539 if current_col >= start_col {
2540 capturing = true;
2541 }
2542
2543 if capturing {
2544 result.push(ch);
2545 }
2546
2547 current_col += char_width;
2548 }
2549
2550 result
2551}
2552
2553fn extract_text_to_column(line: &str, end_col: usize) -> String {
2555 let mut current_col = 0;
2556 let mut result = String::new();
2557
2558 for ch in line.chars() {
2559 let char_width = ch.width().unwrap_or(0);
2560
2561 if current_col >= end_col {
2562 break;
2563 }
2564
2565 result.push(ch);
2566 current_col += char_width;
2567 }
2568
2569 result
2570}
2571
2572impl PaneContents {
2573 pub fn new(viewport: Vec<String>, selection_start: Position, selection_end: Position) -> Self {
2574 PaneContents {
2575 viewport,
2576 selected_text: SelectedText::from_positions(selection_start, selection_end),
2577 ..Default::default()
2578 }
2579 }
2580 pub fn new_with_scrollback(
2581 viewport: Vec<String>,
2582 selection_start: Position,
2583 selection_end: Position,
2584 lines_above_viewport: Vec<String>,
2585 lines_below_viewport: Vec<String>,
2586 ) -> Self {
2587 PaneContents {
2588 viewport,
2589 selected_text: SelectedText::from_positions(selection_start, selection_end),
2590 lines_above_viewport,
2591 lines_below_viewport,
2592 cursor: None,
2593 }
2594 }
2595
2596 pub fn get_selected_text(&self) -> Option<String> {
2599 let selected_text = self.selected_text?;
2600
2601 let start_line = selected_text.start.line() as usize;
2602 let start_col = selected_text.start.column();
2603 let end_line = selected_text.end.line() as usize;
2604 let end_col = selected_text.end.column();
2605
2606 if start_line >= self.viewport.len() || end_line >= self.viewport.len() {
2608 return None;
2609 }
2610
2611 if start_line == end_line {
2612 let line = &self.viewport[start_line];
2614 Some(extract_text_by_columns(line, start_col, end_col))
2615 } else {
2616 let mut result = String::new();
2618
2619 let first_line = &self.viewport[start_line];
2621 result.push_str(&extract_text_from_column(first_line, start_col));
2622 result.push('\n');
2623
2624 for i in (start_line + 1)..end_line {
2626 result.push_str(&self.viewport[i]);
2627 result.push('\n');
2628 }
2629
2630 let last_line = &self.viewport[end_line];
2632 result.push_str(&extract_text_to_column(last_line, end_col));
2633
2634 Some(result)
2635 }
2636 }
2637}
2638
2639#[derive(Debug, Clone, Serialize, Deserialize)]
2640pub enum PaneScrollbackResponse {
2641 Ok(PaneContents),
2642 Err(String),
2643}
2644
2645#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2646pub enum GetPanePidResponse {
2647 Ok(i32),
2648 Err(String),
2649}
2650
2651#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2652pub enum GetPaneRunningCommandResponse {
2653 Ok(Vec<String>),
2654 Err(String),
2655}
2656
2657#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
2658pub struct SessionListSnapshot {
2659 pub live_sessions: Vec<SessionInfo>,
2660 pub resurrectable_sessions: Vec<(String, Duration)>,
2661}
2662
2663#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2664pub enum GetSessionListResponse {
2665 Ok(SessionListSnapshot),
2666 Err(String),
2667}
2668
2669#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2670pub enum KillSessionsResponse {
2671 Ok,
2672 Err(String),
2673}
2674
2675#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2676pub enum DeleteDeadSessionResponse {
2677 Ok,
2678 Err(String),
2679}
2680
2681#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2682pub enum DeleteAllDeadSessionsResponse {
2683 Ok,
2684 Err(String),
2685}
2686
2687#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2688pub enum GetPaneCwdResponse {
2689 Ok(PathBuf),
2690 Err(String),
2691}
2692
2693#[derive(Debug, Clone, PartialEq)]
2694pub enum GetFocusedPaneInfoResponse {
2695 Ok { tab_index: usize, pane_id: PaneId },
2696 Err(String),
2697}
2698
2699#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2700pub enum SaveLayoutResponse {
2701 Ok(()),
2702 Err(String),
2703}
2704
2705#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2706pub enum DeleteLayoutResponse {
2707 Ok(()),
2708 Err(String),
2709}
2710
2711#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2712pub enum RenameLayoutResponse {
2713 Ok(()),
2714 Err(String),
2715}
2716
2717#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2718pub enum EditLayoutResponse {
2719 Ok(()),
2720 Err(String),
2721}
2722
2723#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2724pub struct SelectedText {
2725 pub start: Position,
2726 pub end: Position,
2727}
2728
2729impl SelectedText {
2730 pub fn new(start: Position, end: Position) -> Self {
2731 let (normalized_start, normalized_end) = if start <= end {
2733 (start, end)
2734 } else {
2735 (end, start)
2736 };
2737
2738 let normalized_start = Position::new(
2741 normalized_start.line().max(0) as i32,
2742 normalized_start.column() as u16,
2743 );
2744 let normalized_end = Position::new(
2745 normalized_end.line().max(0) as i32,
2746 normalized_end.column() as u16,
2747 );
2748
2749 SelectedText {
2750 start: normalized_start,
2751 end: normalized_end,
2752 }
2753 }
2754
2755 pub fn from_positions(start: Position, end: Position) -> Option<Self> {
2756 if start == end {
2757 None
2758 } else {
2759 Some(Self::new(start, end))
2760 }
2761 }
2762}
2763
2764#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2765pub struct PluginIds {
2766 pub plugin_id: u32,
2767 pub zellij_pid: u32,
2768 pub initial_cwd: PathBuf,
2769 pub client_id: ClientId,
2770}
2771
2772#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
2774pub struct PluginTag(String);
2775
2776impl PluginTag {
2777 pub fn new(url: impl Into<String>) -> Self {
2778 PluginTag(url.into())
2779 }
2780}
2781
2782impl From<PluginTag> for String {
2783 fn from(tag: PluginTag) -> Self {
2784 tag.0
2785 }
2786}
2787
2788impl fmt::Display for PluginTag {
2789 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2790 write!(f, "{}", self.0)
2791 }
2792}
2793
2794#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2795pub struct PluginCapabilities {
2796 pub arrow_fonts: bool,
2797}
2798
2799impl Default for PluginCapabilities {
2800 fn default() -> PluginCapabilities {
2801 PluginCapabilities { arrow_fonts: true }
2802 }
2803}
2804
2805#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2807pub enum CopyDestination {
2808 Command,
2809 Primary,
2810 System,
2811}
2812
2813#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2814pub enum PermissionStatus {
2815 Granted,
2816 Denied,
2817}
2818
2819#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
2820pub struct FileToOpen {
2821 pub path: PathBuf,
2822 pub line_number: Option<usize>,
2823 pub cwd: Option<PathBuf>,
2824}
2825
2826impl FileToOpen {
2827 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2828 FileToOpen {
2829 path: path.as_ref().to_path_buf(),
2830 ..Default::default()
2831 }
2832 }
2833 pub fn with_line_number(mut self, line_number: usize) -> Self {
2834 self.line_number = Some(line_number);
2835 self
2836 }
2837 pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
2838 self.cwd = Some(cwd);
2839 self
2840 }
2841}
2842
2843#[derive(Debug, Default, Clone)]
2844pub struct CommandToRun {
2845 pub path: PathBuf,
2846 pub args: Vec<String>,
2847 pub cwd: Option<PathBuf>,
2848}
2849
2850impl CommandToRun {
2851 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2852 CommandToRun {
2853 path: path.as_ref().to_path_buf(),
2854 ..Default::default()
2855 }
2856 }
2857 pub fn new_with_args<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) -> Self {
2858 CommandToRun {
2859 path: path.as_ref().to_path_buf(),
2860 args: args.into_iter().map(|a| a.as_ref().to_owned()).collect(),
2861 ..Default::default()
2862 }
2863 }
2864}
2865
2866#[derive(Debug, Default, Clone)]
2867pub struct MessageToPlugin {
2868 pub plugin_url: Option<String>,
2869 pub destination_plugin_id: Option<u32>,
2870 pub plugin_config: BTreeMap<String, String>,
2871 pub message_name: String,
2872 pub message_payload: Option<String>,
2873 pub message_args: BTreeMap<String, String>,
2874 pub new_plugin_args: Option<NewPluginArgs>,
2877 pub floating_pane_coordinates: Option<FloatingPaneCoordinates>,
2878}
2879
2880#[derive(Debug, Default, Clone)]
2881pub struct NewPluginArgs {
2882 pub should_float: Option<bool>,
2883 pub pane_id_to_replace: Option<PaneId>,
2884 pub pane_title: Option<String>,
2885 pub cwd: Option<PathBuf>,
2886 pub skip_cache: bool,
2887 pub should_focus: Option<bool>,
2888}
2889
2890#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
2891pub enum PaneId {
2892 Terminal(u32),
2893 Plugin(u32),
2894}
2895
2896impl Default for PaneId {
2897 fn default() -> Self {
2898 PaneId::Terminal(0)
2899 }
2900}
2901
2902impl FromStr for PaneId {
2903 type Err = Box<dyn std::error::Error>;
2904 fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
2905 if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
2906 u32::from_str_radix(terminal_stringified_pane_id, 10)
2907 .map(|id| PaneId::Terminal(id))
2908 .map_err(|e| e.into())
2909 } else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
2910 u32::from_str_radix(plugin_pane_id, 10)
2911 .map(|id| PaneId::Plugin(id))
2912 .map_err(|e| e.into())
2913 } else {
2914 u32::from_str_radix(&stringified_pane_id, 10)
2915 .map(|id| PaneId::Terminal(id))
2916 .map_err(|e| e.into())
2917 }
2918 }
2919}
2920
2921impl std::fmt::Display for PaneId {
2922 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2923 match self {
2924 PaneId::Terminal(id) => write!(f, "terminal_{}", id),
2925 PaneId::Plugin(id) => write!(f, "plugin_{}", id),
2926 }
2927 }
2928}
2929
2930impl MessageToPlugin {
2931 pub fn new(message_name: impl Into<String>) -> Self {
2932 MessageToPlugin {
2933 message_name: message_name.into(),
2934 ..Default::default()
2935 }
2936 }
2937 pub fn with_plugin_url(mut self, url: impl Into<String>) -> Self {
2938 self.plugin_url = Some(url.into());
2939 self
2940 }
2941 pub fn with_destination_plugin_id(mut self, destination_plugin_id: u32) -> Self {
2942 self.destination_plugin_id = Some(destination_plugin_id);
2943 self
2944 }
2945 pub fn with_plugin_config(mut self, plugin_config: BTreeMap<String, String>) -> Self {
2946 self.plugin_config = plugin_config;
2947 self
2948 }
2949 pub fn with_payload(mut self, payload: impl Into<String>) -> Self {
2950 self.message_payload = Some(payload.into());
2951 self
2952 }
2953 pub fn with_args(mut self, args: BTreeMap<String, String>) -> Self {
2954 self.message_args = args;
2955 self
2956 }
2957 pub fn with_floating_pane_coordinates(
2958 mut self,
2959 floating_pane_coordinates: FloatingPaneCoordinates,
2960 ) -> Self {
2961 self.floating_pane_coordinates = Some(floating_pane_coordinates);
2962 self
2963 }
2964 pub fn new_plugin_instance_should_float(mut self, should_float: bool) -> Self {
2965 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2966 new_plugin_args.should_float = Some(should_float);
2967 self
2968 }
2969 pub fn new_plugin_instance_should_replace_pane(mut self, pane_id: PaneId) -> Self {
2970 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2971 new_plugin_args.pane_id_to_replace = Some(pane_id);
2972 self
2973 }
2974 pub fn new_plugin_instance_should_have_pane_title(
2975 mut self,
2976 pane_title: impl Into<String>,
2977 ) -> Self {
2978 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2979 new_plugin_args.pane_title = Some(pane_title.into());
2980 self
2981 }
2982 pub fn new_plugin_instance_should_have_cwd(mut self, cwd: PathBuf) -> Self {
2983 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2984 new_plugin_args.cwd = Some(cwd);
2985 self
2986 }
2987 pub fn new_plugin_instance_should_skip_cache(mut self) -> Self {
2988 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2989 new_plugin_args.skip_cache = true;
2990 self
2991 }
2992 pub fn new_plugin_instance_should_be_focused(mut self) -> Self {
2993 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2994 new_plugin_args.should_focus = Some(true);
2995 self
2996 }
2997 pub fn has_cwd(&self) -> bool {
2998 self.new_plugin_args
2999 .as_ref()
3000 .map(|n| n.cwd.is_some())
3001 .unwrap_or(false)
3002 }
3003}
3004
3005#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
3006pub struct ConnectToSession {
3007 pub name: Option<String>,
3008 pub tab_position: Option<usize>,
3009 pub pane_id: Option<(u32, bool)>, pub layout: Option<LayoutInfo>,
3011 pub cwd: Option<PathBuf>,
3012}
3013
3014impl ConnectToSession {
3015 pub fn apply_layout_dir(&mut self, layout_dir: &PathBuf) {
3016 if let Some(LayoutInfo::File(file_path, _layout_metadata)) = self.layout.as_mut() {
3017 *file_path = Path::join(layout_dir, &file_path)
3018 .to_string_lossy()
3019 .to_string();
3020 }
3021 }
3022}
3023
3024#[derive(Debug, Default, Clone)]
3025pub struct PluginMessage {
3026 pub name: String,
3027 pub payload: String,
3028 pub worker_name: Option<String>,
3029}
3030
3031impl PluginMessage {
3032 pub fn new_to_worker(worker_name: &str, message: &str, payload: &str) -> Self {
3033 PluginMessage {
3034 name: message.to_owned(),
3035 payload: payload.to_owned(),
3036 worker_name: Some(worker_name.to_owned()),
3037 }
3038 }
3039 pub fn new_to_plugin(message: &str, payload: &str) -> Self {
3040 PluginMessage {
3041 name: message.to_owned(),
3042 payload: payload.to_owned(),
3043 worker_name: None,
3044 }
3045 }
3046}
3047
3048#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3049pub enum HttpVerb {
3050 Get,
3051 Post,
3052 Put,
3053 Delete,
3054}
3055
3056#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3057pub enum PipeSource {
3058 Cli(String), Plugin(u32), Keybind, }
3062
3063#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3064pub struct PipeMessage {
3065 pub source: PipeSource,
3066 pub name: String,
3067 pub payload: Option<String>,
3068 pub args: BTreeMap<String, String>,
3069 pub is_private: bool,
3070}
3071
3072impl PipeMessage {
3073 pub fn new(
3074 source: PipeSource,
3075 name: impl Into<String>,
3076 payload: &Option<String>,
3077 args: &Option<BTreeMap<String, String>>,
3078 is_private: bool,
3079 ) -> Self {
3080 PipeMessage {
3081 source,
3082 name: name.into(),
3083 payload: payload.clone(),
3084 args: args.clone().unwrap_or_else(|| Default::default()),
3085 is_private,
3086 }
3087 }
3088}
3089
3090#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
3091pub struct FloatingPaneCoordinates {
3092 pub x: Option<PercentOrFixed>,
3093 pub y: Option<PercentOrFixed>,
3094 pub width: Option<PercentOrFixed>,
3095 pub height: Option<PercentOrFixed>,
3096 pub pinned: Option<bool>,
3097 pub borderless: Option<bool>,
3098}
3099
3100impl FloatingPaneCoordinates {
3101 pub fn new(
3102 x: Option<String>,
3103 y: Option<String>,
3104 width: Option<String>,
3105 height: Option<String>,
3106 pinned: Option<bool>,
3107 borderless: Option<bool>,
3108 ) -> Option<Self> {
3109 let x = x.and_then(|x| PercentOrFixed::from_str(&x).ok());
3111 let y = y.and_then(|y| PercentOrFixed::from_str(&y).ok());
3112
3113 let width = width.and_then(|w| {
3115 PercentOrFixed::from_str(&w)
3116 .ok()
3117 .and_then(|size| match size {
3118 PercentOrFixed::Percent(0) => None,
3119 PercentOrFixed::Fixed(0) => None,
3120 _ => Some(size),
3121 })
3122 });
3123 let height = height.and_then(|h| {
3124 PercentOrFixed::from_str(&h)
3125 .ok()
3126 .and_then(|size| match size {
3127 PercentOrFixed::Percent(0) => None,
3128 PercentOrFixed::Fixed(0) => None,
3129 _ => Some(size),
3130 })
3131 });
3132
3133 if x.is_none()
3134 && y.is_none()
3135 && width.is_none()
3136 && height.is_none()
3137 && pinned.is_none()
3138 && borderless.is_none()
3139 {
3140 None
3141 } else {
3142 Some(FloatingPaneCoordinates {
3143 x,
3144 y,
3145 width,
3146 height,
3147 pinned,
3148 borderless,
3149 })
3150 }
3151 }
3152 pub fn with_x_fixed(mut self, x: usize) -> Self {
3153 self.x = Some(PercentOrFixed::Fixed(x));
3154 self
3155 }
3156 pub fn with_x_percent(mut self, x: usize) -> Self {
3157 if x > 100 {
3158 eprintln!("x must be between 0 and 100");
3159 return self;
3160 }
3161 self.x = Some(PercentOrFixed::Percent(x));
3162 self
3163 }
3164 pub fn with_y_fixed(mut self, y: usize) -> Self {
3165 self.y = Some(PercentOrFixed::Fixed(y));
3166 self
3167 }
3168 pub fn with_y_percent(mut self, y: usize) -> Self {
3169 if y > 100 {
3170 eprintln!("y must be between 0 and 100");
3171 return self;
3172 }
3173 self.y = Some(PercentOrFixed::Percent(y));
3174 self
3175 }
3176 pub fn with_width_fixed(mut self, width: usize) -> Self {
3177 self.width = Some(PercentOrFixed::Fixed(width));
3178 self
3179 }
3180 pub fn with_width_percent(mut self, width: usize) -> Self {
3181 if width > 100 {
3182 eprintln!("width must be between 0 and 100");
3183 return self;
3184 }
3185 self.width = Some(PercentOrFixed::Percent(width));
3186 self
3187 }
3188 pub fn with_height_fixed(mut self, height: usize) -> Self {
3189 self.height = Some(PercentOrFixed::Fixed(height));
3190 self
3191 }
3192 pub fn with_height_percent(mut self, height: usize) -> Self {
3193 if height > 100 {
3194 eprintln!("height must be between 0 and 100");
3195 return self;
3196 }
3197 self.height = Some(PercentOrFixed::Percent(height));
3198 self
3199 }
3200}
3201
3202impl From<PaneGeom> for FloatingPaneCoordinates {
3203 fn from(pane_geom: PaneGeom) -> Self {
3204 FloatingPaneCoordinates {
3205 x: Some(PercentOrFixed::Fixed(pane_geom.x)),
3206 y: Some(PercentOrFixed::Fixed(pane_geom.y)),
3207 width: Some(PercentOrFixed::Fixed(pane_geom.cols.as_usize())),
3208 height: Some(PercentOrFixed::Fixed(pane_geom.rows.as_usize())),
3209 pinned: Some(pane_geom.is_pinned),
3210 borderless: None,
3211 }
3212 }
3213}
3214
3215#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3216pub struct OriginatingPlugin {
3217 pub plugin_id: u32,
3218 pub client_id: ClientId,
3219 pub context: Context,
3220}
3221
3222impl OriginatingPlugin {
3223 pub fn new(plugin_id: u32, client_id: ClientId, context: Context) -> Self {
3224 OriginatingPlugin {
3225 plugin_id,
3226 client_id,
3227 context,
3228 }
3229 }
3230}
3231
3232#[derive(ValueEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
3233pub enum WebSharing {
3234 #[serde(alias = "on")]
3235 On,
3236 #[serde(alias = "off")]
3237 Off,
3238 #[serde(alias = "disabled")]
3239 Disabled,
3240}
3241
3242impl Default for WebSharing {
3243 fn default() -> Self {
3244 Self::Off
3245 }
3246}
3247
3248impl WebSharing {
3249 pub fn is_on(&self) -> bool {
3250 match self {
3251 WebSharing::On => true,
3252 _ => false,
3253 }
3254 }
3255 pub fn web_clients_allowed(&self) -> bool {
3256 match self {
3257 WebSharing::On => true,
3258 _ => false,
3259 }
3260 }
3261 pub fn sharing_is_disabled(&self) -> bool {
3262 match self {
3263 WebSharing::Disabled => true,
3264 _ => false,
3265 }
3266 }
3267 pub fn set_sharing(&mut self) -> bool {
3268 match self {
3270 WebSharing::On => true,
3271 WebSharing::Off => {
3272 *self = WebSharing::On;
3273 true
3274 },
3275 WebSharing::Disabled => false,
3276 }
3277 }
3278 pub fn set_not_sharing(&mut self) -> bool {
3279 match self {
3281 WebSharing::On => {
3282 *self = WebSharing::Off;
3283 true
3284 },
3285 WebSharing::Off => true,
3286 WebSharing::Disabled => false,
3287 }
3288 }
3289}
3290
3291impl FromStr for WebSharing {
3292 type Err = String;
3293 fn from_str(s: &str) -> Result<Self, Self::Err> {
3294 match s {
3295 "On" | "on" => Ok(Self::On),
3296 "Off" | "off" => Ok(Self::Off),
3297 "Disabled" | "disabled" => Ok(Self::Disabled),
3298 _ => Err(format!("No such option: {}", s)),
3299 }
3300 }
3301}
3302
3303#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3304pub enum NewPanePlacement {
3305 NoPreference {
3306 borderless: Option<bool>,
3307 },
3308 Tiled {
3309 direction: Option<Direction>,
3310 borderless: Option<bool>,
3311 },
3312 Floating(Option<FloatingPaneCoordinates>),
3313 InPlace {
3314 pane_id_to_replace: Option<PaneId>,
3315 close_replaced_pane: bool,
3316 borderless: Option<bool>,
3317 },
3318 Stacked {
3319 pane_id_to_stack_under: Option<PaneId>,
3320 borderless: Option<bool>,
3321 },
3322}
3323
3324impl Default for NewPanePlacement {
3325 fn default() -> Self {
3326 NewPanePlacement::NoPreference { borderless: None }
3327 }
3328}
3329
3330impl NewPanePlacement {
3331 pub fn with_floating_pane_coordinates(
3332 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3333 ) -> Self {
3334 NewPanePlacement::Floating(floating_pane_coordinates)
3335 }
3336 pub fn with_should_be_in_place(
3337 self,
3338 should_be_in_place: bool,
3339 close_replaced_pane: bool,
3340 ) -> Self {
3341 if should_be_in_place {
3342 NewPanePlacement::InPlace {
3343 pane_id_to_replace: None,
3344 close_replaced_pane,
3345 borderless: None,
3346 }
3347 } else {
3348 self
3349 }
3350 }
3351 pub fn with_pane_id_to_replace(
3352 pane_id_to_replace: Option<PaneId>,
3353 close_replaced_pane: bool,
3354 ) -> Self {
3355 NewPanePlacement::InPlace {
3356 pane_id_to_replace,
3357 close_replaced_pane,
3358 borderless: None,
3359 }
3360 }
3361 pub fn should_float(&self) -> Option<bool> {
3362 match self {
3363 NewPanePlacement::Floating(_) => Some(true),
3364 NewPanePlacement::Tiled { .. } => Some(false),
3365 _ => None,
3366 }
3367 }
3368 pub fn floating_pane_coordinates(&self) -> Option<FloatingPaneCoordinates> {
3369 match self {
3370 NewPanePlacement::Floating(floating_pane_coordinates) => {
3371 floating_pane_coordinates.clone()
3372 },
3373 _ => None,
3374 }
3375 }
3376 pub fn should_stack(&self) -> bool {
3377 match self {
3378 NewPanePlacement::Stacked { .. } => true,
3379 _ => false,
3380 }
3381 }
3382 pub fn id_of_stack_root(&self) -> Option<PaneId> {
3383 match self {
3384 NewPanePlacement::Stacked {
3385 pane_id_to_stack_under,
3386 ..
3387 } => *pane_id_to_stack_under,
3388 _ => None,
3389 }
3390 }
3391 pub fn get_borderless(&self) -> Option<bool> {
3392 match self {
3393 NewPanePlacement::NoPreference { borderless } => *borderless,
3394 NewPanePlacement::Tiled { borderless, .. } => *borderless,
3395 NewPanePlacement::Floating(coords) => coords.as_ref().and_then(|c| c.borderless),
3396 NewPanePlacement::InPlace { borderless, .. } => *borderless,
3397 NewPanePlacement::Stacked { borderless, .. } => *borderless,
3398 }
3399 }
3400}
3401
3402type Context = BTreeMap<String, String>;
3403
3404#[derive(Debug, Clone, EnumDiscriminants, Display)]
3405#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
3406#[strum_discriminants(name(CommandType))]
3407pub enum PluginCommand {
3408 Subscribe(HashSet<EventType>),
3409 Unsubscribe(HashSet<EventType>),
3410 SetSelectable(bool),
3411 ShowCursor(Option<(usize, usize)>),
3412 GetPluginIds,
3413 GetZellijVersion,
3414 OpenFile(FileToOpen, Context),
3415 OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3416 OpenTerminal(FileToOpen), OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), OpenCommandPane(CommandToRun, Context),
3419 OpenCommandPaneFloating(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3420 SwitchTabTo(u32), SetTimeout(f64), ExecCmd(Vec<String>),
3423 PostMessageTo(PluginMessage),
3424 PostMessageToPlugin(PluginMessage),
3425 HideSelf,
3426 ShowSelf(bool), SwitchToMode(InputMode),
3428 NewTabsWithLayout(String), NewTab {
3430 name: Option<String>,
3431 cwd: Option<String>,
3432 },
3433 NewTabUnfocused {
3434 name: Option<String>,
3435 cwd: Option<String>,
3436 },
3437 NewTiledPaneInTab {
3438 tab_position: usize,
3439 },
3440 ToggleFloatingPanes {
3441 tab_id: Option<u64>,
3442 },
3443 NewPane,
3444 GoToNextTab,
3445 GoToPreviousTab,
3446 Resize(Resize),
3447 ResizeWithDirection(ResizeStrategy),
3448 FocusNextPane,
3449 FocusPreviousPane,
3450 FocusLastPane,
3451 MoveFocus(Direction),
3452 MoveFocusOrTab(Direction),
3453 Detach,
3454 EditScrollback,
3455 Write(Vec<u8>), WriteChars(String),
3457 ToggleTab,
3458 MovePane,
3459 MovePaneWithDirection(Direction),
3460 ClearScreen,
3461 ScrollUp,
3462 ScrollDown,
3463 ScrollToTop,
3464 ScrollToBottom,
3465 PageScrollUp,
3466 PageScrollDown,
3467 ToggleFocusFullscreen,
3468 ToggleFocusNoUiFullscreen,
3469 TogglePaneFrames,
3470 SetPaneFrameStyle(PaneFrameStyle),
3471 TogglePaneEmbedOrEject,
3472 UndoRenamePane,
3473 CloseFocus,
3474 ToggleActiveTabSync,
3475 CloseFocusedTab,
3476 UndoRenameTab,
3477 QuitZellij,
3478 PreviousSwapLayout,
3479 NextSwapLayout,
3480 GoToTabName(String),
3481 FocusOrCreateTab(String),
3482 GoToTab(u32), StartOrReloadPlugin(String), CloseTerminalPane(u32), ClosePluginPane(u32), FocusTerminalPane(u32, bool, bool), FocusPluginPane(u32, bool, bool), RenameTerminalPane(u32, String), RenamePluginPane(u32, String), RenameTab(u32, String), ReportPanic(String), RequestPluginPermissions(Vec<PermissionType>),
3493 SwitchSession(ConnectToSession),
3494 DeleteDeadSession(String), DeleteAllDeadSessions, OpenTerminalInPlace(FileToOpen), OpenFileInPlace(FileToOpen, Context),
3498 OpenCommandPaneInPlace(CommandToRun, Context),
3499 RunCommand(
3500 Vec<String>, BTreeMap<String, String>, PathBuf, BTreeMap<String, String>, ),
3505 WebRequest(
3506 String, HttpVerb,
3508 BTreeMap<String, String>, Vec<u8>, BTreeMap<String, String>, ),
3512 RenameSession(String), UnblockCliPipeInput(String), BlockCliPipeInput(String), CliPipeOutput(String, String), MessageToPlugin(MessageToPlugin),
3517 DisconnectOtherClients,
3518 KillSessions(Vec<String>), ScanHostFolder(PathBuf), WatchFilesystem,
3521 DumpSessionLayout {
3522 tab_index: Option<usize>,
3523 },
3524 CloseSelf,
3525 NewTabsWithLayoutInfo(LayoutInfo),
3526 Reconfigure(String, bool), HidePaneWithId(PaneId),
3529 ShowPaneWithId(PaneId, bool, bool), OpenCommandPaneBackground(CommandToRun, Context),
3531 RerunCommandPane(u32), ResizePaneIdWithDirection(ResizeStrategy, PaneId),
3533 EditScrollbackForPaneWithId(PaneId),
3534 GetPaneScrollback {
3535 pane_id: PaneId,
3536 get_full_scrollback: bool,
3537 },
3538 WriteToPaneId(Vec<u8>, PaneId),
3539 WriteCharsToPaneId(String, PaneId),
3540 SendSigintToPaneId(PaneId),
3541 SendSigkillToPaneId(PaneId),
3542 GetPanePid {
3543 pane_id: PaneId,
3544 },
3545 GetPaneRunningCommand {
3546 pane_id: PaneId,
3547 },
3548 GetPaneCwd {
3549 pane_id: PaneId,
3550 },
3551 MovePaneWithPaneId(PaneId),
3552 MovePaneWithPaneIdInDirection(PaneId, Direction),
3553 ClearScreenForPaneId(PaneId),
3554 ScrollUpInPaneId(PaneId),
3555 ScrollDownInPaneId(PaneId),
3556 ScrollToTopInPaneId(PaneId),
3557 ScrollToBottomInPaneId(PaneId),
3558 PageScrollUpInPaneId(PaneId),
3559 PageScrollDownInPaneId(PaneId),
3560 TogglePaneIdFullscreen(PaneId),
3561 TogglePaneEmbedOrEjectForPaneId(PaneId),
3562 CloseTabWithIndex(usize), BreakPanesToNewTab(Vec<PaneId>, Option<String>, bool), BreakPanesToTabWithIndex(Vec<PaneId>, usize, bool), SwitchTabToId(u64), GoToTabWithId(u64), CloseTabWithId(u64), RenameTabWithId(u64, String), BreakPanesToTabWithId(Vec<PaneId>, u64, bool), ReloadPlugin(u32), LoadNewPlugin {
3577 url: String,
3578 config: BTreeMap<String, String>,
3579 load_in_background: bool,
3580 skip_plugin_cache: bool,
3581 },
3582 RebindKeys {
3583 keys_to_rebind: Vec<(InputMode, KeyWithModifier, Vec<Action>)>,
3584 keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
3585 write_config_to_disk: bool,
3586 },
3587 ListClients,
3588 ChangeHostFolder(PathBuf),
3589 SetFloatingPanePinned(PaneId, bool), StackPanes(Vec<PaneId>),
3591 ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
3592 TogglePaneBorderless(PaneId),
3593 SetPaneBorderless(PaneId, bool),
3594 OpenCommandPaneNearPlugin(CommandToRun, Context),
3595 OpenTerminalNearPlugin(FileToOpen),
3596 OpenTerminalFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>),
3597 OpenTerminalInPlaceOfPlugin(FileToOpen, bool), OpenCommandPaneFloatingNearPlugin(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3599 OpenCommandPaneInPlaceOfPlugin(CommandToRun, bool, Context), OpenFileNearPlugin(FileToOpen, Context),
3602 OpenFileFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3603 StartWebServer,
3604 StopWebServer,
3605 ShareCurrentSession,
3606 StopSharingCurrentSession,
3607 OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
3613 FloatMultiplePanes(Vec<PaneId>),
3614 EmbedMultiplePanes(Vec<PaneId>),
3615 QueryWebServerStatus,
3616 SetSelfMouseSelectionSupport(bool),
3617 GenerateWebLoginToken(Option<String>, bool), RevokeWebLoginToken(String), ListWebLoginTokens,
3620 RevokeAllWebLoginTokens,
3621 RenameWebLoginToken(String, String), InterceptKeyPresses,
3623 ClearKeyPressesIntercepts,
3624 ReplacePaneWithExistingPane(PaneId, PaneId, bool), RunAction(Action, BTreeMap<String, String>),
3627 CopyToClipboard(String), OverrideLayout(
3629 LayoutInfo,
3630 bool, bool, bool, BTreeMap<String, String>, ),
3635 SaveLayout {
3636 layout_name: String,
3637 layout_kdl: String,
3638 overwrite: bool,
3639 },
3640 DeleteLayout {
3641 layout_name: String,
3642 },
3643 RenameLayout {
3644 old_layout_name: String,
3645 new_layout_name: String,
3646 },
3647 EditLayout {
3648 layout_name: String,
3649 context: Context,
3650 },
3651 GenerateRandomName,
3652 DumpLayout(String),
3653 ParseLayout(String), GetLayoutDir,
3655 GetFocusedPaneInfo,
3656 SaveSession,
3657 CurrentSessionLastSavedTime,
3658 GetPaneInfo(PaneId),
3659 GetTabInfo(usize), GetSessionEnvironmentVariables,
3661 OpenCommandPaneInNewTab(CommandToRun, Context),
3662 OpenPluginPaneInNewTab {
3663 plugin_url: String,
3664 configuration: BTreeMap<String, String>,
3665 context: Context,
3666 },
3667 OpenEditorPaneInNewTab(FileToOpen, Context),
3668 OpenCommandPaneInPlaceOfPaneId(PaneId, CommandToRun, bool, Context), OpenTerminalPaneInPlaceOfPaneId(PaneId, FileToOpen, bool),
3670 OpenEditPaneInPlaceOfPaneId(PaneId, FileToOpen, bool, Context),
3671 HideFloatingPanes {
3672 tab_id: Option<usize>,
3673 },
3674 ShowFloatingPanes {
3675 tab_id: Option<usize>,
3676 },
3677 SetPaneColor(PaneId, Option<String>, Option<String>), SetPaneRegexHighlights(PaneId, Vec<RegexHighlight>),
3679 ClearPaneHighlights(PaneId),
3680 OpenPluginPaneFloating {
3681 plugin_url: String,
3682 configuration: BTreeMap<String, String>,
3683 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3684 context: BTreeMap<String, String>,
3685 },
3686 ListWindowsVolumes,
3687 GetSessionList,
3688 KillSessionsAndReply(Vec<String>), DeleteDeadSessionAndReply(String), DeleteAllDeadSessionsAndReply, SetSoftKeyboard(bool),
3692 FocusHostSession,
3693}
3694
3695#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3697pub struct OpenPaneInNewTabResponse {
3698 pub tab_id: Option<usize>,
3699 pub pane_id: Option<PaneId>,
3700}
3701
3702pub type NewTabResponse = Option<usize>;
3704pub type NewTabUnfocusedResponse = Option<usize>;
3705pub type NewTabsResponse = Vec<usize>;
3706pub type FocusOrCreateTabResponse = Option<usize>;
3707pub type BreakPanesToNewTabResponse = Option<usize>;
3708pub type BreakPanesToTabWithIndexResponse = Option<usize>;
3709pub type BreakPanesToTabWithIdResponse = Option<usize>;
3710
3711pub type OpenFileResponse = Option<PaneId>;
3713pub type OpenFileFloatingResponse = Option<PaneId>;
3714pub type OpenFileInPlaceResponse = Option<PaneId>;
3715pub type OpenFileNearPluginResponse = Option<PaneId>;
3716pub type OpenFileFloatingNearPluginResponse = Option<PaneId>;
3717pub type OpenFileInPlaceOfPluginResponse = Option<PaneId>;
3718
3719pub type OpenTerminalResponse = Option<PaneId>;
3720pub type OpenTerminalFloatingResponse = Option<PaneId>;
3721pub type OpenTerminalInPlaceResponse = Option<PaneId>;
3722pub type OpenTerminalNearPluginResponse = Option<PaneId>;
3723pub type OpenTerminalFloatingNearPluginResponse = Option<PaneId>;
3724pub type OpenTerminalInPlaceOfPluginResponse = Option<PaneId>;
3725pub type NewTiledPaneInTabResponse = Option<PaneId>;
3726
3727pub type OpenCommandPaneResponse = Option<PaneId>;
3728pub type OpenCommandPaneFloatingResponse = Option<PaneId>;
3729pub type OpenCommandPaneInPlaceResponse = Option<PaneId>;
3730pub type OpenCommandPaneNearPluginResponse = Option<PaneId>;
3731pub type OpenCommandPaneFloatingNearPluginResponse = Option<PaneId>;
3732pub type OpenCommandPaneInPlaceOfPluginResponse = Option<PaneId>;
3733pub type OpenCommandPaneBackgroundResponse = Option<PaneId>;
3734pub type OpenCommandPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3735pub type OpenTerminalPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3736pub type OpenEditPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3737pub type OpenPluginPaneFloatingResponse = Option<PaneId>;
3738
3739#[test]
3740pub fn can_parse_unicode_bare_keys() {
3741 let key = "1087"; assert_eq!(
3743 BareKey::from_bytes_with_u(&key.as_bytes()),
3744 Some(BareKey::Char('п')),
3745 "Can parse a bare 'п' keypress"
3746 );
3747 let key = "1255"; assert_eq!(
3749 BareKey::from_bytes_with_u(&key.as_bytes()),
3750 Some(BareKey::Char('ӧ')),
3751 "Can parse a bare 'ӧ' keypress"
3752 );
3753 let key = "1098"; assert_eq!(
3755 BareKey::from_bytes_with_u(&key.as_bytes()),
3756 Some(BareKey::Char('ъ')),
3757 "Can parse a bare 'ъ' keypress"
3758 );
3759}