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)]
1212pub enum ThemeHue {
1213 Light,
1214 Dark,
1215}
1216impl Default for ThemeHue {
1217 fn default() -> ThemeHue {
1218 ThemeHue::Dark
1219 }
1220}
1221
1222#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1223pub enum PaletteColor {
1224 Rgb((u8, u8, u8)),
1225 EightBit(u8),
1226}
1227impl Default for PaletteColor {
1228 fn default() -> PaletteColor {
1229 PaletteColor::EightBit(0)
1230 }
1231}
1232
1233#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
1238pub enum HighlightLayer {
1239 Hint, Tool, ActionFeedback, }
1243
1244impl Default for HighlightLayer {
1245 fn default() -> Self {
1246 HighlightLayer::Hint
1247 }
1248}
1249
1250#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1253pub enum HighlightStyle {
1254 None, Emphasis0, Emphasis1, Emphasis2, Emphasis3, BackgroundEmphasis0, BackgroundEmphasis1, BackgroundEmphasis2, BackgroundEmphasis3, CustomRgb {
1264 fg: Option<(u8, u8, u8)>,
1265 bg: Option<(u8, u8, u8)>,
1266 },
1267 CustomIndex {
1268 fg: Option<u8>,
1269 bg: Option<u8>,
1270 },
1271}
1272
1273#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1275pub struct RegexHighlight {
1276 pub pattern: String, pub style: HighlightStyle,
1278 pub layer: HighlightLayer,
1279 pub context: BTreeMap<String, String>, pub on_hover: bool, pub bold: bool,
1282 pub italic: bool,
1283 pub underline: bool,
1284 pub tooltip_text: Option<String>, }
1286
1287impl PaletteColor {
1289 pub fn as_rgb_str(&self) -> String {
1290 let (r, g, b) = match *self {
1291 Self::Rgb((r, g, b)) => (r, g, b),
1292 Self::EightBit(c) => eightbit_to_rgb(c),
1293 };
1294 format!("rgb({}, {}, {})", r, g, b)
1295 }
1296 pub fn from_rgb_str(rgb_str: &str) -> Self {
1297 let trimmed = rgb_str.trim();
1298
1299 if !trimmed.starts_with("rgb(") || !trimmed.ends_with(')') {
1300 return Self::default();
1301 }
1302
1303 let inner = trimmed
1304 .strip_prefix("rgb(")
1305 .and_then(|s| s.strip_suffix(')'))
1306 .unwrap_or("");
1307
1308 let parts: Vec<&str> = inner.split(',').collect();
1309
1310 if parts.len() != 3 {
1311 return Self::default();
1312 }
1313
1314 let mut rgb_values = [0u8; 3];
1315 for (i, part) in parts.iter().enumerate() {
1316 if let Some(rgb_val) = rgb_values.get_mut(i) {
1317 if let Ok(parsed) = part.trim().parse::<u8>() {
1318 *rgb_val = parsed;
1319 } else {
1320 return Self::default();
1321 }
1322 }
1323 }
1324
1325 Self::Rgb((rgb_values[0], rgb_values[1], rgb_values[2]))
1326 }
1327}
1328
1329impl FromStr for InputMode {
1330 type Err = ConversionError;
1331
1332 fn from_str(s: &str) -> Result<Self, ConversionError> {
1333 match s {
1334 "normal" | "Normal" => Ok(InputMode::Normal),
1335 "locked" | "Locked" => Ok(InputMode::Locked),
1336 "resize" | "Resize" => Ok(InputMode::Resize),
1337 "pane" | "Pane" => Ok(InputMode::Pane),
1338 "tab" | "Tab" => Ok(InputMode::Tab),
1339 "search" | "Search" => Ok(InputMode::Search),
1340 "scroll" | "Scroll" => Ok(InputMode::Scroll),
1341 "renametab" | "RenameTab" => Ok(InputMode::RenameTab),
1342 "renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
1343 "session" | "Session" => Ok(InputMode::Session),
1344 "move" | "Move" => Ok(InputMode::Move),
1345 "prompt" | "Prompt" => Ok(InputMode::Prompt),
1346 "tmux" | "Tmux" => Ok(InputMode::Tmux),
1347 "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch),
1348 e => Err(ConversionError::UnknownInputMode(e.into())),
1349 }
1350 }
1351}
1352
1353#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1354pub enum PaletteSource {
1355 Default,
1356 Xresources,
1357}
1358impl Default for PaletteSource {
1359 fn default() -> PaletteSource {
1360 PaletteSource::Default
1361 }
1362}
1363#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
1364pub struct Palette {
1365 pub source: PaletteSource,
1366 pub theme_hue: ThemeHue,
1367 pub fg: PaletteColor,
1368 pub bg: PaletteColor,
1369 pub black: PaletteColor,
1370 pub red: PaletteColor,
1371 pub green: PaletteColor,
1372 pub yellow: PaletteColor,
1373 pub blue: PaletteColor,
1374 pub magenta: PaletteColor,
1375 pub cyan: PaletteColor,
1376 pub white: PaletteColor,
1377 pub orange: PaletteColor,
1378 pub gray: PaletteColor,
1379 pub purple: PaletteColor,
1380 pub gold: PaletteColor,
1381 pub silver: PaletteColor,
1382 pub pink: PaletteColor,
1383 pub brown: PaletteColor,
1384}
1385
1386#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1387pub struct Style {
1388 pub colors: Styling,
1389 pub rounded_corners: bool,
1390 pub hide_session_name: bool,
1391}
1392
1393#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1394pub enum Coloration {
1395 NoStyling,
1396 Styled(StyleDeclaration),
1397}
1398
1399impl Coloration {
1400 pub fn with_fallback(&self, fallback: StyleDeclaration) -> StyleDeclaration {
1401 match &self {
1402 Coloration::NoStyling => fallback,
1403 Coloration::Styled(style) => *style,
1404 }
1405 }
1406}
1407
1408#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1409pub struct Styling {
1410 pub text_unselected: StyleDeclaration,
1411 pub text_selected: StyleDeclaration,
1412 pub ribbon_unselected: StyleDeclaration,
1413 pub ribbon_selected: StyleDeclaration,
1414 pub table_title: StyleDeclaration,
1415 pub table_cell_unselected: StyleDeclaration,
1416 pub table_cell_selected: StyleDeclaration,
1417 pub list_unselected: StyleDeclaration,
1418 pub list_selected: StyleDeclaration,
1419 pub frame_unselected: Option<StyleDeclaration>,
1420 pub frame_selected: StyleDeclaration,
1421 pub frame_highlight: StyleDeclaration,
1422 pub exit_code_success: StyleDeclaration,
1423 pub exit_code_error: StyleDeclaration,
1424 pub multiplayer_user_colors: MultiplayerColors,
1425}
1426
1427#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1428pub struct StyleDeclaration {
1429 pub base: PaletteColor,
1430 pub background: PaletteColor,
1431 pub emphasis_0: PaletteColor,
1432 pub emphasis_1: PaletteColor,
1433 pub emphasis_2: PaletteColor,
1434 pub emphasis_3: PaletteColor,
1435}
1436
1437#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1438pub struct MultiplayerColors {
1439 pub player_1: PaletteColor,
1440 pub player_2: PaletteColor,
1441 pub player_3: PaletteColor,
1442 pub player_4: PaletteColor,
1443 pub player_5: PaletteColor,
1444 pub player_6: PaletteColor,
1445 pub player_7: PaletteColor,
1446 pub player_8: PaletteColor,
1447 pub player_9: PaletteColor,
1448 pub player_10: PaletteColor,
1449}
1450
1451pub const DEFAULT_STYLES: Styling = Styling {
1452 text_unselected: StyleDeclaration {
1453 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1454 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1455 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1456 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1457 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1458 background: PaletteColor::EightBit(default_colors::GRAY),
1459 },
1460 text_selected: StyleDeclaration {
1461 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1462 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1463 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1464 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1465 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1466 background: PaletteColor::EightBit(default_colors::GRAY),
1467 },
1468 ribbon_unselected: StyleDeclaration {
1469 base: PaletteColor::EightBit(default_colors::BLACK),
1470 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1471 emphasis_1: PaletteColor::EightBit(default_colors::WHITE),
1472 emphasis_2: PaletteColor::EightBit(default_colors::BLUE),
1473 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1474 background: PaletteColor::EightBit(default_colors::GRAY),
1475 },
1476 ribbon_selected: StyleDeclaration {
1477 base: PaletteColor::EightBit(default_colors::BLACK),
1478 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1479 emphasis_1: PaletteColor::EightBit(default_colors::ORANGE),
1480 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1481 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1482 background: PaletteColor::EightBit(default_colors::GREEN),
1483 },
1484 exit_code_success: StyleDeclaration {
1485 base: PaletteColor::EightBit(default_colors::GREEN),
1486 emphasis_0: PaletteColor::EightBit(default_colors::CYAN),
1487 emphasis_1: PaletteColor::EightBit(default_colors::BLACK),
1488 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1489 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1490 background: PaletteColor::EightBit(default_colors::GRAY),
1491 },
1492 exit_code_error: StyleDeclaration {
1493 base: PaletteColor::EightBit(default_colors::RED),
1494 emphasis_0: PaletteColor::EightBit(default_colors::YELLOW),
1495 emphasis_1: PaletteColor::EightBit(default_colors::GOLD),
1496 emphasis_2: PaletteColor::EightBit(default_colors::SILVER),
1497 emphasis_3: PaletteColor::EightBit(default_colors::PURPLE),
1498 background: PaletteColor::EightBit(default_colors::GRAY),
1499 },
1500 frame_unselected: None,
1501 frame_selected: StyleDeclaration {
1502 base: PaletteColor::EightBit(default_colors::GREEN),
1503 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1504 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1505 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1506 emphasis_3: PaletteColor::EightBit(default_colors::BROWN),
1507 background: PaletteColor::EightBit(default_colors::GRAY),
1508 },
1509 frame_highlight: StyleDeclaration {
1510 base: PaletteColor::EightBit(default_colors::ORANGE),
1511 emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA),
1512 emphasis_1: PaletteColor::EightBit(default_colors::PURPLE),
1513 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1514 emphasis_3: PaletteColor::EightBit(default_colors::GREEN),
1515 background: PaletteColor::EightBit(default_colors::GREEN),
1516 },
1517 table_title: StyleDeclaration {
1518 base: PaletteColor::EightBit(default_colors::GREEN),
1519 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1520 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1521 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1522 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1523 background: PaletteColor::EightBit(default_colors::GRAY),
1524 },
1525 table_cell_unselected: StyleDeclaration {
1526 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1527 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1528 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1529 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1530 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1531 background: PaletteColor::EightBit(default_colors::GRAY),
1532 },
1533 table_cell_selected: StyleDeclaration {
1534 base: PaletteColor::EightBit(default_colors::GREEN),
1535 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1536 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1537 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1538 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1539 background: PaletteColor::EightBit(default_colors::GRAY),
1540 },
1541 list_unselected: StyleDeclaration {
1542 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1543 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1544 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1545 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1546 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1547 background: PaletteColor::EightBit(default_colors::GRAY),
1548 },
1549 list_selected: StyleDeclaration {
1550 base: PaletteColor::EightBit(default_colors::GREEN),
1551 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1552 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1553 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1554 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1555 background: PaletteColor::EightBit(default_colors::GRAY),
1556 },
1557 multiplayer_user_colors: MultiplayerColors {
1558 player_1: PaletteColor::EightBit(default_colors::MAGENTA),
1559 player_2: PaletteColor::EightBit(default_colors::BLUE),
1560 player_3: PaletteColor::EightBit(default_colors::PURPLE),
1561 player_4: PaletteColor::EightBit(default_colors::YELLOW),
1562 player_5: PaletteColor::EightBit(default_colors::CYAN),
1563 player_6: PaletteColor::EightBit(default_colors::GOLD),
1564 player_7: PaletteColor::EightBit(default_colors::RED),
1565 player_8: PaletteColor::EightBit(default_colors::SILVER),
1566 player_9: PaletteColor::EightBit(default_colors::PINK),
1567 player_10: PaletteColor::EightBit(default_colors::BROWN),
1568 },
1569};
1570
1571impl Default for Styling {
1572 fn default() -> Self {
1573 DEFAULT_STYLES
1574 }
1575}
1576
1577impl From<Styling> for Palette {
1578 fn from(styling: Styling) -> Self {
1579 Palette {
1580 theme_hue: ThemeHue::Dark,
1581 source: PaletteSource::Default,
1582 fg: styling.ribbon_unselected.background,
1583 bg: styling.text_unselected.background,
1584 red: styling.exit_code_error.base,
1585 green: styling.text_unselected.emphasis_2,
1586 yellow: styling.exit_code_error.emphasis_0,
1587 blue: styling.ribbon_unselected.emphasis_2,
1588 magenta: styling.text_unselected.emphasis_3,
1589 orange: styling.text_unselected.emphasis_0,
1590 cyan: styling.text_unselected.emphasis_1,
1591 black: styling.ribbon_unselected.base,
1592 white: styling.ribbon_unselected.emphasis_1,
1593 gray: styling.list_unselected.background,
1594 purple: styling.multiplayer_user_colors.player_3,
1595 gold: styling.multiplayer_user_colors.player_6,
1596 silver: styling.multiplayer_user_colors.player_8,
1597 pink: styling.multiplayer_user_colors.player_9,
1598 brown: styling.multiplayer_user_colors.player_10,
1599 }
1600 }
1601}
1602
1603impl From<Palette> for Styling {
1604 fn from(palette: Palette) -> Self {
1605 let (fg, bg) = match palette.theme_hue {
1606 ThemeHue::Light => (palette.black, palette.white),
1607 ThemeHue::Dark => (palette.white, palette.black),
1608 };
1609 Styling {
1610 text_unselected: StyleDeclaration {
1611 base: fg,
1612 emphasis_0: palette.orange,
1613 emphasis_1: palette.cyan,
1614 emphasis_2: palette.green,
1615 emphasis_3: palette.magenta,
1616 background: bg,
1617 },
1618 text_selected: StyleDeclaration {
1619 base: fg,
1620 emphasis_0: palette.orange,
1621 emphasis_1: palette.cyan,
1622 emphasis_2: palette.green,
1623 emphasis_3: palette.magenta,
1624 background: palette.bg,
1625 },
1626 ribbon_unselected: StyleDeclaration {
1627 base: palette.black,
1628 emphasis_0: palette.red,
1629 emphasis_1: palette.white,
1630 emphasis_2: palette.blue,
1631 emphasis_3: palette.magenta,
1632 background: palette.fg,
1633 },
1634 ribbon_selected: StyleDeclaration {
1635 base: palette.black,
1636 emphasis_0: palette.red,
1637 emphasis_1: palette.orange,
1638 emphasis_2: palette.magenta,
1639 emphasis_3: palette.blue,
1640 background: palette.green,
1641 },
1642 exit_code_success: StyleDeclaration {
1643 base: palette.green,
1644 emphasis_0: palette.cyan,
1645 emphasis_1: palette.black,
1646 emphasis_2: palette.magenta,
1647 emphasis_3: palette.blue,
1648 background: Default::default(),
1649 },
1650 exit_code_error: StyleDeclaration {
1651 base: palette.red,
1652 emphasis_0: palette.yellow,
1653 emphasis_1: palette.gold,
1654 emphasis_2: palette.silver,
1655 emphasis_3: palette.purple,
1656 background: Default::default(),
1657 },
1658 frame_unselected: None,
1659 frame_selected: StyleDeclaration {
1660 base: palette.green,
1661 emphasis_0: palette.orange,
1662 emphasis_1: palette.cyan,
1663 emphasis_2: palette.magenta,
1664 emphasis_3: palette.brown,
1665 background: Default::default(),
1666 },
1667 frame_highlight: StyleDeclaration {
1668 base: palette.orange,
1669 emphasis_0: palette.magenta,
1670 emphasis_1: palette.purple,
1671 emphasis_2: palette.orange,
1672 emphasis_3: palette.orange,
1673 background: Default::default(),
1674 },
1675 table_title: StyleDeclaration {
1676 base: palette.green,
1677 emphasis_0: palette.orange,
1678 emphasis_1: palette.cyan,
1679 emphasis_2: palette.green,
1680 emphasis_3: palette.magenta,
1681 background: palette.gray,
1682 },
1683 table_cell_unselected: StyleDeclaration {
1684 base: fg,
1685 emphasis_0: palette.orange,
1686 emphasis_1: palette.cyan,
1687 emphasis_2: palette.green,
1688 emphasis_3: palette.magenta,
1689 background: palette.black,
1690 },
1691 table_cell_selected: StyleDeclaration {
1692 base: fg,
1693 emphasis_0: palette.orange,
1694 emphasis_1: palette.cyan,
1695 emphasis_2: palette.green,
1696 emphasis_3: palette.magenta,
1697 background: palette.bg,
1698 },
1699 list_unselected: StyleDeclaration {
1700 base: palette.white,
1701 emphasis_0: palette.orange,
1702 emphasis_1: palette.cyan,
1703 emphasis_2: palette.green,
1704 emphasis_3: palette.magenta,
1705 background: palette.black,
1706 },
1707 list_selected: StyleDeclaration {
1708 base: palette.white,
1709 emphasis_0: palette.orange,
1710 emphasis_1: palette.cyan,
1711 emphasis_2: palette.green,
1712 emphasis_3: palette.magenta,
1713 background: palette.bg,
1714 },
1715 multiplayer_user_colors: MultiplayerColors {
1716 player_1: palette.magenta,
1717 player_2: palette.blue,
1718 player_3: palette.purple,
1719 player_4: palette.yellow,
1720 player_5: palette.cyan,
1721 player_6: palette.gold,
1722 player_7: palette.red,
1723 player_8: palette.silver,
1724 player_9: palette.pink,
1725 player_10: palette.brown,
1726 },
1727 }
1728 }
1729}
1730
1731pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
1733
1734#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1736pub struct ModeInfo {
1737 pub mode: InputMode,
1738 pub base_mode: Option<InputMode>,
1739 pub keybinds: KeybindsVec,
1740 pub style: Style,
1741 pub capabilities: PluginCapabilities,
1742 pub session_name: Option<String>,
1743 pub editor: Option<PathBuf>,
1744 pub shell: Option<PathBuf>,
1745 pub web_clients_allowed: Option<bool>,
1746 pub web_sharing: Option<WebSharing>,
1747 pub currently_marking_pane_group: Option<bool>,
1748 pub is_web_client: Option<bool>,
1749 pub web_server_ip: Option<IpAddr>,
1751 pub web_server_port: Option<u16>,
1752 pub web_server_capability: Option<bool>,
1753 pub pane_frame_style: Option<PaneFrameStyle>,
1754 pub session_dimmed: Option<bool>,
1755 pub session_ancestry: Vec<String>,
1756 pub host_fullscreen: Option<bool>,
1757 pub nested_ascend_keys: Vec<KeyWithModifier>,
1758 pub session_ascended: Option<bool>,
1759 pub nested_descend_keys: Vec<KeyWithModifier>,
1760}
1761
1762impl ModeInfo {
1763 pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
1764 self.get_keybinds_for_mode(self.mode)
1765 }
1766
1767 pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
1768 for (vec_mode, map) in &self.keybinds {
1769 if mode == *vec_mode {
1770 return map.to_vec();
1771 }
1772 }
1773 vec![]
1774 }
1775 pub fn update_keybinds(&mut self, keybinds: Keybinds) {
1776 self.keybinds = keybinds.to_keybinds_vec();
1777 }
1778 pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
1779 self.base_mode = Some(new_default_mode);
1780 }
1781 pub fn update_theme(&mut self, theme: Styling) {
1782 self.style.colors = theme.into();
1783 }
1784 pub fn update_rounded_corners(&mut self, rounded_corners: bool) {
1785 self.style.rounded_corners = rounded_corners;
1786 }
1787 pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) {
1788 self.capabilities.arrow_fonts = !should_support_arrow_fonts;
1791 }
1792 pub fn update_hide_session_name(&mut self, hide_session_name: bool) {
1793 self.style.hide_session_name = hide_session_name;
1794 }
1795 pub fn change_to_default_mode(&mut self) {
1796 if let Some(base_mode) = self.base_mode {
1797 self.mode = base_mode;
1798 }
1799 }
1800}
1801
1802#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1803pub struct SessionInfo {
1804 pub name: String,
1805 pub tabs: Vec<TabInfo>,
1806 pub panes: PaneManifest,
1807 pub connected_clients: usize,
1808 pub is_current_session: bool,
1809 pub available_layouts: Vec<LayoutInfo>,
1810 pub plugins: BTreeMap<u32, PluginInfo>,
1811 pub web_clients_allowed: bool,
1812 pub web_client_count: usize,
1813 pub tab_history: BTreeMap<ClientId, Vec<usize>>,
1814 pub pane_history: BTreeMap<ClientId, Vec<PaneId>>,
1815 pub creation_time: Duration,
1816}
1817
1818#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1819pub struct PluginInfo {
1820 pub location: String,
1821 pub configuration: BTreeMap<String, String>,
1822}
1823
1824impl From<RunPlugin> for PluginInfo {
1825 fn from(run_plugin: RunPlugin) -> Self {
1826 PluginInfo {
1827 location: run_plugin.location.display(),
1828 configuration: run_plugin.configuration.inner().clone(),
1829 }
1830 }
1831}
1832
1833#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1834pub enum LayoutInfo {
1835 BuiltIn(String),
1836 File(String, LayoutMetadata),
1837 Url(String),
1838 Stringified(String),
1839}
1840
1841#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1842pub struct LayoutWithError {
1843 pub layout_name: String,
1844 pub error: LayoutParsingError,
1845}
1846
1847#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1848pub enum LayoutParsingError {
1849 KdlError {
1850 kdl_error: KdlError,
1851 file_name: String,
1852 source_code: String,
1853 },
1854 SyntaxError,
1855}
1856
1857impl AsRef<LayoutInfo> for LayoutInfo {
1858 fn as_ref(&self) -> &LayoutInfo {
1859 self
1860 }
1861}
1862
1863#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1864pub struct LayoutMetadata {
1865 pub tabs: Vec<TabMetadata>,
1866 pub creation_time: String,
1867 pub update_time: String,
1868}
1869
1870impl From<&PathBuf> for LayoutMetadata {
1871 fn from(path: &PathBuf) -> LayoutMetadata {
1872 match Layout::stringified_from_path(path) {
1873 Ok((path_str, stringified_layout, _swap_layouts)) => {
1874 match Layout::from_kdl(&stringified_layout, Some(path_str), None, None) {
1875 Ok(layout) => {
1876 let layout_tabs = layout.tabs();
1877 let tabs = if layout_tabs.is_empty() {
1878 let (tiled_pane_layout, floating_pane_layout) = layout.new_tab();
1879 vec![TabMetadata::from(&(
1880 None,
1881 tiled_pane_layout,
1882 floating_pane_layout,
1883 ))]
1884 } else {
1885 layout
1886 .tabs()
1887 .into_iter()
1888 .map(|tab| TabMetadata::from(&tab))
1889 .collect()
1890 };
1891
1892 let (creation_time, update_time) =
1894 LayoutMetadata::creation_and_update_times(&path);
1895
1896 LayoutMetadata {
1897 tabs,
1898 creation_time,
1899 update_time,
1900 }
1901 },
1902 Err(e) => {
1903 log::error!("Failed to parse layout: {}", e);
1904 LayoutMetadata::default()
1905 },
1906 }
1907 },
1908 Err(e) => {
1909 log::error!("Failed to read layout file: {}", e);
1910 LayoutMetadata::default()
1911 },
1912 }
1913 }
1914}
1915
1916impl LayoutMetadata {
1917 fn creation_and_update_times(path: &PathBuf) -> (String, String) {
1918 match std::fs::metadata(path) {
1920 Ok(metadata) => {
1921 let creation_time = metadata
1922 .created()
1923 .ok()
1924 .and_then(|t| {
1925 t.duration_since(std::time::UNIX_EPOCH)
1926 .ok()
1927 .map(|d| d.as_secs().to_string())
1928 })
1929 .unwrap_or_default();
1930
1931 let update_time = metadata
1932 .modified()
1933 .ok()
1934 .and_then(|t| {
1935 t.duration_since(std::time::UNIX_EPOCH)
1936 .ok()
1937 .map(|d| d.as_secs().to_string())
1938 })
1939 .unwrap_or_default();
1940
1941 (creation_time, update_time)
1942 },
1943 Err(_) => (String::new(), String::new()),
1944 }
1945 }
1946}
1947
1948#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1949pub struct TabMetadata {
1950 pub panes: Vec<PaneMetadata>,
1951 pub name: Option<String>,
1952}
1953
1954impl
1955 From<&(
1956 Option<String>,
1957 crate::input::layout::TiledPaneLayout,
1958 Vec<crate::input::layout::FloatingPaneLayout>,
1959 )> for TabMetadata
1960{
1961 fn from(
1962 tab: &(
1963 Option<String>,
1964 crate::input::layout::TiledPaneLayout,
1965 Vec<crate::input::layout::FloatingPaneLayout>,
1966 ),
1967 ) -> Self {
1968 let (tab_name, tiled_pane_layout, floating_panes) = tab;
1969
1970 let mut panes = Vec::new();
1972 collect_leaf_panes(&tiled_pane_layout, &mut panes);
1973
1974 for floating_pane in floating_panes {
1976 panes.push(PaneMetadata::from(floating_pane));
1977 }
1978
1979 TabMetadata {
1980 panes,
1981 name: tab_name.clone(),
1982 }
1983 }
1984}
1985
1986#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1987pub struct PaneMetadata {
1988 pub name: Option<String>,
1989 pub is_plugin: bool,
1990 pub is_builtin_plugin: bool,
1991}
1992
1993impl From<&crate::input::layout::TiledPaneLayout> for PaneMetadata {
1994 fn from(pane: &crate::input::layout::TiledPaneLayout) -> Self {
1995 let mut is_plugin = false;
1996 let mut is_builtin_plugin = false;
1997
1998 let name = if let Some(ref name) = pane.name {
2000 Some(name.clone())
2001 } else if let Some(ref run) = pane.run {
2002 match run {
2004 Run::Command(cmd) => {
2005 Some(cmd.command.to_string_lossy().to_string())
2007 },
2008 Run::EditFile(path, _line, _cwd) => {
2009 path.file_name().map(|n| n.to_string_lossy().to_string())
2011 },
2012 Run::Plugin(plugin) => {
2013 is_plugin = true;
2014 is_builtin_plugin = plugin.is_builtin_plugin();
2015 Some(plugin.location_string())
2016 },
2017 Run::Cwd(_) => None,
2018 }
2019 } else {
2020 None
2021 };
2022
2023 PaneMetadata {
2024 name,
2025 is_plugin,
2026 is_builtin_plugin,
2027 }
2028 }
2029}
2030
2031impl From<&crate::input::layout::FloatingPaneLayout> for PaneMetadata {
2032 fn from(pane: &crate::input::layout::FloatingPaneLayout) -> Self {
2033 let mut is_plugin = false;
2034 let mut is_builtin_plugin = false;
2035
2036 let name = if let Some(ref name) = pane.name {
2038 Some(name.clone())
2039 } else if let Some(ref run) = pane.run {
2040 match run {
2042 Run::Command(cmd) => {
2043 Some(cmd.command.to_string_lossy().to_string())
2045 },
2046 Run::EditFile(path, _line, _cwd) => {
2047 path.file_name().map(|n| n.to_string_lossy().to_string())
2049 },
2050 Run::Plugin(plugin) => {
2051 is_plugin = true;
2052 is_builtin_plugin = match plugin {
2053 crate::input::layout::RunPluginOrAlias::RunPlugin(run_plugin) => {
2054 matches!(run_plugin.location, RunPluginLocation::Zellij(_))
2055 },
2056 crate::input::layout::RunPluginOrAlias::Alias(_) => false,
2057 };
2058 Some(plugin.location_string())
2060 },
2061 Run::Cwd(_) => None,
2062 }
2063 } else {
2064 None
2065 };
2066
2067 PaneMetadata {
2068 name,
2069 is_plugin,
2070 is_builtin_plugin,
2071 }
2072 }
2073}
2074
2075fn collect_leaf_panes(
2077 pane: &crate::input::layout::TiledPaneLayout,
2078 result: &mut Vec<PaneMetadata>,
2079) {
2080 if pane.children.is_empty() {
2081 result.push(PaneMetadata::from(pane));
2083 } else {
2084 for child in &pane.children {
2086 collect_leaf_panes(child, result);
2087 }
2088 }
2089}
2090
2091impl LayoutInfo {
2092 pub fn name(&self) -> &str {
2093 match self {
2094 LayoutInfo::BuiltIn(name) => &name,
2095 LayoutInfo::File(name, _) => &name,
2096 LayoutInfo::Url(url) => &url,
2097 LayoutInfo::Stringified(layout) => &layout,
2098 }
2099 }
2100 pub fn is_builtin(&self) -> bool {
2101 match self {
2102 LayoutInfo::BuiltIn(_name) => true,
2103 LayoutInfo::File(_name, _) => false,
2104 LayoutInfo::Url(_url) => false,
2105 LayoutInfo::Stringified(_stringified) => false,
2106 }
2107 }
2108 pub fn from_cli(
2109 layout_dir: &Option<PathBuf>,
2110 maybe_layout_path: &Option<PathBuf>,
2111 cwd: PathBuf,
2112 ) -> Option<Self> {
2113 let layout_path = maybe_layout_path
2120 .clone()
2121 .unwrap_or(PathBuf::from("default"));
2122
2123 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2124 Some(LayoutInfo::Url(layout_path.display().to_string()))
2125 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2126 let layout_dir = cwd;
2127 let file_path = layout_dir.join(layout_path);
2128 Some(LayoutInfo::File(
2129 file_path.display().to_string(),
2131 LayoutMetadata::from(&file_path),
2132 ))
2133 } else {
2134 if let Some(layout_dir) = layout_dir
2138 .as_ref()
2139 .map(|l| l.clone())
2140 .or_else(default_layout_dir)
2141 {
2142 let file_path = layout_dir.join(&layout_path);
2143 if file_path.exists() {
2144 return Some(LayoutInfo::File(
2145 file_path.display().to_string(),
2146 LayoutMetadata::from(&file_path),
2147 ));
2148 }
2149 let file_path_with_ext = file_path.with_extension("kdl");
2150 if file_path_with_ext.exists() {
2151 return Some(LayoutInfo::File(
2152 file_path_with_ext.display().to_string(),
2153 LayoutMetadata::from(&file_path_with_ext),
2154 ));
2155 }
2156 }
2157 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2159 }
2160 }
2161 pub fn from_config(
2162 layout_dir: &Option<PathBuf>,
2163 maybe_layout_path: &Option<PathBuf>,
2164 ) -> Option<Self> {
2165 let layout_path = maybe_layout_path
2172 .clone()
2173 .unwrap_or(PathBuf::from("default"));
2174
2175 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2176 Some(LayoutInfo::Url(layout_path.display().to_string()))
2177 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2178 let Some(layout_dir) = layout_dir
2179 .as_ref()
2180 .map(|l| l.clone())
2181 .or_else(default_layout_dir)
2182 else {
2183 return None;
2184 };
2185 let file_path = layout_dir.join(layout_path);
2186 Some(LayoutInfo::File(
2187 file_path.display().to_string(),
2189 LayoutMetadata::from(&file_path),
2190 ))
2191 } else {
2192 if let Some(layout_dir) = layout_dir
2196 .as_ref()
2197 .map(|l| l.clone())
2198 .or_else(default_layout_dir)
2199 {
2200 let file_path = layout_dir.join(&layout_path);
2201 if file_path.exists() {
2202 return Some(LayoutInfo::File(
2203 file_path.display().to_string(),
2204 LayoutMetadata::from(&file_path),
2205 ));
2206 }
2207 let file_path_with_ext = file_path.with_extension("kdl");
2208 if file_path_with_ext.exists() {
2209 return Some(LayoutInfo::File(
2210 file_path_with_ext.display().to_string(),
2211 LayoutMetadata::from(&file_path_with_ext),
2212 ));
2213 }
2214 }
2215 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2217 }
2218 }
2219}
2220
2221#[allow(clippy::derive_hash_xor_eq)]
2222impl Hash for SessionInfo {
2223 fn hash<H: Hasher>(&self, state: &mut H) {
2224 self.name.hash(state);
2225 }
2226}
2227
2228impl SessionInfo {
2229 pub fn new(name: String) -> Self {
2230 SessionInfo {
2231 name,
2232 ..Default::default()
2233 }
2234 }
2235 pub fn update_tab_info(&mut self, new_tab_info: Vec<TabInfo>) {
2236 self.tabs = new_tab_info;
2237 }
2238 pub fn update_pane_info(&mut self, new_pane_info: PaneManifest) {
2239 self.panes = new_pane_info;
2240 }
2241 pub fn update_connected_clients(&mut self, new_connected_clients: usize) {
2242 self.connected_clients = new_connected_clients;
2243 }
2244 pub fn populate_plugin_list(&mut self, plugins: BTreeMap<u32, RunPlugin>) {
2245 let mut plugin_list = BTreeMap::new();
2247 for (plugin_id, run_plugin) in plugins {
2248 plugin_list.insert(plugin_id, run_plugin.into());
2249 }
2250 self.plugins = plugin_list;
2251 }
2252}
2253
2254#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2256pub struct TabInfo {
2257 pub position: usize,
2259 pub name: String,
2261 pub active: bool,
2263 pub panes_to_hide: usize,
2265 pub is_fullscreen_active: bool,
2267 pub is_sync_panes_active: bool,
2269 pub are_floating_panes_visible: bool,
2270 pub other_focused_clients: Vec<ClientId>,
2271 pub active_swap_layout_name: Option<String>,
2272 pub is_swap_layout_dirty: bool,
2274 pub viewport_rows: usize,
2276 pub viewport_columns: usize,
2278 pub display_area_rows: usize,
2281 pub display_area_columns: usize,
2284 pub selectable_tiled_panes_count: usize,
2286 pub selectable_floating_panes_count: usize,
2288 pub tab_id: usize,
2290 pub has_bell_notification: bool,
2292 pub is_flashing_bell: bool,
2294}
2295
2296#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
2300pub struct PaneManifest {
2301 pub panes: HashMap<usize, Vec<PaneInfo>>, }
2303
2304#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2315pub struct PaneInfo {
2316 pub id: u32,
2318 pub is_plugin: bool,
2321 pub is_focused: bool,
2323 pub is_fullscreen: bool,
2324 pub is_floating: bool,
2326 pub is_suppressed: bool,
2329 pub title: String,
2331 pub exited: bool,
2334 pub exit_status: Option<i32>,
2336 pub is_held: bool,
2339 pub pane_x: usize,
2340 pub pane_content_x: usize,
2341 pub pane_y: usize,
2342 pub pane_content_y: usize,
2343 pub pane_rows: usize,
2344 pub pane_content_rows: usize,
2345 pub pane_columns: usize,
2346 pub pane_content_columns: usize,
2347 pub cursor_coordinates_in_pane: Option<(usize, usize)>, pub terminal_command: Option<String>,
2353 pub plugin_url: Option<String>,
2356 pub is_selectable: bool,
2359 pub index_in_pane_group: BTreeMap<ClientId, usize>,
2362 pub default_fg: Option<String>,
2364 pub default_bg: Option<String>,
2366}
2367
2368#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
2369pub struct PaneListEntry {
2370 #[serde(flatten)]
2371 pub pane_info: PaneInfo,
2372 pub tab_id: usize,
2373 pub tab_position: usize,
2374 pub tab_name: String,
2375 #[serde(skip_serializing_if = "Option::is_none")]
2376 pub pane_command: Option<String>,
2377 #[serde(skip_serializing_if = "Option::is_none")]
2378 pub pane_cwd: Option<String>,
2379}
2380
2381pub type ListPanesResponse = Vec<PaneListEntry>;
2382pub type ListTabsResponse = Vec<TabInfo>;
2383
2384#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
2385pub struct ClientInfo {
2386 pub client_id: ClientId,
2387 pub pane_id: PaneId,
2388 pub running_command: String,
2389 pub is_current_client: bool,
2390}
2391
2392impl ClientInfo {
2393 pub fn new(
2394 client_id: ClientId,
2395 pane_id: PaneId,
2396 running_command: String,
2397 is_current_client: bool,
2398 ) -> Self {
2399 ClientInfo {
2400 client_id,
2401 pane_id,
2402 running_command,
2403 is_current_client,
2404 }
2405 }
2406}
2407
2408#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2409pub struct PaneRenderReport {
2410 pub all_pane_contents: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2411 pub all_pane_contents_with_ansi: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2412}
2413
2414impl PaneRenderReport {
2415 pub fn add_pane_contents(
2416 &mut self,
2417 client_ids: &[ClientId],
2418 pane_id: PaneId,
2419 pane_contents: PaneContents,
2420 ) {
2421 for client_id in client_ids {
2422 let p = self
2423 .all_pane_contents
2424 .entry(*client_id)
2425 .or_insert_with(|| HashMap::new());
2426 p.insert(pane_id, pane_contents.clone());
2427 }
2428 }
2429 pub fn add_pane_contents_with_ansi(
2430 &mut self,
2431 client_ids: &[ClientId],
2432 pane_id: PaneId,
2433 pane_contents: PaneContents,
2434 ) {
2435 for client_id in client_ids {
2436 let p = self
2437 .all_pane_contents_with_ansi
2438 .entry(*client_id)
2439 .or_insert_with(|| HashMap::new());
2440 p.insert(pane_id, pane_contents.clone());
2441 }
2442 }
2443}
2444
2445#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2446pub struct PaneContents {
2447 pub lines_above_viewport: Vec<String>,
2451 pub lines_below_viewport: Vec<String>,
2452 pub viewport: Vec<String>,
2453 pub selected_text: Option<SelectedText>,
2454 pub cursor: Option<(usize, usize)>,
2455}
2456
2457fn extract_text_by_columns(line: &str, start_col: usize, end_col: usize) -> String {
2459 let mut current_col = 0;
2460 let mut result = String::new();
2461 let mut capturing = false;
2462
2463 for ch in line.chars() {
2464 let char_width = ch.width().unwrap_or(0);
2465
2466 if current_col >= start_col && !capturing {
2468 capturing = true;
2469 }
2470
2471 if current_col >= end_col {
2473 break;
2474 }
2475
2476 if capturing {
2478 result.push(ch);
2479 }
2480
2481 current_col += char_width;
2482 }
2483
2484 result
2485}
2486
2487fn extract_text_from_column(line: &str, start_col: usize) -> String {
2489 let mut current_col = 0;
2490 let mut result = String::new();
2491 let mut capturing = false;
2492
2493 for ch in line.chars() {
2494 let char_width = ch.width().unwrap_or(0);
2495
2496 if current_col >= start_col {
2497 capturing = true;
2498 }
2499
2500 if capturing {
2501 result.push(ch);
2502 }
2503
2504 current_col += char_width;
2505 }
2506
2507 result
2508}
2509
2510fn extract_text_to_column(line: &str, end_col: usize) -> String {
2512 let mut current_col = 0;
2513 let mut result = String::new();
2514
2515 for ch in line.chars() {
2516 let char_width = ch.width().unwrap_or(0);
2517
2518 if current_col >= end_col {
2519 break;
2520 }
2521
2522 result.push(ch);
2523 current_col += char_width;
2524 }
2525
2526 result
2527}
2528
2529impl PaneContents {
2530 pub fn new(viewport: Vec<String>, selection_start: Position, selection_end: Position) -> Self {
2531 PaneContents {
2532 viewport,
2533 selected_text: SelectedText::from_positions(selection_start, selection_end),
2534 ..Default::default()
2535 }
2536 }
2537 pub fn new_with_scrollback(
2538 viewport: Vec<String>,
2539 selection_start: Position,
2540 selection_end: Position,
2541 lines_above_viewport: Vec<String>,
2542 lines_below_viewport: Vec<String>,
2543 ) -> Self {
2544 PaneContents {
2545 viewport,
2546 selected_text: SelectedText::from_positions(selection_start, selection_end),
2547 lines_above_viewport,
2548 lines_below_viewport,
2549 cursor: None,
2550 }
2551 }
2552
2553 pub fn get_selected_text(&self) -> Option<String> {
2556 let selected_text = self.selected_text?;
2557
2558 let start_line = selected_text.start.line() as usize;
2559 let start_col = selected_text.start.column();
2560 let end_line = selected_text.end.line() as usize;
2561 let end_col = selected_text.end.column();
2562
2563 if start_line >= self.viewport.len() || end_line >= self.viewport.len() {
2565 return None;
2566 }
2567
2568 if start_line == end_line {
2569 let line = &self.viewport[start_line];
2571 Some(extract_text_by_columns(line, start_col, end_col))
2572 } else {
2573 let mut result = String::new();
2575
2576 let first_line = &self.viewport[start_line];
2578 result.push_str(&extract_text_from_column(first_line, start_col));
2579 result.push('\n');
2580
2581 for i in (start_line + 1)..end_line {
2583 result.push_str(&self.viewport[i]);
2584 result.push('\n');
2585 }
2586
2587 let last_line = &self.viewport[end_line];
2589 result.push_str(&extract_text_to_column(last_line, end_col));
2590
2591 Some(result)
2592 }
2593 }
2594}
2595
2596#[derive(Debug, Clone, Serialize, Deserialize)]
2597pub enum PaneScrollbackResponse {
2598 Ok(PaneContents),
2599 Err(String),
2600}
2601
2602#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2603pub enum GetPanePidResponse {
2604 Ok(i32),
2605 Err(String),
2606}
2607
2608#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2609pub enum GetPaneRunningCommandResponse {
2610 Ok(Vec<String>),
2611 Err(String),
2612}
2613
2614#[derive(Debug, Default, Clone, PartialEq, Serialize, Deserialize)]
2615pub struct SessionListSnapshot {
2616 pub live_sessions: Vec<SessionInfo>,
2617 pub resurrectable_sessions: Vec<(String, Duration)>,
2618}
2619
2620#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2621pub enum GetSessionListResponse {
2622 Ok(SessionListSnapshot),
2623 Err(String),
2624}
2625
2626#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2627pub enum KillSessionsResponse {
2628 Ok,
2629 Err(String),
2630}
2631
2632#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2633pub enum DeleteDeadSessionResponse {
2634 Ok,
2635 Err(String),
2636}
2637
2638#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2639pub enum DeleteAllDeadSessionsResponse {
2640 Ok,
2641 Err(String),
2642}
2643
2644#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2645pub enum GetPaneCwdResponse {
2646 Ok(PathBuf),
2647 Err(String),
2648}
2649
2650#[derive(Debug, Clone, PartialEq)]
2651pub enum GetFocusedPaneInfoResponse {
2652 Ok { tab_index: usize, pane_id: PaneId },
2653 Err(String),
2654}
2655
2656#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2657pub enum SaveLayoutResponse {
2658 Ok(()),
2659 Err(String),
2660}
2661
2662#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2663pub enum DeleteLayoutResponse {
2664 Ok(()),
2665 Err(String),
2666}
2667
2668#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2669pub enum RenameLayoutResponse {
2670 Ok(()),
2671 Err(String),
2672}
2673
2674#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2675pub enum EditLayoutResponse {
2676 Ok(()),
2677 Err(String),
2678}
2679
2680#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2681pub struct SelectedText {
2682 pub start: Position,
2683 pub end: Position,
2684}
2685
2686impl SelectedText {
2687 pub fn new(start: Position, end: Position) -> Self {
2688 let (normalized_start, normalized_end) = if start <= end {
2690 (start, end)
2691 } else {
2692 (end, start)
2693 };
2694
2695 let normalized_start = Position::new(
2698 normalized_start.line().max(0) as i32,
2699 normalized_start.column() as u16,
2700 );
2701 let normalized_end = Position::new(
2702 normalized_end.line().max(0) as i32,
2703 normalized_end.column() as u16,
2704 );
2705
2706 SelectedText {
2707 start: normalized_start,
2708 end: normalized_end,
2709 }
2710 }
2711
2712 pub fn from_positions(start: Position, end: Position) -> Option<Self> {
2713 if start == end {
2714 None
2715 } else {
2716 Some(Self::new(start, end))
2717 }
2718 }
2719}
2720
2721#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2722pub struct PluginIds {
2723 pub plugin_id: u32,
2724 pub zellij_pid: u32,
2725 pub initial_cwd: PathBuf,
2726 pub client_id: ClientId,
2727}
2728
2729#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
2731pub struct PluginTag(String);
2732
2733impl PluginTag {
2734 pub fn new(url: impl Into<String>) -> Self {
2735 PluginTag(url.into())
2736 }
2737}
2738
2739impl From<PluginTag> for String {
2740 fn from(tag: PluginTag) -> Self {
2741 tag.0
2742 }
2743}
2744
2745impl fmt::Display for PluginTag {
2746 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2747 write!(f, "{}", self.0)
2748 }
2749}
2750
2751#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2752pub struct PluginCapabilities {
2753 pub arrow_fonts: bool,
2754}
2755
2756impl Default for PluginCapabilities {
2757 fn default() -> PluginCapabilities {
2758 PluginCapabilities { arrow_fonts: true }
2759 }
2760}
2761
2762#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2764pub enum CopyDestination {
2765 Command,
2766 Primary,
2767 System,
2768}
2769
2770#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2771pub enum PermissionStatus {
2772 Granted,
2773 Denied,
2774}
2775
2776#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
2777pub struct FileToOpen {
2778 pub path: PathBuf,
2779 pub line_number: Option<usize>,
2780 pub cwd: Option<PathBuf>,
2781}
2782
2783impl FileToOpen {
2784 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2785 FileToOpen {
2786 path: path.as_ref().to_path_buf(),
2787 ..Default::default()
2788 }
2789 }
2790 pub fn with_line_number(mut self, line_number: usize) -> Self {
2791 self.line_number = Some(line_number);
2792 self
2793 }
2794 pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
2795 self.cwd = Some(cwd);
2796 self
2797 }
2798}
2799
2800#[derive(Debug, Default, Clone)]
2801pub struct CommandToRun {
2802 pub path: PathBuf,
2803 pub args: Vec<String>,
2804 pub cwd: Option<PathBuf>,
2805}
2806
2807impl CommandToRun {
2808 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2809 CommandToRun {
2810 path: path.as_ref().to_path_buf(),
2811 ..Default::default()
2812 }
2813 }
2814 pub fn new_with_args<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) -> Self {
2815 CommandToRun {
2816 path: path.as_ref().to_path_buf(),
2817 args: args.into_iter().map(|a| a.as_ref().to_owned()).collect(),
2818 ..Default::default()
2819 }
2820 }
2821}
2822
2823#[derive(Debug, Default, Clone)]
2824pub struct MessageToPlugin {
2825 pub plugin_url: Option<String>,
2826 pub destination_plugin_id: Option<u32>,
2827 pub plugin_config: BTreeMap<String, String>,
2828 pub message_name: String,
2829 pub message_payload: Option<String>,
2830 pub message_args: BTreeMap<String, String>,
2831 pub new_plugin_args: Option<NewPluginArgs>,
2834 pub floating_pane_coordinates: Option<FloatingPaneCoordinates>,
2835}
2836
2837#[derive(Debug, Default, Clone)]
2838pub struct NewPluginArgs {
2839 pub should_float: Option<bool>,
2840 pub pane_id_to_replace: Option<PaneId>,
2841 pub pane_title: Option<String>,
2842 pub cwd: Option<PathBuf>,
2843 pub skip_cache: bool,
2844 pub should_focus: Option<bool>,
2845}
2846
2847#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
2848pub enum PaneId {
2849 Terminal(u32),
2850 Plugin(u32),
2851}
2852
2853impl Default for PaneId {
2854 fn default() -> Self {
2855 PaneId::Terminal(0)
2856 }
2857}
2858
2859impl FromStr for PaneId {
2860 type Err = Box<dyn std::error::Error>;
2861 fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
2862 if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
2863 u32::from_str_radix(terminal_stringified_pane_id, 10)
2864 .map(|id| PaneId::Terminal(id))
2865 .map_err(|e| e.into())
2866 } else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
2867 u32::from_str_radix(plugin_pane_id, 10)
2868 .map(|id| PaneId::Plugin(id))
2869 .map_err(|e| e.into())
2870 } else {
2871 u32::from_str_radix(&stringified_pane_id, 10)
2872 .map(|id| PaneId::Terminal(id))
2873 .map_err(|e| e.into())
2874 }
2875 }
2876}
2877
2878impl std::fmt::Display for PaneId {
2879 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2880 match self {
2881 PaneId::Terminal(id) => write!(f, "terminal_{}", id),
2882 PaneId::Plugin(id) => write!(f, "plugin_{}", id),
2883 }
2884 }
2885}
2886
2887impl MessageToPlugin {
2888 pub fn new(message_name: impl Into<String>) -> Self {
2889 MessageToPlugin {
2890 message_name: message_name.into(),
2891 ..Default::default()
2892 }
2893 }
2894 pub fn with_plugin_url(mut self, url: impl Into<String>) -> Self {
2895 self.plugin_url = Some(url.into());
2896 self
2897 }
2898 pub fn with_destination_plugin_id(mut self, destination_plugin_id: u32) -> Self {
2899 self.destination_plugin_id = Some(destination_plugin_id);
2900 self
2901 }
2902 pub fn with_plugin_config(mut self, plugin_config: BTreeMap<String, String>) -> Self {
2903 self.plugin_config = plugin_config;
2904 self
2905 }
2906 pub fn with_payload(mut self, payload: impl Into<String>) -> Self {
2907 self.message_payload = Some(payload.into());
2908 self
2909 }
2910 pub fn with_args(mut self, args: BTreeMap<String, String>) -> Self {
2911 self.message_args = args;
2912 self
2913 }
2914 pub fn with_floating_pane_coordinates(
2915 mut self,
2916 floating_pane_coordinates: FloatingPaneCoordinates,
2917 ) -> Self {
2918 self.floating_pane_coordinates = Some(floating_pane_coordinates);
2919 self
2920 }
2921 pub fn new_plugin_instance_should_float(mut self, should_float: bool) -> Self {
2922 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2923 new_plugin_args.should_float = Some(should_float);
2924 self
2925 }
2926 pub fn new_plugin_instance_should_replace_pane(mut self, pane_id: PaneId) -> Self {
2927 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2928 new_plugin_args.pane_id_to_replace = Some(pane_id);
2929 self
2930 }
2931 pub fn new_plugin_instance_should_have_pane_title(
2932 mut self,
2933 pane_title: impl Into<String>,
2934 ) -> Self {
2935 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2936 new_plugin_args.pane_title = Some(pane_title.into());
2937 self
2938 }
2939 pub fn new_plugin_instance_should_have_cwd(mut self, cwd: PathBuf) -> Self {
2940 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2941 new_plugin_args.cwd = Some(cwd);
2942 self
2943 }
2944 pub fn new_plugin_instance_should_skip_cache(mut self) -> Self {
2945 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2946 new_plugin_args.skip_cache = true;
2947 self
2948 }
2949 pub fn new_plugin_instance_should_be_focused(mut self) -> Self {
2950 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2951 new_plugin_args.should_focus = Some(true);
2952 self
2953 }
2954 pub fn has_cwd(&self) -> bool {
2955 self.new_plugin_args
2956 .as_ref()
2957 .map(|n| n.cwd.is_some())
2958 .unwrap_or(false)
2959 }
2960}
2961
2962#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
2963pub struct ConnectToSession {
2964 pub name: Option<String>,
2965 pub tab_position: Option<usize>,
2966 pub pane_id: Option<(u32, bool)>, pub layout: Option<LayoutInfo>,
2968 pub cwd: Option<PathBuf>,
2969}
2970
2971impl ConnectToSession {
2972 pub fn apply_layout_dir(&mut self, layout_dir: &PathBuf) {
2973 if let Some(LayoutInfo::File(file_path, _layout_metadata)) = self.layout.as_mut() {
2974 *file_path = Path::join(layout_dir, &file_path)
2975 .to_string_lossy()
2976 .to_string();
2977 }
2978 }
2979}
2980
2981#[derive(Debug, Default, Clone)]
2982pub struct PluginMessage {
2983 pub name: String,
2984 pub payload: String,
2985 pub worker_name: Option<String>,
2986}
2987
2988impl PluginMessage {
2989 pub fn new_to_worker(worker_name: &str, message: &str, payload: &str) -> Self {
2990 PluginMessage {
2991 name: message.to_owned(),
2992 payload: payload.to_owned(),
2993 worker_name: Some(worker_name.to_owned()),
2994 }
2995 }
2996 pub fn new_to_plugin(message: &str, payload: &str) -> Self {
2997 PluginMessage {
2998 name: message.to_owned(),
2999 payload: payload.to_owned(),
3000 worker_name: None,
3001 }
3002 }
3003}
3004
3005#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
3006pub enum HttpVerb {
3007 Get,
3008 Post,
3009 Put,
3010 Delete,
3011}
3012
3013#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3014pub enum PipeSource {
3015 Cli(String), Plugin(u32), Keybind, }
3019
3020#[derive(Debug, Clone, PartialEq, Eq, Hash)]
3021pub struct PipeMessage {
3022 pub source: PipeSource,
3023 pub name: String,
3024 pub payload: Option<String>,
3025 pub args: BTreeMap<String, String>,
3026 pub is_private: bool,
3027}
3028
3029impl PipeMessage {
3030 pub fn new(
3031 source: PipeSource,
3032 name: impl Into<String>,
3033 payload: &Option<String>,
3034 args: &Option<BTreeMap<String, String>>,
3035 is_private: bool,
3036 ) -> Self {
3037 PipeMessage {
3038 source,
3039 name: name.into(),
3040 payload: payload.clone(),
3041 args: args.clone().unwrap_or_else(|| Default::default()),
3042 is_private,
3043 }
3044 }
3045}
3046
3047#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
3048pub struct FloatingPaneCoordinates {
3049 pub x: Option<PercentOrFixed>,
3050 pub y: Option<PercentOrFixed>,
3051 pub width: Option<PercentOrFixed>,
3052 pub height: Option<PercentOrFixed>,
3053 pub pinned: Option<bool>,
3054 pub borderless: Option<bool>,
3055}
3056
3057impl FloatingPaneCoordinates {
3058 pub fn new(
3059 x: Option<String>,
3060 y: Option<String>,
3061 width: Option<String>,
3062 height: Option<String>,
3063 pinned: Option<bool>,
3064 borderless: Option<bool>,
3065 ) -> Option<Self> {
3066 let x = x.and_then(|x| PercentOrFixed::from_str(&x).ok());
3068 let y = y.and_then(|y| PercentOrFixed::from_str(&y).ok());
3069
3070 let width = width.and_then(|w| {
3072 PercentOrFixed::from_str(&w)
3073 .ok()
3074 .and_then(|size| match size {
3075 PercentOrFixed::Percent(0) => None,
3076 PercentOrFixed::Fixed(0) => None,
3077 _ => Some(size),
3078 })
3079 });
3080 let height = height.and_then(|h| {
3081 PercentOrFixed::from_str(&h)
3082 .ok()
3083 .and_then(|size| match size {
3084 PercentOrFixed::Percent(0) => None,
3085 PercentOrFixed::Fixed(0) => None,
3086 _ => Some(size),
3087 })
3088 });
3089
3090 if x.is_none()
3091 && y.is_none()
3092 && width.is_none()
3093 && height.is_none()
3094 && pinned.is_none()
3095 && borderless.is_none()
3096 {
3097 None
3098 } else {
3099 Some(FloatingPaneCoordinates {
3100 x,
3101 y,
3102 width,
3103 height,
3104 pinned,
3105 borderless,
3106 })
3107 }
3108 }
3109 pub fn with_x_fixed(mut self, x: usize) -> Self {
3110 self.x = Some(PercentOrFixed::Fixed(x));
3111 self
3112 }
3113 pub fn with_x_percent(mut self, x: usize) -> Self {
3114 if x > 100 {
3115 eprintln!("x must be between 0 and 100");
3116 return self;
3117 }
3118 self.x = Some(PercentOrFixed::Percent(x));
3119 self
3120 }
3121 pub fn with_y_fixed(mut self, y: usize) -> Self {
3122 self.y = Some(PercentOrFixed::Fixed(y));
3123 self
3124 }
3125 pub fn with_y_percent(mut self, y: usize) -> Self {
3126 if y > 100 {
3127 eprintln!("y must be between 0 and 100");
3128 return self;
3129 }
3130 self.y = Some(PercentOrFixed::Percent(y));
3131 self
3132 }
3133 pub fn with_width_fixed(mut self, width: usize) -> Self {
3134 self.width = Some(PercentOrFixed::Fixed(width));
3135 self
3136 }
3137 pub fn with_width_percent(mut self, width: usize) -> Self {
3138 if width > 100 {
3139 eprintln!("width must be between 0 and 100");
3140 return self;
3141 }
3142 self.width = Some(PercentOrFixed::Percent(width));
3143 self
3144 }
3145 pub fn with_height_fixed(mut self, height: usize) -> Self {
3146 self.height = Some(PercentOrFixed::Fixed(height));
3147 self
3148 }
3149 pub fn with_height_percent(mut self, height: usize) -> Self {
3150 if height > 100 {
3151 eprintln!("height must be between 0 and 100");
3152 return self;
3153 }
3154 self.height = Some(PercentOrFixed::Percent(height));
3155 self
3156 }
3157}
3158
3159impl From<PaneGeom> for FloatingPaneCoordinates {
3160 fn from(pane_geom: PaneGeom) -> Self {
3161 FloatingPaneCoordinates {
3162 x: Some(PercentOrFixed::Fixed(pane_geom.x)),
3163 y: Some(PercentOrFixed::Fixed(pane_geom.y)),
3164 width: Some(PercentOrFixed::Fixed(pane_geom.cols.as_usize())),
3165 height: Some(PercentOrFixed::Fixed(pane_geom.rows.as_usize())),
3166 pinned: Some(pane_geom.is_pinned),
3167 borderless: None,
3168 }
3169 }
3170}
3171
3172#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3173pub struct OriginatingPlugin {
3174 pub plugin_id: u32,
3175 pub client_id: ClientId,
3176 pub context: Context,
3177}
3178
3179impl OriginatingPlugin {
3180 pub fn new(plugin_id: u32, client_id: ClientId, context: Context) -> Self {
3181 OriginatingPlugin {
3182 plugin_id,
3183 client_id,
3184 context,
3185 }
3186 }
3187}
3188
3189#[derive(ValueEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
3190pub enum WebSharing {
3191 #[serde(alias = "on")]
3192 On,
3193 #[serde(alias = "off")]
3194 Off,
3195 #[serde(alias = "disabled")]
3196 Disabled,
3197}
3198
3199impl Default for WebSharing {
3200 fn default() -> Self {
3201 Self::Off
3202 }
3203}
3204
3205impl WebSharing {
3206 pub fn is_on(&self) -> bool {
3207 match self {
3208 WebSharing::On => true,
3209 _ => false,
3210 }
3211 }
3212 pub fn web_clients_allowed(&self) -> bool {
3213 match self {
3214 WebSharing::On => true,
3215 _ => false,
3216 }
3217 }
3218 pub fn sharing_is_disabled(&self) -> bool {
3219 match self {
3220 WebSharing::Disabled => true,
3221 _ => false,
3222 }
3223 }
3224 pub fn set_sharing(&mut self) -> bool {
3225 match self {
3227 WebSharing::On => true,
3228 WebSharing::Off => {
3229 *self = WebSharing::On;
3230 true
3231 },
3232 WebSharing::Disabled => false,
3233 }
3234 }
3235 pub fn set_not_sharing(&mut self) -> bool {
3236 match self {
3238 WebSharing::On => {
3239 *self = WebSharing::Off;
3240 true
3241 },
3242 WebSharing::Off => true,
3243 WebSharing::Disabled => false,
3244 }
3245 }
3246}
3247
3248impl FromStr for WebSharing {
3249 type Err = String;
3250 fn from_str(s: &str) -> Result<Self, Self::Err> {
3251 match s {
3252 "On" | "on" => Ok(Self::On),
3253 "Off" | "off" => Ok(Self::Off),
3254 "Disabled" | "disabled" => Ok(Self::Disabled),
3255 _ => Err(format!("No such option: {}", s)),
3256 }
3257 }
3258}
3259
3260#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3261pub enum NewPanePlacement {
3262 NoPreference {
3263 borderless: Option<bool>,
3264 },
3265 Tiled {
3266 direction: Option<Direction>,
3267 borderless: Option<bool>,
3268 },
3269 Floating(Option<FloatingPaneCoordinates>),
3270 InPlace {
3271 pane_id_to_replace: Option<PaneId>,
3272 close_replaced_pane: bool,
3273 borderless: Option<bool>,
3274 },
3275 Stacked {
3276 pane_id_to_stack_under: Option<PaneId>,
3277 borderless: Option<bool>,
3278 },
3279}
3280
3281impl Default for NewPanePlacement {
3282 fn default() -> Self {
3283 NewPanePlacement::NoPreference { borderless: None }
3284 }
3285}
3286
3287impl NewPanePlacement {
3288 pub fn with_floating_pane_coordinates(
3289 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3290 ) -> Self {
3291 NewPanePlacement::Floating(floating_pane_coordinates)
3292 }
3293 pub fn with_should_be_in_place(
3294 self,
3295 should_be_in_place: bool,
3296 close_replaced_pane: bool,
3297 ) -> Self {
3298 if should_be_in_place {
3299 NewPanePlacement::InPlace {
3300 pane_id_to_replace: None,
3301 close_replaced_pane,
3302 borderless: None,
3303 }
3304 } else {
3305 self
3306 }
3307 }
3308 pub fn with_pane_id_to_replace(
3309 pane_id_to_replace: Option<PaneId>,
3310 close_replaced_pane: bool,
3311 ) -> Self {
3312 NewPanePlacement::InPlace {
3313 pane_id_to_replace,
3314 close_replaced_pane,
3315 borderless: None,
3316 }
3317 }
3318 pub fn should_float(&self) -> Option<bool> {
3319 match self {
3320 NewPanePlacement::Floating(_) => Some(true),
3321 NewPanePlacement::Tiled { .. } => Some(false),
3322 _ => None,
3323 }
3324 }
3325 pub fn floating_pane_coordinates(&self) -> Option<FloatingPaneCoordinates> {
3326 match self {
3327 NewPanePlacement::Floating(floating_pane_coordinates) => {
3328 floating_pane_coordinates.clone()
3329 },
3330 _ => None,
3331 }
3332 }
3333 pub fn should_stack(&self) -> bool {
3334 match self {
3335 NewPanePlacement::Stacked { .. } => true,
3336 _ => false,
3337 }
3338 }
3339 pub fn id_of_stack_root(&self) -> Option<PaneId> {
3340 match self {
3341 NewPanePlacement::Stacked {
3342 pane_id_to_stack_under,
3343 ..
3344 } => *pane_id_to_stack_under,
3345 _ => None,
3346 }
3347 }
3348 pub fn get_borderless(&self) -> Option<bool> {
3349 match self {
3350 NewPanePlacement::NoPreference { borderless } => *borderless,
3351 NewPanePlacement::Tiled { borderless, .. } => *borderless,
3352 NewPanePlacement::Floating(coords) => coords.as_ref().and_then(|c| c.borderless),
3353 NewPanePlacement::InPlace { borderless, .. } => *borderless,
3354 NewPanePlacement::Stacked { borderless, .. } => *borderless,
3355 }
3356 }
3357}
3358
3359type Context = BTreeMap<String, String>;
3360
3361#[derive(Debug, Clone, EnumDiscriminants, Display)]
3362#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
3363#[strum_discriminants(name(CommandType))]
3364pub enum PluginCommand {
3365 Subscribe(HashSet<EventType>),
3366 Unsubscribe(HashSet<EventType>),
3367 SetSelectable(bool),
3368 ShowCursor(Option<(usize, usize)>),
3369 GetPluginIds,
3370 GetZellijVersion,
3371 OpenFile(FileToOpen, Context),
3372 OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3373 OpenTerminal(FileToOpen), OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), OpenCommandPane(CommandToRun, Context),
3376 OpenCommandPaneFloating(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3377 SwitchTabTo(u32), SetTimeout(f64), ExecCmd(Vec<String>),
3380 PostMessageTo(PluginMessage),
3381 PostMessageToPlugin(PluginMessage),
3382 HideSelf,
3383 ShowSelf(bool), SwitchToMode(InputMode),
3385 NewTabsWithLayout(String), NewTab {
3387 name: Option<String>,
3388 cwd: Option<String>,
3389 },
3390 NewTabUnfocused {
3391 name: Option<String>,
3392 cwd: Option<String>,
3393 },
3394 NewTiledPaneInTab {
3395 tab_position: usize,
3396 },
3397 ToggleFloatingPanes {
3398 tab_id: Option<u64>,
3399 },
3400 NewPane,
3401 GoToNextTab,
3402 GoToPreviousTab,
3403 Resize(Resize),
3404 ResizeWithDirection(ResizeStrategy),
3405 FocusNextPane,
3406 FocusPreviousPane,
3407 FocusLastPane,
3408 MoveFocus(Direction),
3409 MoveFocusOrTab(Direction),
3410 Detach,
3411 EditScrollback,
3412 Write(Vec<u8>), WriteChars(String),
3414 ToggleTab,
3415 MovePane,
3416 MovePaneWithDirection(Direction),
3417 ClearScreen,
3418 ScrollUp,
3419 ScrollDown,
3420 ScrollToTop,
3421 ScrollToBottom,
3422 PageScrollUp,
3423 PageScrollDown,
3424 ToggleFocusFullscreen,
3425 ToggleFocusNoUiFullscreen,
3426 TogglePaneFrames,
3427 SetPaneFrameStyle(PaneFrameStyle),
3428 TogglePaneEmbedOrEject,
3429 UndoRenamePane,
3430 CloseFocus,
3431 ToggleActiveTabSync,
3432 CloseFocusedTab,
3433 UndoRenameTab,
3434 QuitZellij,
3435 PreviousSwapLayout,
3436 NextSwapLayout,
3437 GoToTabName(String),
3438 FocusOrCreateTab(String),
3439 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>),
3450 SwitchSession(ConnectToSession),
3451 DeleteDeadSession(String), DeleteAllDeadSessions, OpenTerminalInPlace(FileToOpen), OpenFileInPlace(FileToOpen, Context),
3455 OpenCommandPaneInPlace(CommandToRun, Context),
3456 RunCommand(
3457 Vec<String>, BTreeMap<String, String>, PathBuf, BTreeMap<String, String>, ),
3462 WebRequest(
3463 String, HttpVerb,
3465 BTreeMap<String, String>, Vec<u8>, BTreeMap<String, String>, ),
3469 RenameSession(String), UnblockCliPipeInput(String), BlockCliPipeInput(String), CliPipeOutput(String, String), MessageToPlugin(MessageToPlugin),
3474 DisconnectOtherClients,
3475 KillSessions(Vec<String>), ScanHostFolder(PathBuf), WatchFilesystem,
3478 DumpSessionLayout {
3479 tab_index: Option<usize>,
3480 },
3481 CloseSelf,
3482 NewTabsWithLayoutInfo(LayoutInfo),
3483 Reconfigure(String, bool), HidePaneWithId(PaneId),
3486 ShowPaneWithId(PaneId, bool, bool), OpenCommandPaneBackground(CommandToRun, Context),
3488 RerunCommandPane(u32), ResizePaneIdWithDirection(ResizeStrategy, PaneId),
3490 EditScrollbackForPaneWithId(PaneId),
3491 GetPaneScrollback {
3492 pane_id: PaneId,
3493 get_full_scrollback: bool,
3494 },
3495 WriteToPaneId(Vec<u8>, PaneId),
3496 WriteCharsToPaneId(String, PaneId),
3497 SendSigintToPaneId(PaneId),
3498 SendSigkillToPaneId(PaneId),
3499 GetPanePid {
3500 pane_id: PaneId,
3501 },
3502 GetPaneRunningCommand {
3503 pane_id: PaneId,
3504 },
3505 GetPaneCwd {
3506 pane_id: PaneId,
3507 },
3508 MovePaneWithPaneId(PaneId),
3509 MovePaneWithPaneIdInDirection(PaneId, Direction),
3510 ClearScreenForPaneId(PaneId),
3511 ScrollUpInPaneId(PaneId),
3512 ScrollDownInPaneId(PaneId),
3513 ScrollToTopInPaneId(PaneId),
3514 ScrollToBottomInPaneId(PaneId),
3515 PageScrollUpInPaneId(PaneId),
3516 PageScrollDownInPaneId(PaneId),
3517 TogglePaneIdFullscreen(PaneId),
3518 TogglePaneEmbedOrEjectForPaneId(PaneId),
3519 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 {
3534 url: String,
3535 config: BTreeMap<String, String>,
3536 load_in_background: bool,
3537 skip_plugin_cache: bool,
3538 },
3539 RebindKeys {
3540 keys_to_rebind: Vec<(InputMode, KeyWithModifier, Vec<Action>)>,
3541 keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
3542 write_config_to_disk: bool,
3543 },
3544 ListClients,
3545 ChangeHostFolder(PathBuf),
3546 SetFloatingPanePinned(PaneId, bool), StackPanes(Vec<PaneId>),
3548 ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
3549 TogglePaneBorderless(PaneId),
3550 SetPaneBorderless(PaneId, bool),
3551 OpenCommandPaneNearPlugin(CommandToRun, Context),
3552 OpenTerminalNearPlugin(FileToOpen),
3553 OpenTerminalFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>),
3554 OpenTerminalInPlaceOfPlugin(FileToOpen, bool), OpenCommandPaneFloatingNearPlugin(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3556 OpenCommandPaneInPlaceOfPlugin(CommandToRun, bool, Context), OpenFileNearPlugin(FileToOpen, Context),
3559 OpenFileFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3560 StartWebServer,
3561 StopWebServer,
3562 ShareCurrentSession,
3563 StopSharingCurrentSession,
3564 OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
3570 FloatMultiplePanes(Vec<PaneId>),
3571 EmbedMultiplePanes(Vec<PaneId>),
3572 QueryWebServerStatus,
3573 SetSelfMouseSelectionSupport(bool),
3574 GenerateWebLoginToken(Option<String>, bool), RevokeWebLoginToken(String), ListWebLoginTokens,
3577 RevokeAllWebLoginTokens,
3578 RenameWebLoginToken(String, String), InterceptKeyPresses,
3580 ClearKeyPressesIntercepts,
3581 ReplacePaneWithExistingPane(PaneId, PaneId, bool), RunAction(Action, BTreeMap<String, String>),
3584 CopyToClipboard(String), OverrideLayout(
3586 LayoutInfo,
3587 bool, bool, bool, BTreeMap<String, String>, ),
3592 SaveLayout {
3593 layout_name: String,
3594 layout_kdl: String,
3595 overwrite: bool,
3596 },
3597 DeleteLayout {
3598 layout_name: String,
3599 },
3600 RenameLayout {
3601 old_layout_name: String,
3602 new_layout_name: String,
3603 },
3604 EditLayout {
3605 layout_name: String,
3606 context: Context,
3607 },
3608 GenerateRandomName,
3609 DumpLayout(String),
3610 ParseLayout(String), GetLayoutDir,
3612 GetFocusedPaneInfo,
3613 SaveSession,
3614 CurrentSessionLastSavedTime,
3615 GetPaneInfo(PaneId),
3616 GetTabInfo(usize), GetSessionEnvironmentVariables,
3618 OpenCommandPaneInNewTab(CommandToRun, Context),
3619 OpenPluginPaneInNewTab {
3620 plugin_url: String,
3621 configuration: BTreeMap<String, String>,
3622 context: Context,
3623 },
3624 OpenEditorPaneInNewTab(FileToOpen, Context),
3625 OpenCommandPaneInPlaceOfPaneId(PaneId, CommandToRun, bool, Context), OpenTerminalPaneInPlaceOfPaneId(PaneId, FileToOpen, bool),
3627 OpenEditPaneInPlaceOfPaneId(PaneId, FileToOpen, bool, Context),
3628 HideFloatingPanes {
3629 tab_id: Option<usize>,
3630 },
3631 ShowFloatingPanes {
3632 tab_id: Option<usize>,
3633 },
3634 SetPaneColor(PaneId, Option<String>, Option<String>), SetPaneRegexHighlights(PaneId, Vec<RegexHighlight>),
3636 ClearPaneHighlights(PaneId),
3637 OpenPluginPaneFloating {
3638 plugin_url: String,
3639 configuration: BTreeMap<String, String>,
3640 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3641 context: BTreeMap<String, String>,
3642 },
3643 ListWindowsVolumes,
3644 GetSessionList,
3645 KillSessionsAndReply(Vec<String>), DeleteDeadSessionAndReply(String), DeleteAllDeadSessionsAndReply, SetSoftKeyboard(bool),
3649 FocusHostSession,
3650}
3651
3652#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3654pub struct OpenPaneInNewTabResponse {
3655 pub tab_id: Option<usize>,
3656 pub pane_id: Option<PaneId>,
3657}
3658
3659pub type NewTabResponse = Option<usize>;
3661pub type NewTabUnfocusedResponse = Option<usize>;
3662pub type NewTabsResponse = Vec<usize>;
3663pub type FocusOrCreateTabResponse = Option<usize>;
3664pub type BreakPanesToNewTabResponse = Option<usize>;
3665pub type BreakPanesToTabWithIndexResponse = Option<usize>;
3666pub type BreakPanesToTabWithIdResponse = Option<usize>;
3667
3668pub type OpenFileResponse = Option<PaneId>;
3670pub type OpenFileFloatingResponse = Option<PaneId>;
3671pub type OpenFileInPlaceResponse = Option<PaneId>;
3672pub type OpenFileNearPluginResponse = Option<PaneId>;
3673pub type OpenFileFloatingNearPluginResponse = Option<PaneId>;
3674pub type OpenFileInPlaceOfPluginResponse = Option<PaneId>;
3675
3676pub type OpenTerminalResponse = Option<PaneId>;
3677pub type OpenTerminalFloatingResponse = Option<PaneId>;
3678pub type OpenTerminalInPlaceResponse = Option<PaneId>;
3679pub type OpenTerminalNearPluginResponse = Option<PaneId>;
3680pub type OpenTerminalFloatingNearPluginResponse = Option<PaneId>;
3681pub type OpenTerminalInPlaceOfPluginResponse = Option<PaneId>;
3682pub type NewTiledPaneInTabResponse = Option<PaneId>;
3683
3684pub type OpenCommandPaneResponse = Option<PaneId>;
3685pub type OpenCommandPaneFloatingResponse = Option<PaneId>;
3686pub type OpenCommandPaneInPlaceResponse = Option<PaneId>;
3687pub type OpenCommandPaneNearPluginResponse = Option<PaneId>;
3688pub type OpenCommandPaneFloatingNearPluginResponse = Option<PaneId>;
3689pub type OpenCommandPaneInPlaceOfPluginResponse = Option<PaneId>;
3690pub type OpenCommandPaneBackgroundResponse = Option<PaneId>;
3691pub type OpenCommandPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3692pub type OpenTerminalPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3693pub type OpenEditPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3694pub type OpenPluginPaneFloatingResponse = Option<PaneId>;
3695
3696#[test]
3697pub fn can_parse_unicode_bare_keys() {
3698 let key = "1087"; assert_eq!(
3700 BareKey::from_bytes_with_u(&key.as_bytes()),
3701 Some(BareKey::Char('п')),
3702 "Can parse a bare 'п' keypress"
3703 );
3704 let key = "1255"; assert_eq!(
3706 BareKey::from_bytes_with_u(&key.as_bytes()),
3707 Some(BareKey::Char('ӧ')),
3708 "Can parse a bare 'ӧ' keypress"
3709 );
3710 let key = "1098"; assert_eq!(
3712 BareKey::from_bytes_with_u(&key.as_bytes()),
3713 Some(BareKey::Char('ъ')),
3714 "Can parse a bare 'ъ' keypress"
3715 );
3716}