1use crate::home::default_layout_dir;
2use crate::input::actions::Action;
3use crate::input::config::ConversionError;
4use crate::input::keybinds::Keybinds;
5use crate::input::layout::{RunPlugin, SplitSize};
6use crate::pane_size::PaneGeom;
7use crate::shared::{colors as default_colors, eightbit_to_rgb};
8use clap::ArgEnum;
9use serde::{Deserialize, Serialize};
10use std::collections::{BTreeMap, BTreeSet, HashMap, HashSet};
11use std::fmt;
12use std::fs::Metadata;
13use std::net::IpAddr;
14use std::path::{Path, PathBuf};
15use std::str::{self, FromStr};
16use std::time::Duration;
17use strum_macros::{Display, EnumDiscriminants, EnumIter, EnumString, ToString};
18
19#[cfg(not(target_family = "wasm"))]
20use termwiz::{
21 escape::csi::KittyKeyboardFlags,
22 input::{KeyCode, KeyCodeEncodeModes, KeyboardEncoding, Modifiers},
23};
24
25pub type ClientId = u16; pub fn client_id_to_colors(
28 client_id: ClientId,
29 colors: MultiplayerColors,
30) -> Option<(PaletteColor, PaletteColor)> {
31 let black = PaletteColor::EightBit(default_colors::BLACK);
33 match client_id {
34 1 => Some((colors.player_1, black)),
35 2 => Some((colors.player_2, black)),
36 3 => Some((colors.player_3, black)),
37 4 => Some((colors.player_4, black)),
38 5 => Some((colors.player_5, black)),
39 6 => Some((colors.player_6, black)),
40 7 => Some((colors.player_7, black)),
41 8 => Some((colors.player_8, black)),
42 9 => Some((colors.player_9, black)),
43 10 => Some((colors.player_10, black)),
44 _ => None,
45 }
46}
47
48pub fn single_client_color(colors: Palette) -> (PaletteColor, PaletteColor) {
49 (colors.green, colors.black)
50}
51
52impl FromStr for KeyWithModifier {
53 type Err = Box<dyn std::error::Error>;
54 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
55 let mut key_string_parts: Vec<&str> = key_str.split_ascii_whitespace().collect();
56 let bare_key: BareKey = BareKey::from_str(key_string_parts.pop().ok_or("empty key")?)?;
57 let mut key_modifiers: BTreeSet<KeyModifier> = BTreeSet::new();
58 for stringified_modifier in key_string_parts {
59 key_modifiers.insert(KeyModifier::from_str(stringified_modifier)?);
60 }
61 Ok(KeyWithModifier {
62 bare_key,
63 key_modifiers,
64 })
65 }
66}
67
68#[derive(Debug, Clone, Eq, Serialize, Deserialize, PartialOrd, Ord)]
69pub struct KeyWithModifier {
70 pub bare_key: BareKey,
71 pub key_modifiers: BTreeSet<KeyModifier>,
72}
73
74impl PartialEq for KeyWithModifier {
75 fn eq(&self, other: &Self) -> bool {
76 match (self.bare_key, other.bare_key) {
77 (BareKey::Char(self_char), BareKey::Char(other_char))
78 if self_char.to_ascii_lowercase() == other_char.to_ascii_lowercase() =>
79 {
80 let mut self_cloned = self.clone();
81 let mut other_cloned = other.clone();
82 if self_char.is_ascii_uppercase() {
83 self_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
84 self_cloned.key_modifiers.insert(KeyModifier::Shift);
85 }
86 if other_char.is_ascii_uppercase() {
87 other_cloned.bare_key = BareKey::Char(self_char.to_ascii_lowercase());
88 other_cloned.key_modifiers.insert(KeyModifier::Shift);
89 }
90 self_cloned.bare_key == other_cloned.bare_key
91 && self_cloned.key_modifiers == other_cloned.key_modifiers
92 },
93 _ => self.bare_key == other.bare_key && self.key_modifiers == other.key_modifiers,
94 }
95 }
96}
97
98impl Hash for KeyWithModifier {
99 fn hash<H: Hasher>(&self, state: &mut H) {
100 match self.bare_key {
101 BareKey::Char(character) if character.is_ascii_uppercase() => {
102 let mut to_hash = self.clone();
103 to_hash.bare_key = BareKey::Char(character.to_ascii_lowercase());
104 to_hash.key_modifiers.insert(KeyModifier::Shift);
105 to_hash.bare_key.hash(state);
106 to_hash.key_modifiers.hash(state);
107 },
108 _ => {
109 self.bare_key.hash(state);
110 self.key_modifiers.hash(state);
111 },
112 }
113 }
114}
115
116impl fmt::Display for KeyWithModifier {
117 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
118 if self.key_modifiers.is_empty() {
119 write!(f, "{}", self.bare_key)
120 } else {
121 write!(
122 f,
123 "{} {}",
124 self.key_modifiers
125 .iter()
126 .map(|m| m.to_string())
127 .collect::<Vec<_>>()
128 .join(" "),
129 self.bare_key
130 )
131 }
132 }
133}
134
135#[cfg(not(target_family = "wasm"))]
136impl Into<Modifiers> for &KeyModifier {
137 fn into(self) -> Modifiers {
138 match self {
139 KeyModifier::Shift => Modifiers::SHIFT,
140 KeyModifier::Alt => Modifiers::ALT,
141 KeyModifier::Ctrl => Modifiers::CTRL,
142 KeyModifier::Super => Modifiers::SUPER,
143 }
144 }
145}
146
147#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
148pub enum BareKey {
149 PageDown,
150 PageUp,
151 Left,
152 Down,
153 Up,
154 Right,
155 Home,
156 End,
157 Backspace,
158 Delete,
159 Insert,
160 F(u8),
161 Char(char),
162 Tab,
163 Esc,
164 Enter,
165 CapsLock,
166 ScrollLock,
167 NumLock,
168 PrintScreen,
169 Pause,
170 Menu,
171}
172
173impl fmt::Display for BareKey {
174 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
175 match self {
176 BareKey::PageDown => write!(f, "PgDn"),
177 BareKey::PageUp => write!(f, "PgUp"),
178 BareKey::Left => write!(f, "←"),
179 BareKey::Down => write!(f, "↓"),
180 BareKey::Up => write!(f, "↑"),
181 BareKey::Right => write!(f, "→"),
182 BareKey::Home => write!(f, "HOME"),
183 BareKey::End => write!(f, "END"),
184 BareKey::Backspace => write!(f, "BACKSPACE"),
185 BareKey::Delete => write!(f, "DEL"),
186 BareKey::Insert => write!(f, "INS"),
187 BareKey::F(index) => write!(f, "F{}", index),
188 BareKey::Char(' ') => write!(f, "SPACE"),
189 BareKey::Char(character) => write!(f, "{}", character),
190 BareKey::Tab => write!(f, "TAB"),
191 BareKey::Esc => write!(f, "ESC"),
192 BareKey::Enter => write!(f, "ENTER"),
193 BareKey::CapsLock => write!(f, "CAPSlOCK"),
194 BareKey::ScrollLock => write!(f, "SCROLLlOCK"),
195 BareKey::NumLock => write!(f, "NUMLOCK"),
196 BareKey::PrintScreen => write!(f, "PRINTSCREEN"),
197 BareKey::Pause => write!(f, "PAUSE"),
198 BareKey::Menu => write!(f, "MENU"),
199 }
200 }
201}
202
203impl FromStr for BareKey {
204 type Err = Box<dyn std::error::Error>;
205 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
206 match key_str.to_ascii_lowercase().as_str() {
207 "pagedown" => Ok(BareKey::PageDown),
208 "pageup" => Ok(BareKey::PageUp),
209 "left" => Ok(BareKey::Left),
210 "down" => Ok(BareKey::Down),
211 "up" => Ok(BareKey::Up),
212 "right" => Ok(BareKey::Right),
213 "home" => Ok(BareKey::Home),
214 "end" => Ok(BareKey::End),
215 "backspace" => Ok(BareKey::Backspace),
216 "delete" => Ok(BareKey::Delete),
217 "insert" => Ok(BareKey::Insert),
218 "f1" => Ok(BareKey::F(1)),
219 "f2" => Ok(BareKey::F(2)),
220 "f3" => Ok(BareKey::F(3)),
221 "f4" => Ok(BareKey::F(4)),
222 "f5" => Ok(BareKey::F(5)),
223 "f6" => Ok(BareKey::F(6)),
224 "f7" => Ok(BareKey::F(7)),
225 "f8" => Ok(BareKey::F(8)),
226 "f9" => Ok(BareKey::F(9)),
227 "f10" => Ok(BareKey::F(10)),
228 "f11" => Ok(BareKey::F(11)),
229 "f12" => Ok(BareKey::F(12)),
230 "tab" => Ok(BareKey::Tab),
231 "esc" => Ok(BareKey::Esc),
232 "enter" => Ok(BareKey::Enter),
233 "capslock" => Ok(BareKey::CapsLock),
234 "scrolllock" => Ok(BareKey::ScrollLock),
235 "numlock" => Ok(BareKey::NumLock),
236 "printscreen" => Ok(BareKey::PrintScreen),
237 "pause" => Ok(BareKey::Pause),
238 "menu" => Ok(BareKey::Menu),
239 "space" => Ok(BareKey::Char(' ')),
240 _ => {
241 if key_str.chars().count() == 1 {
242 if let Some(character) = key_str.chars().next() {
243 return Ok(BareKey::Char(character));
244 }
245 }
246 Err("unsupported key".into())
247 },
248 }
249 }
250}
251
252#[derive(
253 Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord, ToString,
254)]
255pub enum KeyModifier {
256 Ctrl,
257 Alt,
258 Shift,
259 Super,
260}
261
262impl FromStr for KeyModifier {
263 type Err = Box<dyn std::error::Error>;
264 fn from_str(key_str: &str) -> Result<Self, Self::Err> {
265 match key_str.to_ascii_lowercase().as_str() {
266 "shift" => Ok(KeyModifier::Shift),
267 "alt" => Ok(KeyModifier::Alt),
268 "ctrl" => Ok(KeyModifier::Ctrl),
269 "super" => Ok(KeyModifier::Super),
270 _ => Err("unsupported modifier".into()),
271 }
272 }
273}
274
275impl BareKey {
276 pub fn from_bytes_with_u(bytes: &[u8]) -> Option<Self> {
277 match str::from_utf8(bytes) {
278 Ok("27") => Some(BareKey::Esc),
279 Ok("13") => Some(BareKey::Enter),
280 Ok("9") => Some(BareKey::Tab),
281 Ok("127") => Some(BareKey::Backspace),
282 Ok("57358") => Some(BareKey::CapsLock),
283 Ok("57359") => Some(BareKey::ScrollLock),
284 Ok("57360") => Some(BareKey::NumLock),
285 Ok("57361") => Some(BareKey::PrintScreen),
286 Ok("57362") => Some(BareKey::Pause),
287 Ok("57363") => Some(BareKey::Menu),
288 Ok("57399") => Some(BareKey::Char('0')),
289 Ok("57400") => Some(BareKey::Char('1')),
290 Ok("57401") => Some(BareKey::Char('2')),
291 Ok("57402") => Some(BareKey::Char('3')),
292 Ok("57403") => Some(BareKey::Char('4')),
293 Ok("57404") => Some(BareKey::Char('5')),
294 Ok("57405") => Some(BareKey::Char('6')),
295 Ok("57406") => Some(BareKey::Char('7')),
296 Ok("57407") => Some(BareKey::Char('8')),
297 Ok("57408") => Some(BareKey::Char('9')),
298 Ok("57409") => Some(BareKey::Char('.')),
299 Ok("57410") => Some(BareKey::Char('/')),
300 Ok("57411") => Some(BareKey::Char('*')),
301 Ok("57412") => Some(BareKey::Char('-')),
302 Ok("57413") => Some(BareKey::Char('+')),
303 Ok("57414") => Some(BareKey::Enter),
304 Ok("57415") => Some(BareKey::Char('=')),
305 Ok("57417") => Some(BareKey::Left),
306 Ok("57418") => Some(BareKey::Right),
307 Ok("57419") => Some(BareKey::Up),
308 Ok("57420") => Some(BareKey::Down),
309 Ok("57421") => Some(BareKey::PageUp),
310 Ok("57422") => Some(BareKey::PageDown),
311 Ok("57423") => Some(BareKey::Home),
312 Ok("57424") => Some(BareKey::End),
313 Ok("57425") => Some(BareKey::Insert),
314 Ok("57426") => Some(BareKey::Delete),
315 Ok(num) => u8::from_str_radix(num, 10)
316 .ok()
317 .map(|n| BareKey::Char((n as char).to_ascii_lowercase())),
318 _ => None,
319 }
320 }
321 pub fn from_bytes_with_tilde(bytes: &[u8]) -> Option<Self> {
322 match str::from_utf8(bytes) {
323 Ok("2") => Some(BareKey::Insert),
324 Ok("3") => Some(BareKey::Delete),
325 Ok("5") => Some(BareKey::PageUp),
326 Ok("6") => Some(BareKey::PageDown),
327 Ok("7") => Some(BareKey::Home),
328 Ok("8") => Some(BareKey::End),
329 Ok("11") => Some(BareKey::F(1)),
330 Ok("12") => Some(BareKey::F(2)),
331 Ok("13") => Some(BareKey::F(3)),
332 Ok("14") => Some(BareKey::F(4)),
333 Ok("15") => Some(BareKey::F(5)),
334 Ok("17") => Some(BareKey::F(6)),
335 Ok("18") => Some(BareKey::F(7)),
336 Ok("19") => Some(BareKey::F(8)),
337 Ok("20") => Some(BareKey::F(9)),
338 Ok("21") => Some(BareKey::F(10)),
339 Ok("23") => Some(BareKey::F(11)),
340 Ok("24") => Some(BareKey::F(12)),
341 _ => None,
342 }
343 }
344 pub fn from_bytes_with_no_ending_byte(bytes: &[u8]) -> Option<Self> {
345 match str::from_utf8(bytes) {
346 Ok("1D") | Ok("D") => Some(BareKey::Left),
347 Ok("1C") | Ok("C") => Some(BareKey::Right),
348 Ok("1A") | Ok("A") => Some(BareKey::Up),
349 Ok("1B") | Ok("B") => Some(BareKey::Down),
350 Ok("1H") | Ok("H") => Some(BareKey::Home),
351 Ok("1F") | Ok("F") => Some(BareKey::End),
352 Ok("1P") | Ok("P") => Some(BareKey::F(1)),
353 Ok("1Q") | Ok("Q") => Some(BareKey::F(2)),
354 Ok("1S") | Ok("S") => Some(BareKey::F(4)),
355 _ => None,
356 }
357 }
358}
359
360bitflags::bitflags! {
361 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
362 struct ModifierFlags: u8 {
363 const SHIFT = 0b0000_0001;
364 const ALT = 0b0000_0010;
365 const CONTROL = 0b0000_0100;
366 const SUPER = 0b0000_1000;
367 const HYPER = 0b0001_0000;
370 const META = 0b0010_0000;
371 const CAPS_LOCK = 0b0100_0000;
372 const NUM_LOCK = 0b1000_0000;
373 }
374}
375
376impl KeyModifier {
377 pub fn from_bytes(bytes: &[u8]) -> BTreeSet<KeyModifier> {
378 let modifier_flags = str::from_utf8(bytes)
379 .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();
384 if let Some(modifier_flags) = modifier_flags {
385 for name in modifier_flags.iter() {
386 match name {
387 ModifierFlags::SHIFT => key_modifiers.insert(KeyModifier::Shift),
388 ModifierFlags::ALT => key_modifiers.insert(KeyModifier::Alt),
389 ModifierFlags::CONTROL => key_modifiers.insert(KeyModifier::Ctrl),
390 ModifierFlags::SUPER => key_modifiers.insert(KeyModifier::Super),
391 _ => false,
392 };
393 }
394 }
395 key_modifiers
396 }
397}
398
399impl KeyWithModifier {
400 pub fn new(bare_key: BareKey) -> Self {
401 KeyWithModifier {
402 bare_key,
403 key_modifiers: BTreeSet::new(),
404 }
405 }
406 pub fn new_with_modifiers(bare_key: BareKey, key_modifiers: BTreeSet<KeyModifier>) -> Self {
407 KeyWithModifier {
408 bare_key,
409 key_modifiers,
410 }
411 }
412 pub fn with_shift_modifier(mut self) -> Self {
413 self.key_modifiers.insert(KeyModifier::Shift);
414 self
415 }
416 pub fn with_alt_modifier(mut self) -> Self {
417 self.key_modifiers.insert(KeyModifier::Alt);
418 self
419 }
420 pub fn with_ctrl_modifier(mut self) -> Self {
421 self.key_modifiers.insert(KeyModifier::Ctrl);
422 self
423 }
424 pub fn with_super_modifier(mut self) -> Self {
425 self.key_modifiers.insert(KeyModifier::Super);
426 self
427 }
428 pub fn from_bytes_with_u(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
429 let bare_key = BareKey::from_bytes_with_u(number_bytes);
431 match bare_key {
432 Some(bare_key) => {
433 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
434 Some(KeyWithModifier {
435 bare_key,
436 key_modifiers,
437 })
438 },
439 _ => None,
440 }
441 }
442 pub fn from_bytes_with_tilde(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
443 let bare_key = BareKey::from_bytes_with_tilde(number_bytes);
445 match bare_key {
446 Some(bare_key) => {
447 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
448 Some(KeyWithModifier {
449 bare_key,
450 key_modifiers,
451 })
452 },
453 _ => None,
454 }
455 }
456 pub fn from_bytes_with_no_ending_byte(
457 number_bytes: &[u8],
458 modifier_bytes: &[u8],
459 ) -> Option<Self> {
460 let bare_key = BareKey::from_bytes_with_no_ending_byte(number_bytes);
462 match bare_key {
463 Some(bare_key) => {
464 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
465 Some(KeyWithModifier {
466 bare_key,
467 key_modifiers,
468 })
469 },
470 _ => None,
471 }
472 }
473 pub fn strip_common_modifiers(&self, common_modifiers: &Vec<KeyModifier>) -> Self {
474 let common_modifiers: BTreeSet<&KeyModifier> = common_modifiers.into_iter().collect();
475 KeyWithModifier {
476 bare_key: self.bare_key.clone(),
477 key_modifiers: self
478 .key_modifiers
479 .iter()
480 .filter(|m| !common_modifiers.contains(m))
481 .cloned()
482 .collect(),
483 }
484 }
485 pub fn is_key_without_modifier(&self, key: BareKey) -> bool {
486 self.bare_key == key && self.key_modifiers.is_empty()
487 }
488 pub fn is_key_with_ctrl_modifier(&self, key: BareKey) -> bool {
489 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Ctrl)
490 }
491 pub fn is_key_with_alt_modifier(&self, key: BareKey) -> bool {
492 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Alt)
493 }
494 pub fn is_key_with_shift_modifier(&self, key: BareKey) -> bool {
495 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Shift)
496 }
497 pub fn is_key_with_super_modifier(&self, key: BareKey) -> bool {
498 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Super)
499 }
500 pub fn is_cancel_key(&self) -> bool {
501 self.bare_key == BareKey::Esc
503 }
504 #[cfg(not(target_family = "wasm"))]
505 pub fn to_termwiz_modifiers(&self) -> Modifiers {
506 let mut modifiers = Modifiers::empty();
507 for modifier in &self.key_modifiers {
508 modifiers.set(modifier.into(), true);
509 }
510 modifiers
511 }
512 #[cfg(not(target_family = "wasm"))]
513 pub fn to_termwiz_keycode(&self) -> KeyCode {
514 match self.bare_key {
515 BareKey::PageDown => KeyCode::PageDown,
516 BareKey::PageUp => KeyCode::PageUp,
517 BareKey::Left => KeyCode::LeftArrow,
518 BareKey::Down => KeyCode::DownArrow,
519 BareKey::Up => KeyCode::UpArrow,
520 BareKey::Right => KeyCode::RightArrow,
521 BareKey::Home => KeyCode::Home,
522 BareKey::End => KeyCode::End,
523 BareKey::Backspace => KeyCode::Backspace,
524 BareKey::Delete => KeyCode::Delete,
525 BareKey::Insert => KeyCode::Insert,
526 BareKey::F(index) => KeyCode::Function(index),
527 BareKey::Char(character) => KeyCode::Char(character),
528 BareKey::Tab => KeyCode::Tab,
529 BareKey::Esc => KeyCode::Escape,
530 BareKey::Enter => KeyCode::Enter,
531 BareKey::CapsLock => KeyCode::CapsLock,
532 BareKey::ScrollLock => KeyCode::ScrollLock,
533 BareKey::NumLock => KeyCode::NumLock,
534 BareKey::PrintScreen => KeyCode::PrintScreen,
535 BareKey::Pause => KeyCode::Pause,
536 BareKey::Menu => KeyCode::Menu,
537 }
538 }
539 #[cfg(not(target_family = "wasm"))]
540 pub fn serialize_non_kitty(&self) -> Option<String> {
541 let modifiers = self.to_termwiz_modifiers();
542 let key_code_encode_modes = KeyCodeEncodeModes {
543 encoding: KeyboardEncoding::Xterm,
544 application_cursor_keys: false,
547 newline_mode: false,
548 modify_other_keys: None,
549 };
550 self.to_termwiz_keycode()
551 .encode(modifiers, key_code_encode_modes, true)
552 .ok()
553 }
554 #[cfg(not(target_family = "wasm"))]
555 pub fn serialize_kitty(&self) -> Option<String> {
556 let modifiers = self.to_termwiz_modifiers();
557 let key_code_encode_modes = KeyCodeEncodeModes {
558 encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
559 application_cursor_keys: false,
562 newline_mode: false,
563 modify_other_keys: None,
564 };
565 self.to_termwiz_keycode()
566 .encode(modifiers, key_code_encode_modes, true)
567 .ok()
568 }
569 pub fn has_no_modifiers(&self) -> bool {
570 self.key_modifiers.is_empty()
571 }
572 pub fn has_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
573 for modifier in modifiers {
574 if !self.key_modifiers.contains(modifier) {
575 return false;
576 }
577 }
578 true
579 }
580}
581
582#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
583pub enum Direction {
584 Left,
585 Right,
586 Up,
587 Down,
588}
589
590impl Direction {
591 pub fn invert(&self) -> Direction {
592 match *self {
593 Direction::Left => Direction::Right,
594 Direction::Down => Direction::Up,
595 Direction::Up => Direction::Down,
596 Direction::Right => Direction::Left,
597 }
598 }
599
600 pub fn is_horizontal(&self) -> bool {
601 matches!(self, Direction::Left | Direction::Right)
602 }
603
604 pub fn is_vertical(&self) -> bool {
605 matches!(self, Direction::Down | Direction::Up)
606 }
607}
608
609impl fmt::Display for Direction {
610 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
611 match self {
612 Direction::Left => write!(f, "←"),
613 Direction::Right => write!(f, "→"),
614 Direction::Up => write!(f, "↑"),
615 Direction::Down => write!(f, "↓"),
616 }
617 }
618}
619
620impl FromStr for Direction {
621 type Err = String;
622 fn from_str(s: &str) -> Result<Self, Self::Err> {
623 match s {
624 "Left" | "left" => Ok(Direction::Left),
625 "Right" | "right" => Ok(Direction::Right),
626 "Up" | "up" => Ok(Direction::Up),
627 "Down" | "down" => Ok(Direction::Down),
628 _ => Err(format!(
629 "Failed to parse Direction. Unknown Direction: {}",
630 s
631 )),
632 }
633 }
634}
635
636#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
638pub enum Resize {
639 Increase,
640 Decrease,
641}
642
643impl Resize {
644 pub fn invert(&self) -> Self {
645 match self {
646 Resize::Increase => Resize::Decrease,
647 Resize::Decrease => Resize::Increase,
648 }
649 }
650}
651
652impl fmt::Display for Resize {
653 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
654 match self {
655 Resize::Increase => write!(f, "+"),
656 Resize::Decrease => write!(f, "-"),
657 }
658 }
659}
660
661impl FromStr for Resize {
662 type Err = String;
663 fn from_str(s: &str) -> Result<Self, Self::Err> {
664 match s {
665 "Increase" | "increase" | "+" => Ok(Resize::Increase),
666 "Decrease" | "decrease" | "-" => Ok(Resize::Decrease),
667 _ => Err(format!(
668 "failed to parse resize type. Unknown specifier '{}'",
669 s
670 )),
671 }
672 }
673}
674
675#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
685pub struct ResizeStrategy {
686 pub resize: Resize,
688 pub direction: Option<Direction>,
690 pub invert_on_boundaries: bool,
707}
708
709impl From<Direction> for ResizeStrategy {
710 fn from(direction: Direction) -> Self {
711 ResizeStrategy::new(Resize::Increase, Some(direction))
712 }
713}
714
715impl From<Resize> for ResizeStrategy {
716 fn from(resize: Resize) -> Self {
717 ResizeStrategy::new(resize, None)
718 }
719}
720
721impl ResizeStrategy {
722 pub fn new(resize: Resize, direction: Option<Direction>) -> Self {
723 ResizeStrategy {
724 resize,
725 direction,
726 invert_on_boundaries: true,
727 }
728 }
729
730 pub fn invert(&self) -> ResizeStrategy {
731 let resize = match self.resize {
732 Resize::Increase => Resize::Decrease,
733 Resize::Decrease => Resize::Increase,
734 };
735 let direction = match self.direction {
736 Some(direction) => Some(direction.invert()),
737 None => None,
738 };
739
740 ResizeStrategy::new(resize, direction)
741 }
742
743 pub fn resize_type(&self) -> Resize {
744 self.resize
745 }
746
747 pub fn direction(&self) -> Option<Direction> {
748 self.direction
749 }
750
751 pub fn direction_horizontal(&self) -> bool {
752 matches!(
753 self.direction,
754 Some(Direction::Left) | Some(Direction::Right)
755 )
756 }
757
758 pub fn direction_vertical(&self) -> bool {
759 matches!(self.direction, Some(Direction::Up) | Some(Direction::Down))
760 }
761
762 pub fn resize_increase(&self) -> bool {
763 self.resize == Resize::Increase
764 }
765
766 pub fn resize_decrease(&self) -> bool {
767 self.resize == Resize::Decrease
768 }
769
770 pub fn move_left_border_left(&self) -> bool {
771 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Left))
772 }
773
774 pub fn move_left_border_right(&self) -> bool {
775 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Left))
776 }
777
778 pub fn move_lower_border_down(&self) -> bool {
779 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Down))
780 }
781
782 pub fn move_lower_border_up(&self) -> bool {
783 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Down))
784 }
785
786 pub fn move_upper_border_up(&self) -> bool {
787 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Up))
788 }
789
790 pub fn move_upper_border_down(&self) -> bool {
791 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Up))
792 }
793
794 pub fn move_right_border_right(&self) -> bool {
795 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Right))
796 }
797
798 pub fn move_right_border_left(&self) -> bool {
799 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Right))
800 }
801
802 pub fn move_all_borders_out(&self) -> bool {
803 (self.resize == Resize::Increase) && (self.direction == None)
804 }
805
806 pub fn move_all_borders_in(&self) -> bool {
807 (self.resize == Resize::Decrease) && (self.direction == None)
808 }
809}
810
811impl fmt::Display for ResizeStrategy {
812 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
813 let resize = match self.resize {
814 Resize::Increase => "increase",
815 Resize::Decrease => "decrease",
816 };
817 let border = match self.direction {
818 Some(Direction::Left) => "left",
819 Some(Direction::Down) => "bottom",
820 Some(Direction::Up) => "top",
821 Some(Direction::Right) => "right",
822 None => "every",
823 };
824
825 write!(f, "{} size on {} border", resize, border)
826 }
827}
828
829#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
830pub enum Mouse {
834 ScrollUp(usize), ScrollDown(usize), LeftClick(isize, usize), RightClick(isize, usize), Hold(isize, usize), Release(isize, usize), Hover(isize, usize), }
842
843impl Mouse {
844 pub fn position(&self) -> Option<(usize, usize)> {
845 match self {
847 Mouse::LeftClick(line, column) => Some((*line as usize, *column as usize)),
848 Mouse::RightClick(line, column) => Some((*line as usize, *column as usize)),
849 Mouse::Hold(line, column) => Some((*line as usize, *column as usize)),
850 Mouse::Release(line, column) => Some((*line as usize, *column as usize)),
851 Mouse::Hover(line, column) => Some((*line as usize, *column as usize)),
852 _ => None,
853 }
854 }
855}
856
857#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
858pub struct FileMetadata {
859 pub is_dir: bool,
860 pub is_file: bool,
861 pub is_symlink: bool,
862 pub len: u64,
863}
864
865impl From<Metadata> for FileMetadata {
866 fn from(metadata: Metadata) -> Self {
867 FileMetadata {
868 is_dir: metadata.is_dir(),
869 is_file: metadata.is_file(),
870 is_symlink: metadata.is_symlink(),
871 len: metadata.len(),
872 }
873 }
874}
875
876#[derive(Debug, Clone, PartialEq, EnumDiscriminants, ToString, Serialize, Deserialize)]
879#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
880#[strum_discriminants(name(EventType))]
881#[non_exhaustive]
882pub enum Event {
883 ModeUpdate(ModeInfo),
884 TabUpdate(Vec<TabInfo>),
885 PaneUpdate(PaneManifest),
886 Key(KeyWithModifier),
888 Mouse(Mouse),
890 Timer(f64),
892 CopyToClipboard(CopyDestination),
894 SystemClipboardFailure,
896 InputReceived,
898 Visible(bool),
900 CustomMessage(
902 String, String, ),
905 FileSystemCreate(Vec<(PathBuf, Option<FileMetadata>)>),
907 FileSystemRead(Vec<(PathBuf, Option<FileMetadata>)>),
909 FileSystemUpdate(Vec<(PathBuf, Option<FileMetadata>)>),
911 FileSystemDelete(Vec<(PathBuf, Option<FileMetadata>)>),
913 PermissionRequestResult(PermissionStatus),
915 SessionUpdate(
916 Vec<SessionInfo>,
917 Vec<(String, Duration)>, ),
919 RunCommandResult(Option<i32>, Vec<u8>, Vec<u8>, BTreeMap<String, String>), WebRequestResult(
922 u16,
923 BTreeMap<String, String>,
924 Vec<u8>,
925 BTreeMap<String, String>,
926 ), CommandPaneOpened(u32, Context), CommandPaneExited(u32, Option<i32>, Context), PaneClosed(PaneId),
934 EditPaneOpened(u32, Context), EditPaneExited(u32, Option<i32>, Context), CommandPaneReRun(u32, Context), FailedToWriteConfigToDisk(Option<String>), ListClients(Vec<ClientInfo>),
939 HostFolderChanged(PathBuf), FailedToChangeHostFolder(Option<String>), PastedText(String),
942 ConfigWasWrittenToDisk,
943 WebServerStatus(WebServerStatus),
944 FailedToStartWebServer(String),
945 BeforeClose,
946 InterceptedKeyPress(KeyWithModifier),
947}
948
949#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants, ToString, Serialize, Deserialize)]
950pub enum WebServerStatus {
951 Online(String), Offline,
953 DifferentVersion(String), }
955
956#[derive(
957 Debug,
958 PartialEq,
959 Eq,
960 Hash,
961 Copy,
962 Clone,
963 EnumDiscriminants,
964 ToString,
965 Serialize,
966 Deserialize,
967 PartialOrd,
968 Ord,
969)]
970#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize, Display, PartialOrd, Ord))]
971#[strum_discriminants(name(PermissionType))]
972#[non_exhaustive]
973pub enum Permission {
974 ReadApplicationState,
975 ChangeApplicationState,
976 OpenFiles,
977 RunCommands,
978 OpenTerminalsOrPlugins,
979 WriteToStdin,
980 WebAccess,
981 ReadCliPipes,
982 MessageAndLaunchOtherPlugins,
983 Reconfigure,
984 FullHdAccess,
985 StartWebServer,
986 InterceptInput,
987}
988
989impl PermissionType {
990 pub fn display_name(&self) -> String {
991 match self {
992 PermissionType::ReadApplicationState => {
993 "Access Zellij state (Panes, Tabs and UI)".to_owned()
994 },
995 PermissionType::ChangeApplicationState => {
996 "Change Zellij state (Panes, Tabs and UI) and run commands".to_owned()
997 },
998 PermissionType::OpenFiles => "Open files (eg. for editing)".to_owned(),
999 PermissionType::RunCommands => "Run commands".to_owned(),
1000 PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
1001 PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
1002 PermissionType::WebAccess => "Make web requests".to_owned(),
1003 PermissionType::ReadCliPipes => "Control command line pipes and output".to_owned(),
1004 PermissionType::MessageAndLaunchOtherPlugins => {
1005 "Send messages to and launch other plugins".to_owned()
1006 },
1007 PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
1008 PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
1009 PermissionType::StartWebServer => {
1010 "Start a local web server to serve Zellij sessions".to_owned()
1011 },
1012 PermissionType::InterceptInput => "Intercept Input (keyboard & mouse)".to_owned(),
1013 }
1014 }
1015}
1016
1017#[derive(Debug, Clone)]
1018pub struct PluginPermission {
1019 pub name: String,
1020 pub permissions: Vec<PermissionType>,
1021}
1022
1023impl PluginPermission {
1024 pub fn new(name: String, permissions: Vec<PermissionType>) -> Self {
1025 PluginPermission { name, permissions }
1026 }
1027}
1028
1029#[derive(
1031 Debug,
1032 PartialEq,
1033 Eq,
1034 Hash,
1035 Copy,
1036 Clone,
1037 EnumIter,
1038 Serialize,
1039 Deserialize,
1040 ArgEnum,
1041 PartialOrd,
1042 Ord,
1043)]
1044pub enum InputMode {
1045 #[serde(alias = "normal")]
1048 Normal,
1049 #[serde(alias = "locked")]
1052 Locked,
1053 #[serde(alias = "resize")]
1055 Resize,
1056 #[serde(alias = "pane")]
1058 Pane,
1059 #[serde(alias = "tab")]
1061 Tab,
1062 #[serde(alias = "scroll")]
1064 Scroll,
1065 #[serde(alias = "entersearch")]
1067 EnterSearch,
1068 #[serde(alias = "search")]
1070 Search,
1071 #[serde(alias = "renametab")]
1073 RenameTab,
1074 #[serde(alias = "renamepane")]
1076 RenamePane,
1077 #[serde(alias = "session")]
1079 Session,
1080 #[serde(alias = "move")]
1082 Move,
1083 #[serde(alias = "prompt")]
1085 Prompt,
1086 #[serde(alias = "tmux")]
1088 Tmux,
1089}
1090
1091impl Default for InputMode {
1092 fn default() -> InputMode {
1093 InputMode::Normal
1094 }
1095}
1096
1097#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1098pub enum ThemeHue {
1099 Light,
1100 Dark,
1101}
1102impl Default for ThemeHue {
1103 fn default() -> ThemeHue {
1104 ThemeHue::Dark
1105 }
1106}
1107
1108#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1109pub enum PaletteColor {
1110 Rgb((u8, u8, u8)),
1111 EightBit(u8),
1112}
1113impl Default for PaletteColor {
1114 fn default() -> PaletteColor {
1115 PaletteColor::EightBit(0)
1116 }
1117}
1118
1119impl PaletteColor {
1121 pub fn as_rgb_str(&self) -> String {
1122 let (r, g, b) = match *self {
1123 Self::Rgb((r, g, b)) => (r, g, b),
1124 Self::EightBit(c) => eightbit_to_rgb(c),
1125 };
1126 format!("rgb({}, {}, {})", r, g, b)
1127 }
1128 pub fn from_rgb_str(rgb_str: &str) -> Self {
1129 let trimmed = rgb_str.trim();
1130
1131 if !trimmed.starts_with("rgb(") || !trimmed.ends_with(')') {
1132 return Self::default();
1133 }
1134
1135 let inner = trimmed
1136 .strip_prefix("rgb(")
1137 .and_then(|s| s.strip_suffix(')'))
1138 .unwrap_or("");
1139
1140 let parts: Vec<&str> = inner.split(',').collect();
1141
1142 if parts.len() != 3 {
1143 return Self::default();
1144 }
1145
1146 let mut rgb_values = [0u8; 3];
1147 for (i, part) in parts.iter().enumerate() {
1148 if let Some(rgb_val) = rgb_values.get_mut(i) {
1149 if let Ok(parsed) = part.trim().parse::<u8>() {
1150 *rgb_val = parsed;
1151 } else {
1152 return Self::default();
1153 }
1154 }
1155 }
1156
1157 Self::Rgb((rgb_values[0], rgb_values[1], rgb_values[2]))
1158 }
1159}
1160
1161impl FromStr for InputMode {
1162 type Err = ConversionError;
1163
1164 fn from_str(s: &str) -> Result<Self, ConversionError> {
1165 match s {
1166 "normal" | "Normal" => Ok(InputMode::Normal),
1167 "locked" | "Locked" => Ok(InputMode::Locked),
1168 "resize" | "Resize" => Ok(InputMode::Resize),
1169 "pane" | "Pane" => Ok(InputMode::Pane),
1170 "tab" | "Tab" => Ok(InputMode::Tab),
1171 "search" | "Search" => Ok(InputMode::Search),
1172 "scroll" | "Scroll" => Ok(InputMode::Scroll),
1173 "renametab" | "RenameTab" => Ok(InputMode::RenameTab),
1174 "renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
1175 "session" | "Session" => Ok(InputMode::Session),
1176 "move" | "Move" => Ok(InputMode::Move),
1177 "prompt" | "Prompt" => Ok(InputMode::Prompt),
1178 "tmux" | "Tmux" => Ok(InputMode::Tmux),
1179 "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch),
1180 e => Err(ConversionError::UnknownInputMode(e.into())),
1181 }
1182 }
1183}
1184
1185#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1186pub enum PaletteSource {
1187 Default,
1188 Xresources,
1189}
1190impl Default for PaletteSource {
1191 fn default() -> PaletteSource {
1192 PaletteSource::Default
1193 }
1194}
1195#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
1196pub struct Palette {
1197 pub source: PaletteSource,
1198 pub theme_hue: ThemeHue,
1199 pub fg: PaletteColor,
1200 pub bg: PaletteColor,
1201 pub black: PaletteColor,
1202 pub red: PaletteColor,
1203 pub green: PaletteColor,
1204 pub yellow: PaletteColor,
1205 pub blue: PaletteColor,
1206 pub magenta: PaletteColor,
1207 pub cyan: PaletteColor,
1208 pub white: PaletteColor,
1209 pub orange: PaletteColor,
1210 pub gray: PaletteColor,
1211 pub purple: PaletteColor,
1212 pub gold: PaletteColor,
1213 pub silver: PaletteColor,
1214 pub pink: PaletteColor,
1215 pub brown: PaletteColor,
1216}
1217
1218#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1219pub struct Style {
1220 pub colors: Styling,
1221 pub rounded_corners: bool,
1222 pub hide_session_name: bool,
1223}
1224
1225#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1226pub enum Coloration {
1227 NoStyling,
1228 Styled(StyleDeclaration),
1229}
1230
1231impl Coloration {
1232 pub fn with_fallback(&self, fallback: StyleDeclaration) -> StyleDeclaration {
1233 match &self {
1234 Coloration::NoStyling => fallback,
1235 Coloration::Styled(style) => *style,
1236 }
1237 }
1238}
1239
1240#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1241pub struct Styling {
1242 pub text_unselected: StyleDeclaration,
1243 pub text_selected: StyleDeclaration,
1244 pub ribbon_unselected: StyleDeclaration,
1245 pub ribbon_selected: StyleDeclaration,
1246 pub table_title: StyleDeclaration,
1247 pub table_cell_unselected: StyleDeclaration,
1248 pub table_cell_selected: StyleDeclaration,
1249 pub list_unselected: StyleDeclaration,
1250 pub list_selected: StyleDeclaration,
1251 pub frame_unselected: Option<StyleDeclaration>,
1252 pub frame_selected: StyleDeclaration,
1253 pub frame_highlight: StyleDeclaration,
1254 pub exit_code_success: StyleDeclaration,
1255 pub exit_code_error: StyleDeclaration,
1256 pub multiplayer_user_colors: MultiplayerColors,
1257}
1258
1259#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1260pub struct StyleDeclaration {
1261 pub base: PaletteColor,
1262 pub background: PaletteColor,
1263 pub emphasis_0: PaletteColor,
1264 pub emphasis_1: PaletteColor,
1265 pub emphasis_2: PaletteColor,
1266 pub emphasis_3: PaletteColor,
1267}
1268
1269#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1270pub struct MultiplayerColors {
1271 pub player_1: PaletteColor,
1272 pub player_2: PaletteColor,
1273 pub player_3: PaletteColor,
1274 pub player_4: PaletteColor,
1275 pub player_5: PaletteColor,
1276 pub player_6: PaletteColor,
1277 pub player_7: PaletteColor,
1278 pub player_8: PaletteColor,
1279 pub player_9: PaletteColor,
1280 pub player_10: PaletteColor,
1281}
1282
1283pub const DEFAULT_STYLES: Styling = Styling {
1284 text_unselected: StyleDeclaration {
1285 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1286 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1287 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1288 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1289 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1290 background: PaletteColor::EightBit(default_colors::GRAY),
1291 },
1292 text_selected: StyleDeclaration {
1293 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1294 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1295 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1296 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1297 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1298 background: PaletteColor::EightBit(default_colors::GRAY),
1299 },
1300 ribbon_unselected: StyleDeclaration {
1301 base: PaletteColor::EightBit(default_colors::BLACK),
1302 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1303 emphasis_1: PaletteColor::EightBit(default_colors::WHITE),
1304 emphasis_2: PaletteColor::EightBit(default_colors::BLUE),
1305 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1306 background: PaletteColor::EightBit(default_colors::GRAY),
1307 },
1308 ribbon_selected: StyleDeclaration {
1309 base: PaletteColor::EightBit(default_colors::BLACK),
1310 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1311 emphasis_1: PaletteColor::EightBit(default_colors::ORANGE),
1312 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1313 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1314 background: PaletteColor::EightBit(default_colors::GREEN),
1315 },
1316 exit_code_success: StyleDeclaration {
1317 base: PaletteColor::EightBit(default_colors::GREEN),
1318 emphasis_0: PaletteColor::EightBit(default_colors::CYAN),
1319 emphasis_1: PaletteColor::EightBit(default_colors::BLACK),
1320 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1321 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1322 background: PaletteColor::EightBit(default_colors::GRAY),
1323 },
1324 exit_code_error: StyleDeclaration {
1325 base: PaletteColor::EightBit(default_colors::RED),
1326 emphasis_0: PaletteColor::EightBit(default_colors::YELLOW),
1327 emphasis_1: PaletteColor::EightBit(default_colors::GOLD),
1328 emphasis_2: PaletteColor::EightBit(default_colors::SILVER),
1329 emphasis_3: PaletteColor::EightBit(default_colors::PURPLE),
1330 background: PaletteColor::EightBit(default_colors::GRAY),
1331 },
1332 frame_unselected: None,
1333 frame_selected: StyleDeclaration {
1334 base: PaletteColor::EightBit(default_colors::GREEN),
1335 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1336 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1337 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1338 emphasis_3: PaletteColor::EightBit(default_colors::BROWN),
1339 background: PaletteColor::EightBit(default_colors::GRAY),
1340 },
1341 frame_highlight: StyleDeclaration {
1342 base: PaletteColor::EightBit(default_colors::ORANGE),
1343 emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA),
1344 emphasis_1: PaletteColor::EightBit(default_colors::PURPLE),
1345 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1346 emphasis_3: PaletteColor::EightBit(default_colors::GREEN),
1347 background: PaletteColor::EightBit(default_colors::GREEN),
1348 },
1349 table_title: StyleDeclaration {
1350 base: PaletteColor::EightBit(default_colors::GREEN),
1351 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1352 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1353 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1354 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1355 background: PaletteColor::EightBit(default_colors::GRAY),
1356 },
1357 table_cell_unselected: StyleDeclaration {
1358 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1359 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1360 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1361 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1362 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1363 background: PaletteColor::EightBit(default_colors::GRAY),
1364 },
1365 table_cell_selected: StyleDeclaration {
1366 base: PaletteColor::EightBit(default_colors::GREEN),
1367 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1368 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1369 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1370 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1371 background: PaletteColor::EightBit(default_colors::GRAY),
1372 },
1373 list_unselected: StyleDeclaration {
1374 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1375 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1376 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1377 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1378 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1379 background: PaletteColor::EightBit(default_colors::GRAY),
1380 },
1381 list_selected: StyleDeclaration {
1382 base: PaletteColor::EightBit(default_colors::GREEN),
1383 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1384 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1385 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1386 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1387 background: PaletteColor::EightBit(default_colors::GRAY),
1388 },
1389 multiplayer_user_colors: MultiplayerColors {
1390 player_1: PaletteColor::EightBit(default_colors::MAGENTA),
1391 player_2: PaletteColor::EightBit(default_colors::BLUE),
1392 player_3: PaletteColor::EightBit(default_colors::PURPLE),
1393 player_4: PaletteColor::EightBit(default_colors::YELLOW),
1394 player_5: PaletteColor::EightBit(default_colors::CYAN),
1395 player_6: PaletteColor::EightBit(default_colors::GOLD),
1396 player_7: PaletteColor::EightBit(default_colors::RED),
1397 player_8: PaletteColor::EightBit(default_colors::SILVER),
1398 player_9: PaletteColor::EightBit(default_colors::PINK),
1399 player_10: PaletteColor::EightBit(default_colors::BROWN),
1400 },
1401};
1402
1403impl Default for Styling {
1404 fn default() -> Self {
1405 DEFAULT_STYLES
1406 }
1407}
1408
1409impl From<Styling> for Palette {
1410 fn from(styling: Styling) -> Self {
1411 Palette {
1412 theme_hue: ThemeHue::Dark,
1413 source: PaletteSource::Default,
1414 fg: styling.ribbon_unselected.background,
1415 bg: styling.text_unselected.background,
1416 red: styling.exit_code_error.base,
1417 green: styling.text_unselected.emphasis_2,
1418 yellow: styling.exit_code_error.emphasis_0,
1419 blue: styling.ribbon_unselected.emphasis_2,
1420 magenta: styling.text_unselected.emphasis_3,
1421 orange: styling.text_unselected.emphasis_0,
1422 cyan: styling.text_unselected.emphasis_1,
1423 black: styling.ribbon_unselected.base,
1424 white: styling.ribbon_unselected.emphasis_1,
1425 gray: styling.list_unselected.background,
1426 purple: styling.multiplayer_user_colors.player_3,
1427 gold: styling.multiplayer_user_colors.player_6,
1428 silver: styling.multiplayer_user_colors.player_8,
1429 pink: styling.multiplayer_user_colors.player_9,
1430 brown: styling.multiplayer_user_colors.player_10,
1431 }
1432 }
1433}
1434
1435impl From<Palette> for Styling {
1436 fn from(palette: Palette) -> Self {
1437 let (fg, bg) = match palette.theme_hue {
1438 ThemeHue::Light => (palette.black, palette.white),
1439 ThemeHue::Dark => (palette.white, palette.black),
1440 };
1441 Styling {
1442 text_unselected: StyleDeclaration {
1443 base: fg,
1444 emphasis_0: palette.orange,
1445 emphasis_1: palette.cyan,
1446 emphasis_2: palette.green,
1447 emphasis_3: palette.magenta,
1448 background: bg,
1449 },
1450 text_selected: StyleDeclaration {
1451 base: fg,
1452 emphasis_0: palette.orange,
1453 emphasis_1: palette.cyan,
1454 emphasis_2: palette.green,
1455 emphasis_3: palette.magenta,
1456 background: palette.bg,
1457 },
1458 ribbon_unselected: StyleDeclaration {
1459 base: palette.black,
1460 emphasis_0: palette.red,
1461 emphasis_1: palette.white,
1462 emphasis_2: palette.blue,
1463 emphasis_3: palette.magenta,
1464 background: palette.fg,
1465 },
1466 ribbon_selected: StyleDeclaration {
1467 base: palette.black,
1468 emphasis_0: palette.red,
1469 emphasis_1: palette.orange,
1470 emphasis_2: palette.magenta,
1471 emphasis_3: palette.blue,
1472 background: palette.green,
1473 },
1474 exit_code_success: StyleDeclaration {
1475 base: palette.green,
1476 emphasis_0: palette.cyan,
1477 emphasis_1: palette.black,
1478 emphasis_2: palette.magenta,
1479 emphasis_3: palette.blue,
1480 background: Default::default(),
1481 },
1482 exit_code_error: StyleDeclaration {
1483 base: palette.red,
1484 emphasis_0: palette.yellow,
1485 emphasis_1: palette.gold,
1486 emphasis_2: palette.silver,
1487 emphasis_3: palette.purple,
1488 background: Default::default(),
1489 },
1490 frame_unselected: None,
1491 frame_selected: StyleDeclaration {
1492 base: palette.green,
1493 emphasis_0: palette.orange,
1494 emphasis_1: palette.cyan,
1495 emphasis_2: palette.magenta,
1496 emphasis_3: palette.brown,
1497 background: Default::default(),
1498 },
1499 frame_highlight: StyleDeclaration {
1500 base: palette.orange,
1501 emphasis_0: palette.magenta,
1502 emphasis_1: palette.purple,
1503 emphasis_2: palette.orange,
1504 emphasis_3: palette.orange,
1505 background: Default::default(),
1506 },
1507 table_title: StyleDeclaration {
1508 base: palette.green,
1509 emphasis_0: palette.orange,
1510 emphasis_1: palette.cyan,
1511 emphasis_2: palette.green,
1512 emphasis_3: palette.magenta,
1513 background: palette.gray,
1514 },
1515 table_cell_unselected: StyleDeclaration {
1516 base: fg,
1517 emphasis_0: palette.orange,
1518 emphasis_1: palette.cyan,
1519 emphasis_2: palette.green,
1520 emphasis_3: palette.magenta,
1521 background: palette.black,
1522 },
1523 table_cell_selected: StyleDeclaration {
1524 base: fg,
1525 emphasis_0: palette.orange,
1526 emphasis_1: palette.cyan,
1527 emphasis_2: palette.green,
1528 emphasis_3: palette.magenta,
1529 background: palette.bg,
1530 },
1531 list_unselected: StyleDeclaration {
1532 base: palette.white,
1533 emphasis_0: palette.orange,
1534 emphasis_1: palette.cyan,
1535 emphasis_2: palette.green,
1536 emphasis_3: palette.magenta,
1537 background: palette.black,
1538 },
1539 list_selected: StyleDeclaration {
1540 base: palette.white,
1541 emphasis_0: palette.orange,
1542 emphasis_1: palette.cyan,
1543 emphasis_2: palette.green,
1544 emphasis_3: palette.magenta,
1545 background: palette.bg,
1546 },
1547 multiplayer_user_colors: MultiplayerColors {
1548 player_1: palette.magenta,
1549 player_2: palette.blue,
1550 player_3: palette.purple,
1551 player_4: palette.yellow,
1552 player_5: palette.cyan,
1553 player_6: palette.gold,
1554 player_7: palette.red,
1555 player_8: palette.silver,
1556 player_9: palette.pink,
1557 player_10: palette.brown,
1558 },
1559 }
1560 }
1561}
1562
1563pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
1565
1566#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1568pub struct ModeInfo {
1569 pub mode: InputMode,
1570 pub base_mode: Option<InputMode>,
1571 pub keybinds: KeybindsVec,
1572 pub style: Style,
1573 pub capabilities: PluginCapabilities,
1574 pub session_name: Option<String>,
1575 pub editor: Option<PathBuf>,
1576 pub shell: Option<PathBuf>,
1577 pub web_clients_allowed: Option<bool>,
1578 pub web_sharing: Option<WebSharing>,
1579 pub currently_marking_pane_group: Option<bool>,
1580 pub is_web_client: Option<bool>,
1581 pub web_server_ip: Option<IpAddr>,
1583 pub web_server_port: Option<u16>,
1584 pub web_server_capability: Option<bool>,
1585}
1586
1587impl ModeInfo {
1588 pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
1589 self.get_keybinds_for_mode(self.mode)
1590 }
1591
1592 pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
1593 for (vec_mode, map) in &self.keybinds {
1594 if mode == *vec_mode {
1595 return map.to_vec();
1596 }
1597 }
1598 vec![]
1599 }
1600 pub fn update_keybinds(&mut self, keybinds: Keybinds) {
1601 self.keybinds = keybinds.to_keybinds_vec();
1602 }
1603 pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
1604 self.base_mode = Some(new_default_mode);
1605 }
1606 pub fn update_theme(&mut self, theme: Styling) {
1607 self.style.colors = theme.into();
1608 }
1609 pub fn update_rounded_corners(&mut self, rounded_corners: bool) {
1610 self.style.rounded_corners = rounded_corners;
1611 }
1612 pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) {
1613 self.capabilities.arrow_fonts = !should_support_arrow_fonts;
1616 }
1617 pub fn update_hide_session_name(&mut self, hide_session_name: bool) {
1618 self.style.hide_session_name = hide_session_name;
1619 }
1620}
1621
1622#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1623pub struct SessionInfo {
1624 pub name: String,
1625 pub tabs: Vec<TabInfo>,
1626 pub panes: PaneManifest,
1627 pub connected_clients: usize,
1628 pub is_current_session: bool,
1629 pub available_layouts: Vec<LayoutInfo>,
1630 pub plugins: BTreeMap<u32, PluginInfo>,
1631 pub web_clients_allowed: bool,
1632 pub web_client_count: usize,
1633 pub tab_history: BTreeMap<ClientId, Vec<usize>>,
1634}
1635
1636#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1637pub struct PluginInfo {
1638 pub location: String,
1639 pub configuration: BTreeMap<String, String>,
1640}
1641
1642impl From<RunPlugin> for PluginInfo {
1643 fn from(run_plugin: RunPlugin) -> Self {
1644 PluginInfo {
1645 location: run_plugin.location.display(),
1646 configuration: run_plugin.configuration.inner().clone(),
1647 }
1648 }
1649}
1650
1651#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1652pub enum LayoutInfo {
1653 BuiltIn(String),
1654 File(String),
1655 Url(String),
1656 Stringified(String),
1657}
1658
1659impl LayoutInfo {
1660 pub fn name(&self) -> &str {
1661 match self {
1662 LayoutInfo::BuiltIn(name) => &name,
1663 LayoutInfo::File(name) => &name,
1664 LayoutInfo::Url(url) => &url,
1665 LayoutInfo::Stringified(layout) => &layout,
1666 }
1667 }
1668 pub fn is_builtin(&self) -> bool {
1669 match self {
1670 LayoutInfo::BuiltIn(_name) => true,
1671 LayoutInfo::File(_name) => false,
1672 LayoutInfo::Url(_url) => false,
1673 LayoutInfo::Stringified(_stringified) => false,
1674 }
1675 }
1676 pub fn from_config(
1677 layout_dir: &Option<PathBuf>,
1678 default_layout: &Option<PathBuf>,
1679 ) -> Option<Self> {
1680 match default_layout {
1681 Some(default_layout) => {
1682 if default_layout.extension().is_some() || default_layout.components().count() > 1 {
1683 let Some(layout_dir) = layout_dir
1684 .as_ref()
1685 .map(|l| l.clone())
1686 .or_else(default_layout_dir)
1687 else {
1688 return None;
1689 };
1690 Some(LayoutInfo::File(
1691 layout_dir.join(default_layout).display().to_string(),
1692 ))
1693 } else if default_layout.starts_with("http://")
1694 || default_layout.starts_with("https://")
1695 {
1696 Some(LayoutInfo::Url(default_layout.display().to_string()))
1697 } else {
1698 Some(LayoutInfo::BuiltIn(default_layout.display().to_string()))
1699 }
1700 },
1701 None => None,
1702 }
1703 }
1704}
1705
1706use std::hash::{Hash, Hasher};
1707
1708#[allow(clippy::derive_hash_xor_eq)]
1709impl Hash for SessionInfo {
1710 fn hash<H: Hasher>(&self, state: &mut H) {
1711 self.name.hash(state);
1712 }
1713}
1714
1715impl SessionInfo {
1716 pub fn new(name: String) -> Self {
1717 SessionInfo {
1718 name,
1719 ..Default::default()
1720 }
1721 }
1722 pub fn update_tab_info(&mut self, new_tab_info: Vec<TabInfo>) {
1723 self.tabs = new_tab_info;
1724 }
1725 pub fn update_pane_info(&mut self, new_pane_info: PaneManifest) {
1726 self.panes = new_pane_info;
1727 }
1728 pub fn update_connected_clients(&mut self, new_connected_clients: usize) {
1729 self.connected_clients = new_connected_clients;
1730 }
1731 pub fn populate_plugin_list(&mut self, plugins: BTreeMap<u32, RunPlugin>) {
1732 let mut plugin_list = BTreeMap::new();
1734 for (plugin_id, run_plugin) in plugins {
1735 plugin_list.insert(plugin_id, run_plugin.into());
1736 }
1737 self.plugins = plugin_list;
1738 }
1739}
1740
1741#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1743pub struct TabInfo {
1744 pub position: usize,
1746 pub name: String,
1748 pub active: bool,
1750 pub panes_to_hide: usize,
1752 pub is_fullscreen_active: bool,
1754 pub is_sync_panes_active: bool,
1756 pub are_floating_panes_visible: bool,
1757 pub other_focused_clients: Vec<ClientId>,
1758 pub active_swap_layout_name: Option<String>,
1759 pub is_swap_layout_dirty: bool,
1761 pub viewport_rows: usize,
1763 pub viewport_columns: usize,
1765 pub display_area_rows: usize,
1768 pub display_area_columns: usize,
1771 pub selectable_tiled_panes_count: usize,
1773 pub selectable_floating_panes_count: usize,
1775}
1776
1777#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1781pub struct PaneManifest {
1782 pub panes: HashMap<usize, Vec<PaneInfo>>, }
1784
1785#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1796pub struct PaneInfo {
1797 pub id: u32,
1799 pub is_plugin: bool,
1802 pub is_focused: bool,
1804 pub is_fullscreen: bool,
1805 pub is_floating: bool,
1807 pub is_suppressed: bool,
1810 pub title: String,
1812 pub exited: bool,
1815 pub exit_status: Option<i32>,
1817 pub is_held: bool,
1820 pub pane_x: usize,
1821 pub pane_content_x: usize,
1822 pub pane_y: usize,
1823 pub pane_content_y: usize,
1824 pub pane_rows: usize,
1825 pub pane_content_rows: usize,
1826 pub pane_columns: usize,
1827 pub pane_content_columns: usize,
1828 pub cursor_coordinates_in_pane: Option<(usize, usize)>, pub terminal_command: Option<String>,
1834 pub plugin_url: Option<String>,
1837 pub is_selectable: bool,
1840 pub index_in_pane_group: BTreeMap<ClientId, usize>,
1843}
1844#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1845pub struct ClientInfo {
1846 pub client_id: ClientId,
1847 pub pane_id: PaneId,
1848 pub running_command: String,
1849 pub is_current_client: bool,
1850}
1851
1852impl ClientInfo {
1853 pub fn new(
1854 client_id: ClientId,
1855 pane_id: PaneId,
1856 running_command: String,
1857 is_current_client: bool,
1858 ) -> Self {
1859 ClientInfo {
1860 client_id,
1861 pane_id,
1862 running_command,
1863 is_current_client,
1864 }
1865 }
1866}
1867
1868#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1869pub struct PluginIds {
1870 pub plugin_id: u32,
1871 pub zellij_pid: u32,
1872 pub initial_cwd: PathBuf,
1873 pub client_id: ClientId,
1874}
1875
1876#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
1878pub struct PluginTag(String);
1879
1880impl PluginTag {
1881 pub fn new(url: impl Into<String>) -> Self {
1882 PluginTag(url.into())
1883 }
1884}
1885
1886impl From<PluginTag> for String {
1887 fn from(tag: PluginTag) -> Self {
1888 tag.0
1889 }
1890}
1891
1892impl fmt::Display for PluginTag {
1893 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
1894 write!(f, "{}", self.0)
1895 }
1896}
1897
1898#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1899pub struct PluginCapabilities {
1900 pub arrow_fonts: bool,
1901}
1902
1903impl Default for PluginCapabilities {
1904 fn default() -> PluginCapabilities {
1905 PluginCapabilities { arrow_fonts: true }
1906 }
1907}
1908
1909#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
1911pub enum CopyDestination {
1912 Command,
1913 Primary,
1914 System,
1915}
1916
1917#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
1918pub enum PermissionStatus {
1919 Granted,
1920 Denied,
1921}
1922
1923#[derive(Debug, Default, Clone)]
1924pub struct FileToOpen {
1925 pub path: PathBuf,
1926 pub line_number: Option<usize>,
1927 pub cwd: Option<PathBuf>,
1928}
1929
1930impl FileToOpen {
1931 pub fn new<P: AsRef<Path>>(path: P) -> Self {
1932 FileToOpen {
1933 path: path.as_ref().to_path_buf(),
1934 ..Default::default()
1935 }
1936 }
1937 pub fn with_line_number(mut self, line_number: usize) -> Self {
1938 self.line_number = Some(line_number);
1939 self
1940 }
1941 pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
1942 self.cwd = Some(cwd);
1943 self
1944 }
1945}
1946
1947#[derive(Debug, Default, Clone)]
1948pub struct CommandToRun {
1949 pub path: PathBuf,
1950 pub args: Vec<String>,
1951 pub cwd: Option<PathBuf>,
1952}
1953
1954impl CommandToRun {
1955 pub fn new<P: AsRef<Path>>(path: P) -> Self {
1956 CommandToRun {
1957 path: path.as_ref().to_path_buf(),
1958 ..Default::default()
1959 }
1960 }
1961 pub fn new_with_args<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) -> Self {
1962 CommandToRun {
1963 path: path.as_ref().to_path_buf(),
1964 args: args.into_iter().map(|a| a.as_ref().to_owned()).collect(),
1965 ..Default::default()
1966 }
1967 }
1968}
1969
1970#[derive(Debug, Default, Clone)]
1971pub struct MessageToPlugin {
1972 pub plugin_url: Option<String>,
1973 pub destination_plugin_id: Option<u32>,
1974 pub plugin_config: BTreeMap<String, String>,
1975 pub message_name: String,
1976 pub message_payload: Option<String>,
1977 pub message_args: BTreeMap<String, String>,
1978 pub new_plugin_args: Option<NewPluginArgs>,
1981 pub floating_pane_coordinates: Option<FloatingPaneCoordinates>,
1982}
1983
1984#[derive(Debug, Default, Clone)]
1985pub struct NewPluginArgs {
1986 pub should_float: Option<bool>,
1987 pub pane_id_to_replace: Option<PaneId>,
1988 pub pane_title: Option<String>,
1989 pub cwd: Option<PathBuf>,
1990 pub skip_cache: bool,
1991 pub should_focus: Option<bool>,
1992}
1993
1994#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
1995pub enum PaneId {
1996 Terminal(u32),
1997 Plugin(u32),
1998}
1999
2000impl FromStr for PaneId {
2001 type Err = Box<dyn std::error::Error>;
2002 fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
2003 if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
2004 u32::from_str_radix(terminal_stringified_pane_id, 10)
2005 .map(|id| PaneId::Terminal(id))
2006 .map_err(|e| e.into())
2007 } else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
2008 u32::from_str_radix(plugin_pane_id, 10)
2009 .map(|id| PaneId::Plugin(id))
2010 .map_err(|e| e.into())
2011 } else {
2012 u32::from_str_radix(&stringified_pane_id, 10)
2013 .map(|id| PaneId::Terminal(id))
2014 .map_err(|e| e.into())
2015 }
2016 }
2017}
2018
2019impl MessageToPlugin {
2020 pub fn new(message_name: impl Into<String>) -> Self {
2021 MessageToPlugin {
2022 message_name: message_name.into(),
2023 ..Default::default()
2024 }
2025 }
2026 pub fn with_plugin_url(mut self, url: impl Into<String>) -> Self {
2027 self.plugin_url = Some(url.into());
2028 self
2029 }
2030 pub fn with_destination_plugin_id(mut self, destination_plugin_id: u32) -> Self {
2031 self.destination_plugin_id = Some(destination_plugin_id);
2032 self
2033 }
2034 pub fn with_plugin_config(mut self, plugin_config: BTreeMap<String, String>) -> Self {
2035 self.plugin_config = plugin_config;
2036 self
2037 }
2038 pub fn with_payload(mut self, payload: impl Into<String>) -> Self {
2039 self.message_payload = Some(payload.into());
2040 self
2041 }
2042 pub fn with_args(mut self, args: BTreeMap<String, String>) -> Self {
2043 self.message_args = args;
2044 self
2045 }
2046 pub fn with_floating_pane_coordinates(
2047 mut self,
2048 floating_pane_coordinates: FloatingPaneCoordinates,
2049 ) -> Self {
2050 self.floating_pane_coordinates = Some(floating_pane_coordinates);
2051 self
2052 }
2053 pub fn new_plugin_instance_should_float(mut self, should_float: bool) -> Self {
2054 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2055 new_plugin_args.should_float = Some(should_float);
2056 self
2057 }
2058 pub fn new_plugin_instance_should_replace_pane(mut self, pane_id: PaneId) -> Self {
2059 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2060 new_plugin_args.pane_id_to_replace = Some(pane_id);
2061 self
2062 }
2063 pub fn new_plugin_instance_should_have_pane_title(
2064 mut self,
2065 pane_title: impl Into<String>,
2066 ) -> Self {
2067 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2068 new_plugin_args.pane_title = Some(pane_title.into());
2069 self
2070 }
2071 pub fn new_plugin_instance_should_have_cwd(mut self, cwd: PathBuf) -> Self {
2072 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2073 new_plugin_args.cwd = Some(cwd);
2074 self
2075 }
2076 pub fn new_plugin_instance_should_skip_cache(mut self) -> Self {
2077 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2078 new_plugin_args.skip_cache = true;
2079 self
2080 }
2081 pub fn new_plugin_instance_should_be_focused(mut self) -> Self {
2082 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2083 new_plugin_args.should_focus = Some(true);
2084 self
2085 }
2086}
2087
2088#[derive(Debug, Default, Clone, Serialize, Deserialize)]
2089pub struct ConnectToSession {
2090 pub name: Option<String>,
2091 pub tab_position: Option<usize>,
2092 pub pane_id: Option<(u32, bool)>, pub layout: Option<LayoutInfo>,
2094 pub cwd: Option<PathBuf>,
2095}
2096
2097impl ConnectToSession {
2098 pub fn apply_layout_dir(&mut self, layout_dir: &PathBuf) {
2099 if let Some(LayoutInfo::File(file_path)) = self.layout.as_mut() {
2100 *file_path = Path::join(layout_dir, &file_path)
2101 .to_string_lossy()
2102 .to_string();
2103 }
2104 }
2105}
2106
2107#[derive(Debug, Default, Clone)]
2108pub struct PluginMessage {
2109 pub name: String,
2110 pub payload: String,
2111 pub worker_name: Option<String>,
2112}
2113
2114impl PluginMessage {
2115 pub fn new_to_worker(worker_name: &str, message: &str, payload: &str) -> Self {
2116 PluginMessage {
2117 name: message.to_owned(),
2118 payload: payload.to_owned(),
2119 worker_name: Some(worker_name.to_owned()),
2120 }
2121 }
2122 pub fn new_to_plugin(message: &str, payload: &str) -> Self {
2123 PluginMessage {
2124 name: message.to_owned(),
2125 payload: payload.to_owned(),
2126 worker_name: None,
2127 }
2128 }
2129}
2130
2131#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2132pub enum HttpVerb {
2133 Get,
2134 Post,
2135 Put,
2136 Delete,
2137}
2138
2139#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2140pub enum PipeSource {
2141 Cli(String), Plugin(u32), Keybind, }
2145
2146#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2147pub struct PipeMessage {
2148 pub source: PipeSource,
2149 pub name: String,
2150 pub payload: Option<String>,
2151 pub args: BTreeMap<String, String>,
2152 pub is_private: bool,
2153}
2154
2155impl PipeMessage {
2156 pub fn new(
2157 source: PipeSource,
2158 name: impl Into<String>,
2159 payload: &Option<String>,
2160 args: &Option<BTreeMap<String, String>>,
2161 is_private: bool,
2162 ) -> Self {
2163 PipeMessage {
2164 source,
2165 name: name.into(),
2166 payload: payload.clone(),
2167 args: args.clone().unwrap_or_else(|| Default::default()),
2168 is_private,
2169 }
2170 }
2171}
2172
2173#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
2174pub struct FloatingPaneCoordinates {
2175 pub x: Option<SplitSize>,
2176 pub y: Option<SplitSize>,
2177 pub width: Option<SplitSize>,
2178 pub height: Option<SplitSize>,
2179 pub pinned: Option<bool>,
2180}
2181
2182impl FloatingPaneCoordinates {
2183 pub fn new(
2184 x: Option<String>,
2185 y: Option<String>,
2186 width: Option<String>,
2187 height: Option<String>,
2188 pinned: Option<bool>,
2189 ) -> Option<Self> {
2190 let x = x.and_then(|x| SplitSize::from_str(&x).ok());
2191 let y = y.and_then(|y| SplitSize::from_str(&y).ok());
2192 let width = width.and_then(|width| SplitSize::from_str(&width).ok());
2193 let height = height.and_then(|height| SplitSize::from_str(&height).ok());
2194 if x.is_none() && y.is_none() && width.is_none() && height.is_none() && pinned.is_none() {
2195 None
2196 } else {
2197 Some(FloatingPaneCoordinates {
2198 x,
2199 y,
2200 width,
2201 height,
2202 pinned,
2203 })
2204 }
2205 }
2206 pub fn with_x_fixed(mut self, x: usize) -> Self {
2207 self.x = Some(SplitSize::Fixed(x));
2208 self
2209 }
2210 pub fn with_x_percent(mut self, x: usize) -> Self {
2211 if x > 100 {
2212 eprintln!("x must be between 0 and 100");
2213 return self;
2214 }
2215 self.x = Some(SplitSize::Percent(x));
2216 self
2217 }
2218 pub fn with_y_fixed(mut self, y: usize) -> Self {
2219 self.y = Some(SplitSize::Fixed(y));
2220 self
2221 }
2222 pub fn with_y_percent(mut self, y: usize) -> Self {
2223 if y > 100 {
2224 eprintln!("y must be between 0 and 100");
2225 return self;
2226 }
2227 self.y = Some(SplitSize::Percent(y));
2228 self
2229 }
2230 pub fn with_width_fixed(mut self, width: usize) -> Self {
2231 self.width = Some(SplitSize::Fixed(width));
2232 self
2233 }
2234 pub fn with_width_percent(mut self, width: usize) -> Self {
2235 if width > 100 {
2236 eprintln!("width must be between 0 and 100");
2237 return self;
2238 }
2239 self.width = Some(SplitSize::Percent(width));
2240 self
2241 }
2242 pub fn with_height_fixed(mut self, height: usize) -> Self {
2243 self.height = Some(SplitSize::Fixed(height));
2244 self
2245 }
2246 pub fn with_height_percent(mut self, height: usize) -> Self {
2247 if height > 100 {
2248 eprintln!("height must be between 0 and 100");
2249 return self;
2250 }
2251 self.height = Some(SplitSize::Percent(height));
2252 self
2253 }
2254}
2255
2256impl From<PaneGeom> for FloatingPaneCoordinates {
2257 fn from(pane_geom: PaneGeom) -> Self {
2258 FloatingPaneCoordinates {
2259 x: Some(SplitSize::Fixed(pane_geom.x)),
2260 y: Some(SplitSize::Fixed(pane_geom.y)),
2261 width: Some(SplitSize::Fixed(pane_geom.cols.as_usize())),
2262 height: Some(SplitSize::Fixed(pane_geom.rows.as_usize())),
2263 pinned: Some(pane_geom.is_pinned),
2264 }
2265 }
2266}
2267
2268#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
2269pub struct OriginatingPlugin {
2270 pub plugin_id: u32,
2271 pub client_id: ClientId,
2272 pub context: Context,
2273}
2274
2275impl OriginatingPlugin {
2276 pub fn new(plugin_id: u32, client_id: ClientId, context: Context) -> Self {
2277 OriginatingPlugin {
2278 plugin_id,
2279 client_id,
2280 context,
2281 }
2282 }
2283}
2284
2285#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
2286pub enum WebSharing {
2287 #[serde(alias = "on")]
2288 On,
2289 #[serde(alias = "off")]
2290 Off,
2291 #[serde(alias = "disabled")]
2292 Disabled,
2293}
2294
2295impl Default for WebSharing {
2296 fn default() -> Self {
2297 Self::Off
2298 }
2299}
2300
2301impl WebSharing {
2302 pub fn is_on(&self) -> bool {
2303 match self {
2304 WebSharing::On => true,
2305 _ => false,
2306 }
2307 }
2308 pub fn web_clients_allowed(&self) -> bool {
2309 match self {
2310 WebSharing::On => true,
2311 _ => false,
2312 }
2313 }
2314 pub fn sharing_is_disabled(&self) -> bool {
2315 match self {
2316 WebSharing::Disabled => true,
2317 _ => false,
2318 }
2319 }
2320 pub fn set_sharing(&mut self) -> bool {
2321 match self {
2323 WebSharing::On => true,
2324 WebSharing::Off => {
2325 *self = WebSharing::On;
2326 true
2327 },
2328 WebSharing::Disabled => false,
2329 }
2330 }
2331 pub fn set_not_sharing(&mut self) -> bool {
2332 match self {
2334 WebSharing::On => {
2335 *self = WebSharing::Off;
2336 true
2337 },
2338 WebSharing::Off => true,
2339 WebSharing::Disabled => false,
2340 }
2341 }
2342}
2343
2344impl FromStr for WebSharing {
2345 type Err = String;
2346 fn from_str(s: &str) -> Result<Self, Self::Err> {
2347 match s {
2348 "On" | "on" => Ok(Self::On),
2349 "Off" | "off" => Ok(Self::Off),
2350 "Disabled" | "disabled" => Ok(Self::Disabled),
2351 _ => Err(format!("No such option: {}", s)),
2352 }
2353 }
2354}
2355
2356type Context = BTreeMap<String, String>;
2357
2358#[derive(Debug, Clone, EnumDiscriminants, ToString)]
2359#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
2360#[strum_discriminants(name(CommandType))]
2361pub enum PluginCommand {
2362 Subscribe(HashSet<EventType>),
2363 Unsubscribe(HashSet<EventType>),
2364 SetSelectable(bool),
2365 GetPluginIds,
2366 GetZellijVersion,
2367 OpenFile(FileToOpen, Context),
2368 OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
2369 OpenTerminal(FileToOpen), OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), OpenCommandPane(CommandToRun, Context),
2372 OpenCommandPaneFloating(CommandToRun, Option<FloatingPaneCoordinates>, Context),
2373 SwitchTabTo(u32), SetTimeout(f64), ExecCmd(Vec<String>),
2376 PostMessageTo(PluginMessage),
2377 PostMessageToPlugin(PluginMessage),
2378 HideSelf,
2379 ShowSelf(bool), SwitchToMode(InputMode),
2381 NewTabsWithLayout(String), NewTab {
2383 name: Option<String>,
2384 cwd: Option<String>,
2385 },
2386 GoToNextTab,
2387 GoToPreviousTab,
2388 Resize(Resize),
2389 ResizeWithDirection(ResizeStrategy),
2390 FocusNextPane,
2391 FocusPreviousPane,
2392 MoveFocus(Direction),
2393 MoveFocusOrTab(Direction),
2394 Detach,
2395 EditScrollback,
2396 Write(Vec<u8>), WriteChars(String),
2398 ToggleTab,
2399 MovePane,
2400 MovePaneWithDirection(Direction),
2401 ClearScreen,
2402 ScrollUp,
2403 ScrollDown,
2404 ScrollToTop,
2405 ScrollToBottom,
2406 PageScrollUp,
2407 PageScrollDown,
2408 ToggleFocusFullscreen,
2409 TogglePaneFrames,
2410 TogglePaneEmbedOrEject,
2411 UndoRenamePane,
2412 CloseFocus,
2413 ToggleActiveTabSync,
2414 CloseFocusedTab,
2415 UndoRenameTab,
2416 QuitZellij,
2417 PreviousSwapLayout,
2418 NextSwapLayout,
2419 GoToTabName(String),
2420 FocusOrCreateTab(String),
2421 GoToTab(u32), StartOrReloadPlugin(String), CloseTerminalPane(u32), ClosePluginPane(u32), FocusTerminalPane(u32, bool), FocusPluginPane(u32, bool), RenameTerminalPane(u32, String), RenamePluginPane(u32, String), RenameTab(u32, String), ReportPanic(String), RequestPluginPermissions(Vec<PermissionType>),
2432 SwitchSession(ConnectToSession),
2433 DeleteDeadSession(String), DeleteAllDeadSessions, OpenTerminalInPlace(FileToOpen), OpenFileInPlace(FileToOpen, Context),
2437 OpenCommandPaneInPlace(CommandToRun, Context),
2438 RunCommand(
2439 Vec<String>, BTreeMap<String, String>, PathBuf, BTreeMap<String, String>, ),
2444 WebRequest(
2445 String, HttpVerb,
2447 BTreeMap<String, String>, Vec<u8>, BTreeMap<String, String>, ),
2451 RenameSession(String), UnblockCliPipeInput(String), BlockCliPipeInput(String), CliPipeOutput(String, String), MessageToPlugin(MessageToPlugin),
2456 DisconnectOtherClients,
2457 KillSessions(Vec<String>), ScanHostFolder(PathBuf), WatchFilesystem,
2460 DumpSessionLayout,
2461 CloseSelf,
2462 NewTabsWithLayoutInfo(LayoutInfo),
2463 Reconfigure(String, bool), HidePaneWithId(PaneId),
2466 ShowPaneWithId(PaneId, bool), OpenCommandPaneBackground(CommandToRun, Context),
2468 RerunCommandPane(u32), ResizePaneIdWithDirection(ResizeStrategy, PaneId),
2470 EditScrollbackForPaneWithId(PaneId),
2471 WriteToPaneId(Vec<u8>, PaneId),
2472 WriteCharsToPaneId(String, PaneId),
2473 MovePaneWithPaneId(PaneId),
2474 MovePaneWithPaneIdInDirection(PaneId, Direction),
2475 ClearScreenForPaneId(PaneId),
2476 ScrollUpInPaneId(PaneId),
2477 ScrollDownInPaneId(PaneId),
2478 ScrollToTopInPaneId(PaneId),
2479 ScrollToBottomInPaneId(PaneId),
2480 PageScrollUpInPaneId(PaneId),
2481 PageScrollDownInPaneId(PaneId),
2482 TogglePaneIdFullscreen(PaneId),
2483 TogglePaneEmbedOrEjectForPaneId(PaneId),
2484 CloseTabWithIndex(usize), BreakPanesToNewTab(Vec<PaneId>, Option<String>, bool), BreakPanesToTabWithIndex(Vec<PaneId>, usize, bool), ReloadPlugin(u32), LoadNewPlugin {
2493 url: String,
2494 config: BTreeMap<String, String>,
2495 load_in_background: bool,
2496 skip_plugin_cache: bool,
2497 },
2498 RebindKeys {
2499 keys_to_rebind: Vec<(InputMode, KeyWithModifier, Vec<Action>)>,
2500 keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
2501 write_config_to_disk: bool,
2502 },
2503 ListClients,
2504 ChangeHostFolder(PathBuf),
2505 SetFloatingPanePinned(PaneId, bool), StackPanes(Vec<PaneId>),
2507 ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
2508 OpenCommandPaneNearPlugin(CommandToRun, Context),
2509 OpenTerminalNearPlugin(FileToOpen),
2510 OpenTerminalFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>),
2511 OpenTerminalInPlaceOfPlugin(FileToOpen, bool), OpenCommandPaneFloatingNearPlugin(CommandToRun, Option<FloatingPaneCoordinates>, Context),
2513 OpenCommandPaneInPlaceOfPlugin(CommandToRun, bool, Context), OpenFileNearPlugin(FileToOpen, Context),
2516 OpenFileFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>, Context),
2517 StartWebServer,
2518 StopWebServer,
2519 ShareCurrentSession,
2520 StopSharingCurrentSession,
2521 OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
2527 FloatMultiplePanes(Vec<PaneId>),
2528 EmbedMultiplePanes(Vec<PaneId>),
2529 QueryWebServerStatus,
2530 SetSelfMouseSelectionSupport(bool),
2531 GenerateWebLoginToken(Option<String>), RevokeWebLoginToken(String), ListWebLoginTokens,
2534 RevokeAllWebLoginTokens,
2535 RenameWebLoginToken(String, String), InterceptKeyPresses,
2537 ClearKeyPressesIntercepts,
2538 ReplacePaneWithExistingPane(PaneId, PaneId), }