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) => u8::from_str_radix(num, 10)
355 .ok()
356 .map(|n| BareKey::Char((n as char).to_ascii_lowercase())),
357 _ => None,
358 }
359 }
360 pub fn from_bytes_with_tilde(bytes: &[u8]) -> Option<Self> {
361 match str::from_utf8(bytes) {
362 Ok("2") => Some(BareKey::Insert),
363 Ok("3") => Some(BareKey::Delete),
364 Ok("5") => Some(BareKey::PageUp),
365 Ok("6") => Some(BareKey::PageDown),
366 Ok("7") => Some(BareKey::Home),
367 Ok("8") => Some(BareKey::End),
368 Ok("11") => Some(BareKey::F(1)),
369 Ok("12") => Some(BareKey::F(2)),
370 Ok("13") => Some(BareKey::F(3)),
371 Ok("14") => Some(BareKey::F(4)),
372 Ok("15") => Some(BareKey::F(5)),
373 Ok("17") => Some(BareKey::F(6)),
374 Ok("18") => Some(BareKey::F(7)),
375 Ok("19") => Some(BareKey::F(8)),
376 Ok("20") => Some(BareKey::F(9)),
377 Ok("21") => Some(BareKey::F(10)),
378 Ok("23") => Some(BareKey::F(11)),
379 Ok("24") => Some(BareKey::F(12)),
380 _ => None,
381 }
382 }
383 pub fn from_bytes_with_no_ending_byte(bytes: &[u8]) -> Option<Self> {
384 match str::from_utf8(bytes) {
385 Ok("1D") | Ok("D") => Some(BareKey::Left),
386 Ok("1C") | Ok("C") => Some(BareKey::Right),
387 Ok("1A") | Ok("A") => Some(BareKey::Up),
388 Ok("1B") | Ok("B") => Some(BareKey::Down),
389 Ok("1H") | Ok("H") => Some(BareKey::Home),
390 Ok("1F") | Ok("F") => Some(BareKey::End),
391 Ok("1P") | Ok("P") => Some(BareKey::F(1)),
392 Ok("1Q") | Ok("Q") => Some(BareKey::F(2)),
393 Ok("1S") | Ok("S") => Some(BareKey::F(4)),
394 _ => None,
395 }
396 }
397}
398
399bitflags::bitflags! {
400 #[derive(Debug, Clone, Copy, PartialEq, Eq)]
401 struct ModifierFlags: u8 {
402 const SHIFT = 0b0000_0001;
403 const ALT = 0b0000_0010;
404 const CONTROL = 0b0000_0100;
405 const SUPER = 0b0000_1000;
406 const HYPER = 0b0001_0000;
409 const META = 0b0010_0000;
410 const CAPS_LOCK = 0b0100_0000;
411 const NUM_LOCK = 0b1000_0000;
412 }
413}
414
415impl KeyModifier {
416 pub fn from_bytes(bytes: &[u8]) -> BTreeSet<KeyModifier> {
417 let modifier_flags = str::from_utf8(bytes)
418 .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();
423 if let Some(modifier_flags) = modifier_flags {
424 for name in modifier_flags.iter() {
425 match name {
426 ModifierFlags::SHIFT => key_modifiers.insert(KeyModifier::Shift),
427 ModifierFlags::ALT => key_modifiers.insert(KeyModifier::Alt),
428 ModifierFlags::CONTROL => key_modifiers.insert(KeyModifier::Ctrl),
429 ModifierFlags::SUPER => key_modifiers.insert(KeyModifier::Super),
430 _ => false,
431 };
432 }
433 }
434 key_modifiers
435 }
436}
437
438impl KeyWithModifier {
439 pub fn new(bare_key: BareKey) -> Self {
440 KeyWithModifier {
441 bare_key,
442 key_modifiers: BTreeSet::new(),
443 }
444 }
445 pub fn new_with_modifiers(bare_key: BareKey, key_modifiers: BTreeSet<KeyModifier>) -> Self {
446 KeyWithModifier {
447 bare_key,
448 key_modifiers,
449 }
450 }
451 pub fn with_shift_modifier(mut self) -> Self {
452 self.key_modifiers.insert(KeyModifier::Shift);
453 self
454 }
455 pub fn with_alt_modifier(mut self) -> Self {
456 self.key_modifiers.insert(KeyModifier::Alt);
457 self
458 }
459 pub fn with_ctrl_modifier(mut self) -> Self {
460 self.key_modifiers.insert(KeyModifier::Ctrl);
461 self
462 }
463 pub fn with_super_modifier(mut self) -> Self {
464 self.key_modifiers.insert(KeyModifier::Super);
465 self
466 }
467 pub fn from_bytes_with_u(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
468 let bare_key = BareKey::from_bytes_with_u(number_bytes);
470 match bare_key {
471 Some(bare_key) => {
472 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
473 Some(KeyWithModifier {
474 bare_key,
475 key_modifiers,
476 })
477 },
478 _ => None,
479 }
480 }
481 pub fn from_bytes_with_tilde(number_bytes: &[u8], modifier_bytes: &[u8]) -> Option<Self> {
482 let bare_key = BareKey::from_bytes_with_tilde(number_bytes);
484 match bare_key {
485 Some(bare_key) => {
486 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
487 Some(KeyWithModifier {
488 bare_key,
489 key_modifiers,
490 })
491 },
492 _ => None,
493 }
494 }
495 pub fn from_bytes_with_no_ending_byte(
496 number_bytes: &[u8],
497 modifier_bytes: &[u8],
498 ) -> Option<Self> {
499 let bare_key = BareKey::from_bytes_with_no_ending_byte(number_bytes);
501 match bare_key {
502 Some(bare_key) => {
503 let key_modifiers = KeyModifier::from_bytes(modifier_bytes);
504 Some(KeyWithModifier {
505 bare_key,
506 key_modifiers,
507 })
508 },
509 _ => None,
510 }
511 }
512 pub fn strip_common_modifiers(&self, common_modifiers: &Vec<KeyModifier>) -> Self {
513 let common_modifiers: BTreeSet<&KeyModifier> = common_modifiers.into_iter().collect();
514 KeyWithModifier {
515 bare_key: self.bare_key.clone(),
516 key_modifiers: self
517 .key_modifiers
518 .iter()
519 .filter(|m| !common_modifiers.contains(m))
520 .cloned()
521 .collect(),
522 }
523 }
524 pub fn is_key_without_modifier(&self, key: BareKey) -> bool {
525 self.bare_key == key && self.key_modifiers.is_empty()
526 }
527 pub fn is_key_with_ctrl_modifier(&self, key: BareKey) -> bool {
528 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Ctrl)
529 }
530 pub fn is_key_with_alt_modifier(&self, key: BareKey) -> bool {
531 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Alt)
532 }
533 pub fn is_key_with_shift_modifier(&self, key: BareKey) -> bool {
534 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Shift)
535 }
536 pub fn is_key_with_super_modifier(&self, key: BareKey) -> bool {
537 self.bare_key == key && self.key_modifiers.contains(&KeyModifier::Super)
538 }
539 pub fn is_cancel_key(&self) -> bool {
540 self.bare_key == BareKey::Esc
542 }
543 #[cfg(not(target_family = "wasm"))]
544 pub fn to_termwiz_modifiers(&self) -> Modifiers {
545 let mut modifiers = Modifiers::empty();
546 for modifier in &self.key_modifiers {
547 modifiers.set(modifier.into(), true);
548 }
549 modifiers
550 }
551 #[cfg(not(target_family = "wasm"))]
552 pub fn to_termwiz_keycode(&self) -> KeyCode {
553 match self.bare_key {
554 BareKey::PageDown => KeyCode::PageDown,
555 BareKey::PageUp => KeyCode::PageUp,
556 BareKey::Left => KeyCode::LeftArrow,
557 BareKey::Down => KeyCode::DownArrow,
558 BareKey::Up => KeyCode::UpArrow,
559 BareKey::Right => KeyCode::RightArrow,
560 BareKey::Home => KeyCode::Home,
561 BareKey::End => KeyCode::End,
562 BareKey::Backspace => KeyCode::Backspace,
563 BareKey::Delete => KeyCode::Delete,
564 BareKey::Insert => KeyCode::Insert,
565 BareKey::F(index) => KeyCode::Function(index),
566 BareKey::Char(character) => KeyCode::Char(character),
567 BareKey::Tab => KeyCode::Tab,
568 BareKey::Esc => KeyCode::Escape,
569 BareKey::Enter => KeyCode::Enter,
570 BareKey::CapsLock => KeyCode::CapsLock,
571 BareKey::ScrollLock => KeyCode::ScrollLock,
572 BareKey::NumLock => KeyCode::NumLock,
573 BareKey::PrintScreen => KeyCode::PrintScreen,
574 BareKey::Pause => KeyCode::Pause,
575 BareKey::Menu => KeyCode::Menu,
576 }
577 }
578 #[cfg(not(target_family = "wasm"))]
579 pub fn serialize_non_kitty(&self) -> Option<String> {
580 let modifiers = self.to_termwiz_modifiers();
581 let key_code_encode_modes = KeyCodeEncodeModes {
582 encoding: KeyboardEncoding::Xterm,
583 application_cursor_keys: false,
586 newline_mode: false,
587 modify_other_keys: None,
588 };
589 self.to_termwiz_keycode()
590 .encode(modifiers, key_code_encode_modes, true)
591 .ok()
592 }
593 #[cfg(not(target_family = "wasm"))]
594 pub fn serialize_kitty(&self) -> Option<String> {
595 let modifiers = self.to_termwiz_modifiers();
596 let key_code_encode_modes = KeyCodeEncodeModes {
597 encoding: KeyboardEncoding::Kitty(KittyKeyboardFlags::DISAMBIGUATE_ESCAPE_CODES),
598 application_cursor_keys: false,
601 newline_mode: false,
602 modify_other_keys: None,
603 };
604 self.to_termwiz_keycode()
605 .encode(modifiers, key_code_encode_modes, true)
606 .ok()
607 }
608 pub fn has_no_modifiers(&self) -> bool {
609 self.key_modifiers.is_empty()
610 }
611 pub fn has_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
612 for modifier in modifiers {
613 if !self.key_modifiers.contains(modifier) {
614 return false;
615 }
616 }
617 true
618 }
619 pub fn has_only_modifiers(&self, modifiers: &[KeyModifier]) -> bool {
620 for modifier in modifiers {
621 if !self.key_modifiers.contains(modifier) {
622 return false;
623 }
624 }
625 if self.key_modifiers.len() != modifiers.len() {
626 return false;
627 }
628 true
629 }
630}
631
632#[derive(Eq, Clone, Copy, Debug, PartialEq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
633pub enum Direction {
634 Left,
635 Right,
636 Up,
637 Down,
638}
639
640impl Default for Direction {
641 fn default() -> Self {
642 Direction::Left
643 }
644}
645
646impl Direction {
647 pub fn invert(&self) -> Direction {
648 match *self {
649 Direction::Left => Direction::Right,
650 Direction::Down => Direction::Up,
651 Direction::Up => Direction::Down,
652 Direction::Right => Direction::Left,
653 }
654 }
655
656 pub fn is_horizontal(&self) -> bool {
657 matches!(self, Direction::Left | Direction::Right)
658 }
659
660 pub fn is_vertical(&self) -> bool {
661 matches!(self, Direction::Down | Direction::Up)
662 }
663}
664
665impl fmt::Display for Direction {
666 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
667 match self {
668 Direction::Left => write!(f, "←"),
669 Direction::Right => write!(f, "→"),
670 Direction::Up => write!(f, "↑"),
671 Direction::Down => write!(f, "↓"),
672 }
673 }
674}
675
676impl FromStr for Direction {
677 type Err = String;
678 fn from_str(s: &str) -> Result<Self, Self::Err> {
679 match s {
680 "Left" | "left" => Ok(Direction::Left),
681 "Right" | "right" => Ok(Direction::Right),
682 "Up" | "up" => Ok(Direction::Up),
683 "Down" | "down" => Ok(Direction::Down),
684 _ => Err(format!(
685 "Failed to parse Direction. Unknown Direction: {}",
686 s
687 )),
688 }
689 }
690}
691
692#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
694pub enum Resize {
695 Increase,
696 Decrease,
697}
698
699impl Default for Resize {
700 fn default() -> Self {
701 Resize::Increase
702 }
703}
704
705impl Resize {
706 pub fn invert(&self) -> Self {
707 match self {
708 Resize::Increase => Resize::Decrease,
709 Resize::Decrease => Resize::Increase,
710 }
711 }
712}
713
714impl fmt::Display for Resize {
715 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
716 match self {
717 Resize::Increase => write!(f, "+"),
718 Resize::Decrease => write!(f, "-"),
719 }
720 }
721}
722
723impl FromStr for Resize {
724 type Err = String;
725 fn from_str(s: &str) -> Result<Self, Self::Err> {
726 match s {
727 "Increase" | "increase" | "+" => Ok(Resize::Increase),
728 "Decrease" | "decrease" | "-" => Ok(Resize::Decrease),
729 _ => Err(format!(
730 "failed to parse resize type. Unknown specifier '{}'",
731 s
732 )),
733 }
734 }
735}
736
737#[derive(Debug, Clone, Copy, Eq, PartialEq, Hash, Deserialize, Serialize)]
747pub struct ResizeStrategy {
748 pub resize: Resize,
750 pub direction: Option<Direction>,
752 pub invert_on_boundaries: bool,
769}
770
771impl From<Direction> for ResizeStrategy {
772 fn from(direction: Direction) -> Self {
773 ResizeStrategy::new(Resize::Increase, Some(direction))
774 }
775}
776
777impl From<Resize> for ResizeStrategy {
778 fn from(resize: Resize) -> Self {
779 ResizeStrategy::new(resize, None)
780 }
781}
782
783impl ResizeStrategy {
784 pub fn new(resize: Resize, direction: Option<Direction>) -> Self {
785 ResizeStrategy {
786 resize,
787 direction,
788 invert_on_boundaries: true,
789 }
790 }
791
792 pub fn invert(&self) -> ResizeStrategy {
793 let resize = match self.resize {
794 Resize::Increase => Resize::Decrease,
795 Resize::Decrease => Resize::Increase,
796 };
797 let direction = match self.direction {
798 Some(direction) => Some(direction.invert()),
799 None => None,
800 };
801
802 ResizeStrategy::new(resize, direction)
803 }
804
805 pub fn resize_type(&self) -> Resize {
806 self.resize
807 }
808
809 pub fn direction(&self) -> Option<Direction> {
810 self.direction
811 }
812
813 pub fn direction_horizontal(&self) -> bool {
814 matches!(
815 self.direction,
816 Some(Direction::Left) | Some(Direction::Right)
817 )
818 }
819
820 pub fn direction_vertical(&self) -> bool {
821 matches!(self.direction, Some(Direction::Up) | Some(Direction::Down))
822 }
823
824 pub fn resize_increase(&self) -> bool {
825 self.resize == Resize::Increase
826 }
827
828 pub fn resize_decrease(&self) -> bool {
829 self.resize == Resize::Decrease
830 }
831
832 pub fn move_left_border_left(&self) -> bool {
833 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Left))
834 }
835
836 pub fn move_left_border_right(&self) -> bool {
837 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Left))
838 }
839
840 pub fn move_lower_border_down(&self) -> bool {
841 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Down))
842 }
843
844 pub fn move_lower_border_up(&self) -> bool {
845 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Down))
846 }
847
848 pub fn move_upper_border_up(&self) -> bool {
849 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Up))
850 }
851
852 pub fn move_upper_border_down(&self) -> bool {
853 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Up))
854 }
855
856 pub fn move_right_border_right(&self) -> bool {
857 (self.resize == Resize::Increase) && (self.direction == Some(Direction::Right))
858 }
859
860 pub fn move_right_border_left(&self) -> bool {
861 (self.resize == Resize::Decrease) && (self.direction == Some(Direction::Right))
862 }
863
864 pub fn move_all_borders_out(&self) -> bool {
865 (self.resize == Resize::Increase) && (self.direction == None)
866 }
867
868 pub fn move_all_borders_in(&self) -> bool {
869 (self.resize == Resize::Decrease) && (self.direction == None)
870 }
871}
872
873impl fmt::Display for ResizeStrategy {
874 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
875 let resize = match self.resize {
876 Resize::Increase => "increase",
877 Resize::Decrease => "decrease",
878 };
879 let border = match self.direction {
880 Some(Direction::Left) => "left",
881 Some(Direction::Down) => "bottom",
882 Some(Direction::Up) => "top",
883 Some(Direction::Right) => "right",
884 None => "every",
885 };
886
887 write!(f, "{} size on {} border", resize, border)
888 }
889}
890
891#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
892pub enum Mouse {
896 ScrollUp(usize), ScrollDown(usize), LeftClick(isize, usize), RightClick(isize, usize), Hold(isize, usize), Release(isize, usize), Hover(isize, usize), }
904
905impl Mouse {
906 pub fn position(&self) -> Option<(usize, usize)> {
907 match self {
909 Mouse::LeftClick(line, column) => Some((*line as usize, *column as usize)),
910 Mouse::RightClick(line, column) => Some((*line as usize, *column as usize)),
911 Mouse::Hold(line, column) => Some((*line as usize, *column as usize)),
912 Mouse::Release(line, column) => Some((*line as usize, *column as usize)),
913 Mouse::Hover(line, column) => Some((*line as usize, *column as usize)),
914 _ => None,
915 }
916 }
917}
918
919#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
920pub struct FileMetadata {
921 pub is_dir: bool,
922 pub is_file: bool,
923 pub is_symlink: bool,
924 pub len: u64,
925}
926
927impl From<Metadata> for FileMetadata {
928 fn from(metadata: Metadata) -> Self {
929 FileMetadata {
930 is_dir: metadata.is_dir(),
931 is_file: metadata.is_file(),
932 is_symlink: metadata.is_symlink(),
933 len: metadata.len(),
934 }
935 }
936}
937
938#[derive(Debug, Clone, PartialEq, EnumDiscriminants, Display, Serialize, Deserialize)]
941#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
942#[strum_discriminants(name(EventType))]
943#[non_exhaustive]
944pub enum Event {
945 ModeUpdate(ModeInfo),
946 TabUpdate(Vec<TabInfo>),
947 PaneUpdate(PaneManifest),
948 Key(KeyWithModifier),
950 Mouse(Mouse),
952 Timer(f64),
954 CopyToClipboard(CopyDestination),
956 SystemClipboardFailure,
958 InputReceived,
960 Visible(bool),
962 CustomMessage(
964 String, String, ),
967 FileSystemCreate(Vec<(PathBuf, Option<FileMetadata>)>),
969 FileSystemRead(Vec<(PathBuf, Option<FileMetadata>)>),
971 FileSystemUpdate(Vec<(PathBuf, Option<FileMetadata>)>),
973 FileSystemDelete(Vec<(PathBuf, Option<FileMetadata>)>),
975 PermissionRequestResult(PermissionStatus),
977 SessionUpdate(
978 Vec<SessionInfo>,
979 Vec<(String, Duration)>, ),
981 RunCommandResult(Option<i32>, Vec<u8>, Vec<u8>, BTreeMap<String, String>), WebRequestResult(
984 u16,
985 BTreeMap<String, String>,
986 Vec<u8>,
987 BTreeMap<String, String>,
988 ), CommandPaneOpened(u32, Context), CommandPaneExited(u32, Option<i32>, Context), PaneClosed(PaneId),
996 EditPaneOpened(u32, Context), EditPaneExited(u32, Option<i32>, Context), CommandPaneReRun(u32, Context), FailedToWriteConfigToDisk(Option<String>), ListClients(Vec<ClientInfo>),
1001 HostFolderChanged(PathBuf), FailedToChangeHostFolder(Option<String>), PastedText(String),
1004 ConfigWasWrittenToDisk,
1005 WebServerStatus(WebServerStatus),
1006 FailedToStartWebServer(String),
1007 BeforeClose,
1008 InterceptedKeyPress(KeyWithModifier),
1009 UserAction(Action, ClientId, Option<u32>, Option<ClientId>), PaneRenderReport(HashMap<PaneId, PaneContents>),
1012 PaneRenderReportWithAnsi(HashMap<PaneId, PaneContents>),
1013 ActionComplete(Action, Option<PaneId>, BTreeMap<String, String>), CwdChanged(PaneId, PathBuf, Vec<ClientId>), AvailableLayoutInfo(Vec<LayoutInfo>, Vec<LayoutWithError>),
1016 PluginConfigurationChanged(BTreeMap<String, String>),
1017 HighlightClicked {
1018 pane_id: PaneId,
1019 pattern: String,
1020 matched_string: String,
1021 context: BTreeMap<String, String>,
1022 },
1023}
1024
1025#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants, Display, Serialize, Deserialize)]
1026pub enum WebServerStatus {
1027 Online(String), Offline,
1029 DifferentVersion(String), }
1031
1032#[derive(
1033 Debug,
1034 PartialEq,
1035 Eq,
1036 Hash,
1037 Copy,
1038 Clone,
1039 EnumDiscriminants,
1040 Display,
1041 Serialize,
1042 Deserialize,
1043 PartialOrd,
1044 Ord,
1045)]
1046#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize, Display, PartialOrd, Ord))]
1047#[strum_discriminants(name(PermissionType))]
1048#[non_exhaustive]
1049pub enum Permission {
1050 ReadApplicationState,
1051 ChangeApplicationState,
1052 OpenFiles,
1053 RunCommands,
1054 OpenTerminalsOrPlugins,
1055 WriteToStdin,
1056 WebAccess,
1057 ReadCliPipes,
1058 MessageAndLaunchOtherPlugins,
1059 Reconfigure,
1060 FullHdAccess,
1061 StartWebServer,
1062 InterceptInput,
1063 ReadPaneContents,
1064 RunActionsAsUser,
1065 WriteToClipboard,
1066 ReadSessionEnvironmentVariables,
1067}
1068
1069impl PermissionType {
1070 pub fn display_name(&self) -> String {
1071 match self {
1072 PermissionType::ReadApplicationState => {
1073 "Access Zellij state (Panes, Tabs and UI)".to_owned()
1074 },
1075 PermissionType::ChangeApplicationState => {
1076 "Change Zellij state (Panes, Tabs and UI) and run commands".to_owned()
1077 },
1078 PermissionType::OpenFiles => "Open files (eg. for editing)".to_owned(),
1079 PermissionType::RunCommands => "Run commands".to_owned(),
1080 PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
1081 PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
1082 PermissionType::WebAccess => "Make web requests".to_owned(),
1083 PermissionType::ReadCliPipes => "Control command line pipes and output".to_owned(),
1084 PermissionType::MessageAndLaunchOtherPlugins => {
1085 "Send messages to and launch other plugins".to_owned()
1086 },
1087 PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
1088 PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
1089 PermissionType::StartWebServer => {
1090 "Start a local web server to serve Zellij sessions".to_owned()
1091 },
1092 PermissionType::InterceptInput => "Intercept Input (keyboard & mouse)".to_owned(),
1093 PermissionType::ReadPaneContents => {
1094 "Read pane contents (viewport and selection)".to_owned()
1095 },
1096 PermissionType::RunActionsAsUser => "Execute actions as the user".to_owned(),
1097 PermissionType::WriteToClipboard => "Write to clipboard".to_owned(),
1098 PermissionType::ReadSessionEnvironmentVariables => {
1099 "Read environment variables present upon session creation".to_owned()
1100 },
1101 }
1102 }
1103}
1104
1105#[derive(Debug, Clone)]
1106pub struct PluginPermission {
1107 pub name: String,
1108 pub permissions: Vec<PermissionType>,
1109}
1110
1111impl PluginPermission {
1112 pub fn new(name: String, permissions: Vec<PermissionType>) -> Self {
1113 PluginPermission { name, permissions }
1114 }
1115}
1116
1117#[derive(
1119 Debug,
1120 PartialEq,
1121 Eq,
1122 Hash,
1123 Copy,
1124 Clone,
1125 EnumIter,
1126 Serialize,
1127 Deserialize,
1128 ArgEnum,
1129 PartialOrd,
1130 Ord,
1131)]
1132pub enum InputMode {
1133 #[serde(alias = "normal")]
1136 Normal,
1137 #[serde(alias = "locked")]
1140 Locked,
1141 #[serde(alias = "resize")]
1143 Resize,
1144 #[serde(alias = "pane")]
1146 Pane,
1147 #[serde(alias = "tab")]
1149 Tab,
1150 #[serde(alias = "scroll")]
1152 Scroll,
1153 #[serde(alias = "entersearch")]
1155 EnterSearch,
1156 #[serde(alias = "search")]
1158 Search,
1159 #[serde(alias = "renametab")]
1161 RenameTab,
1162 #[serde(alias = "renamepane")]
1164 RenamePane,
1165 #[serde(alias = "session")]
1167 Session,
1168 #[serde(alias = "move")]
1170 Move,
1171 #[serde(alias = "prompt")]
1173 Prompt,
1174 #[serde(alias = "tmux")]
1176 Tmux,
1177}
1178
1179impl Default for InputMode {
1180 fn default() -> InputMode {
1181 InputMode::Normal
1182 }
1183}
1184
1185#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1186pub enum ThemeHue {
1187 Light,
1188 Dark,
1189}
1190impl Default for ThemeHue {
1191 fn default() -> ThemeHue {
1192 ThemeHue::Dark
1193 }
1194}
1195
1196#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1197pub enum PaletteColor {
1198 Rgb((u8, u8, u8)),
1199 EightBit(u8),
1200}
1201impl Default for PaletteColor {
1202 fn default() -> PaletteColor {
1203 PaletteColor::EightBit(0)
1204 }
1205}
1206
1207#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
1212pub enum HighlightLayer {
1213 Hint, Tool, ActionFeedback, }
1217
1218impl Default for HighlightLayer {
1219 fn default() -> Self {
1220 HighlightLayer::Hint
1221 }
1222}
1223
1224#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1227pub enum HighlightStyle {
1228 None, Emphasis0, Emphasis1, Emphasis2, Emphasis3, BackgroundEmphasis0, BackgroundEmphasis1, BackgroundEmphasis2, BackgroundEmphasis3, CustomRgb {
1238 fg: Option<(u8, u8, u8)>,
1239 bg: Option<(u8, u8, u8)>,
1240 },
1241 CustomIndex {
1242 fg: Option<u8>,
1243 bg: Option<u8>,
1244 },
1245}
1246
1247#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1249pub struct RegexHighlight {
1250 pub pattern: String, pub style: HighlightStyle,
1252 pub layer: HighlightLayer,
1253 pub context: BTreeMap<String, String>, pub on_hover: bool, pub bold: bool,
1256 pub italic: bool,
1257 pub underline: bool,
1258 pub tooltip_text: Option<String>, }
1260
1261impl PaletteColor {
1263 pub fn as_rgb_str(&self) -> String {
1264 let (r, g, b) = match *self {
1265 Self::Rgb((r, g, b)) => (r, g, b),
1266 Self::EightBit(c) => eightbit_to_rgb(c),
1267 };
1268 format!("rgb({}, {}, {})", r, g, b)
1269 }
1270 pub fn from_rgb_str(rgb_str: &str) -> Self {
1271 let trimmed = rgb_str.trim();
1272
1273 if !trimmed.starts_with("rgb(") || !trimmed.ends_with(')') {
1274 return Self::default();
1275 }
1276
1277 let inner = trimmed
1278 .strip_prefix("rgb(")
1279 .and_then(|s| s.strip_suffix(')'))
1280 .unwrap_or("");
1281
1282 let parts: Vec<&str> = inner.split(',').collect();
1283
1284 if parts.len() != 3 {
1285 return Self::default();
1286 }
1287
1288 let mut rgb_values = [0u8; 3];
1289 for (i, part) in parts.iter().enumerate() {
1290 if let Some(rgb_val) = rgb_values.get_mut(i) {
1291 if let Ok(parsed) = part.trim().parse::<u8>() {
1292 *rgb_val = parsed;
1293 } else {
1294 return Self::default();
1295 }
1296 }
1297 }
1298
1299 Self::Rgb((rgb_values[0], rgb_values[1], rgb_values[2]))
1300 }
1301}
1302
1303impl FromStr for InputMode {
1304 type Err = ConversionError;
1305
1306 fn from_str(s: &str) -> Result<Self, ConversionError> {
1307 match s {
1308 "normal" | "Normal" => Ok(InputMode::Normal),
1309 "locked" | "Locked" => Ok(InputMode::Locked),
1310 "resize" | "Resize" => Ok(InputMode::Resize),
1311 "pane" | "Pane" => Ok(InputMode::Pane),
1312 "tab" | "Tab" => Ok(InputMode::Tab),
1313 "search" | "Search" => Ok(InputMode::Search),
1314 "scroll" | "Scroll" => Ok(InputMode::Scroll),
1315 "renametab" | "RenameTab" => Ok(InputMode::RenameTab),
1316 "renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
1317 "session" | "Session" => Ok(InputMode::Session),
1318 "move" | "Move" => Ok(InputMode::Move),
1319 "prompt" | "Prompt" => Ok(InputMode::Prompt),
1320 "tmux" | "Tmux" => Ok(InputMode::Tmux),
1321 "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch),
1322 e => Err(ConversionError::UnknownInputMode(e.into())),
1323 }
1324 }
1325}
1326
1327#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1328pub enum PaletteSource {
1329 Default,
1330 Xresources,
1331}
1332impl Default for PaletteSource {
1333 fn default() -> PaletteSource {
1334 PaletteSource::Default
1335 }
1336}
1337#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
1338pub struct Palette {
1339 pub source: PaletteSource,
1340 pub theme_hue: ThemeHue,
1341 pub fg: PaletteColor,
1342 pub bg: PaletteColor,
1343 pub black: PaletteColor,
1344 pub red: PaletteColor,
1345 pub green: PaletteColor,
1346 pub yellow: PaletteColor,
1347 pub blue: PaletteColor,
1348 pub magenta: PaletteColor,
1349 pub cyan: PaletteColor,
1350 pub white: PaletteColor,
1351 pub orange: PaletteColor,
1352 pub gray: PaletteColor,
1353 pub purple: PaletteColor,
1354 pub gold: PaletteColor,
1355 pub silver: PaletteColor,
1356 pub pink: PaletteColor,
1357 pub brown: PaletteColor,
1358}
1359
1360#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1361pub struct Style {
1362 pub colors: Styling,
1363 pub rounded_corners: bool,
1364 pub hide_session_name: bool,
1365}
1366
1367#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1368pub enum Coloration {
1369 NoStyling,
1370 Styled(StyleDeclaration),
1371}
1372
1373impl Coloration {
1374 pub fn with_fallback(&self, fallback: StyleDeclaration) -> StyleDeclaration {
1375 match &self {
1376 Coloration::NoStyling => fallback,
1377 Coloration::Styled(style) => *style,
1378 }
1379 }
1380}
1381
1382#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1383pub struct Styling {
1384 pub text_unselected: StyleDeclaration,
1385 pub text_selected: StyleDeclaration,
1386 pub ribbon_unselected: StyleDeclaration,
1387 pub ribbon_selected: StyleDeclaration,
1388 pub table_title: StyleDeclaration,
1389 pub table_cell_unselected: StyleDeclaration,
1390 pub table_cell_selected: StyleDeclaration,
1391 pub list_unselected: StyleDeclaration,
1392 pub list_selected: StyleDeclaration,
1393 pub frame_unselected: Option<StyleDeclaration>,
1394 pub frame_selected: StyleDeclaration,
1395 pub frame_highlight: StyleDeclaration,
1396 pub exit_code_success: StyleDeclaration,
1397 pub exit_code_error: StyleDeclaration,
1398 pub multiplayer_user_colors: MultiplayerColors,
1399}
1400
1401#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1402pub struct StyleDeclaration {
1403 pub base: PaletteColor,
1404 pub background: PaletteColor,
1405 pub emphasis_0: PaletteColor,
1406 pub emphasis_1: PaletteColor,
1407 pub emphasis_2: PaletteColor,
1408 pub emphasis_3: PaletteColor,
1409}
1410
1411#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1412pub struct MultiplayerColors {
1413 pub player_1: PaletteColor,
1414 pub player_2: PaletteColor,
1415 pub player_3: PaletteColor,
1416 pub player_4: PaletteColor,
1417 pub player_5: PaletteColor,
1418 pub player_6: PaletteColor,
1419 pub player_7: PaletteColor,
1420 pub player_8: PaletteColor,
1421 pub player_9: PaletteColor,
1422 pub player_10: PaletteColor,
1423}
1424
1425pub const DEFAULT_STYLES: Styling = Styling {
1426 text_unselected: StyleDeclaration {
1427 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1428 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1429 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1430 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1431 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1432 background: PaletteColor::EightBit(default_colors::GRAY),
1433 },
1434 text_selected: StyleDeclaration {
1435 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1436 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1437 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1438 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1439 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1440 background: PaletteColor::EightBit(default_colors::GRAY),
1441 },
1442 ribbon_unselected: StyleDeclaration {
1443 base: PaletteColor::EightBit(default_colors::BLACK),
1444 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1445 emphasis_1: PaletteColor::EightBit(default_colors::WHITE),
1446 emphasis_2: PaletteColor::EightBit(default_colors::BLUE),
1447 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1448 background: PaletteColor::EightBit(default_colors::GRAY),
1449 },
1450 ribbon_selected: StyleDeclaration {
1451 base: PaletteColor::EightBit(default_colors::BLACK),
1452 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1453 emphasis_1: PaletteColor::EightBit(default_colors::ORANGE),
1454 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1455 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1456 background: PaletteColor::EightBit(default_colors::GREEN),
1457 },
1458 exit_code_success: StyleDeclaration {
1459 base: PaletteColor::EightBit(default_colors::GREEN),
1460 emphasis_0: PaletteColor::EightBit(default_colors::CYAN),
1461 emphasis_1: PaletteColor::EightBit(default_colors::BLACK),
1462 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1463 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1464 background: PaletteColor::EightBit(default_colors::GRAY),
1465 },
1466 exit_code_error: StyleDeclaration {
1467 base: PaletteColor::EightBit(default_colors::RED),
1468 emphasis_0: PaletteColor::EightBit(default_colors::YELLOW),
1469 emphasis_1: PaletteColor::EightBit(default_colors::GOLD),
1470 emphasis_2: PaletteColor::EightBit(default_colors::SILVER),
1471 emphasis_3: PaletteColor::EightBit(default_colors::PURPLE),
1472 background: PaletteColor::EightBit(default_colors::GRAY),
1473 },
1474 frame_unselected: None,
1475 frame_selected: StyleDeclaration {
1476 base: PaletteColor::EightBit(default_colors::GREEN),
1477 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1478 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1479 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1480 emphasis_3: PaletteColor::EightBit(default_colors::BROWN),
1481 background: PaletteColor::EightBit(default_colors::GRAY),
1482 },
1483 frame_highlight: StyleDeclaration {
1484 base: PaletteColor::EightBit(default_colors::ORANGE),
1485 emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA),
1486 emphasis_1: PaletteColor::EightBit(default_colors::PURPLE),
1487 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1488 emphasis_3: PaletteColor::EightBit(default_colors::GREEN),
1489 background: PaletteColor::EightBit(default_colors::GREEN),
1490 },
1491 table_title: StyleDeclaration {
1492 base: PaletteColor::EightBit(default_colors::GREEN),
1493 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1494 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1495 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1496 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1497 background: PaletteColor::EightBit(default_colors::GRAY),
1498 },
1499 table_cell_unselected: StyleDeclaration {
1500 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1501 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1502 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1503 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1504 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1505 background: PaletteColor::EightBit(default_colors::GRAY),
1506 },
1507 table_cell_selected: StyleDeclaration {
1508 base: PaletteColor::EightBit(default_colors::GREEN),
1509 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1510 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1511 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1512 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1513 background: PaletteColor::EightBit(default_colors::GRAY),
1514 },
1515 list_unselected: StyleDeclaration {
1516 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1517 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1518 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1519 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1520 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1521 background: PaletteColor::EightBit(default_colors::GRAY),
1522 },
1523 list_selected: StyleDeclaration {
1524 base: PaletteColor::EightBit(default_colors::GREEN),
1525 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1526 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1527 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1528 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1529 background: PaletteColor::EightBit(default_colors::GRAY),
1530 },
1531 multiplayer_user_colors: MultiplayerColors {
1532 player_1: PaletteColor::EightBit(default_colors::MAGENTA),
1533 player_2: PaletteColor::EightBit(default_colors::BLUE),
1534 player_3: PaletteColor::EightBit(default_colors::PURPLE),
1535 player_4: PaletteColor::EightBit(default_colors::YELLOW),
1536 player_5: PaletteColor::EightBit(default_colors::CYAN),
1537 player_6: PaletteColor::EightBit(default_colors::GOLD),
1538 player_7: PaletteColor::EightBit(default_colors::RED),
1539 player_8: PaletteColor::EightBit(default_colors::SILVER),
1540 player_9: PaletteColor::EightBit(default_colors::PINK),
1541 player_10: PaletteColor::EightBit(default_colors::BROWN),
1542 },
1543};
1544
1545impl Default for Styling {
1546 fn default() -> Self {
1547 DEFAULT_STYLES
1548 }
1549}
1550
1551impl From<Styling> for Palette {
1552 fn from(styling: Styling) -> Self {
1553 Palette {
1554 theme_hue: ThemeHue::Dark,
1555 source: PaletteSource::Default,
1556 fg: styling.ribbon_unselected.background,
1557 bg: styling.text_unselected.background,
1558 red: styling.exit_code_error.base,
1559 green: styling.text_unselected.emphasis_2,
1560 yellow: styling.exit_code_error.emphasis_0,
1561 blue: styling.ribbon_unselected.emphasis_2,
1562 magenta: styling.text_unselected.emphasis_3,
1563 orange: styling.text_unselected.emphasis_0,
1564 cyan: styling.text_unselected.emphasis_1,
1565 black: styling.ribbon_unselected.base,
1566 white: styling.ribbon_unselected.emphasis_1,
1567 gray: styling.list_unselected.background,
1568 purple: styling.multiplayer_user_colors.player_3,
1569 gold: styling.multiplayer_user_colors.player_6,
1570 silver: styling.multiplayer_user_colors.player_8,
1571 pink: styling.multiplayer_user_colors.player_9,
1572 brown: styling.multiplayer_user_colors.player_10,
1573 }
1574 }
1575}
1576
1577impl From<Palette> for Styling {
1578 fn from(palette: Palette) -> Self {
1579 let (fg, bg) = match palette.theme_hue {
1580 ThemeHue::Light => (palette.black, palette.white),
1581 ThemeHue::Dark => (palette.white, palette.black),
1582 };
1583 Styling {
1584 text_unselected: StyleDeclaration {
1585 base: fg,
1586 emphasis_0: palette.orange,
1587 emphasis_1: palette.cyan,
1588 emphasis_2: palette.green,
1589 emphasis_3: palette.magenta,
1590 background: bg,
1591 },
1592 text_selected: StyleDeclaration {
1593 base: fg,
1594 emphasis_0: palette.orange,
1595 emphasis_1: palette.cyan,
1596 emphasis_2: palette.green,
1597 emphasis_3: palette.magenta,
1598 background: palette.bg,
1599 },
1600 ribbon_unselected: StyleDeclaration {
1601 base: palette.black,
1602 emphasis_0: palette.red,
1603 emphasis_1: palette.white,
1604 emphasis_2: palette.blue,
1605 emphasis_3: palette.magenta,
1606 background: palette.fg,
1607 },
1608 ribbon_selected: StyleDeclaration {
1609 base: palette.black,
1610 emphasis_0: palette.red,
1611 emphasis_1: palette.orange,
1612 emphasis_2: palette.magenta,
1613 emphasis_3: palette.blue,
1614 background: palette.green,
1615 },
1616 exit_code_success: StyleDeclaration {
1617 base: palette.green,
1618 emphasis_0: palette.cyan,
1619 emphasis_1: palette.black,
1620 emphasis_2: palette.magenta,
1621 emphasis_3: palette.blue,
1622 background: Default::default(),
1623 },
1624 exit_code_error: StyleDeclaration {
1625 base: palette.red,
1626 emphasis_0: palette.yellow,
1627 emphasis_1: palette.gold,
1628 emphasis_2: palette.silver,
1629 emphasis_3: palette.purple,
1630 background: Default::default(),
1631 },
1632 frame_unselected: None,
1633 frame_selected: StyleDeclaration {
1634 base: palette.green,
1635 emphasis_0: palette.orange,
1636 emphasis_1: palette.cyan,
1637 emphasis_2: palette.magenta,
1638 emphasis_3: palette.brown,
1639 background: Default::default(),
1640 },
1641 frame_highlight: StyleDeclaration {
1642 base: palette.orange,
1643 emphasis_0: palette.magenta,
1644 emphasis_1: palette.purple,
1645 emphasis_2: palette.orange,
1646 emphasis_3: palette.orange,
1647 background: Default::default(),
1648 },
1649 table_title: StyleDeclaration {
1650 base: palette.green,
1651 emphasis_0: palette.orange,
1652 emphasis_1: palette.cyan,
1653 emphasis_2: palette.green,
1654 emphasis_3: palette.magenta,
1655 background: palette.gray,
1656 },
1657 table_cell_unselected: StyleDeclaration {
1658 base: fg,
1659 emphasis_0: palette.orange,
1660 emphasis_1: palette.cyan,
1661 emphasis_2: palette.green,
1662 emphasis_3: palette.magenta,
1663 background: palette.black,
1664 },
1665 table_cell_selected: StyleDeclaration {
1666 base: fg,
1667 emphasis_0: palette.orange,
1668 emphasis_1: palette.cyan,
1669 emphasis_2: palette.green,
1670 emphasis_3: palette.magenta,
1671 background: palette.bg,
1672 },
1673 list_unselected: StyleDeclaration {
1674 base: palette.white,
1675 emphasis_0: palette.orange,
1676 emphasis_1: palette.cyan,
1677 emphasis_2: palette.green,
1678 emphasis_3: palette.magenta,
1679 background: palette.black,
1680 },
1681 list_selected: StyleDeclaration {
1682 base: palette.white,
1683 emphasis_0: palette.orange,
1684 emphasis_1: palette.cyan,
1685 emphasis_2: palette.green,
1686 emphasis_3: palette.magenta,
1687 background: palette.bg,
1688 },
1689 multiplayer_user_colors: MultiplayerColors {
1690 player_1: palette.magenta,
1691 player_2: palette.blue,
1692 player_3: palette.purple,
1693 player_4: palette.yellow,
1694 player_5: palette.cyan,
1695 player_6: palette.gold,
1696 player_7: palette.red,
1697 player_8: palette.silver,
1698 player_9: palette.pink,
1699 player_10: palette.brown,
1700 },
1701 }
1702 }
1703}
1704
1705pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
1707
1708#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1710pub struct ModeInfo {
1711 pub mode: InputMode,
1712 pub base_mode: Option<InputMode>,
1713 pub keybinds: KeybindsVec,
1714 pub style: Style,
1715 pub capabilities: PluginCapabilities,
1716 pub session_name: Option<String>,
1717 pub editor: Option<PathBuf>,
1718 pub shell: Option<PathBuf>,
1719 pub web_clients_allowed: Option<bool>,
1720 pub web_sharing: Option<WebSharing>,
1721 pub currently_marking_pane_group: Option<bool>,
1722 pub is_web_client: Option<bool>,
1723 pub web_server_ip: Option<IpAddr>,
1725 pub web_server_port: Option<u16>,
1726 pub web_server_capability: Option<bool>,
1727}
1728
1729impl ModeInfo {
1730 pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
1731 self.get_keybinds_for_mode(self.mode)
1732 }
1733
1734 pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
1735 for (vec_mode, map) in &self.keybinds {
1736 if mode == *vec_mode {
1737 return map.to_vec();
1738 }
1739 }
1740 vec![]
1741 }
1742 pub fn update_keybinds(&mut self, keybinds: Keybinds) {
1743 self.keybinds = keybinds.to_keybinds_vec();
1744 }
1745 pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
1746 self.base_mode = Some(new_default_mode);
1747 }
1748 pub fn update_theme(&mut self, theme: Styling) {
1749 self.style.colors = theme.into();
1750 }
1751 pub fn update_rounded_corners(&mut self, rounded_corners: bool) {
1752 self.style.rounded_corners = rounded_corners;
1753 }
1754 pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) {
1755 self.capabilities.arrow_fonts = !should_support_arrow_fonts;
1758 }
1759 pub fn update_hide_session_name(&mut self, hide_session_name: bool) {
1760 self.style.hide_session_name = hide_session_name;
1761 }
1762 pub fn change_to_default_mode(&mut self) {
1763 if let Some(base_mode) = self.base_mode {
1764 self.mode = base_mode;
1765 }
1766 }
1767}
1768
1769#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1770pub struct SessionInfo {
1771 pub name: String,
1772 pub tabs: Vec<TabInfo>,
1773 pub panes: PaneManifest,
1774 pub connected_clients: usize,
1775 pub is_current_session: bool,
1776 pub available_layouts: Vec<LayoutInfo>,
1777 pub plugins: BTreeMap<u32, PluginInfo>,
1778 pub web_clients_allowed: bool,
1779 pub web_client_count: usize,
1780 pub tab_history: BTreeMap<ClientId, Vec<usize>>,
1781 pub pane_history: BTreeMap<ClientId, Vec<PaneId>>,
1782 pub creation_time: Duration,
1783}
1784
1785#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1786pub struct PluginInfo {
1787 pub location: String,
1788 pub configuration: BTreeMap<String, String>,
1789}
1790
1791impl From<RunPlugin> for PluginInfo {
1792 fn from(run_plugin: RunPlugin) -> Self {
1793 PluginInfo {
1794 location: run_plugin.location.display(),
1795 configuration: run_plugin.configuration.inner().clone(),
1796 }
1797 }
1798}
1799
1800#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1801pub enum LayoutInfo {
1802 BuiltIn(String),
1803 File(String, LayoutMetadata),
1804 Url(String),
1805 Stringified(String),
1806}
1807
1808#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1809pub struct LayoutWithError {
1810 pub layout_name: String,
1811 pub error: LayoutParsingError,
1812}
1813
1814#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1815pub enum LayoutParsingError {
1816 KdlError {
1817 kdl_error: KdlError,
1818 file_name: String,
1819 source_code: String,
1820 },
1821 SyntaxError,
1822}
1823
1824impl AsRef<LayoutInfo> for LayoutInfo {
1825 fn as_ref(&self) -> &LayoutInfo {
1826 self
1827 }
1828}
1829
1830#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1831pub struct LayoutMetadata {
1832 pub tabs: Vec<TabMetadata>,
1833 pub creation_time: String,
1834 pub update_time: String,
1835}
1836
1837impl From<&PathBuf> for LayoutMetadata {
1838 fn from(path: &PathBuf) -> LayoutMetadata {
1839 match Layout::stringified_from_path(path) {
1840 Ok((path_str, stringified_layout, _swap_layouts)) => {
1841 match Layout::from_kdl(&stringified_layout, Some(path_str), None, None) {
1842 Ok(layout) => {
1843 let layout_tabs = layout.tabs();
1844 let tabs = if layout_tabs.is_empty() {
1845 let (tiled_pane_layout, floating_pane_layout) = layout.new_tab();
1846 vec![TabMetadata::from(&(
1847 None,
1848 tiled_pane_layout,
1849 floating_pane_layout,
1850 ))]
1851 } else {
1852 layout
1853 .tabs()
1854 .into_iter()
1855 .map(|tab| TabMetadata::from(&tab))
1856 .collect()
1857 };
1858
1859 let (creation_time, update_time) =
1861 LayoutMetadata::creation_and_update_times(&path);
1862
1863 LayoutMetadata {
1864 tabs,
1865 creation_time,
1866 update_time,
1867 }
1868 },
1869 Err(e) => {
1870 log::error!("Failed to parse layout: {}", e);
1871 LayoutMetadata::default()
1872 },
1873 }
1874 },
1875 Err(e) => {
1876 log::error!("Failed to read layout file: {}", e);
1877 LayoutMetadata::default()
1878 },
1879 }
1880 }
1881}
1882
1883impl LayoutMetadata {
1884 fn creation_and_update_times(path: &PathBuf) -> (String, String) {
1885 match std::fs::metadata(path) {
1887 Ok(metadata) => {
1888 let creation_time = metadata
1889 .created()
1890 .ok()
1891 .and_then(|t| {
1892 t.duration_since(std::time::UNIX_EPOCH)
1893 .ok()
1894 .map(|d| d.as_secs().to_string())
1895 })
1896 .unwrap_or_default();
1897
1898 let update_time = metadata
1899 .modified()
1900 .ok()
1901 .and_then(|t| {
1902 t.duration_since(std::time::UNIX_EPOCH)
1903 .ok()
1904 .map(|d| d.as_secs().to_string())
1905 })
1906 .unwrap_or_default();
1907
1908 (creation_time, update_time)
1909 },
1910 Err(_) => (String::new(), String::new()),
1911 }
1912 }
1913}
1914
1915#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1916pub struct TabMetadata {
1917 pub panes: Vec<PaneMetadata>,
1918 pub name: Option<String>,
1919}
1920
1921impl
1922 From<&(
1923 Option<String>,
1924 crate::input::layout::TiledPaneLayout,
1925 Vec<crate::input::layout::FloatingPaneLayout>,
1926 )> for TabMetadata
1927{
1928 fn from(
1929 tab: &(
1930 Option<String>,
1931 crate::input::layout::TiledPaneLayout,
1932 Vec<crate::input::layout::FloatingPaneLayout>,
1933 ),
1934 ) -> Self {
1935 let (tab_name, tiled_pane_layout, floating_panes) = tab;
1936
1937 let mut panes = Vec::new();
1939 collect_leaf_panes(&tiled_pane_layout, &mut panes);
1940
1941 for floating_pane in floating_panes {
1943 panes.push(PaneMetadata::from(floating_pane));
1944 }
1945
1946 TabMetadata {
1947 panes,
1948 name: tab_name.clone(),
1949 }
1950 }
1951}
1952
1953#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1954pub struct PaneMetadata {
1955 pub name: Option<String>,
1956 pub is_plugin: bool,
1957 pub is_builtin_plugin: bool,
1958}
1959
1960impl From<&crate::input::layout::TiledPaneLayout> for PaneMetadata {
1961 fn from(pane: &crate::input::layout::TiledPaneLayout) -> Self {
1962 let mut is_plugin = false;
1963 let mut is_builtin_plugin = false;
1964
1965 let name = if let Some(ref name) = pane.name {
1967 Some(name.clone())
1968 } else if let Some(ref run) = pane.run {
1969 match run {
1971 Run::Command(cmd) => {
1972 Some(cmd.command.to_string_lossy().to_string())
1974 },
1975 Run::EditFile(path, _line, _cwd) => {
1976 path.file_name().map(|n| n.to_string_lossy().to_string())
1978 },
1979 Run::Plugin(plugin) => {
1980 is_plugin = true;
1981 is_builtin_plugin = plugin.is_builtin_plugin();
1982 Some(plugin.location_string())
1983 },
1984 Run::Cwd(_) => None,
1985 }
1986 } else {
1987 None
1988 };
1989
1990 PaneMetadata {
1991 name,
1992 is_plugin,
1993 is_builtin_plugin,
1994 }
1995 }
1996}
1997
1998impl From<&crate::input::layout::FloatingPaneLayout> for PaneMetadata {
1999 fn from(pane: &crate::input::layout::FloatingPaneLayout) -> Self {
2000 let mut is_plugin = false;
2001 let mut is_builtin_plugin = false;
2002
2003 let name = if let Some(ref name) = pane.name {
2005 Some(name.clone())
2006 } else if let Some(ref run) = pane.run {
2007 match run {
2009 Run::Command(cmd) => {
2010 Some(cmd.command.to_string_lossy().to_string())
2012 },
2013 Run::EditFile(path, _line, _cwd) => {
2014 path.file_name().map(|n| n.to_string_lossy().to_string())
2016 },
2017 Run::Plugin(plugin) => {
2018 is_plugin = true;
2019 is_builtin_plugin = match plugin {
2020 crate::input::layout::RunPluginOrAlias::RunPlugin(run_plugin) => {
2021 matches!(run_plugin.location, RunPluginLocation::Zellij(_))
2022 },
2023 crate::input::layout::RunPluginOrAlias::Alias(_) => false,
2024 };
2025 Some(plugin.location_string())
2027 },
2028 Run::Cwd(_) => None,
2029 }
2030 } else {
2031 None
2032 };
2033
2034 PaneMetadata {
2035 name,
2036 is_plugin,
2037 is_builtin_plugin,
2038 }
2039 }
2040}
2041
2042fn collect_leaf_panes(
2044 pane: &crate::input::layout::TiledPaneLayout,
2045 result: &mut Vec<PaneMetadata>,
2046) {
2047 if pane.children.is_empty() {
2048 result.push(PaneMetadata::from(pane));
2050 } else {
2051 for child in &pane.children {
2053 collect_leaf_panes(child, result);
2054 }
2055 }
2056}
2057
2058impl LayoutInfo {
2059 pub fn name(&self) -> &str {
2060 match self {
2061 LayoutInfo::BuiltIn(name) => &name,
2062 LayoutInfo::File(name, _) => &name,
2063 LayoutInfo::Url(url) => &url,
2064 LayoutInfo::Stringified(layout) => &layout,
2065 }
2066 }
2067 pub fn is_builtin(&self) -> bool {
2068 match self {
2069 LayoutInfo::BuiltIn(_name) => true,
2070 LayoutInfo::File(_name, _) => false,
2071 LayoutInfo::Url(_url) => false,
2072 LayoutInfo::Stringified(_stringified) => false,
2073 }
2074 }
2075 pub fn from_cli(
2076 layout_dir: &Option<PathBuf>,
2077 maybe_layout_path: &Option<PathBuf>,
2078 cwd: PathBuf,
2079 ) -> Option<Self> {
2080 let layout_path = maybe_layout_path
2087 .clone()
2088 .unwrap_or(PathBuf::from("default"));
2089
2090 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2091 Some(LayoutInfo::Url(layout_path.display().to_string()))
2092 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2093 let layout_dir = cwd;
2094 let file_path = layout_dir.join(layout_path);
2095 Some(LayoutInfo::File(
2096 file_path.display().to_string(),
2098 LayoutMetadata::from(&file_path),
2099 ))
2100 } else {
2101 if let Some(layout_dir) = layout_dir
2105 .as_ref()
2106 .map(|l| l.clone())
2107 .or_else(default_layout_dir)
2108 {
2109 let file_path = layout_dir.join(&layout_path);
2110 if file_path.exists() {
2111 return Some(LayoutInfo::File(
2112 file_path.display().to_string(),
2113 LayoutMetadata::from(&file_path),
2114 ));
2115 }
2116 let file_path_with_ext = file_path.with_extension("kdl");
2117 if file_path_with_ext.exists() {
2118 return Some(LayoutInfo::File(
2119 file_path_with_ext.display().to_string(),
2120 LayoutMetadata::from(&file_path_with_ext),
2121 ));
2122 }
2123 }
2124 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2126 }
2127 }
2128 pub fn from_config(
2129 layout_dir: &Option<PathBuf>,
2130 maybe_layout_path: &Option<PathBuf>,
2131 ) -> Option<Self> {
2132 let layout_path = maybe_layout_path
2139 .clone()
2140 .unwrap_or(PathBuf::from("default"));
2141
2142 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2143 Some(LayoutInfo::Url(layout_path.display().to_string()))
2144 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2145 let Some(layout_dir) = layout_dir
2146 .as_ref()
2147 .map(|l| l.clone())
2148 .or_else(default_layout_dir)
2149 else {
2150 return None;
2151 };
2152 let file_path = layout_dir.join(layout_path);
2153 Some(LayoutInfo::File(
2154 file_path.display().to_string(),
2156 LayoutMetadata::from(&file_path),
2157 ))
2158 } else {
2159 if let Some(layout_dir) = layout_dir
2163 .as_ref()
2164 .map(|l| l.clone())
2165 .or_else(default_layout_dir)
2166 {
2167 let file_path = layout_dir.join(&layout_path);
2168 if file_path.exists() {
2169 return Some(LayoutInfo::File(
2170 file_path.display().to_string(),
2171 LayoutMetadata::from(&file_path),
2172 ));
2173 }
2174 let file_path_with_ext = file_path.with_extension("kdl");
2175 if file_path_with_ext.exists() {
2176 return Some(LayoutInfo::File(
2177 file_path_with_ext.display().to_string(),
2178 LayoutMetadata::from(&file_path_with_ext),
2179 ));
2180 }
2181 }
2182 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2184 }
2185 }
2186}
2187
2188#[allow(clippy::derive_hash_xor_eq)]
2189impl Hash for SessionInfo {
2190 fn hash<H: Hasher>(&self, state: &mut H) {
2191 self.name.hash(state);
2192 }
2193}
2194
2195impl SessionInfo {
2196 pub fn new(name: String) -> Self {
2197 SessionInfo {
2198 name,
2199 ..Default::default()
2200 }
2201 }
2202 pub fn update_tab_info(&mut self, new_tab_info: Vec<TabInfo>) {
2203 self.tabs = new_tab_info;
2204 }
2205 pub fn update_pane_info(&mut self, new_pane_info: PaneManifest) {
2206 self.panes = new_pane_info;
2207 }
2208 pub fn update_connected_clients(&mut self, new_connected_clients: usize) {
2209 self.connected_clients = new_connected_clients;
2210 }
2211 pub fn populate_plugin_list(&mut self, plugins: BTreeMap<u32, RunPlugin>) {
2212 let mut plugin_list = BTreeMap::new();
2214 for (plugin_id, run_plugin) in plugins {
2215 plugin_list.insert(plugin_id, run_plugin.into());
2216 }
2217 self.plugins = plugin_list;
2218 }
2219}
2220
2221#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2223pub struct TabInfo {
2224 pub position: usize,
2226 pub name: String,
2228 pub active: bool,
2230 pub panes_to_hide: usize,
2232 pub is_fullscreen_active: bool,
2234 pub is_sync_panes_active: bool,
2236 pub are_floating_panes_visible: bool,
2237 pub other_focused_clients: Vec<ClientId>,
2238 pub active_swap_layout_name: Option<String>,
2239 pub is_swap_layout_dirty: bool,
2241 pub viewport_rows: usize,
2243 pub viewport_columns: usize,
2245 pub display_area_rows: usize,
2248 pub display_area_columns: usize,
2251 pub selectable_tiled_panes_count: usize,
2253 pub selectable_floating_panes_count: usize,
2255 pub tab_id: usize,
2257 pub has_bell_notification: bool,
2259 pub is_flashing_bell: bool,
2261}
2262
2263#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
2267pub struct PaneManifest {
2268 pub panes: HashMap<usize, Vec<PaneInfo>>, }
2270
2271#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2282pub struct PaneInfo {
2283 pub id: u32,
2285 pub is_plugin: bool,
2288 pub is_focused: bool,
2290 pub is_fullscreen: bool,
2291 pub is_floating: bool,
2293 pub is_suppressed: bool,
2296 pub title: String,
2298 pub exited: bool,
2301 pub exit_status: Option<i32>,
2303 pub is_held: bool,
2306 pub pane_x: usize,
2307 pub pane_content_x: usize,
2308 pub pane_y: usize,
2309 pub pane_content_y: usize,
2310 pub pane_rows: usize,
2311 pub pane_content_rows: usize,
2312 pub pane_columns: usize,
2313 pub pane_content_columns: usize,
2314 pub cursor_coordinates_in_pane: Option<(usize, usize)>, pub terminal_command: Option<String>,
2320 pub plugin_url: Option<String>,
2323 pub is_selectable: bool,
2326 pub index_in_pane_group: BTreeMap<ClientId, usize>,
2329 pub default_fg: Option<String>,
2331 pub default_bg: Option<String>,
2333}
2334
2335#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
2336pub struct PaneListEntry {
2337 #[serde(flatten)]
2338 pub pane_info: PaneInfo,
2339 pub tab_id: usize,
2340 pub tab_position: usize,
2341 pub tab_name: String,
2342 #[serde(skip_serializing_if = "Option::is_none")]
2343 pub pane_command: Option<String>,
2344 #[serde(skip_serializing_if = "Option::is_none")]
2345 pub pane_cwd: Option<String>,
2346}
2347
2348pub type ListPanesResponse = Vec<PaneListEntry>;
2349pub type ListTabsResponse = Vec<TabInfo>;
2350
2351#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
2352pub struct ClientInfo {
2353 pub client_id: ClientId,
2354 pub pane_id: PaneId,
2355 pub running_command: String,
2356 pub is_current_client: bool,
2357}
2358
2359impl ClientInfo {
2360 pub fn new(
2361 client_id: ClientId,
2362 pane_id: PaneId,
2363 running_command: String,
2364 is_current_client: bool,
2365 ) -> Self {
2366 ClientInfo {
2367 client_id,
2368 pane_id,
2369 running_command,
2370 is_current_client,
2371 }
2372 }
2373}
2374
2375#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2376pub struct PaneRenderReport {
2377 pub all_pane_contents: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2378 pub all_pane_contents_with_ansi: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2379}
2380
2381impl PaneRenderReport {
2382 pub fn add_pane_contents(
2383 &mut self,
2384 client_ids: &[ClientId],
2385 pane_id: PaneId,
2386 pane_contents: PaneContents,
2387 ) {
2388 for client_id in client_ids {
2389 let p = self
2390 .all_pane_contents
2391 .entry(*client_id)
2392 .or_insert_with(|| HashMap::new());
2393 p.insert(pane_id, pane_contents.clone());
2394 }
2395 }
2396 pub fn add_pane_contents_with_ansi(
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_with_ansi
2405 .entry(*client_id)
2406 .or_insert_with(|| HashMap::new());
2407 p.insert(pane_id, pane_contents.clone());
2408 }
2409 }
2410}
2411
2412#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2413pub struct PaneContents {
2414 pub lines_above_viewport: Vec<String>,
2418 pub lines_below_viewport: Vec<String>,
2419 pub viewport: Vec<String>,
2420 pub selected_text: Option<SelectedText>,
2421}
2422
2423fn extract_text_by_columns(line: &str, start_col: usize, end_col: usize) -> String {
2425 let mut current_col = 0;
2426 let mut result = String::new();
2427 let mut capturing = false;
2428
2429 for ch in line.chars() {
2430 let char_width = ch.width().unwrap_or(0);
2431
2432 if current_col >= start_col && !capturing {
2434 capturing = true;
2435 }
2436
2437 if current_col >= end_col {
2439 break;
2440 }
2441
2442 if capturing {
2444 result.push(ch);
2445 }
2446
2447 current_col += char_width;
2448 }
2449
2450 result
2451}
2452
2453fn extract_text_from_column(line: &str, start_col: usize) -> String {
2455 let mut current_col = 0;
2456 let mut result = String::new();
2457 let mut capturing = false;
2458
2459 for ch in line.chars() {
2460 let char_width = ch.width().unwrap_or(0);
2461
2462 if current_col >= start_col {
2463 capturing = true;
2464 }
2465
2466 if capturing {
2467 result.push(ch);
2468 }
2469
2470 current_col += char_width;
2471 }
2472
2473 result
2474}
2475
2476fn extract_text_to_column(line: &str, end_col: usize) -> String {
2478 let mut current_col = 0;
2479 let mut result = String::new();
2480
2481 for ch in line.chars() {
2482 let char_width = ch.width().unwrap_or(0);
2483
2484 if current_col >= end_col {
2485 break;
2486 }
2487
2488 result.push(ch);
2489 current_col += char_width;
2490 }
2491
2492 result
2493}
2494
2495impl PaneContents {
2496 pub fn new(viewport: Vec<String>, selection_start: Position, selection_end: Position) -> Self {
2497 PaneContents {
2498 viewport,
2499 selected_text: SelectedText::from_positions(selection_start, selection_end),
2500 ..Default::default()
2501 }
2502 }
2503 pub fn new_with_scrollback(
2504 viewport: Vec<String>,
2505 selection_start: Position,
2506 selection_end: Position,
2507 lines_above_viewport: Vec<String>,
2508 lines_below_viewport: Vec<String>,
2509 ) -> Self {
2510 PaneContents {
2511 viewport,
2512 selected_text: SelectedText::from_positions(selection_start, selection_end),
2513 lines_above_viewport,
2514 lines_below_viewport,
2515 }
2516 }
2517
2518 pub fn get_selected_text(&self) -> Option<String> {
2521 let selected_text = self.selected_text?;
2522
2523 let start_line = selected_text.start.line() as usize;
2524 let start_col = selected_text.start.column();
2525 let end_line = selected_text.end.line() as usize;
2526 let end_col = selected_text.end.column();
2527
2528 if start_line >= self.viewport.len() || end_line >= self.viewport.len() {
2530 return None;
2531 }
2532
2533 if start_line == end_line {
2534 let line = &self.viewport[start_line];
2536 Some(extract_text_by_columns(line, start_col, end_col))
2537 } else {
2538 let mut result = String::new();
2540
2541 let first_line = &self.viewport[start_line];
2543 result.push_str(&extract_text_from_column(first_line, start_col));
2544 result.push('\n');
2545
2546 for i in (start_line + 1)..end_line {
2548 result.push_str(&self.viewport[i]);
2549 result.push('\n');
2550 }
2551
2552 let last_line = &self.viewport[end_line];
2554 result.push_str(&extract_text_to_column(last_line, end_col));
2555
2556 Some(result)
2557 }
2558 }
2559}
2560
2561#[derive(Debug, Clone, Serialize, Deserialize)]
2562pub enum PaneScrollbackResponse {
2563 Ok(PaneContents),
2564 Err(String),
2565}
2566
2567#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2568pub enum GetPanePidResponse {
2569 Ok(i32),
2570 Err(String),
2571}
2572
2573#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2574pub enum GetPaneRunningCommandResponse {
2575 Ok(Vec<String>),
2576 Err(String),
2577}
2578
2579#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2580pub enum GetPaneCwdResponse {
2581 Ok(PathBuf),
2582 Err(String),
2583}
2584
2585#[derive(Debug, Clone, PartialEq)]
2586pub enum GetFocusedPaneInfoResponse {
2587 Ok { tab_index: usize, pane_id: PaneId },
2588 Err(String),
2589}
2590
2591#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2592pub enum SaveLayoutResponse {
2593 Ok(()),
2594 Err(String),
2595}
2596
2597#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2598pub enum DeleteLayoutResponse {
2599 Ok(()),
2600 Err(String),
2601}
2602
2603#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2604pub enum RenameLayoutResponse {
2605 Ok(()),
2606 Err(String),
2607}
2608
2609#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2610pub enum EditLayoutResponse {
2611 Ok(()),
2612 Err(String),
2613}
2614
2615#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2616pub struct SelectedText {
2617 pub start: Position,
2618 pub end: Position,
2619}
2620
2621impl SelectedText {
2622 pub fn new(start: Position, end: Position) -> Self {
2623 let (normalized_start, normalized_end) = if start <= end {
2625 (start, end)
2626 } else {
2627 (end, start)
2628 };
2629
2630 let normalized_start = Position::new(
2633 normalized_start.line().max(0) as i32,
2634 normalized_start.column() as u16,
2635 );
2636 let normalized_end = Position::new(
2637 normalized_end.line().max(0) as i32,
2638 normalized_end.column() as u16,
2639 );
2640
2641 SelectedText {
2642 start: normalized_start,
2643 end: normalized_end,
2644 }
2645 }
2646
2647 pub fn from_positions(start: Position, end: Position) -> Option<Self> {
2648 if start == end {
2649 None
2650 } else {
2651 Some(Self::new(start, end))
2652 }
2653 }
2654}
2655
2656#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2657pub struct PluginIds {
2658 pub plugin_id: u32,
2659 pub zellij_pid: u32,
2660 pub initial_cwd: PathBuf,
2661 pub client_id: ClientId,
2662}
2663
2664#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
2666pub struct PluginTag(String);
2667
2668impl PluginTag {
2669 pub fn new(url: impl Into<String>) -> Self {
2670 PluginTag(url.into())
2671 }
2672}
2673
2674impl From<PluginTag> for String {
2675 fn from(tag: PluginTag) -> Self {
2676 tag.0
2677 }
2678}
2679
2680impl fmt::Display for PluginTag {
2681 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2682 write!(f, "{}", self.0)
2683 }
2684}
2685
2686#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2687pub struct PluginCapabilities {
2688 pub arrow_fonts: bool,
2689}
2690
2691impl Default for PluginCapabilities {
2692 fn default() -> PluginCapabilities {
2693 PluginCapabilities { arrow_fonts: true }
2694 }
2695}
2696
2697#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2699pub enum CopyDestination {
2700 Command,
2701 Primary,
2702 System,
2703}
2704
2705#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2706pub enum PermissionStatus {
2707 Granted,
2708 Denied,
2709}
2710
2711#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
2712pub struct FileToOpen {
2713 pub path: PathBuf,
2714 pub line_number: Option<usize>,
2715 pub cwd: Option<PathBuf>,
2716}
2717
2718impl FileToOpen {
2719 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2720 FileToOpen {
2721 path: path.as_ref().to_path_buf(),
2722 ..Default::default()
2723 }
2724 }
2725 pub fn with_line_number(mut self, line_number: usize) -> Self {
2726 self.line_number = Some(line_number);
2727 self
2728 }
2729 pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
2730 self.cwd = Some(cwd);
2731 self
2732 }
2733}
2734
2735#[derive(Debug, Default, Clone)]
2736pub struct CommandToRun {
2737 pub path: PathBuf,
2738 pub args: Vec<String>,
2739 pub cwd: Option<PathBuf>,
2740}
2741
2742impl CommandToRun {
2743 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2744 CommandToRun {
2745 path: path.as_ref().to_path_buf(),
2746 ..Default::default()
2747 }
2748 }
2749 pub fn new_with_args<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) -> Self {
2750 CommandToRun {
2751 path: path.as_ref().to_path_buf(),
2752 args: args.into_iter().map(|a| a.as_ref().to_owned()).collect(),
2753 ..Default::default()
2754 }
2755 }
2756}
2757
2758#[derive(Debug, Default, Clone)]
2759pub struct MessageToPlugin {
2760 pub plugin_url: Option<String>,
2761 pub destination_plugin_id: Option<u32>,
2762 pub plugin_config: BTreeMap<String, String>,
2763 pub message_name: String,
2764 pub message_payload: Option<String>,
2765 pub message_args: BTreeMap<String, String>,
2766 pub new_plugin_args: Option<NewPluginArgs>,
2769 pub floating_pane_coordinates: Option<FloatingPaneCoordinates>,
2770}
2771
2772#[derive(Debug, Default, Clone)]
2773pub struct NewPluginArgs {
2774 pub should_float: Option<bool>,
2775 pub pane_id_to_replace: Option<PaneId>,
2776 pub pane_title: Option<String>,
2777 pub cwd: Option<PathBuf>,
2778 pub skip_cache: bool,
2779 pub should_focus: Option<bool>,
2780}
2781
2782#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
2783pub enum PaneId {
2784 Terminal(u32),
2785 Plugin(u32),
2786}
2787
2788impl Default for PaneId {
2789 fn default() -> Self {
2790 PaneId::Terminal(0)
2791 }
2792}
2793
2794impl FromStr for PaneId {
2795 type Err = Box<dyn std::error::Error>;
2796 fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
2797 if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
2798 u32::from_str_radix(terminal_stringified_pane_id, 10)
2799 .map(|id| PaneId::Terminal(id))
2800 .map_err(|e| e.into())
2801 } else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
2802 u32::from_str_radix(plugin_pane_id, 10)
2803 .map(|id| PaneId::Plugin(id))
2804 .map_err(|e| e.into())
2805 } else {
2806 u32::from_str_radix(&stringified_pane_id, 10)
2807 .map(|id| PaneId::Terminal(id))
2808 .map_err(|e| e.into())
2809 }
2810 }
2811}
2812
2813impl std::fmt::Display for PaneId {
2814 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2815 match self {
2816 PaneId::Terminal(id) => write!(f, "terminal_{}", id),
2817 PaneId::Plugin(id) => write!(f, "plugin_{}", id),
2818 }
2819 }
2820}
2821
2822impl MessageToPlugin {
2823 pub fn new(message_name: impl Into<String>) -> Self {
2824 MessageToPlugin {
2825 message_name: message_name.into(),
2826 ..Default::default()
2827 }
2828 }
2829 pub fn with_plugin_url(mut self, url: impl Into<String>) -> Self {
2830 self.plugin_url = Some(url.into());
2831 self
2832 }
2833 pub fn with_destination_plugin_id(mut self, destination_plugin_id: u32) -> Self {
2834 self.destination_plugin_id = Some(destination_plugin_id);
2835 self
2836 }
2837 pub fn with_plugin_config(mut self, plugin_config: BTreeMap<String, String>) -> Self {
2838 self.plugin_config = plugin_config;
2839 self
2840 }
2841 pub fn with_payload(mut self, payload: impl Into<String>) -> Self {
2842 self.message_payload = Some(payload.into());
2843 self
2844 }
2845 pub fn with_args(mut self, args: BTreeMap<String, String>) -> Self {
2846 self.message_args = args;
2847 self
2848 }
2849 pub fn with_floating_pane_coordinates(
2850 mut self,
2851 floating_pane_coordinates: FloatingPaneCoordinates,
2852 ) -> Self {
2853 self.floating_pane_coordinates = Some(floating_pane_coordinates);
2854 self
2855 }
2856 pub fn new_plugin_instance_should_float(mut self, should_float: bool) -> Self {
2857 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2858 new_plugin_args.should_float = Some(should_float);
2859 self
2860 }
2861 pub fn new_plugin_instance_should_replace_pane(mut self, pane_id: PaneId) -> Self {
2862 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2863 new_plugin_args.pane_id_to_replace = Some(pane_id);
2864 self
2865 }
2866 pub fn new_plugin_instance_should_have_pane_title(
2867 mut self,
2868 pane_title: impl Into<String>,
2869 ) -> Self {
2870 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2871 new_plugin_args.pane_title = Some(pane_title.into());
2872 self
2873 }
2874 pub fn new_plugin_instance_should_have_cwd(mut self, cwd: PathBuf) -> Self {
2875 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2876 new_plugin_args.cwd = Some(cwd);
2877 self
2878 }
2879 pub fn new_plugin_instance_should_skip_cache(mut self) -> Self {
2880 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2881 new_plugin_args.skip_cache = true;
2882 self
2883 }
2884 pub fn new_plugin_instance_should_be_focused(mut self) -> Self {
2885 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2886 new_plugin_args.should_focus = Some(true);
2887 self
2888 }
2889 pub fn has_cwd(&self) -> bool {
2890 self.new_plugin_args
2891 .as_ref()
2892 .map(|n| n.cwd.is_some())
2893 .unwrap_or(false)
2894 }
2895}
2896
2897#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
2898pub struct ConnectToSession {
2899 pub name: Option<String>,
2900 pub tab_position: Option<usize>,
2901 pub pane_id: Option<(u32, bool)>, pub layout: Option<LayoutInfo>,
2903 pub cwd: Option<PathBuf>,
2904}
2905
2906impl ConnectToSession {
2907 pub fn apply_layout_dir(&mut self, layout_dir: &PathBuf) {
2908 if let Some(LayoutInfo::File(file_path, _layout_metadata)) = self.layout.as_mut() {
2909 *file_path = Path::join(layout_dir, &file_path)
2910 .to_string_lossy()
2911 .to_string();
2912 }
2913 }
2914}
2915
2916#[derive(Debug, Default, Clone)]
2917pub struct PluginMessage {
2918 pub name: String,
2919 pub payload: String,
2920 pub worker_name: Option<String>,
2921}
2922
2923impl PluginMessage {
2924 pub fn new_to_worker(worker_name: &str, message: &str, payload: &str) -> Self {
2925 PluginMessage {
2926 name: message.to_owned(),
2927 payload: payload.to_owned(),
2928 worker_name: Some(worker_name.to_owned()),
2929 }
2930 }
2931 pub fn new_to_plugin(message: &str, payload: &str) -> Self {
2932 PluginMessage {
2933 name: message.to_owned(),
2934 payload: payload.to_owned(),
2935 worker_name: None,
2936 }
2937 }
2938}
2939
2940#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2941pub enum HttpVerb {
2942 Get,
2943 Post,
2944 Put,
2945 Delete,
2946}
2947
2948#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2949pub enum PipeSource {
2950 Cli(String), Plugin(u32), Keybind, }
2954
2955#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2956pub struct PipeMessage {
2957 pub source: PipeSource,
2958 pub name: String,
2959 pub payload: Option<String>,
2960 pub args: BTreeMap<String, String>,
2961 pub is_private: bool,
2962}
2963
2964impl PipeMessage {
2965 pub fn new(
2966 source: PipeSource,
2967 name: impl Into<String>,
2968 payload: &Option<String>,
2969 args: &Option<BTreeMap<String, String>>,
2970 is_private: bool,
2971 ) -> Self {
2972 PipeMessage {
2973 source,
2974 name: name.into(),
2975 payload: payload.clone(),
2976 args: args.clone().unwrap_or_else(|| Default::default()),
2977 is_private,
2978 }
2979 }
2980}
2981
2982#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
2983pub struct FloatingPaneCoordinates {
2984 pub x: Option<PercentOrFixed>,
2985 pub y: Option<PercentOrFixed>,
2986 pub width: Option<PercentOrFixed>,
2987 pub height: Option<PercentOrFixed>,
2988 pub pinned: Option<bool>,
2989 pub borderless: Option<bool>,
2990}
2991
2992impl FloatingPaneCoordinates {
2993 pub fn new(
2994 x: Option<String>,
2995 y: Option<String>,
2996 width: Option<String>,
2997 height: Option<String>,
2998 pinned: Option<bool>,
2999 borderless: Option<bool>,
3000 ) -> Option<Self> {
3001 let x = x.and_then(|x| PercentOrFixed::from_str(&x).ok());
3003 let y = y.and_then(|y| PercentOrFixed::from_str(&y).ok());
3004
3005 let width = width.and_then(|w| {
3007 PercentOrFixed::from_str(&w)
3008 .ok()
3009 .and_then(|size| match size {
3010 PercentOrFixed::Percent(0) => None,
3011 PercentOrFixed::Fixed(0) => None,
3012 _ => Some(size),
3013 })
3014 });
3015 let height = height.and_then(|h| {
3016 PercentOrFixed::from_str(&h)
3017 .ok()
3018 .and_then(|size| match size {
3019 PercentOrFixed::Percent(0) => None,
3020 PercentOrFixed::Fixed(0) => None,
3021 _ => Some(size),
3022 })
3023 });
3024
3025 if x.is_none()
3026 && y.is_none()
3027 && width.is_none()
3028 && height.is_none()
3029 && pinned.is_none()
3030 && borderless.is_none()
3031 {
3032 None
3033 } else {
3034 Some(FloatingPaneCoordinates {
3035 x,
3036 y,
3037 width,
3038 height,
3039 pinned,
3040 borderless,
3041 })
3042 }
3043 }
3044 pub fn with_x_fixed(mut self, x: usize) -> Self {
3045 self.x = Some(PercentOrFixed::Fixed(x));
3046 self
3047 }
3048 pub fn with_x_percent(mut self, x: usize) -> Self {
3049 if x > 100 {
3050 eprintln!("x must be between 0 and 100");
3051 return self;
3052 }
3053 self.x = Some(PercentOrFixed::Percent(x));
3054 self
3055 }
3056 pub fn with_y_fixed(mut self, y: usize) -> Self {
3057 self.y = Some(PercentOrFixed::Fixed(y));
3058 self
3059 }
3060 pub fn with_y_percent(mut self, y: usize) -> Self {
3061 if y > 100 {
3062 eprintln!("y must be between 0 and 100");
3063 return self;
3064 }
3065 self.y = Some(PercentOrFixed::Percent(y));
3066 self
3067 }
3068 pub fn with_width_fixed(mut self, width: usize) -> Self {
3069 self.width = Some(PercentOrFixed::Fixed(width));
3070 self
3071 }
3072 pub fn with_width_percent(mut self, width: usize) -> Self {
3073 if width > 100 {
3074 eprintln!("width must be between 0 and 100");
3075 return self;
3076 }
3077 self.width = Some(PercentOrFixed::Percent(width));
3078 self
3079 }
3080 pub fn with_height_fixed(mut self, height: usize) -> Self {
3081 self.height = Some(PercentOrFixed::Fixed(height));
3082 self
3083 }
3084 pub fn with_height_percent(mut self, height: usize) -> Self {
3085 if height > 100 {
3086 eprintln!("height must be between 0 and 100");
3087 return self;
3088 }
3089 self.height = Some(PercentOrFixed::Percent(height));
3090 self
3091 }
3092}
3093
3094impl From<PaneGeom> for FloatingPaneCoordinates {
3095 fn from(pane_geom: PaneGeom) -> Self {
3096 FloatingPaneCoordinates {
3097 x: Some(PercentOrFixed::Fixed(pane_geom.x)),
3098 y: Some(PercentOrFixed::Fixed(pane_geom.y)),
3099 width: Some(PercentOrFixed::Fixed(pane_geom.cols.as_usize())),
3100 height: Some(PercentOrFixed::Fixed(pane_geom.rows.as_usize())),
3101 pinned: Some(pane_geom.is_pinned),
3102 borderless: None,
3103 }
3104 }
3105}
3106
3107#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3108pub struct OriginatingPlugin {
3109 pub plugin_id: u32,
3110 pub client_id: ClientId,
3111 pub context: Context,
3112}
3113
3114impl OriginatingPlugin {
3115 pub fn new(plugin_id: u32, client_id: ClientId, context: Context) -> Self {
3116 OriginatingPlugin {
3117 plugin_id,
3118 client_id,
3119 context,
3120 }
3121 }
3122}
3123
3124#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
3125pub enum WebSharing {
3126 #[serde(alias = "on")]
3127 On,
3128 #[serde(alias = "off")]
3129 Off,
3130 #[serde(alias = "disabled")]
3131 Disabled,
3132}
3133
3134impl Default for WebSharing {
3135 fn default() -> Self {
3136 Self::Off
3137 }
3138}
3139
3140impl WebSharing {
3141 pub fn is_on(&self) -> bool {
3142 match self {
3143 WebSharing::On => true,
3144 _ => false,
3145 }
3146 }
3147 pub fn web_clients_allowed(&self) -> bool {
3148 match self {
3149 WebSharing::On => true,
3150 _ => false,
3151 }
3152 }
3153 pub fn sharing_is_disabled(&self) -> bool {
3154 match self {
3155 WebSharing::Disabled => true,
3156 _ => false,
3157 }
3158 }
3159 pub fn set_sharing(&mut self) -> bool {
3160 match self {
3162 WebSharing::On => true,
3163 WebSharing::Off => {
3164 *self = WebSharing::On;
3165 true
3166 },
3167 WebSharing::Disabled => false,
3168 }
3169 }
3170 pub fn set_not_sharing(&mut self) -> bool {
3171 match self {
3173 WebSharing::On => {
3174 *self = WebSharing::Off;
3175 true
3176 },
3177 WebSharing::Off => true,
3178 WebSharing::Disabled => false,
3179 }
3180 }
3181}
3182
3183impl FromStr for WebSharing {
3184 type Err = String;
3185 fn from_str(s: &str) -> Result<Self, Self::Err> {
3186 match s {
3187 "On" | "on" => Ok(Self::On),
3188 "Off" | "off" => Ok(Self::Off),
3189 "Disabled" | "disabled" => Ok(Self::Disabled),
3190 _ => Err(format!("No such option: {}", s)),
3191 }
3192 }
3193}
3194
3195#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3196pub enum NewPanePlacement {
3197 NoPreference {
3198 borderless: Option<bool>,
3199 },
3200 Tiled {
3201 direction: Option<Direction>,
3202 borderless: Option<bool>,
3203 },
3204 Floating(Option<FloatingPaneCoordinates>),
3205 InPlace {
3206 pane_id_to_replace: Option<PaneId>,
3207 close_replaced_pane: bool,
3208 borderless: Option<bool>,
3209 },
3210 Stacked {
3211 pane_id_to_stack_under: Option<PaneId>,
3212 borderless: Option<bool>,
3213 },
3214}
3215
3216impl Default for NewPanePlacement {
3217 fn default() -> Self {
3218 NewPanePlacement::NoPreference { borderless: None }
3219 }
3220}
3221
3222impl NewPanePlacement {
3223 pub fn with_floating_pane_coordinates(
3224 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3225 ) -> Self {
3226 NewPanePlacement::Floating(floating_pane_coordinates)
3227 }
3228 pub fn with_should_be_in_place(
3229 self,
3230 should_be_in_place: bool,
3231 close_replaced_pane: bool,
3232 ) -> Self {
3233 if should_be_in_place {
3234 NewPanePlacement::InPlace {
3235 pane_id_to_replace: None,
3236 close_replaced_pane,
3237 borderless: None,
3238 }
3239 } else {
3240 self
3241 }
3242 }
3243 pub fn with_pane_id_to_replace(
3244 pane_id_to_replace: Option<PaneId>,
3245 close_replaced_pane: bool,
3246 ) -> Self {
3247 NewPanePlacement::InPlace {
3248 pane_id_to_replace,
3249 close_replaced_pane,
3250 borderless: None,
3251 }
3252 }
3253 pub fn should_float(&self) -> Option<bool> {
3254 match self {
3255 NewPanePlacement::Floating(_) => Some(true),
3256 NewPanePlacement::Tiled { .. } => Some(false),
3257 _ => None,
3258 }
3259 }
3260 pub fn floating_pane_coordinates(&self) -> Option<FloatingPaneCoordinates> {
3261 match self {
3262 NewPanePlacement::Floating(floating_pane_coordinates) => {
3263 floating_pane_coordinates.clone()
3264 },
3265 _ => None,
3266 }
3267 }
3268 pub fn should_stack(&self) -> bool {
3269 match self {
3270 NewPanePlacement::Stacked { .. } => true,
3271 _ => false,
3272 }
3273 }
3274 pub fn id_of_stack_root(&self) -> Option<PaneId> {
3275 match self {
3276 NewPanePlacement::Stacked {
3277 pane_id_to_stack_under,
3278 ..
3279 } => *pane_id_to_stack_under,
3280 _ => None,
3281 }
3282 }
3283 pub fn get_borderless(&self) -> Option<bool> {
3284 match self {
3285 NewPanePlacement::NoPreference { borderless } => *borderless,
3286 NewPanePlacement::Tiled { borderless, .. } => *borderless,
3287 NewPanePlacement::Floating(coords) => coords.as_ref().and_then(|c| c.borderless),
3288 NewPanePlacement::InPlace { borderless, .. } => *borderless,
3289 NewPanePlacement::Stacked { borderless, .. } => *borderless,
3290 }
3291 }
3292}
3293
3294type Context = BTreeMap<String, String>;
3295
3296#[derive(Debug, Clone, EnumDiscriminants, Display)]
3297#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
3298#[strum_discriminants(name(CommandType))]
3299pub enum PluginCommand {
3300 Subscribe(HashSet<EventType>),
3301 Unsubscribe(HashSet<EventType>),
3302 SetSelectable(bool),
3303 ShowCursor(Option<(usize, usize)>),
3304 GetPluginIds,
3305 GetZellijVersion,
3306 OpenFile(FileToOpen, Context),
3307 OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3308 OpenTerminal(FileToOpen), OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), OpenCommandPane(CommandToRun, Context),
3311 OpenCommandPaneFloating(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3312 SwitchTabTo(u32), SetTimeout(f64), ExecCmd(Vec<String>),
3315 PostMessageTo(PluginMessage),
3316 PostMessageToPlugin(PluginMessage),
3317 HideSelf,
3318 ShowSelf(bool), SwitchToMode(InputMode),
3320 NewTabsWithLayout(String), NewTab {
3322 name: Option<String>,
3323 cwd: Option<String>,
3324 },
3325 GoToNextTab,
3326 GoToPreviousTab,
3327 Resize(Resize),
3328 ResizeWithDirection(ResizeStrategy),
3329 FocusNextPane,
3330 FocusPreviousPane,
3331 MoveFocus(Direction),
3332 MoveFocusOrTab(Direction),
3333 Detach,
3334 EditScrollback,
3335 Write(Vec<u8>), WriteChars(String),
3337 ToggleTab,
3338 MovePane,
3339 MovePaneWithDirection(Direction),
3340 ClearScreen,
3341 ScrollUp,
3342 ScrollDown,
3343 ScrollToTop,
3344 ScrollToBottom,
3345 PageScrollUp,
3346 PageScrollDown,
3347 ToggleFocusFullscreen,
3348 TogglePaneFrames,
3349 TogglePaneEmbedOrEject,
3350 UndoRenamePane,
3351 CloseFocus,
3352 ToggleActiveTabSync,
3353 CloseFocusedTab,
3354 UndoRenameTab,
3355 QuitZellij,
3356 PreviousSwapLayout,
3357 NextSwapLayout,
3358 GoToTabName(String),
3359 FocusOrCreateTab(String),
3360 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>),
3371 SwitchSession(ConnectToSession),
3372 DeleteDeadSession(String), DeleteAllDeadSessions, OpenTerminalInPlace(FileToOpen), OpenFileInPlace(FileToOpen, Context),
3376 OpenCommandPaneInPlace(CommandToRun, Context),
3377 RunCommand(
3378 Vec<String>, BTreeMap<String, String>, PathBuf, BTreeMap<String, String>, ),
3383 WebRequest(
3384 String, HttpVerb,
3386 BTreeMap<String, String>, Vec<u8>, BTreeMap<String, String>, ),
3390 RenameSession(String), UnblockCliPipeInput(String), BlockCliPipeInput(String), CliPipeOutput(String, String), MessageToPlugin(MessageToPlugin),
3395 DisconnectOtherClients,
3396 KillSessions(Vec<String>), ScanHostFolder(PathBuf), WatchFilesystem,
3399 DumpSessionLayout {
3400 tab_index: Option<usize>,
3401 },
3402 CloseSelf,
3403 NewTabsWithLayoutInfo(LayoutInfo),
3404 Reconfigure(String, bool), HidePaneWithId(PaneId),
3407 ShowPaneWithId(PaneId, bool, bool), OpenCommandPaneBackground(CommandToRun, Context),
3409 RerunCommandPane(u32), ResizePaneIdWithDirection(ResizeStrategy, PaneId),
3411 EditScrollbackForPaneWithId(PaneId),
3412 GetPaneScrollback {
3413 pane_id: PaneId,
3414 get_full_scrollback: bool,
3415 },
3416 WriteToPaneId(Vec<u8>, PaneId),
3417 WriteCharsToPaneId(String, PaneId),
3418 SendSigintToPaneId(PaneId),
3419 SendSigkillToPaneId(PaneId),
3420 GetPanePid {
3421 pane_id: PaneId,
3422 },
3423 GetPaneRunningCommand {
3424 pane_id: PaneId,
3425 },
3426 GetPaneCwd {
3427 pane_id: PaneId,
3428 },
3429 MovePaneWithPaneId(PaneId),
3430 MovePaneWithPaneIdInDirection(PaneId, Direction),
3431 ClearScreenForPaneId(PaneId),
3432 ScrollUpInPaneId(PaneId),
3433 ScrollDownInPaneId(PaneId),
3434 ScrollToTopInPaneId(PaneId),
3435 ScrollToBottomInPaneId(PaneId),
3436 PageScrollUpInPaneId(PaneId),
3437 PageScrollDownInPaneId(PaneId),
3438 TogglePaneIdFullscreen(PaneId),
3439 TogglePaneEmbedOrEjectForPaneId(PaneId),
3440 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 {
3455 url: String,
3456 config: BTreeMap<String, String>,
3457 load_in_background: bool,
3458 skip_plugin_cache: bool,
3459 },
3460 RebindKeys {
3461 keys_to_rebind: Vec<(InputMode, KeyWithModifier, Vec<Action>)>,
3462 keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
3463 write_config_to_disk: bool,
3464 },
3465 ListClients,
3466 ChangeHostFolder(PathBuf),
3467 SetFloatingPanePinned(PaneId, bool), StackPanes(Vec<PaneId>),
3469 ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
3470 TogglePaneBorderless(PaneId),
3471 SetPaneBorderless(PaneId, bool),
3472 OpenCommandPaneNearPlugin(CommandToRun, Context),
3473 OpenTerminalNearPlugin(FileToOpen),
3474 OpenTerminalFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>),
3475 OpenTerminalInPlaceOfPlugin(FileToOpen, bool), OpenCommandPaneFloatingNearPlugin(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3477 OpenCommandPaneInPlaceOfPlugin(CommandToRun, bool, Context), OpenFileNearPlugin(FileToOpen, Context),
3480 OpenFileFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3481 StartWebServer,
3482 StopWebServer,
3483 ShareCurrentSession,
3484 StopSharingCurrentSession,
3485 OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
3491 FloatMultiplePanes(Vec<PaneId>),
3492 EmbedMultiplePanes(Vec<PaneId>),
3493 QueryWebServerStatus,
3494 SetSelfMouseSelectionSupport(bool),
3495 GenerateWebLoginToken(Option<String>, bool), RevokeWebLoginToken(String), ListWebLoginTokens,
3498 RevokeAllWebLoginTokens,
3499 RenameWebLoginToken(String, String), InterceptKeyPresses,
3501 ClearKeyPressesIntercepts,
3502 ReplacePaneWithExistingPane(PaneId, PaneId, bool), RunAction(Action, BTreeMap<String, String>),
3505 CopyToClipboard(String), OverrideLayout(
3507 LayoutInfo,
3508 bool, bool, bool, BTreeMap<String, String>, ),
3513 SaveLayout {
3514 layout_name: String,
3515 layout_kdl: String,
3516 overwrite: bool,
3517 },
3518 DeleteLayout {
3519 layout_name: String,
3520 },
3521 RenameLayout {
3522 old_layout_name: String,
3523 new_layout_name: String,
3524 },
3525 EditLayout {
3526 layout_name: String,
3527 context: Context,
3528 },
3529 GenerateRandomName,
3530 DumpLayout(String),
3531 ParseLayout(String), GetLayoutDir,
3533 GetFocusedPaneInfo,
3534 SaveSession,
3535 CurrentSessionLastSavedTime,
3536 GetPaneInfo(PaneId),
3537 GetTabInfo(usize), GetSessionEnvironmentVariables,
3539 OpenCommandPaneInNewTab(CommandToRun, Context),
3540 OpenPluginPaneInNewTab {
3541 plugin_url: String,
3542 configuration: BTreeMap<String, String>,
3543 context: Context,
3544 },
3545 OpenEditorPaneInNewTab(FileToOpen, Context),
3546 OpenCommandPaneInPlaceOfPaneId(PaneId, CommandToRun, bool, Context), OpenTerminalPaneInPlaceOfPaneId(PaneId, FileToOpen, bool),
3548 OpenEditPaneInPlaceOfPaneId(PaneId, FileToOpen, bool, Context),
3549 HideFloatingPanes {
3550 tab_id: Option<usize>,
3551 },
3552 ShowFloatingPanes {
3553 tab_id: Option<usize>,
3554 },
3555 SetPaneColor(PaneId, Option<String>, Option<String>), SetPaneRegexHighlights(PaneId, Vec<RegexHighlight>),
3557 ClearPaneHighlights(PaneId),
3558 OpenPluginPaneFloating {
3559 plugin_url: String,
3560 configuration: BTreeMap<String, String>,
3561 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3562 context: BTreeMap<String, String>,
3563 },
3564 ListWindowsVolumes,
3565}
3566
3567#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3569pub struct OpenPaneInNewTabResponse {
3570 pub tab_id: Option<usize>,
3571 pub pane_id: Option<PaneId>,
3572}
3573
3574pub type NewTabResponse = Option<usize>;
3576pub type NewTabsResponse = Vec<usize>;
3577pub type FocusOrCreateTabResponse = Option<usize>;
3578pub type BreakPanesToNewTabResponse = Option<usize>;
3579pub type BreakPanesToTabWithIndexResponse = Option<usize>;
3580pub type BreakPanesToTabWithIdResponse = Option<usize>;
3581
3582pub type OpenFileResponse = Option<PaneId>;
3584pub type OpenFileFloatingResponse = Option<PaneId>;
3585pub type OpenFileInPlaceResponse = Option<PaneId>;
3586pub type OpenFileNearPluginResponse = Option<PaneId>;
3587pub type OpenFileFloatingNearPluginResponse = Option<PaneId>;
3588pub type OpenFileInPlaceOfPluginResponse = Option<PaneId>;
3589
3590pub type OpenTerminalResponse = Option<PaneId>;
3591pub type OpenTerminalFloatingResponse = Option<PaneId>;
3592pub type OpenTerminalInPlaceResponse = Option<PaneId>;
3593pub type OpenTerminalNearPluginResponse = Option<PaneId>;
3594pub type OpenTerminalFloatingNearPluginResponse = Option<PaneId>;
3595pub type OpenTerminalInPlaceOfPluginResponse = Option<PaneId>;
3596
3597pub type OpenCommandPaneResponse = Option<PaneId>;
3598pub type OpenCommandPaneFloatingResponse = Option<PaneId>;
3599pub type OpenCommandPaneInPlaceResponse = Option<PaneId>;
3600pub type OpenCommandPaneNearPluginResponse = Option<PaneId>;
3601pub type OpenCommandPaneFloatingNearPluginResponse = Option<PaneId>;
3602pub type OpenCommandPaneInPlaceOfPluginResponse = Option<PaneId>;
3603pub type OpenCommandPaneBackgroundResponse = Option<PaneId>;
3604pub type OpenCommandPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3605pub type OpenTerminalPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3606pub type OpenEditPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3607pub type OpenPluginPaneFloatingResponse = Option<PaneId>;