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 InitialKeybinds(KeybindsVec),
1027}
1028
1029#[derive(Debug, Clone, PartialEq, Eq, EnumDiscriminants, Display, Serialize, Deserialize)]
1030pub enum WebServerStatus {
1031 Online(String), Offline,
1033 DifferentVersion(String), }
1035
1036#[derive(
1037 Debug,
1038 PartialEq,
1039 Eq,
1040 Hash,
1041 Copy,
1042 Clone,
1043 EnumDiscriminants,
1044 Display,
1045 Serialize,
1046 Deserialize,
1047 PartialOrd,
1048 Ord,
1049)]
1050#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize, Display, PartialOrd, Ord))]
1051#[strum_discriminants(name(PermissionType))]
1052#[non_exhaustive]
1053pub enum Permission {
1054 ReadApplicationState,
1055 ChangeApplicationState,
1056 OpenFiles,
1057 RunCommands,
1058 OpenTerminalsOrPlugins,
1059 WriteToStdin,
1060 WebAccess,
1061 ReadCliPipes,
1062 MessageAndLaunchOtherPlugins,
1063 Reconfigure,
1064 FullHdAccess,
1065 StartWebServer,
1066 InterceptInput,
1067 ReadPaneContents,
1068 RunActionsAsUser,
1069 WriteToClipboard,
1070 ReadSessionEnvironmentVariables,
1071}
1072
1073impl PermissionType {
1074 pub fn display_name(&self) -> String {
1075 match self {
1076 PermissionType::ReadApplicationState => {
1077 "Access Zellij state (Panes, Tabs and UI)".to_owned()
1078 },
1079 PermissionType::ChangeApplicationState => {
1080 "Change Zellij state (Panes, Tabs and UI) and run commands".to_owned()
1081 },
1082 PermissionType::OpenFiles => "Open files (eg. for editing)".to_owned(),
1083 PermissionType::RunCommands => "Run commands".to_owned(),
1084 PermissionType::OpenTerminalsOrPlugins => "Start new terminals and plugins".to_owned(),
1085 PermissionType::WriteToStdin => "Write to standard input (STDIN)".to_owned(),
1086 PermissionType::WebAccess => "Make web requests".to_owned(),
1087 PermissionType::ReadCliPipes => "Control command line pipes and output".to_owned(),
1088 PermissionType::MessageAndLaunchOtherPlugins => {
1089 "Send messages to and launch other plugins".to_owned()
1090 },
1091 PermissionType::Reconfigure => "Change Zellij runtime configuration".to_owned(),
1092 PermissionType::FullHdAccess => "Full access to the hard-drive".to_owned(),
1093 PermissionType::StartWebServer => {
1094 "Start a local web server to serve Zellij sessions".to_owned()
1095 },
1096 PermissionType::InterceptInput => "Intercept Input (keyboard & mouse)".to_owned(),
1097 PermissionType::ReadPaneContents => {
1098 "Read pane contents (viewport and selection)".to_owned()
1099 },
1100 PermissionType::RunActionsAsUser => "Execute actions as the user".to_owned(),
1101 PermissionType::WriteToClipboard => "Write to clipboard".to_owned(),
1102 PermissionType::ReadSessionEnvironmentVariables => {
1103 "Read environment variables present upon session creation".to_owned()
1104 },
1105 }
1106 }
1107}
1108
1109#[derive(Debug, Clone)]
1110pub struct PluginPermission {
1111 pub name: String,
1112 pub permissions: Vec<PermissionType>,
1113}
1114
1115impl PluginPermission {
1116 pub fn new(name: String, permissions: Vec<PermissionType>) -> Self {
1117 PluginPermission { name, permissions }
1118 }
1119}
1120
1121#[derive(
1123 Debug,
1124 PartialEq,
1125 Eq,
1126 Hash,
1127 Copy,
1128 Clone,
1129 EnumIter,
1130 Serialize,
1131 Deserialize,
1132 ArgEnum,
1133 PartialOrd,
1134 Ord,
1135)]
1136pub enum InputMode {
1137 #[serde(alias = "normal")]
1140 Normal,
1141 #[serde(alias = "locked")]
1144 Locked,
1145 #[serde(alias = "resize")]
1147 Resize,
1148 #[serde(alias = "pane")]
1150 Pane,
1151 #[serde(alias = "tab")]
1153 Tab,
1154 #[serde(alias = "scroll")]
1156 Scroll,
1157 #[serde(alias = "entersearch")]
1159 EnterSearch,
1160 #[serde(alias = "search")]
1162 Search,
1163 #[serde(alias = "renametab")]
1165 RenameTab,
1166 #[serde(alias = "renamepane")]
1168 RenamePane,
1169 #[serde(alias = "session")]
1171 Session,
1172 #[serde(alias = "move")]
1174 Move,
1175 #[serde(alias = "prompt")]
1177 Prompt,
1178 #[serde(alias = "tmux")]
1180 Tmux,
1181}
1182
1183impl Default for InputMode {
1184 fn default() -> InputMode {
1185 InputMode::Normal
1186 }
1187}
1188
1189#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1190pub enum ThemeHue {
1191 Light,
1192 Dark,
1193}
1194impl Default for ThemeHue {
1195 fn default() -> ThemeHue {
1196 ThemeHue::Dark
1197 }
1198}
1199
1200#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1201pub enum PaletteColor {
1202 Rgb((u8, u8, u8)),
1203 EightBit(u8),
1204}
1205impl Default for PaletteColor {
1206 fn default() -> PaletteColor {
1207 PaletteColor::EightBit(0)
1208 }
1209}
1210
1211#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
1216pub enum HighlightLayer {
1217 Hint, Tool, ActionFeedback, }
1221
1222impl Default for HighlightLayer {
1223 fn default() -> Self {
1224 HighlightLayer::Hint
1225 }
1226}
1227
1228#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1231pub enum HighlightStyle {
1232 None, Emphasis0, Emphasis1, Emphasis2, Emphasis3, BackgroundEmphasis0, BackgroundEmphasis1, BackgroundEmphasis2, BackgroundEmphasis3, CustomRgb {
1242 fg: Option<(u8, u8, u8)>,
1243 bg: Option<(u8, u8, u8)>,
1244 },
1245 CustomIndex {
1246 fg: Option<u8>,
1247 bg: Option<u8>,
1248 },
1249}
1250
1251#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1253pub struct RegexHighlight {
1254 pub pattern: String, pub style: HighlightStyle,
1256 pub layer: HighlightLayer,
1257 pub context: BTreeMap<String, String>, pub on_hover: bool, pub bold: bool,
1260 pub italic: bool,
1261 pub underline: bool,
1262 pub tooltip_text: Option<String>, }
1264
1265impl PaletteColor {
1267 pub fn as_rgb_str(&self) -> String {
1268 let (r, g, b) = match *self {
1269 Self::Rgb((r, g, b)) => (r, g, b),
1270 Self::EightBit(c) => eightbit_to_rgb(c),
1271 };
1272 format!("rgb({}, {}, {})", r, g, b)
1273 }
1274 pub fn from_rgb_str(rgb_str: &str) -> Self {
1275 let trimmed = rgb_str.trim();
1276
1277 if !trimmed.starts_with("rgb(") || !trimmed.ends_with(')') {
1278 return Self::default();
1279 }
1280
1281 let inner = trimmed
1282 .strip_prefix("rgb(")
1283 .and_then(|s| s.strip_suffix(')'))
1284 .unwrap_or("");
1285
1286 let parts: Vec<&str> = inner.split(',').collect();
1287
1288 if parts.len() != 3 {
1289 return Self::default();
1290 }
1291
1292 let mut rgb_values = [0u8; 3];
1293 for (i, part) in parts.iter().enumerate() {
1294 if let Some(rgb_val) = rgb_values.get_mut(i) {
1295 if let Ok(parsed) = part.trim().parse::<u8>() {
1296 *rgb_val = parsed;
1297 } else {
1298 return Self::default();
1299 }
1300 }
1301 }
1302
1303 Self::Rgb((rgb_values[0], rgb_values[1], rgb_values[2]))
1304 }
1305}
1306
1307impl FromStr for InputMode {
1308 type Err = ConversionError;
1309
1310 fn from_str(s: &str) -> Result<Self, ConversionError> {
1311 match s {
1312 "normal" | "Normal" => Ok(InputMode::Normal),
1313 "locked" | "Locked" => Ok(InputMode::Locked),
1314 "resize" | "Resize" => Ok(InputMode::Resize),
1315 "pane" | "Pane" => Ok(InputMode::Pane),
1316 "tab" | "Tab" => Ok(InputMode::Tab),
1317 "search" | "Search" => Ok(InputMode::Search),
1318 "scroll" | "Scroll" => Ok(InputMode::Scroll),
1319 "renametab" | "RenameTab" => Ok(InputMode::RenameTab),
1320 "renamepane" | "RenamePane" => Ok(InputMode::RenamePane),
1321 "session" | "Session" => Ok(InputMode::Session),
1322 "move" | "Move" => Ok(InputMode::Move),
1323 "prompt" | "Prompt" => Ok(InputMode::Prompt),
1324 "tmux" | "Tmux" => Ok(InputMode::Tmux),
1325 "entersearch" | "Entersearch" | "EnterSearch" => Ok(InputMode::EnterSearch),
1326 e => Err(ConversionError::UnknownInputMode(e.into())),
1327 }
1328 }
1329}
1330
1331#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)]
1332pub enum PaletteSource {
1333 Default,
1334 Xresources,
1335}
1336impl Default for PaletteSource {
1337 fn default() -> PaletteSource {
1338 PaletteSource::Default
1339 }
1340}
1341#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
1342pub struct Palette {
1343 pub source: PaletteSource,
1344 pub theme_hue: ThemeHue,
1345 pub fg: PaletteColor,
1346 pub bg: PaletteColor,
1347 pub black: PaletteColor,
1348 pub red: PaletteColor,
1349 pub green: PaletteColor,
1350 pub yellow: PaletteColor,
1351 pub blue: PaletteColor,
1352 pub magenta: PaletteColor,
1353 pub cyan: PaletteColor,
1354 pub white: PaletteColor,
1355 pub orange: PaletteColor,
1356 pub gray: PaletteColor,
1357 pub purple: PaletteColor,
1358 pub gold: PaletteColor,
1359 pub silver: PaletteColor,
1360 pub pink: PaletteColor,
1361 pub brown: PaletteColor,
1362}
1363
1364#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Default, Serialize, Deserialize)]
1365pub struct Style {
1366 pub colors: Styling,
1367 pub rounded_corners: bool,
1368 pub hide_session_name: bool,
1369}
1370
1371#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1372pub enum Coloration {
1373 NoStyling,
1374 Styled(StyleDeclaration),
1375}
1376
1377impl Coloration {
1378 pub fn with_fallback(&self, fallback: StyleDeclaration) -> StyleDeclaration {
1379 match &self {
1380 Coloration::NoStyling => fallback,
1381 Coloration::Styled(style) => *style,
1382 }
1383 }
1384}
1385
1386#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1387pub struct Styling {
1388 pub text_unselected: StyleDeclaration,
1389 pub text_selected: StyleDeclaration,
1390 pub ribbon_unselected: StyleDeclaration,
1391 pub ribbon_selected: StyleDeclaration,
1392 pub table_title: StyleDeclaration,
1393 pub table_cell_unselected: StyleDeclaration,
1394 pub table_cell_selected: StyleDeclaration,
1395 pub list_unselected: StyleDeclaration,
1396 pub list_selected: StyleDeclaration,
1397 pub frame_unselected: Option<StyleDeclaration>,
1398 pub frame_selected: StyleDeclaration,
1399 pub frame_highlight: StyleDeclaration,
1400 pub exit_code_success: StyleDeclaration,
1401 pub exit_code_error: StyleDeclaration,
1402 pub multiplayer_user_colors: MultiplayerColors,
1403}
1404
1405#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1406pub struct StyleDeclaration {
1407 pub base: PaletteColor,
1408 pub background: PaletteColor,
1409 pub emphasis_0: PaletteColor,
1410 pub emphasis_1: PaletteColor,
1411 pub emphasis_2: PaletteColor,
1412 pub emphasis_3: PaletteColor,
1413}
1414
1415#[derive(Debug, Copy, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
1416pub struct MultiplayerColors {
1417 pub player_1: PaletteColor,
1418 pub player_2: PaletteColor,
1419 pub player_3: PaletteColor,
1420 pub player_4: PaletteColor,
1421 pub player_5: PaletteColor,
1422 pub player_6: PaletteColor,
1423 pub player_7: PaletteColor,
1424 pub player_8: PaletteColor,
1425 pub player_9: PaletteColor,
1426 pub player_10: PaletteColor,
1427}
1428
1429pub const DEFAULT_STYLES: Styling = Styling {
1430 text_unselected: StyleDeclaration {
1431 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1432 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1433 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1434 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1435 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1436 background: PaletteColor::EightBit(default_colors::GRAY),
1437 },
1438 text_selected: StyleDeclaration {
1439 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1440 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1441 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1442 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1443 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1444 background: PaletteColor::EightBit(default_colors::GRAY),
1445 },
1446 ribbon_unselected: StyleDeclaration {
1447 base: PaletteColor::EightBit(default_colors::BLACK),
1448 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1449 emphasis_1: PaletteColor::EightBit(default_colors::WHITE),
1450 emphasis_2: PaletteColor::EightBit(default_colors::BLUE),
1451 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1452 background: PaletteColor::EightBit(default_colors::GRAY),
1453 },
1454 ribbon_selected: StyleDeclaration {
1455 base: PaletteColor::EightBit(default_colors::BLACK),
1456 emphasis_0: PaletteColor::EightBit(default_colors::RED),
1457 emphasis_1: PaletteColor::EightBit(default_colors::ORANGE),
1458 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1459 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1460 background: PaletteColor::EightBit(default_colors::GREEN),
1461 },
1462 exit_code_success: StyleDeclaration {
1463 base: PaletteColor::EightBit(default_colors::GREEN),
1464 emphasis_0: PaletteColor::EightBit(default_colors::CYAN),
1465 emphasis_1: PaletteColor::EightBit(default_colors::BLACK),
1466 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1467 emphasis_3: PaletteColor::EightBit(default_colors::BLUE),
1468 background: PaletteColor::EightBit(default_colors::GRAY),
1469 },
1470 exit_code_error: StyleDeclaration {
1471 base: PaletteColor::EightBit(default_colors::RED),
1472 emphasis_0: PaletteColor::EightBit(default_colors::YELLOW),
1473 emphasis_1: PaletteColor::EightBit(default_colors::GOLD),
1474 emphasis_2: PaletteColor::EightBit(default_colors::SILVER),
1475 emphasis_3: PaletteColor::EightBit(default_colors::PURPLE),
1476 background: PaletteColor::EightBit(default_colors::GRAY),
1477 },
1478 frame_unselected: None,
1479 frame_selected: StyleDeclaration {
1480 base: PaletteColor::EightBit(default_colors::GREEN),
1481 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1482 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1483 emphasis_2: PaletteColor::EightBit(default_colors::MAGENTA),
1484 emphasis_3: PaletteColor::EightBit(default_colors::BROWN),
1485 background: PaletteColor::EightBit(default_colors::GRAY),
1486 },
1487 frame_highlight: StyleDeclaration {
1488 base: PaletteColor::EightBit(default_colors::ORANGE),
1489 emphasis_0: PaletteColor::EightBit(default_colors::MAGENTA),
1490 emphasis_1: PaletteColor::EightBit(default_colors::PURPLE),
1491 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1492 emphasis_3: PaletteColor::EightBit(default_colors::GREEN),
1493 background: PaletteColor::EightBit(default_colors::GREEN),
1494 },
1495 table_title: StyleDeclaration {
1496 base: PaletteColor::EightBit(default_colors::GREEN),
1497 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1498 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1499 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1500 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1501 background: PaletteColor::EightBit(default_colors::GRAY),
1502 },
1503 table_cell_unselected: StyleDeclaration {
1504 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1505 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1506 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1507 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1508 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1509 background: PaletteColor::EightBit(default_colors::GRAY),
1510 },
1511 table_cell_selected: StyleDeclaration {
1512 base: PaletteColor::EightBit(default_colors::GREEN),
1513 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1514 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1515 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1516 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1517 background: PaletteColor::EightBit(default_colors::GRAY),
1518 },
1519 list_unselected: StyleDeclaration {
1520 base: PaletteColor::EightBit(default_colors::BRIGHT_GRAY),
1521 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1522 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1523 emphasis_2: PaletteColor::EightBit(default_colors::GREEN),
1524 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1525 background: PaletteColor::EightBit(default_colors::GRAY),
1526 },
1527 list_selected: StyleDeclaration {
1528 base: PaletteColor::EightBit(default_colors::GREEN),
1529 emphasis_0: PaletteColor::EightBit(default_colors::ORANGE),
1530 emphasis_1: PaletteColor::EightBit(default_colors::CYAN),
1531 emphasis_2: PaletteColor::EightBit(default_colors::RED),
1532 emphasis_3: PaletteColor::EightBit(default_colors::MAGENTA),
1533 background: PaletteColor::EightBit(default_colors::GRAY),
1534 },
1535 multiplayer_user_colors: MultiplayerColors {
1536 player_1: PaletteColor::EightBit(default_colors::MAGENTA),
1537 player_2: PaletteColor::EightBit(default_colors::BLUE),
1538 player_3: PaletteColor::EightBit(default_colors::PURPLE),
1539 player_4: PaletteColor::EightBit(default_colors::YELLOW),
1540 player_5: PaletteColor::EightBit(default_colors::CYAN),
1541 player_6: PaletteColor::EightBit(default_colors::GOLD),
1542 player_7: PaletteColor::EightBit(default_colors::RED),
1543 player_8: PaletteColor::EightBit(default_colors::SILVER),
1544 player_9: PaletteColor::EightBit(default_colors::PINK),
1545 player_10: PaletteColor::EightBit(default_colors::BROWN),
1546 },
1547};
1548
1549impl Default for Styling {
1550 fn default() -> Self {
1551 DEFAULT_STYLES
1552 }
1553}
1554
1555impl From<Styling> for Palette {
1556 fn from(styling: Styling) -> Self {
1557 Palette {
1558 theme_hue: ThemeHue::Dark,
1559 source: PaletteSource::Default,
1560 fg: styling.ribbon_unselected.background,
1561 bg: styling.text_unselected.background,
1562 red: styling.exit_code_error.base,
1563 green: styling.text_unselected.emphasis_2,
1564 yellow: styling.exit_code_error.emphasis_0,
1565 blue: styling.ribbon_unselected.emphasis_2,
1566 magenta: styling.text_unselected.emphasis_3,
1567 orange: styling.text_unselected.emphasis_0,
1568 cyan: styling.text_unselected.emphasis_1,
1569 black: styling.ribbon_unselected.base,
1570 white: styling.ribbon_unselected.emphasis_1,
1571 gray: styling.list_unselected.background,
1572 purple: styling.multiplayer_user_colors.player_3,
1573 gold: styling.multiplayer_user_colors.player_6,
1574 silver: styling.multiplayer_user_colors.player_8,
1575 pink: styling.multiplayer_user_colors.player_9,
1576 brown: styling.multiplayer_user_colors.player_10,
1577 }
1578 }
1579}
1580
1581impl From<Palette> for Styling {
1582 fn from(palette: Palette) -> Self {
1583 let (fg, bg) = match palette.theme_hue {
1584 ThemeHue::Light => (palette.black, palette.white),
1585 ThemeHue::Dark => (palette.white, palette.black),
1586 };
1587 Styling {
1588 text_unselected: StyleDeclaration {
1589 base: fg,
1590 emphasis_0: palette.orange,
1591 emphasis_1: palette.cyan,
1592 emphasis_2: palette.green,
1593 emphasis_3: palette.magenta,
1594 background: bg,
1595 },
1596 text_selected: StyleDeclaration {
1597 base: fg,
1598 emphasis_0: palette.orange,
1599 emphasis_1: palette.cyan,
1600 emphasis_2: palette.green,
1601 emphasis_3: palette.magenta,
1602 background: palette.bg,
1603 },
1604 ribbon_unselected: StyleDeclaration {
1605 base: palette.black,
1606 emphasis_0: palette.red,
1607 emphasis_1: palette.white,
1608 emphasis_2: palette.blue,
1609 emphasis_3: palette.magenta,
1610 background: palette.fg,
1611 },
1612 ribbon_selected: StyleDeclaration {
1613 base: palette.black,
1614 emphasis_0: palette.red,
1615 emphasis_1: palette.orange,
1616 emphasis_2: palette.magenta,
1617 emphasis_3: palette.blue,
1618 background: palette.green,
1619 },
1620 exit_code_success: StyleDeclaration {
1621 base: palette.green,
1622 emphasis_0: palette.cyan,
1623 emphasis_1: palette.black,
1624 emphasis_2: palette.magenta,
1625 emphasis_3: palette.blue,
1626 background: Default::default(),
1627 },
1628 exit_code_error: StyleDeclaration {
1629 base: palette.red,
1630 emphasis_0: palette.yellow,
1631 emphasis_1: palette.gold,
1632 emphasis_2: palette.silver,
1633 emphasis_3: palette.purple,
1634 background: Default::default(),
1635 },
1636 frame_unselected: None,
1637 frame_selected: StyleDeclaration {
1638 base: palette.green,
1639 emphasis_0: palette.orange,
1640 emphasis_1: palette.cyan,
1641 emphasis_2: palette.magenta,
1642 emphasis_3: palette.brown,
1643 background: Default::default(),
1644 },
1645 frame_highlight: StyleDeclaration {
1646 base: palette.orange,
1647 emphasis_0: palette.magenta,
1648 emphasis_1: palette.purple,
1649 emphasis_2: palette.orange,
1650 emphasis_3: palette.orange,
1651 background: Default::default(),
1652 },
1653 table_title: StyleDeclaration {
1654 base: palette.green,
1655 emphasis_0: palette.orange,
1656 emphasis_1: palette.cyan,
1657 emphasis_2: palette.green,
1658 emphasis_3: palette.magenta,
1659 background: palette.gray,
1660 },
1661 table_cell_unselected: StyleDeclaration {
1662 base: fg,
1663 emphasis_0: palette.orange,
1664 emphasis_1: palette.cyan,
1665 emphasis_2: palette.green,
1666 emphasis_3: palette.magenta,
1667 background: palette.black,
1668 },
1669 table_cell_selected: StyleDeclaration {
1670 base: fg,
1671 emphasis_0: palette.orange,
1672 emphasis_1: palette.cyan,
1673 emphasis_2: palette.green,
1674 emphasis_3: palette.magenta,
1675 background: palette.bg,
1676 },
1677 list_unselected: StyleDeclaration {
1678 base: palette.white,
1679 emphasis_0: palette.orange,
1680 emphasis_1: palette.cyan,
1681 emphasis_2: palette.green,
1682 emphasis_3: palette.magenta,
1683 background: palette.black,
1684 },
1685 list_selected: StyleDeclaration {
1686 base: palette.white,
1687 emphasis_0: palette.orange,
1688 emphasis_1: palette.cyan,
1689 emphasis_2: palette.green,
1690 emphasis_3: palette.magenta,
1691 background: palette.bg,
1692 },
1693 multiplayer_user_colors: MultiplayerColors {
1694 player_1: palette.magenta,
1695 player_2: palette.blue,
1696 player_3: palette.purple,
1697 player_4: palette.yellow,
1698 player_5: palette.cyan,
1699 player_6: palette.gold,
1700 player_7: palette.red,
1701 player_8: palette.silver,
1702 player_9: palette.pink,
1703 player_10: palette.brown,
1704 },
1705 }
1706 }
1707}
1708
1709pub type KeybindsVec = Vec<(InputMode, Vec<(KeyWithModifier, Vec<Action>)>)>;
1711
1712#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
1714pub struct ModeInfo {
1715 pub mode: InputMode,
1716 pub base_mode: Option<InputMode>,
1717 pub keybinds: KeybindsVec,
1718 pub style: Style,
1719 pub capabilities: PluginCapabilities,
1720 pub session_name: Option<String>,
1721 pub editor: Option<PathBuf>,
1722 pub shell: Option<PathBuf>,
1723 pub web_clients_allowed: Option<bool>,
1724 pub web_sharing: Option<WebSharing>,
1725 pub currently_marking_pane_group: Option<bool>,
1726 pub is_web_client: Option<bool>,
1727 pub web_server_ip: Option<IpAddr>,
1729 pub web_server_port: Option<u16>,
1730 pub web_server_capability: Option<bool>,
1731}
1732
1733impl ModeInfo {
1734 pub fn get_mode_keybinds(&self) -> Vec<(KeyWithModifier, Vec<Action>)> {
1735 self.get_keybinds_for_mode(self.mode)
1736 }
1737
1738 pub fn get_keybinds_for_mode(&self, mode: InputMode) -> Vec<(KeyWithModifier, Vec<Action>)> {
1739 for (vec_mode, map) in &self.keybinds {
1740 if mode == *vec_mode {
1741 return map.to_vec();
1742 }
1743 }
1744 vec![]
1745 }
1746 pub fn update_keybinds(&mut self, keybinds: Keybinds) {
1747 self.keybinds = keybinds.to_keybinds_vec();
1748 }
1749 pub fn update_default_mode(&mut self, new_default_mode: InputMode) {
1750 self.base_mode = Some(new_default_mode);
1751 }
1752 pub fn update_theme(&mut self, theme: Styling) {
1753 self.style.colors = theme.into();
1754 }
1755 pub fn update_rounded_corners(&mut self, rounded_corners: bool) {
1756 self.style.rounded_corners = rounded_corners;
1757 }
1758 pub fn update_arrow_fonts(&mut self, should_support_arrow_fonts: bool) {
1759 self.capabilities.arrow_fonts = !should_support_arrow_fonts;
1762 }
1763 pub fn update_hide_session_name(&mut self, hide_session_name: bool) {
1764 self.style.hide_session_name = hide_session_name;
1765 }
1766 pub fn change_to_default_mode(&mut self) {
1767 if let Some(base_mode) = self.base_mode {
1768 self.mode = base_mode;
1769 }
1770 }
1771}
1772
1773#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1774pub struct SessionInfo {
1775 pub name: String,
1776 pub tabs: Vec<TabInfo>,
1777 pub panes: PaneManifest,
1778 pub connected_clients: usize,
1779 pub is_current_session: bool,
1780 pub available_layouts: Vec<LayoutInfo>,
1781 pub plugins: BTreeMap<u32, PluginInfo>,
1782 pub web_clients_allowed: bool,
1783 pub web_client_count: usize,
1784 pub tab_history: BTreeMap<ClientId, Vec<usize>>,
1785 pub pane_history: BTreeMap<ClientId, Vec<PaneId>>,
1786 pub creation_time: Duration,
1787}
1788
1789#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1790pub struct PluginInfo {
1791 pub location: String,
1792 pub configuration: BTreeMap<String, String>,
1793}
1794
1795impl From<RunPlugin> for PluginInfo {
1796 fn from(run_plugin: RunPlugin) -> Self {
1797 PluginInfo {
1798 location: run_plugin.location.display(),
1799 configuration: run_plugin.configuration.inner().clone(),
1800 }
1801 }
1802}
1803
1804#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1805pub enum LayoutInfo {
1806 BuiltIn(String),
1807 File(String, LayoutMetadata),
1808 Url(String),
1809 Stringified(String),
1810}
1811
1812#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1813pub struct LayoutWithError {
1814 pub layout_name: String,
1815 pub error: LayoutParsingError,
1816}
1817
1818#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1819pub enum LayoutParsingError {
1820 KdlError {
1821 kdl_error: KdlError,
1822 file_name: String,
1823 source_code: String,
1824 },
1825 SyntaxError,
1826}
1827
1828impl AsRef<LayoutInfo> for LayoutInfo {
1829 fn as_ref(&self) -> &LayoutInfo {
1830 self
1831 }
1832}
1833
1834#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
1835pub struct LayoutMetadata {
1836 pub tabs: Vec<TabMetadata>,
1837 pub creation_time: String,
1838 pub update_time: String,
1839}
1840
1841impl From<&PathBuf> for LayoutMetadata {
1842 fn from(path: &PathBuf) -> LayoutMetadata {
1843 match Layout::stringified_from_path(path) {
1844 Ok((path_str, stringified_layout, _swap_layouts)) => {
1845 match Layout::from_kdl(&stringified_layout, Some(path_str), None, None) {
1846 Ok(layout) => {
1847 let layout_tabs = layout.tabs();
1848 let tabs = if layout_tabs.is_empty() {
1849 let (tiled_pane_layout, floating_pane_layout) = layout.new_tab();
1850 vec![TabMetadata::from(&(
1851 None,
1852 tiled_pane_layout,
1853 floating_pane_layout,
1854 ))]
1855 } else {
1856 layout
1857 .tabs()
1858 .into_iter()
1859 .map(|tab| TabMetadata::from(&tab))
1860 .collect()
1861 };
1862
1863 let (creation_time, update_time) =
1865 LayoutMetadata::creation_and_update_times(&path);
1866
1867 LayoutMetadata {
1868 tabs,
1869 creation_time,
1870 update_time,
1871 }
1872 },
1873 Err(e) => {
1874 log::error!("Failed to parse layout: {}", e);
1875 LayoutMetadata::default()
1876 },
1877 }
1878 },
1879 Err(e) => {
1880 log::error!("Failed to read layout file: {}", e);
1881 LayoutMetadata::default()
1882 },
1883 }
1884 }
1885}
1886
1887impl LayoutMetadata {
1888 fn creation_and_update_times(path: &PathBuf) -> (String, String) {
1889 match std::fs::metadata(path) {
1891 Ok(metadata) => {
1892 let creation_time = metadata
1893 .created()
1894 .ok()
1895 .and_then(|t| {
1896 t.duration_since(std::time::UNIX_EPOCH)
1897 .ok()
1898 .map(|d| d.as_secs().to_string())
1899 })
1900 .unwrap_or_default();
1901
1902 let update_time = metadata
1903 .modified()
1904 .ok()
1905 .and_then(|t| {
1906 t.duration_since(std::time::UNIX_EPOCH)
1907 .ok()
1908 .map(|d| d.as_secs().to_string())
1909 })
1910 .unwrap_or_default();
1911
1912 (creation_time, update_time)
1913 },
1914 Err(_) => (String::new(), String::new()),
1915 }
1916 }
1917}
1918
1919#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1920pub struct TabMetadata {
1921 pub panes: Vec<PaneMetadata>,
1922 pub name: Option<String>,
1923}
1924
1925impl
1926 From<&(
1927 Option<String>,
1928 crate::input::layout::TiledPaneLayout,
1929 Vec<crate::input::layout::FloatingPaneLayout>,
1930 )> for TabMetadata
1931{
1932 fn from(
1933 tab: &(
1934 Option<String>,
1935 crate::input::layout::TiledPaneLayout,
1936 Vec<crate::input::layout::FloatingPaneLayout>,
1937 ),
1938 ) -> Self {
1939 let (tab_name, tiled_pane_layout, floating_panes) = tab;
1940
1941 let mut panes = Vec::new();
1943 collect_leaf_panes(&tiled_pane_layout, &mut panes);
1944
1945 for floating_pane in floating_panes {
1947 panes.push(PaneMetadata::from(floating_pane));
1948 }
1949
1950 TabMetadata {
1951 panes,
1952 name: tab_name.clone(),
1953 }
1954 }
1955}
1956
1957#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
1958pub struct PaneMetadata {
1959 pub name: Option<String>,
1960 pub is_plugin: bool,
1961 pub is_builtin_plugin: bool,
1962}
1963
1964impl From<&crate::input::layout::TiledPaneLayout> for PaneMetadata {
1965 fn from(pane: &crate::input::layout::TiledPaneLayout) -> Self {
1966 let mut is_plugin = false;
1967 let mut is_builtin_plugin = false;
1968
1969 let name = if let Some(ref name) = pane.name {
1971 Some(name.clone())
1972 } else if let Some(ref run) = pane.run {
1973 match run {
1975 Run::Command(cmd) => {
1976 Some(cmd.command.to_string_lossy().to_string())
1978 },
1979 Run::EditFile(path, _line, _cwd) => {
1980 path.file_name().map(|n| n.to_string_lossy().to_string())
1982 },
1983 Run::Plugin(plugin) => {
1984 is_plugin = true;
1985 is_builtin_plugin = plugin.is_builtin_plugin();
1986 Some(plugin.location_string())
1987 },
1988 Run::Cwd(_) => None,
1989 }
1990 } else {
1991 None
1992 };
1993
1994 PaneMetadata {
1995 name,
1996 is_plugin,
1997 is_builtin_plugin,
1998 }
1999 }
2000}
2001
2002impl From<&crate::input::layout::FloatingPaneLayout> for PaneMetadata {
2003 fn from(pane: &crate::input::layout::FloatingPaneLayout) -> Self {
2004 let mut is_plugin = false;
2005 let mut is_builtin_plugin = false;
2006
2007 let name = if let Some(ref name) = pane.name {
2009 Some(name.clone())
2010 } else if let Some(ref run) = pane.run {
2011 match run {
2013 Run::Command(cmd) => {
2014 Some(cmd.command.to_string_lossy().to_string())
2016 },
2017 Run::EditFile(path, _line, _cwd) => {
2018 path.file_name().map(|n| n.to_string_lossy().to_string())
2020 },
2021 Run::Plugin(plugin) => {
2022 is_plugin = true;
2023 is_builtin_plugin = match plugin {
2024 crate::input::layout::RunPluginOrAlias::RunPlugin(run_plugin) => {
2025 matches!(run_plugin.location, RunPluginLocation::Zellij(_))
2026 },
2027 crate::input::layout::RunPluginOrAlias::Alias(_) => false,
2028 };
2029 Some(plugin.location_string())
2031 },
2032 Run::Cwd(_) => None,
2033 }
2034 } else {
2035 None
2036 };
2037
2038 PaneMetadata {
2039 name,
2040 is_plugin,
2041 is_builtin_plugin,
2042 }
2043 }
2044}
2045
2046fn collect_leaf_panes(
2048 pane: &crate::input::layout::TiledPaneLayout,
2049 result: &mut Vec<PaneMetadata>,
2050) {
2051 if pane.children.is_empty() {
2052 result.push(PaneMetadata::from(pane));
2054 } else {
2055 for child in &pane.children {
2057 collect_leaf_panes(child, result);
2058 }
2059 }
2060}
2061
2062impl LayoutInfo {
2063 pub fn name(&self) -> &str {
2064 match self {
2065 LayoutInfo::BuiltIn(name) => &name,
2066 LayoutInfo::File(name, _) => &name,
2067 LayoutInfo::Url(url) => &url,
2068 LayoutInfo::Stringified(layout) => &layout,
2069 }
2070 }
2071 pub fn is_builtin(&self) -> bool {
2072 match self {
2073 LayoutInfo::BuiltIn(_name) => true,
2074 LayoutInfo::File(_name, _) => false,
2075 LayoutInfo::Url(_url) => false,
2076 LayoutInfo::Stringified(_stringified) => false,
2077 }
2078 }
2079 pub fn from_cli(
2080 layout_dir: &Option<PathBuf>,
2081 maybe_layout_path: &Option<PathBuf>,
2082 cwd: PathBuf,
2083 ) -> Option<Self> {
2084 let layout_path = maybe_layout_path
2091 .clone()
2092 .unwrap_or(PathBuf::from("default"));
2093
2094 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2095 Some(LayoutInfo::Url(layout_path.display().to_string()))
2096 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2097 let layout_dir = cwd;
2098 let file_path = layout_dir.join(layout_path);
2099 Some(LayoutInfo::File(
2100 file_path.display().to_string(),
2102 LayoutMetadata::from(&file_path),
2103 ))
2104 } else {
2105 if let Some(layout_dir) = layout_dir
2109 .as_ref()
2110 .map(|l| l.clone())
2111 .or_else(default_layout_dir)
2112 {
2113 let file_path = layout_dir.join(&layout_path);
2114 if file_path.exists() {
2115 return Some(LayoutInfo::File(
2116 file_path.display().to_string(),
2117 LayoutMetadata::from(&file_path),
2118 ));
2119 }
2120 let file_path_with_ext = file_path.with_extension("kdl");
2121 if file_path_with_ext.exists() {
2122 return Some(LayoutInfo::File(
2123 file_path_with_ext.display().to_string(),
2124 LayoutMetadata::from(&file_path_with_ext),
2125 ));
2126 }
2127 }
2128 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2130 }
2131 }
2132 pub fn from_config(
2133 layout_dir: &Option<PathBuf>,
2134 maybe_layout_path: &Option<PathBuf>,
2135 ) -> Option<Self> {
2136 let layout_path = maybe_layout_path
2143 .clone()
2144 .unwrap_or(PathBuf::from("default"));
2145
2146 if layout_path.starts_with("http://") || layout_path.starts_with("https://") {
2147 Some(LayoutInfo::Url(layout_path.display().to_string()))
2148 } else if layout_path.extension().is_some() || layout_path.components().count() > 1 {
2149 let Some(layout_dir) = layout_dir
2150 .as_ref()
2151 .map(|l| l.clone())
2152 .or_else(default_layout_dir)
2153 else {
2154 return None;
2155 };
2156 let file_path = layout_dir.join(layout_path);
2157 Some(LayoutInfo::File(
2158 file_path.display().to_string(),
2160 LayoutMetadata::from(&file_path),
2161 ))
2162 } else {
2163 if let Some(layout_dir) = layout_dir
2167 .as_ref()
2168 .map(|l| l.clone())
2169 .or_else(default_layout_dir)
2170 {
2171 let file_path = layout_dir.join(&layout_path);
2172 if file_path.exists() {
2173 return Some(LayoutInfo::File(
2174 file_path.display().to_string(),
2175 LayoutMetadata::from(&file_path),
2176 ));
2177 }
2178 let file_path_with_ext = file_path.with_extension("kdl");
2179 if file_path_with_ext.exists() {
2180 return Some(LayoutInfo::File(
2181 file_path_with_ext.display().to_string(),
2182 LayoutMetadata::from(&file_path_with_ext),
2183 ));
2184 }
2185 }
2186 Some(LayoutInfo::BuiltIn(layout_path.display().to_string()))
2188 }
2189 }
2190}
2191
2192#[allow(clippy::derive_hash_xor_eq)]
2193impl Hash for SessionInfo {
2194 fn hash<H: Hasher>(&self, state: &mut H) {
2195 self.name.hash(state);
2196 }
2197}
2198
2199impl SessionInfo {
2200 pub fn new(name: String) -> Self {
2201 SessionInfo {
2202 name,
2203 ..Default::default()
2204 }
2205 }
2206 pub fn update_tab_info(&mut self, new_tab_info: Vec<TabInfo>) {
2207 self.tabs = new_tab_info;
2208 }
2209 pub fn update_pane_info(&mut self, new_pane_info: PaneManifest) {
2210 self.panes = new_pane_info;
2211 }
2212 pub fn update_connected_clients(&mut self, new_connected_clients: usize) {
2213 self.connected_clients = new_connected_clients;
2214 }
2215 pub fn populate_plugin_list(&mut self, plugins: BTreeMap<u32, RunPlugin>) {
2216 let mut plugin_list = BTreeMap::new();
2218 for (plugin_id, run_plugin) in plugins {
2219 plugin_list.insert(plugin_id, run_plugin.into());
2220 }
2221 self.plugins = plugin_list;
2222 }
2223}
2224
2225#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2227pub struct TabInfo {
2228 pub position: usize,
2230 pub name: String,
2232 pub active: bool,
2234 pub panes_to_hide: usize,
2236 pub is_fullscreen_active: bool,
2238 pub is_sync_panes_active: bool,
2240 pub are_floating_panes_visible: bool,
2241 pub other_focused_clients: Vec<ClientId>,
2242 pub active_swap_layout_name: Option<String>,
2243 pub is_swap_layout_dirty: bool,
2245 pub viewport_rows: usize,
2247 pub viewport_columns: usize,
2249 pub display_area_rows: usize,
2252 pub display_area_columns: usize,
2255 pub selectable_tiled_panes_count: usize,
2257 pub selectable_floating_panes_count: usize,
2259 pub tab_id: usize,
2261 pub has_bell_notification: bool,
2263 pub is_flashing_bell: bool,
2265}
2266
2267#[derive(Debug, Default, Clone, PartialEq, Eq, Deserialize, Serialize)]
2271pub struct PaneManifest {
2272 pub panes: HashMap<usize, Vec<PaneInfo>>, }
2274
2275#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2286pub struct PaneInfo {
2287 pub id: u32,
2289 pub is_plugin: bool,
2292 pub is_focused: bool,
2294 pub is_fullscreen: bool,
2295 pub is_floating: bool,
2297 pub is_suppressed: bool,
2300 pub title: String,
2302 pub exited: bool,
2305 pub exit_status: Option<i32>,
2307 pub is_held: bool,
2310 pub pane_x: usize,
2311 pub pane_content_x: usize,
2312 pub pane_y: usize,
2313 pub pane_content_y: usize,
2314 pub pane_rows: usize,
2315 pub pane_content_rows: usize,
2316 pub pane_columns: usize,
2317 pub pane_content_columns: usize,
2318 pub cursor_coordinates_in_pane: Option<(usize, usize)>, pub terminal_command: Option<String>,
2324 pub plugin_url: Option<String>,
2327 pub is_selectable: bool,
2330 pub index_in_pane_group: BTreeMap<ClientId, usize>,
2333 pub default_fg: Option<String>,
2335 pub default_bg: Option<String>,
2337}
2338
2339#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)]
2340pub struct PaneListEntry {
2341 #[serde(flatten)]
2342 pub pane_info: PaneInfo,
2343 pub tab_id: usize,
2344 pub tab_position: usize,
2345 pub tab_name: String,
2346 #[serde(skip_serializing_if = "Option::is_none")]
2347 pub pane_command: Option<String>,
2348 #[serde(skip_serializing_if = "Option::is_none")]
2349 pub pane_cwd: Option<String>,
2350}
2351
2352pub type ListPanesResponse = Vec<PaneListEntry>;
2353pub type ListTabsResponse = Vec<TabInfo>;
2354
2355#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
2356pub struct ClientInfo {
2357 pub client_id: ClientId,
2358 pub pane_id: PaneId,
2359 pub running_command: String,
2360 pub is_current_client: bool,
2361}
2362
2363impl ClientInfo {
2364 pub fn new(
2365 client_id: ClientId,
2366 pane_id: PaneId,
2367 running_command: String,
2368 is_current_client: bool,
2369 ) -> Self {
2370 ClientInfo {
2371 client_id,
2372 pane_id,
2373 running_command,
2374 is_current_client,
2375 }
2376 }
2377}
2378
2379#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2380pub struct PaneRenderReport {
2381 pub all_pane_contents: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2382 pub all_pane_contents_with_ansi: HashMap<ClientId, HashMap<PaneId, PaneContents>>,
2383}
2384
2385impl PaneRenderReport {
2386 pub fn add_pane_contents(
2387 &mut self,
2388 client_ids: &[ClientId],
2389 pane_id: PaneId,
2390 pane_contents: PaneContents,
2391 ) {
2392 for client_id in client_ids {
2393 let p = self
2394 .all_pane_contents
2395 .entry(*client_id)
2396 .or_insert_with(|| HashMap::new());
2397 p.insert(pane_id, pane_contents.clone());
2398 }
2399 }
2400 pub fn add_pane_contents_with_ansi(
2401 &mut self,
2402 client_ids: &[ClientId],
2403 pane_id: PaneId,
2404 pane_contents: PaneContents,
2405 ) {
2406 for client_id in client_ids {
2407 let p = self
2408 .all_pane_contents_with_ansi
2409 .entry(*client_id)
2410 .or_insert_with(|| HashMap::new());
2411 p.insert(pane_id, pane_contents.clone());
2412 }
2413 }
2414}
2415
2416#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
2417pub struct PaneContents {
2418 pub lines_above_viewport: Vec<String>,
2422 pub lines_below_viewport: Vec<String>,
2423 pub viewport: Vec<String>,
2424 pub selected_text: Option<SelectedText>,
2425}
2426
2427fn extract_text_by_columns(line: &str, start_col: usize, end_col: usize) -> String {
2429 let mut current_col = 0;
2430 let mut result = String::new();
2431 let mut capturing = false;
2432
2433 for ch in line.chars() {
2434 let char_width = ch.width().unwrap_or(0);
2435
2436 if current_col >= start_col && !capturing {
2438 capturing = true;
2439 }
2440
2441 if current_col >= end_col {
2443 break;
2444 }
2445
2446 if capturing {
2448 result.push(ch);
2449 }
2450
2451 current_col += char_width;
2452 }
2453
2454 result
2455}
2456
2457fn extract_text_from_column(line: &str, start_col: usize) -> String {
2459 let mut current_col = 0;
2460 let mut result = String::new();
2461 let mut capturing = false;
2462
2463 for ch in line.chars() {
2464 let char_width = ch.width().unwrap_or(0);
2465
2466 if current_col >= start_col {
2467 capturing = true;
2468 }
2469
2470 if capturing {
2471 result.push(ch);
2472 }
2473
2474 current_col += char_width;
2475 }
2476
2477 result
2478}
2479
2480fn extract_text_to_column(line: &str, end_col: usize) -> String {
2482 let mut current_col = 0;
2483 let mut result = String::new();
2484
2485 for ch in line.chars() {
2486 let char_width = ch.width().unwrap_or(0);
2487
2488 if current_col >= end_col {
2489 break;
2490 }
2491
2492 result.push(ch);
2493 current_col += char_width;
2494 }
2495
2496 result
2497}
2498
2499impl PaneContents {
2500 pub fn new(viewport: Vec<String>, selection_start: Position, selection_end: Position) -> Self {
2501 PaneContents {
2502 viewport,
2503 selected_text: SelectedText::from_positions(selection_start, selection_end),
2504 ..Default::default()
2505 }
2506 }
2507 pub fn new_with_scrollback(
2508 viewport: Vec<String>,
2509 selection_start: Position,
2510 selection_end: Position,
2511 lines_above_viewport: Vec<String>,
2512 lines_below_viewport: Vec<String>,
2513 ) -> Self {
2514 PaneContents {
2515 viewport,
2516 selected_text: SelectedText::from_positions(selection_start, selection_end),
2517 lines_above_viewport,
2518 lines_below_viewport,
2519 }
2520 }
2521
2522 pub fn get_selected_text(&self) -> Option<String> {
2525 let selected_text = self.selected_text?;
2526
2527 let start_line = selected_text.start.line() as usize;
2528 let start_col = selected_text.start.column();
2529 let end_line = selected_text.end.line() as usize;
2530 let end_col = selected_text.end.column();
2531
2532 if start_line >= self.viewport.len() || end_line >= self.viewport.len() {
2534 return None;
2535 }
2536
2537 if start_line == end_line {
2538 let line = &self.viewport[start_line];
2540 Some(extract_text_by_columns(line, start_col, end_col))
2541 } else {
2542 let mut result = String::new();
2544
2545 let first_line = &self.viewport[start_line];
2547 result.push_str(&extract_text_from_column(first_line, start_col));
2548 result.push('\n');
2549
2550 for i in (start_line + 1)..end_line {
2552 result.push_str(&self.viewport[i]);
2553 result.push('\n');
2554 }
2555
2556 let last_line = &self.viewport[end_line];
2558 result.push_str(&extract_text_to_column(last_line, end_col));
2559
2560 Some(result)
2561 }
2562 }
2563}
2564
2565#[derive(Debug, Clone, Serialize, Deserialize)]
2566pub enum PaneScrollbackResponse {
2567 Ok(PaneContents),
2568 Err(String),
2569}
2570
2571#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2572pub enum GetPanePidResponse {
2573 Ok(i32),
2574 Err(String),
2575}
2576
2577#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2578pub enum GetPaneRunningCommandResponse {
2579 Ok(Vec<String>),
2580 Err(String),
2581}
2582
2583#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
2584pub enum GetPaneCwdResponse {
2585 Ok(PathBuf),
2586 Err(String),
2587}
2588
2589#[derive(Debug, Clone, PartialEq)]
2590pub enum GetFocusedPaneInfoResponse {
2591 Ok { tab_index: usize, pane_id: PaneId },
2592 Err(String),
2593}
2594
2595#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2596pub enum SaveLayoutResponse {
2597 Ok(()),
2598 Err(String),
2599}
2600
2601#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2602pub enum DeleteLayoutResponse {
2603 Ok(()),
2604 Err(String),
2605}
2606
2607#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2608pub enum RenameLayoutResponse {
2609 Ok(()),
2610 Err(String),
2611}
2612
2613#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
2614pub enum EditLayoutResponse {
2615 Ok(()),
2616 Err(String),
2617}
2618
2619#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
2620pub struct SelectedText {
2621 pub start: Position,
2622 pub end: Position,
2623}
2624
2625impl SelectedText {
2626 pub fn new(start: Position, end: Position) -> Self {
2627 let (normalized_start, normalized_end) = if start <= end {
2629 (start, end)
2630 } else {
2631 (end, start)
2632 };
2633
2634 let normalized_start = Position::new(
2637 normalized_start.line().max(0) as i32,
2638 normalized_start.column() as u16,
2639 );
2640 let normalized_end = Position::new(
2641 normalized_end.line().max(0) as i32,
2642 normalized_end.column() as u16,
2643 );
2644
2645 SelectedText {
2646 start: normalized_start,
2647 end: normalized_end,
2648 }
2649 }
2650
2651 pub fn from_positions(start: Position, end: Position) -> Option<Self> {
2652 if start == end {
2653 None
2654 } else {
2655 Some(Self::new(start, end))
2656 }
2657 }
2658}
2659
2660#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2661pub struct PluginIds {
2662 pub plugin_id: u32,
2663 pub zellij_pid: u32,
2664 pub initial_cwd: PathBuf,
2665 pub client_id: ClientId,
2666}
2667
2668#[derive(Debug, Default, Clone, PartialEq, Eq, Hash, Deserialize, Serialize, PartialOrd, Ord)]
2670pub struct PluginTag(String);
2671
2672impl PluginTag {
2673 pub fn new(url: impl Into<String>) -> Self {
2674 PluginTag(url.into())
2675 }
2676}
2677
2678impl From<PluginTag> for String {
2679 fn from(tag: PluginTag) -> Self {
2680 tag.0
2681 }
2682}
2683
2684impl fmt::Display for PluginTag {
2685 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
2686 write!(f, "{}", self.0)
2687 }
2688}
2689
2690#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
2691pub struct PluginCapabilities {
2692 pub arrow_fonts: bool,
2693}
2694
2695impl Default for PluginCapabilities {
2696 fn default() -> PluginCapabilities {
2697 PluginCapabilities { arrow_fonts: true }
2698 }
2699}
2700
2701#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2703pub enum CopyDestination {
2704 Command,
2705 Primary,
2706 System,
2707}
2708
2709#[derive(Debug, Copy, Clone, PartialEq, Serialize, Deserialize)]
2710pub enum PermissionStatus {
2711 Granted,
2712 Denied,
2713}
2714
2715#[derive(Debug, Default, Clone, PartialEq, Eq, Serialize, Deserialize)]
2716pub struct FileToOpen {
2717 pub path: PathBuf,
2718 pub line_number: Option<usize>,
2719 pub cwd: Option<PathBuf>,
2720}
2721
2722impl FileToOpen {
2723 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2724 FileToOpen {
2725 path: path.as_ref().to_path_buf(),
2726 ..Default::default()
2727 }
2728 }
2729 pub fn with_line_number(mut self, line_number: usize) -> Self {
2730 self.line_number = Some(line_number);
2731 self
2732 }
2733 pub fn with_cwd(mut self, cwd: PathBuf) -> Self {
2734 self.cwd = Some(cwd);
2735 self
2736 }
2737}
2738
2739#[derive(Debug, Default, Clone)]
2740pub struct CommandToRun {
2741 pub path: PathBuf,
2742 pub args: Vec<String>,
2743 pub cwd: Option<PathBuf>,
2744}
2745
2746impl CommandToRun {
2747 pub fn new<P: AsRef<Path>>(path: P) -> Self {
2748 CommandToRun {
2749 path: path.as_ref().to_path_buf(),
2750 ..Default::default()
2751 }
2752 }
2753 pub fn new_with_args<P: AsRef<Path>, A: AsRef<str>>(path: P, args: Vec<A>) -> Self {
2754 CommandToRun {
2755 path: path.as_ref().to_path_buf(),
2756 args: args.into_iter().map(|a| a.as_ref().to_owned()).collect(),
2757 ..Default::default()
2758 }
2759 }
2760}
2761
2762#[derive(Debug, Default, Clone)]
2763pub struct MessageToPlugin {
2764 pub plugin_url: Option<String>,
2765 pub destination_plugin_id: Option<u32>,
2766 pub plugin_config: BTreeMap<String, String>,
2767 pub message_name: String,
2768 pub message_payload: Option<String>,
2769 pub message_args: BTreeMap<String, String>,
2770 pub new_plugin_args: Option<NewPluginArgs>,
2773 pub floating_pane_coordinates: Option<FloatingPaneCoordinates>,
2774}
2775
2776#[derive(Debug, Default, Clone)]
2777pub struct NewPluginArgs {
2778 pub should_float: Option<bool>,
2779 pub pane_id_to_replace: Option<PaneId>,
2780 pub pane_title: Option<String>,
2781 pub cwd: Option<PathBuf>,
2782 pub skip_cache: bool,
2783 pub should_focus: Option<bool>,
2784}
2785
2786#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, PartialOrd, Ord)]
2787pub enum PaneId {
2788 Terminal(u32),
2789 Plugin(u32),
2790}
2791
2792impl Default for PaneId {
2793 fn default() -> Self {
2794 PaneId::Terminal(0)
2795 }
2796}
2797
2798impl FromStr for PaneId {
2799 type Err = Box<dyn std::error::Error>;
2800 fn from_str(stringified_pane_id: &str) -> Result<Self, Self::Err> {
2801 if let Some(terminal_stringified_pane_id) = stringified_pane_id.strip_prefix("terminal_") {
2802 u32::from_str_radix(terminal_stringified_pane_id, 10)
2803 .map(|id| PaneId::Terminal(id))
2804 .map_err(|e| e.into())
2805 } else if let Some(plugin_pane_id) = stringified_pane_id.strip_prefix("plugin_") {
2806 u32::from_str_radix(plugin_pane_id, 10)
2807 .map(|id| PaneId::Plugin(id))
2808 .map_err(|e| e.into())
2809 } else {
2810 u32::from_str_radix(&stringified_pane_id, 10)
2811 .map(|id| PaneId::Terminal(id))
2812 .map_err(|e| e.into())
2813 }
2814 }
2815}
2816
2817impl std::fmt::Display for PaneId {
2818 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
2819 match self {
2820 PaneId::Terminal(id) => write!(f, "terminal_{}", id),
2821 PaneId::Plugin(id) => write!(f, "plugin_{}", id),
2822 }
2823 }
2824}
2825
2826impl MessageToPlugin {
2827 pub fn new(message_name: impl Into<String>) -> Self {
2828 MessageToPlugin {
2829 message_name: message_name.into(),
2830 ..Default::default()
2831 }
2832 }
2833 pub fn with_plugin_url(mut self, url: impl Into<String>) -> Self {
2834 self.plugin_url = Some(url.into());
2835 self
2836 }
2837 pub fn with_destination_plugin_id(mut self, destination_plugin_id: u32) -> Self {
2838 self.destination_plugin_id = Some(destination_plugin_id);
2839 self
2840 }
2841 pub fn with_plugin_config(mut self, plugin_config: BTreeMap<String, String>) -> Self {
2842 self.plugin_config = plugin_config;
2843 self
2844 }
2845 pub fn with_payload(mut self, payload: impl Into<String>) -> Self {
2846 self.message_payload = Some(payload.into());
2847 self
2848 }
2849 pub fn with_args(mut self, args: BTreeMap<String, String>) -> Self {
2850 self.message_args = args;
2851 self
2852 }
2853 pub fn with_floating_pane_coordinates(
2854 mut self,
2855 floating_pane_coordinates: FloatingPaneCoordinates,
2856 ) -> Self {
2857 self.floating_pane_coordinates = Some(floating_pane_coordinates);
2858 self
2859 }
2860 pub fn new_plugin_instance_should_float(mut self, should_float: bool) -> Self {
2861 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2862 new_plugin_args.should_float = Some(should_float);
2863 self
2864 }
2865 pub fn new_plugin_instance_should_replace_pane(mut self, pane_id: PaneId) -> Self {
2866 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2867 new_plugin_args.pane_id_to_replace = Some(pane_id);
2868 self
2869 }
2870 pub fn new_plugin_instance_should_have_pane_title(
2871 mut self,
2872 pane_title: impl Into<String>,
2873 ) -> Self {
2874 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2875 new_plugin_args.pane_title = Some(pane_title.into());
2876 self
2877 }
2878 pub fn new_plugin_instance_should_have_cwd(mut self, cwd: PathBuf) -> Self {
2879 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2880 new_plugin_args.cwd = Some(cwd);
2881 self
2882 }
2883 pub fn new_plugin_instance_should_skip_cache(mut self) -> Self {
2884 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2885 new_plugin_args.skip_cache = true;
2886 self
2887 }
2888 pub fn new_plugin_instance_should_be_focused(mut self) -> Self {
2889 let new_plugin_args = self.new_plugin_args.get_or_insert_with(Default::default);
2890 new_plugin_args.should_focus = Some(true);
2891 self
2892 }
2893 pub fn has_cwd(&self) -> bool {
2894 self.new_plugin_args
2895 .as_ref()
2896 .map(|n| n.cwd.is_some())
2897 .unwrap_or(false)
2898 }
2899}
2900
2901#[derive(Debug, Default, Clone, Serialize, Deserialize, PartialEq)]
2902pub struct ConnectToSession {
2903 pub name: Option<String>,
2904 pub tab_position: Option<usize>,
2905 pub pane_id: Option<(u32, bool)>, pub layout: Option<LayoutInfo>,
2907 pub cwd: Option<PathBuf>,
2908}
2909
2910impl ConnectToSession {
2911 pub fn apply_layout_dir(&mut self, layout_dir: &PathBuf) {
2912 if let Some(LayoutInfo::File(file_path, _layout_metadata)) = self.layout.as_mut() {
2913 *file_path = Path::join(layout_dir, &file_path)
2914 .to_string_lossy()
2915 .to_string();
2916 }
2917 }
2918}
2919
2920#[derive(Debug, Default, Clone)]
2921pub struct PluginMessage {
2922 pub name: String,
2923 pub payload: String,
2924 pub worker_name: Option<String>,
2925}
2926
2927impl PluginMessage {
2928 pub fn new_to_worker(worker_name: &str, message: &str, payload: &str) -> Self {
2929 PluginMessage {
2930 name: message.to_owned(),
2931 payload: payload.to_owned(),
2932 worker_name: Some(worker_name.to_owned()),
2933 }
2934 }
2935 pub fn new_to_plugin(message: &str, payload: &str) -> Self {
2936 PluginMessage {
2937 name: message.to_owned(),
2938 payload: payload.to_owned(),
2939 worker_name: None,
2940 }
2941 }
2942}
2943
2944#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
2945pub enum HttpVerb {
2946 Get,
2947 Post,
2948 Put,
2949 Delete,
2950}
2951
2952#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2953pub enum PipeSource {
2954 Cli(String), Plugin(u32), Keybind, }
2958
2959#[derive(Debug, Clone, PartialEq, Eq, Hash)]
2960pub struct PipeMessage {
2961 pub source: PipeSource,
2962 pub name: String,
2963 pub payload: Option<String>,
2964 pub args: BTreeMap<String, String>,
2965 pub is_private: bool,
2966}
2967
2968impl PipeMessage {
2969 pub fn new(
2970 source: PipeSource,
2971 name: impl Into<String>,
2972 payload: &Option<String>,
2973 args: &Option<BTreeMap<String, String>>,
2974 is_private: bool,
2975 ) -> Self {
2976 PipeMessage {
2977 source,
2978 name: name.into(),
2979 payload: payload.clone(),
2980 args: args.clone().unwrap_or_else(|| Default::default()),
2981 is_private,
2982 }
2983 }
2984}
2985
2986#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize, Default)]
2987pub struct FloatingPaneCoordinates {
2988 pub x: Option<PercentOrFixed>,
2989 pub y: Option<PercentOrFixed>,
2990 pub width: Option<PercentOrFixed>,
2991 pub height: Option<PercentOrFixed>,
2992 pub pinned: Option<bool>,
2993 pub borderless: Option<bool>,
2994}
2995
2996impl FloatingPaneCoordinates {
2997 pub fn new(
2998 x: Option<String>,
2999 y: Option<String>,
3000 width: Option<String>,
3001 height: Option<String>,
3002 pinned: Option<bool>,
3003 borderless: Option<bool>,
3004 ) -> Option<Self> {
3005 let x = x.and_then(|x| PercentOrFixed::from_str(&x).ok());
3007 let y = y.and_then(|y| PercentOrFixed::from_str(&y).ok());
3008
3009 let width = width.and_then(|w| {
3011 PercentOrFixed::from_str(&w)
3012 .ok()
3013 .and_then(|size| match size {
3014 PercentOrFixed::Percent(0) => None,
3015 PercentOrFixed::Fixed(0) => None,
3016 _ => Some(size),
3017 })
3018 });
3019 let height = height.and_then(|h| {
3020 PercentOrFixed::from_str(&h)
3021 .ok()
3022 .and_then(|size| match size {
3023 PercentOrFixed::Percent(0) => None,
3024 PercentOrFixed::Fixed(0) => None,
3025 _ => Some(size),
3026 })
3027 });
3028
3029 if x.is_none()
3030 && y.is_none()
3031 && width.is_none()
3032 && height.is_none()
3033 && pinned.is_none()
3034 && borderless.is_none()
3035 {
3036 None
3037 } else {
3038 Some(FloatingPaneCoordinates {
3039 x,
3040 y,
3041 width,
3042 height,
3043 pinned,
3044 borderless,
3045 })
3046 }
3047 }
3048 pub fn with_x_fixed(mut self, x: usize) -> Self {
3049 self.x = Some(PercentOrFixed::Fixed(x));
3050 self
3051 }
3052 pub fn with_x_percent(mut self, x: usize) -> Self {
3053 if x > 100 {
3054 eprintln!("x must be between 0 and 100");
3055 return self;
3056 }
3057 self.x = Some(PercentOrFixed::Percent(x));
3058 self
3059 }
3060 pub fn with_y_fixed(mut self, y: usize) -> Self {
3061 self.y = Some(PercentOrFixed::Fixed(y));
3062 self
3063 }
3064 pub fn with_y_percent(mut self, y: usize) -> Self {
3065 if y > 100 {
3066 eprintln!("y must be between 0 and 100");
3067 return self;
3068 }
3069 self.y = Some(PercentOrFixed::Percent(y));
3070 self
3071 }
3072 pub fn with_width_fixed(mut self, width: usize) -> Self {
3073 self.width = Some(PercentOrFixed::Fixed(width));
3074 self
3075 }
3076 pub fn with_width_percent(mut self, width: usize) -> Self {
3077 if width > 100 {
3078 eprintln!("width must be between 0 and 100");
3079 return self;
3080 }
3081 self.width = Some(PercentOrFixed::Percent(width));
3082 self
3083 }
3084 pub fn with_height_fixed(mut self, height: usize) -> Self {
3085 self.height = Some(PercentOrFixed::Fixed(height));
3086 self
3087 }
3088 pub fn with_height_percent(mut self, height: usize) -> Self {
3089 if height > 100 {
3090 eprintln!("height must be between 0 and 100");
3091 return self;
3092 }
3093 self.height = Some(PercentOrFixed::Percent(height));
3094 self
3095 }
3096}
3097
3098impl From<PaneGeom> for FloatingPaneCoordinates {
3099 fn from(pane_geom: PaneGeom) -> Self {
3100 FloatingPaneCoordinates {
3101 x: Some(PercentOrFixed::Fixed(pane_geom.x)),
3102 y: Some(PercentOrFixed::Fixed(pane_geom.y)),
3103 width: Some(PercentOrFixed::Fixed(pane_geom.cols.as_usize())),
3104 height: Some(PercentOrFixed::Fixed(pane_geom.rows.as_usize())),
3105 pinned: Some(pane_geom.is_pinned),
3106 borderless: None,
3107 }
3108 }
3109}
3110
3111#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
3112pub struct OriginatingPlugin {
3113 pub plugin_id: u32,
3114 pub client_id: ClientId,
3115 pub context: Context,
3116}
3117
3118impl OriginatingPlugin {
3119 pub fn new(plugin_id: u32, client_id: ClientId, context: Context) -> Self {
3120 OriginatingPlugin {
3121 plugin_id,
3122 client_id,
3123 context,
3124 }
3125 }
3126}
3127
3128#[derive(ArgEnum, Deserialize, Serialize, Debug, Clone, Copy, PartialEq, Eq)]
3129pub enum WebSharing {
3130 #[serde(alias = "on")]
3131 On,
3132 #[serde(alias = "off")]
3133 Off,
3134 #[serde(alias = "disabled")]
3135 Disabled,
3136}
3137
3138impl Default for WebSharing {
3139 fn default() -> Self {
3140 Self::Off
3141 }
3142}
3143
3144impl WebSharing {
3145 pub fn is_on(&self) -> bool {
3146 match self {
3147 WebSharing::On => true,
3148 _ => false,
3149 }
3150 }
3151 pub fn web_clients_allowed(&self) -> bool {
3152 match self {
3153 WebSharing::On => true,
3154 _ => false,
3155 }
3156 }
3157 pub fn sharing_is_disabled(&self) -> bool {
3158 match self {
3159 WebSharing::Disabled => true,
3160 _ => false,
3161 }
3162 }
3163 pub fn set_sharing(&mut self) -> bool {
3164 match self {
3166 WebSharing::On => true,
3167 WebSharing::Off => {
3168 *self = WebSharing::On;
3169 true
3170 },
3171 WebSharing::Disabled => false,
3172 }
3173 }
3174 pub fn set_not_sharing(&mut self) -> bool {
3175 match self {
3177 WebSharing::On => {
3178 *self = WebSharing::Off;
3179 true
3180 },
3181 WebSharing::Off => true,
3182 WebSharing::Disabled => false,
3183 }
3184 }
3185}
3186
3187impl FromStr for WebSharing {
3188 type Err = String;
3189 fn from_str(s: &str) -> Result<Self, Self::Err> {
3190 match s {
3191 "On" | "on" => Ok(Self::On),
3192 "Off" | "off" => Ok(Self::Off),
3193 "Disabled" | "disabled" => Ok(Self::Disabled),
3194 _ => Err(format!("No such option: {}", s)),
3195 }
3196 }
3197}
3198
3199#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq)]
3200pub enum NewPanePlacement {
3201 NoPreference {
3202 borderless: Option<bool>,
3203 },
3204 Tiled {
3205 direction: Option<Direction>,
3206 borderless: Option<bool>,
3207 },
3208 Floating(Option<FloatingPaneCoordinates>),
3209 InPlace {
3210 pane_id_to_replace: Option<PaneId>,
3211 close_replaced_pane: bool,
3212 borderless: Option<bool>,
3213 },
3214 Stacked {
3215 pane_id_to_stack_under: Option<PaneId>,
3216 borderless: Option<bool>,
3217 },
3218}
3219
3220impl Default for NewPanePlacement {
3221 fn default() -> Self {
3222 NewPanePlacement::NoPreference { borderless: None }
3223 }
3224}
3225
3226impl NewPanePlacement {
3227 pub fn with_floating_pane_coordinates(
3228 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3229 ) -> Self {
3230 NewPanePlacement::Floating(floating_pane_coordinates)
3231 }
3232 pub fn with_should_be_in_place(
3233 self,
3234 should_be_in_place: bool,
3235 close_replaced_pane: bool,
3236 ) -> Self {
3237 if should_be_in_place {
3238 NewPanePlacement::InPlace {
3239 pane_id_to_replace: None,
3240 close_replaced_pane,
3241 borderless: None,
3242 }
3243 } else {
3244 self
3245 }
3246 }
3247 pub fn with_pane_id_to_replace(
3248 pane_id_to_replace: Option<PaneId>,
3249 close_replaced_pane: bool,
3250 ) -> Self {
3251 NewPanePlacement::InPlace {
3252 pane_id_to_replace,
3253 close_replaced_pane,
3254 borderless: None,
3255 }
3256 }
3257 pub fn should_float(&self) -> Option<bool> {
3258 match self {
3259 NewPanePlacement::Floating(_) => Some(true),
3260 NewPanePlacement::Tiled { .. } => Some(false),
3261 _ => None,
3262 }
3263 }
3264 pub fn floating_pane_coordinates(&self) -> Option<FloatingPaneCoordinates> {
3265 match self {
3266 NewPanePlacement::Floating(floating_pane_coordinates) => {
3267 floating_pane_coordinates.clone()
3268 },
3269 _ => None,
3270 }
3271 }
3272 pub fn should_stack(&self) -> bool {
3273 match self {
3274 NewPanePlacement::Stacked { .. } => true,
3275 _ => false,
3276 }
3277 }
3278 pub fn id_of_stack_root(&self) -> Option<PaneId> {
3279 match self {
3280 NewPanePlacement::Stacked {
3281 pane_id_to_stack_under,
3282 ..
3283 } => *pane_id_to_stack_under,
3284 _ => None,
3285 }
3286 }
3287 pub fn get_borderless(&self) -> Option<bool> {
3288 match self {
3289 NewPanePlacement::NoPreference { borderless } => *borderless,
3290 NewPanePlacement::Tiled { borderless, .. } => *borderless,
3291 NewPanePlacement::Floating(coords) => coords.as_ref().and_then(|c| c.borderless),
3292 NewPanePlacement::InPlace { borderless, .. } => *borderless,
3293 NewPanePlacement::Stacked { borderless, .. } => *borderless,
3294 }
3295 }
3296}
3297
3298type Context = BTreeMap<String, String>;
3299
3300#[derive(Debug, Clone, EnumDiscriminants, Display)]
3301#[strum_discriminants(derive(EnumString, Hash, Serialize, Deserialize))]
3302#[strum_discriminants(name(CommandType))]
3303pub enum PluginCommand {
3304 Subscribe(HashSet<EventType>),
3305 Unsubscribe(HashSet<EventType>),
3306 SetSelectable(bool),
3307 ShowCursor(Option<(usize, usize)>),
3308 GetPluginIds,
3309 GetZellijVersion,
3310 OpenFile(FileToOpen, Context),
3311 OpenFileFloating(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3312 OpenTerminal(FileToOpen), OpenTerminalFloating(FileToOpen, Option<FloatingPaneCoordinates>), OpenCommandPane(CommandToRun, Context),
3315 OpenCommandPaneFloating(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3316 SwitchTabTo(u32), SetTimeout(f64), ExecCmd(Vec<String>),
3319 PostMessageTo(PluginMessage),
3320 PostMessageToPlugin(PluginMessage),
3321 HideSelf,
3322 ShowSelf(bool), SwitchToMode(InputMode),
3324 NewTabsWithLayout(String), NewTab {
3326 name: Option<String>,
3327 cwd: Option<String>,
3328 },
3329 GoToNextTab,
3330 GoToPreviousTab,
3331 Resize(Resize),
3332 ResizeWithDirection(ResizeStrategy),
3333 FocusNextPane,
3334 FocusPreviousPane,
3335 MoveFocus(Direction),
3336 MoveFocusOrTab(Direction),
3337 Detach,
3338 EditScrollback,
3339 Write(Vec<u8>), WriteChars(String),
3341 ToggleTab,
3342 MovePane,
3343 MovePaneWithDirection(Direction),
3344 ClearScreen,
3345 ScrollUp,
3346 ScrollDown,
3347 ScrollToTop,
3348 ScrollToBottom,
3349 PageScrollUp,
3350 PageScrollDown,
3351 ToggleFocusFullscreen,
3352 TogglePaneFrames,
3353 TogglePaneEmbedOrEject,
3354 UndoRenamePane,
3355 CloseFocus,
3356 ToggleActiveTabSync,
3357 CloseFocusedTab,
3358 UndoRenameTab,
3359 QuitZellij,
3360 PreviousSwapLayout,
3361 NextSwapLayout,
3362 GoToTabName(String),
3363 FocusOrCreateTab(String),
3364 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>),
3375 SwitchSession(ConnectToSession),
3376 DeleteDeadSession(String), DeleteAllDeadSessions, OpenTerminalInPlace(FileToOpen), OpenFileInPlace(FileToOpen, Context),
3380 OpenCommandPaneInPlace(CommandToRun, Context),
3381 RunCommand(
3382 Vec<String>, BTreeMap<String, String>, PathBuf, BTreeMap<String, String>, ),
3387 WebRequest(
3388 String, HttpVerb,
3390 BTreeMap<String, String>, Vec<u8>, BTreeMap<String, String>, ),
3394 RenameSession(String), UnblockCliPipeInput(String), BlockCliPipeInput(String), CliPipeOutput(String, String), MessageToPlugin(MessageToPlugin),
3399 DisconnectOtherClients,
3400 KillSessions(Vec<String>), ScanHostFolder(PathBuf), WatchFilesystem,
3403 DumpSessionLayout {
3404 tab_index: Option<usize>,
3405 },
3406 CloseSelf,
3407 NewTabsWithLayoutInfo(LayoutInfo),
3408 Reconfigure(String, bool), HidePaneWithId(PaneId),
3411 ShowPaneWithId(PaneId, bool, bool), OpenCommandPaneBackground(CommandToRun, Context),
3413 RerunCommandPane(u32), ResizePaneIdWithDirection(ResizeStrategy, PaneId),
3415 EditScrollbackForPaneWithId(PaneId),
3416 GetPaneScrollback {
3417 pane_id: PaneId,
3418 get_full_scrollback: bool,
3419 },
3420 WriteToPaneId(Vec<u8>, PaneId),
3421 WriteCharsToPaneId(String, PaneId),
3422 SendSigintToPaneId(PaneId),
3423 SendSigkillToPaneId(PaneId),
3424 GetPanePid {
3425 pane_id: PaneId,
3426 },
3427 GetPaneRunningCommand {
3428 pane_id: PaneId,
3429 },
3430 GetPaneCwd {
3431 pane_id: PaneId,
3432 },
3433 MovePaneWithPaneId(PaneId),
3434 MovePaneWithPaneIdInDirection(PaneId, Direction),
3435 ClearScreenForPaneId(PaneId),
3436 ScrollUpInPaneId(PaneId),
3437 ScrollDownInPaneId(PaneId),
3438 ScrollToTopInPaneId(PaneId),
3439 ScrollToBottomInPaneId(PaneId),
3440 PageScrollUpInPaneId(PaneId),
3441 PageScrollDownInPaneId(PaneId),
3442 TogglePaneIdFullscreen(PaneId),
3443 TogglePaneEmbedOrEjectForPaneId(PaneId),
3444 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 {
3459 url: String,
3460 config: BTreeMap<String, String>,
3461 load_in_background: bool,
3462 skip_plugin_cache: bool,
3463 },
3464 RebindKeys {
3465 keys_to_rebind: Vec<(InputMode, KeyWithModifier, Vec<Action>)>,
3466 keys_to_unbind: Vec<(InputMode, KeyWithModifier)>,
3467 write_config_to_disk: bool,
3468 },
3469 ListClients,
3470 ChangeHostFolder(PathBuf),
3471 SetFloatingPanePinned(PaneId, bool), StackPanes(Vec<PaneId>),
3473 ChangeFloatingPanesCoordinates(Vec<(PaneId, FloatingPaneCoordinates)>),
3474 TogglePaneBorderless(PaneId),
3475 SetPaneBorderless(PaneId, bool),
3476 OpenCommandPaneNearPlugin(CommandToRun, Context),
3477 OpenTerminalNearPlugin(FileToOpen),
3478 OpenTerminalFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>),
3479 OpenTerminalInPlaceOfPlugin(FileToOpen, bool), OpenCommandPaneFloatingNearPlugin(CommandToRun, Option<FloatingPaneCoordinates>, Context),
3481 OpenCommandPaneInPlaceOfPlugin(CommandToRun, bool, Context), OpenFileNearPlugin(FileToOpen, Context),
3484 OpenFileFloatingNearPlugin(FileToOpen, Option<FloatingPaneCoordinates>, Context),
3485 StartWebServer,
3486 StopWebServer,
3487 ShareCurrentSession,
3488 StopSharingCurrentSession,
3489 OpenFileInPlaceOfPlugin(FileToOpen, bool, Context), GroupAndUngroupPanes(Vec<PaneId>, Vec<PaneId>, bool), HighlightAndUnhighlightPanes(Vec<PaneId>, Vec<PaneId>), CloseMultiplePanes(Vec<PaneId>),
3495 FloatMultiplePanes(Vec<PaneId>),
3496 EmbedMultiplePanes(Vec<PaneId>),
3497 QueryWebServerStatus,
3498 SetSelfMouseSelectionSupport(bool),
3499 GenerateWebLoginToken(Option<String>, bool), RevokeWebLoginToken(String), ListWebLoginTokens,
3502 RevokeAllWebLoginTokens,
3503 RenameWebLoginToken(String, String), InterceptKeyPresses,
3505 ClearKeyPressesIntercepts,
3506 ReplacePaneWithExistingPane(PaneId, PaneId, bool), RunAction(Action, BTreeMap<String, String>),
3509 CopyToClipboard(String), OverrideLayout(
3511 LayoutInfo,
3512 bool, bool, bool, BTreeMap<String, String>, ),
3517 SaveLayout {
3518 layout_name: String,
3519 layout_kdl: String,
3520 overwrite: bool,
3521 },
3522 DeleteLayout {
3523 layout_name: String,
3524 },
3525 RenameLayout {
3526 old_layout_name: String,
3527 new_layout_name: String,
3528 },
3529 EditLayout {
3530 layout_name: String,
3531 context: Context,
3532 },
3533 GenerateRandomName,
3534 DumpLayout(String),
3535 ParseLayout(String), GetLayoutDir,
3537 GetFocusedPaneInfo,
3538 SaveSession,
3539 CurrentSessionLastSavedTime,
3540 GetPaneInfo(PaneId),
3541 GetTabInfo(usize), GetSessionEnvironmentVariables,
3543 OpenCommandPaneInNewTab(CommandToRun, Context),
3544 OpenPluginPaneInNewTab {
3545 plugin_url: String,
3546 configuration: BTreeMap<String, String>,
3547 context: Context,
3548 },
3549 OpenEditorPaneInNewTab(FileToOpen, Context),
3550 OpenCommandPaneInPlaceOfPaneId(PaneId, CommandToRun, bool, Context), OpenTerminalPaneInPlaceOfPaneId(PaneId, FileToOpen, bool),
3552 OpenEditPaneInPlaceOfPaneId(PaneId, FileToOpen, bool, Context),
3553 HideFloatingPanes {
3554 tab_id: Option<usize>,
3555 },
3556 ShowFloatingPanes {
3557 tab_id: Option<usize>,
3558 },
3559 SetPaneColor(PaneId, Option<String>, Option<String>), SetPaneRegexHighlights(PaneId, Vec<RegexHighlight>),
3561 ClearPaneHighlights(PaneId),
3562 OpenPluginPaneFloating {
3563 plugin_url: String,
3564 configuration: BTreeMap<String, String>,
3565 floating_pane_coordinates: Option<FloatingPaneCoordinates>,
3566 context: BTreeMap<String, String>,
3567 },
3568 ListWindowsVolumes,
3569}
3570
3571#[derive(Debug, Clone, Default, Serialize, Deserialize)]
3573pub struct OpenPaneInNewTabResponse {
3574 pub tab_id: Option<usize>,
3575 pub pane_id: Option<PaneId>,
3576}
3577
3578pub type NewTabResponse = Option<usize>;
3580pub type NewTabsResponse = Vec<usize>;
3581pub type FocusOrCreateTabResponse = Option<usize>;
3582pub type BreakPanesToNewTabResponse = Option<usize>;
3583pub type BreakPanesToTabWithIndexResponse = Option<usize>;
3584pub type BreakPanesToTabWithIdResponse = Option<usize>;
3585
3586pub type OpenFileResponse = Option<PaneId>;
3588pub type OpenFileFloatingResponse = Option<PaneId>;
3589pub type OpenFileInPlaceResponse = Option<PaneId>;
3590pub type OpenFileNearPluginResponse = Option<PaneId>;
3591pub type OpenFileFloatingNearPluginResponse = Option<PaneId>;
3592pub type OpenFileInPlaceOfPluginResponse = Option<PaneId>;
3593
3594pub type OpenTerminalResponse = Option<PaneId>;
3595pub type OpenTerminalFloatingResponse = Option<PaneId>;
3596pub type OpenTerminalInPlaceResponse = Option<PaneId>;
3597pub type OpenTerminalNearPluginResponse = Option<PaneId>;
3598pub type OpenTerminalFloatingNearPluginResponse = Option<PaneId>;
3599pub type OpenTerminalInPlaceOfPluginResponse = Option<PaneId>;
3600
3601pub type OpenCommandPaneResponse = Option<PaneId>;
3602pub type OpenCommandPaneFloatingResponse = Option<PaneId>;
3603pub type OpenCommandPaneInPlaceResponse = Option<PaneId>;
3604pub type OpenCommandPaneNearPluginResponse = Option<PaneId>;
3605pub type OpenCommandPaneFloatingNearPluginResponse = Option<PaneId>;
3606pub type OpenCommandPaneInPlaceOfPluginResponse = Option<PaneId>;
3607pub type OpenCommandPaneBackgroundResponse = Option<PaneId>;
3608pub type OpenCommandPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3609pub type OpenTerminalPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3610pub type OpenEditPaneInPlaceOfPaneIdResponse = Option<PaneId>;
3611pub type OpenPluginPaneFloatingResponse = Option<PaneId>;