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