Skip to main content

kitty_rc/commands/
action.rs

1use crate::protocol::KittyMessage;
2use bon::Builder;
3use serde::Serialize;
4
5#[derive(Builder, Serialize)]
6pub struct ActionCommand {
7    action: String,
8
9    #[builder(default = vec![])]
10    #[serde(skip_serializing_if = "is_empty_args")]
11    args: Vec<String>,
12}
13
14fn is_empty_args(v: &Vec<String>) -> bool {
15    v.is_empty()
16}
17
18impl ActionCommand {
19    pub fn to_message(self) -> Result<KittyMessage, crate::error::CommandError> {
20        let payload = serde_json::to_value(self)
21            .map_err(|e| crate::error::CommandError::ValidationError(e.to_string()))?;
22
23        Ok(KittyMessage::new("send_key", vec![0, 14, 2]).payload(payload))
24    }
25}
26
27// Session actions
28
29pub struct QuitAction;
30
31impl QuitAction {
32    pub fn new() -> ActionCommand {
33        ActionCommand::builder().action("quit".to_string()).build()
34    }
35}
36
37// Tab actions
38
39pub struct NewTabAction;
40
41impl NewTabAction {
42    pub fn new() -> ActionCommand {
43        ActionCommand::builder()
44            .action("new_tab".to_string())
45            .build()
46    }
47}
48
49pub struct CloseTabAction;
50
51impl CloseTabAction {
52    pub fn new() -> ActionCommand {
53        ActionCommand::builder()
54            .action("close_tab".to_string())
55            .build()
56    }
57}
58
59pub struct NextTabAction;
60
61impl NextTabAction {
62    pub fn new() -> ActionCommand {
63        ActionCommand::builder()
64            .action("next_tab".to_string())
65            .build()
66    }
67}
68
69pub struct PreviousTabAction;
70
71impl PreviousTabAction {
72    pub fn new() -> ActionCommand {
73        ActionCommand::builder()
74            .action("previous_tab".to_string())
75            .build()
76    }
77}
78
79pub struct GotoTabAction;
80
81impl GotoTabAction {
82    pub fn new(tab_num: i32) -> ActionCommand {
83        ActionCommand::builder()
84            .action("goto_tab".to_string())
85            .args(vec![tab_num.to_string()])
86            .build()
87    }
88}
89
90pub struct SetTabTitleAction;
91
92impl SetTabTitleAction {
93    pub fn new(title: impl Into<String>) -> ActionCommand {
94        ActionCommand::builder()
95            .action("set_tab_title".to_string())
96            .args(vec![title.into()])
97            .build()
98    }
99}
100
101pub struct DetachTabAction;
102
103impl DetachTabAction {
104    pub fn new() -> ActionCommand {
105        ActionCommand::builder()
106            .action("detach_tab".to_string())
107            .build()
108    }
109}
110
111pub struct MoveTabForwardAction;
112
113impl MoveTabForwardAction {
114    pub fn new() -> ActionCommand {
115        ActionCommand::builder()
116            .action("move_tab_forward".to_string())
117            .build()
118    }
119}
120
121pub struct MoveTabBackwardAction;
122
123impl MoveTabBackwardAction {
124    pub fn new() -> ActionCommand {
125        ActionCommand::builder()
126            .action("move_tab_backward".to_string())
127            .build()
128    }
129}
130
131// Window actions
132
133pub struct NewWindowAction;
134
135impl NewWindowAction {
136    pub fn new() -> ActionCommand {
137        ActionCommand::builder()
138            .action("new_window".to_string())
139            .build()
140    }
141}
142
143pub struct NewWindowWithCwdAction;
144
145impl NewWindowWithCwdAction {
146    pub fn new() -> ActionCommand {
147        ActionCommand::builder()
148            .action("new_window_with_cwd".to_string())
149            .build()
150    }
151}
152
153pub struct CloseWindowAction;
154
155impl CloseWindowAction {
156    pub fn new() -> ActionCommand {
157        ActionCommand::builder()
158            .action("close_window".to_string())
159            .build()
160    }
161}
162
163pub struct CloseWindowWithConfirmationAction;
164
165impl CloseWindowWithConfirmationAction {
166    pub fn new() -> ActionCommand {
167        ActionCommand::builder()
168            .action("close_window_with_confirmation".to_string())
169            .build()
170    }
171}
172
173pub struct NextWindowAction;
174
175impl NextWindowAction {
176    pub fn new() -> ActionCommand {
177        ActionCommand::builder()
178            .action("next_window".to_string())
179            .build()
180    }
181}
182
183pub struct PreviousWindowAction;
184
185impl PreviousWindowAction {
186    pub fn new() -> ActionCommand {
187        ActionCommand::builder()
188            .action("previous_window".to_string())
189            .build()
190    }
191}
192
193pub struct MoveWindowForwardAction;
194
195impl MoveWindowForwardAction {
196    pub fn new() -> ActionCommand {
197        ActionCommand::builder()
198            .action("move_window_forward".to_string())
199            .build()
200    }
201}
202
203pub struct MoveWindowBackwardAction;
204
205impl MoveWindowBackwardAction {
206    pub fn new() -> ActionCommand {
207        ActionCommand::builder()
208            .action("move_window_backward".to_string())
209            .build()
210    }
211}
212
213pub struct NthWindowAction;
214
215impl NthWindowAction {
216    pub fn new(n: i32) -> ActionCommand {
217        ActionCommand::builder()
218            .action("nth_window".to_string())
219            .args(vec![n.to_string()])
220            .build()
221    }
222}
223
224pub struct FirstWindowAction;
225
226impl FirstWindowAction {
227    pub fn new() -> ActionCommand {
228        ActionCommand::builder()
229            .action("first_window".to_string())
230            .build()
231    }
232}
233
234pub struct SetWindowTitleAction;
235
236impl SetWindowTitleAction {
237    pub fn new(title: impl Into<String>) -> ActionCommand {
238        ActionCommand::builder()
239            .action("set_window_title".to_string())
240            .args(vec![title.into()])
241            .build()
242    }
243}
244
245pub struct ResizeWindowAction;
246
247impl ResizeWindowAction {
248    pub fn new(shrink: bool) -> ActionCommand {
249        if shrink {
250            ActionCommand::builder()
251                .action("resize_window".to_string())
252                .args(vec!["shrink".to_string()])
253                .build()
254        } else {
255            ActionCommand::builder()
256                .action("resize_window".to_string())
257                .args(vec!["taller".to_string()])
258                .build()
259        }
260    }
261}
262
263pub struct ResetWindowSizesAction;
264
265impl ResetWindowSizesAction {
266    pub fn new() -> ActionCommand {
267        ActionCommand::builder()
268            .action("reset_window_sizes".to_string())
269            .build()
270    }
271}
272
273pub struct MoveWindowAction;
274
275impl MoveWindowAction {
276    pub fn new(direction: impl Into<String>) -> ActionCommand {
277        ActionCommand::builder()
278            .action("move_window".to_string())
279            .args(vec![direction.into()])
280            .build()
281    }
282}
283
284pub struct NeighboringWindowAction;
285
286impl NeighboringWindowAction {
287    pub fn new(direction: impl Into<String>) -> ActionCommand {
288        ActionCommand::builder()
289            .action("neighboring_window".to_string())
290            .args(vec![direction.into()])
291            .build()
292    }
293}
294
295pub struct ToggleFullscreenAction;
296
297impl ToggleFullscreenAction {
298    pub fn new() -> ActionCommand {
299        ActionCommand::builder()
300            .action("toggle_fullscreen".to_string())
301            .build()
302    }
303}
304
305pub struct ToggleMaximizedAction;
306
307impl ToggleMaximizedAction {
308    pub fn new() -> ActionCommand {
309        ActionCommand::builder()
310            .action("toggle_maximized".to_string())
311            .build()
312    }
313}
314
315// Clipboard actions
316
317pub struct CopyToClipboardAction;
318
319impl CopyToClipboardAction {
320    pub fn new() -> ActionCommand {
321        ActionCommand::builder()
322            .action("copy_to_clipboard".to_string())
323            .build()
324    }
325}
326
327pub struct PasteAction;
328
329impl PasteAction {
330    pub fn new() -> ActionCommand {
331        ActionCommand::builder().action("paste".to_string()).build()
332    }
333}
334
335pub struct PasteFromClipboardAction;
336
337impl PasteFromClipboardAction {
338    pub fn new() -> ActionCommand {
339        ActionCommand::builder()
340            .action("paste_from_clipboard".to_string())
341            .build()
342    }
343}
344
345pub struct PasteSelectionAction;
346
347impl PasteSelectionAction {
348    pub fn new() -> ActionCommand {
349        ActionCommand::builder()
350            .action("paste_selection".to_string())
351            .build()
352    }
353}
354
355pub struct ClearSelectionAction;
356
357impl ClearSelectionAction {
358    pub fn new() -> ActionCommand {
359        ActionCommand::builder()
360            .action("clear_selection".to_string())
361            .build()
362    }
363}
364
365pub struct CopyOrInterruptAction;
366
367impl CopyOrInterruptAction {
368    pub fn new() -> ActionCommand {
369        ActionCommand::builder()
370            .action("copy_or_interrupt".to_string())
371            .build()
372    }
373}
374
375// Layout actions
376
377pub struct GotoLayoutAction;
378
379impl GotoLayoutAction {
380    pub fn new(layout: impl Into<String>) -> ActionCommand {
381        ActionCommand::builder()
382            .action("goto_layout".to_string())
383            .args(vec![layout.into()])
384            .build()
385    }
386}
387
388pub struct NextLayoutAction;
389
390impl NextLayoutAction {
391    pub fn new() -> ActionCommand {
392        ActionCommand::builder()
393            .action("next_layout".to_string())
394            .build()
395    }
396}
397
398pub struct LastUsedLayoutAction;
399
400impl LastUsedLayoutAction {
401    pub fn new() -> ActionCommand {
402        ActionCommand::builder()
403            .action("last_used_layout".to_string())
404            .build()
405    }
406}
407
408pub struct ToggleLayoutAction;
409
410impl ToggleLayoutAction {
411    pub fn new(layout: impl Into<String>) -> ActionCommand {
412        ActionCommand::builder()
413            .action("toggle_layout".to_string())
414            .args(vec![layout.into()])
415            .build()
416    }
417}
418
419// Scroll actions
420
421pub struct ScrollLineUpAction;
422
423impl ScrollLineUpAction {
424    pub fn new() -> ActionCommand {
425        ActionCommand::builder()
426            .action("scroll_line_up".to_string())
427            .build()
428    }
429}
430
431pub struct ScrollLineDownAction;
432
433impl ScrollLineDownAction {
434    pub fn new() -> ActionCommand {
435        ActionCommand::builder()
436            .action("scroll_line_down".to_string())
437            .build()
438    }
439}
440
441pub struct ScrollPageUpAction;
442
443impl ScrollPageUpAction {
444    pub fn new() -> ActionCommand {
445        ActionCommand::builder()
446            .action("scroll_page_up".to_string())
447            .build()
448    }
449}
450
451pub struct ScrollPageDownAction;
452
453impl ScrollPageDownAction {
454    pub fn new() -> ActionCommand {
455        ActionCommand::builder()
456            .action("scroll_page_down".to_string())
457            .build()
458    }
459}
460
461pub struct ScrollHomeAction;
462
463impl ScrollHomeAction {
464    pub fn new() -> ActionCommand {
465        ActionCommand::builder()
466            .action("scroll_home".to_string())
467            .build()
468    }
469}
470
471pub struct ScrollEndAction;
472
473impl ScrollEndAction {
474    pub fn new() -> ActionCommand {
475        ActionCommand::builder()
476            .action("scroll_end".to_string())
477            .build()
478    }
479}
480
481pub struct ScrollToPromptAction;
482
483impl ScrollToPromptAction {
484    pub fn new(direction: i32) -> ActionCommand {
485        ActionCommand::builder()
486            .action("scroll_to_prompt".to_string())
487            .args(vec![direction.to_string()])
488            .build()
489    }
490}
491
492pub struct ShowScrollbackAction;
493
494impl ShowScrollbackAction {
495    pub fn new() -> ActionCommand {
496        ActionCommand::builder()
497            .action("show_scrollback".to_string())
498            .build()
499    }
500}
501
502// Mark actions
503
504pub struct CreateMarkerAction;
505
506impl CreateMarkerAction {
507    pub fn new() -> ActionCommand {
508        ActionCommand::builder()
509            .action("create_marker".to_string())
510            .build()
511    }
512}
513
514pub struct RemoveMarkerAction;
515
516impl RemoveMarkerAction {
517    pub fn new() -> ActionCommand {
518        ActionCommand::builder()
519            .action("remove_marker".to_string())
520            .build()
521    }
522}
523
524pub struct ScrollToMarkAction;
525
526impl ScrollToMarkAction {
527    pub fn new(marker_type: impl Into<String>) -> ActionCommand {
528        ActionCommand::builder()
529            .action("scroll_to_mark".to_string())
530            .args(vec![marker_type.into()])
531            .build()
532    }
533}
534
535pub struct ToggleMarkerAction;
536
537impl ToggleMarkerAction {
538    pub fn new() -> ActionCommand {
539        ActionCommand::builder()
540            .action("toggle_marker".to_string())
541            .build()
542    }
543}
544
545// Mouse actions
546
547pub struct MouseClickUrlAction;
548
549impl MouseClickUrlAction {
550    pub fn new() -> ActionCommand {
551        ActionCommand::builder()
552            .action("mouse_click_url".to_string())
553            .build()
554    }
555}
556
557pub struct MouseSelectionAction;
558
559impl MouseSelectionAction {
560    pub fn new() -> ActionCommand {
561        ActionCommand::builder()
562            .action("mouse_selection".to_string())
563            .build()
564    }
565}
566
567// Debug actions
568
569pub struct DebugConfigAction;
570
571impl DebugConfigAction {
572    pub fn new() -> ActionCommand {
573        ActionCommand::builder()
574            .action("debug_config".to_string())
575            .build()
576    }
577}
578
579pub struct DumpLinesWithAttrsAction;
580
581impl DumpLinesWithAttrsAction {
582    pub fn new() -> ActionCommand {
583        ActionCommand::builder()
584            .action("dump_lines_with_attrs".to_string())
585            .build()
586    }
587}
588
589// Misc actions
590
591pub struct SendKeyAction;
592
593impl SendKeyAction {
594    pub fn new(keys: impl Into<String>) -> ActionCommand {
595        ActionCommand::builder()
596            .action("send_key".to_string())
597            .args(vec![keys.into()])
598            .build()
599    }
600}
601
602pub struct SendTextAction;
603
604impl SendTextAction {
605    pub fn new(text: impl Into<String>) -> ActionCommand {
606        ActionCommand::builder()
607            .action("send_text".to_string())
608            .args(vec![text.into()])
609            .build()
610    }
611}
612
613pub struct KittenAction;
614
615impl KittenAction {
616    pub fn new(kitten_name: impl Into<String>) -> ActionCommand {
617        ActionCommand::builder()
618            .action("kitten".to_string())
619            .args(vec![kitten_name.into()])
620            .build()
621    }
622}
623
624pub struct LaunchAction;
625
626impl LaunchAction {
627    pub fn new(args: impl Into<String>) -> ActionCommand {
628        ActionCommand::builder()
629            .action("launch".to_string())
630            .args(vec![args.into()])
631            .build()
632    }
633}
634
635pub struct SignalChildAction;
636
637impl SignalChildAction {
638    pub fn new(signal: impl Into<String>) -> ActionCommand {
639        ActionCommand::builder()
640            .action("signal_child".to_string())
641            .args(vec![signal.into()])
642            .build()
643    }
644}
645
646pub struct ClearTerminalAction;
647
648impl ClearTerminalAction {
649    pub fn new(mode: impl Into<String>) -> ActionCommand {
650        ActionCommand::builder()
651            .action("clear_terminal".to_string())
652            .args(vec![mode.into()])
653            .build()
654    }
655}
656
657pub struct ShowKittyDocAction;
658
659impl ShowKittyDocAction {
660    pub fn new(topic: impl Into<String>) -> ActionCommand {
661        ActionCommand::builder()
662            .action("show_kitty_doc".to_string())
663            .args(vec![topic.into()])
664            .build()
665    }
666}
667
668pub struct EditConfigFileAction;
669
670impl EditConfigFileAction {
671    pub fn new() -> ActionCommand {
672        ActionCommand::builder()
673            .action("edit_config_file".to_string())
674            .build()
675    }
676}
677
678pub struct SetBackgroundOpacityAction;
679
680impl SetBackgroundOpacityAction {
681    pub fn new(opacity: f32) -> ActionCommand {
682        ActionCommand::builder()
683            .action("set_background_opacity".to_string())
684            .args(vec![opacity.to_string()])
685            .build()
686    }
687}
688
689pub struct ChangeFontSizeAction;
690
691impl ChangeFontSizeAction {
692    pub fn new(delta: impl Into<String>) -> ActionCommand {
693        ActionCommand::builder()
694            .action("change_font_size".to_string())
695            .args(vec![delta.into()])
696            .build()
697    }
698}
699
700pub struct LoadConfigFileAction;
701
702impl LoadConfigFileAction {
703    pub fn new(path: impl Into<String>) -> ActionCommand {
704        ActionCommand::builder()
705            .action("load_config_file".to_string())
706            .args(vec![path.into()])
707            .build()
708    }
709}
710
711pub struct SetColorsAction;
712
713impl SetColorsAction {
714    pub fn new(path: impl Into<String>) -> ActionCommand {
715        ActionCommand::builder()
716            .action("set_colors".to_string())
717            .args(vec![path.into()])
718            .build()
719    }
720}
721
722#[cfg(test)]
723mod tests {
724    use super::*;
725
726    #[test]
727    fn test_action_command_basic() {
728        let cmd = ActionCommand::builder()
729            .action("quit".to_string())
730            .build()
731            .to_message();
732        assert!(cmd.is_ok());
733        let msg = cmd.unwrap();
734        assert_eq!(msg.cmd, "send_key");
735    }
736
737    #[test]
738    fn test_action_command_with_args() {
739        let cmd = ActionCommand::builder()
740            .action("goto_tab".to_string())
741            .args(vec!["1".to_string()])
742            .build()
743            .to_message();
744        assert!(cmd.is_ok());
745        let msg = cmd.unwrap();
746        assert!(msg.payload.is_some());
747    }
748
749    #[test]
750    fn test_quit_action() {
751        let cmd = QuitAction::new().to_message();
752        assert!(cmd.is_ok());
753        let msg = cmd.unwrap();
754        assert_eq!(msg.cmd, "send_key");
755    }
756
757    #[test]
758    fn test_new_tab_action() {
759        let cmd = NewTabAction::new().to_message();
760        assert!(cmd.is_ok());
761    }
762
763    #[test]
764    fn test_goto_tab_action() {
765        let cmd = GotoTabAction::new(5).to_message();
766        assert!(cmd.is_ok());
767        let msg = cmd.unwrap();
768        assert!(msg.payload.is_some());
769    }
770
771    #[test]
772    fn test_new_window_action() {
773        let cmd = NewWindowAction::new().to_message();
774        assert!(cmd.is_ok());
775    }
776
777    #[test]
778    fn test_nth_window_action() {
779        let cmd = NthWindowAction::new(3).to_message();
780        assert!(cmd.is_ok());
781    }
782
783    #[test]
784    fn test_copy_to_clipboard_action() {
785        let cmd = CopyToClipboardAction::new().to_message();
786        assert!(cmd.is_ok());
787    }
788
789    #[test]
790    fn test_paste_action() {
791        let cmd = PasteAction::new().to_message();
792        assert!(cmd.is_ok());
793    }
794
795    #[test]
796    fn test_goto_layout_action() {
797        let cmd = GotoLayoutAction::new("tall".to_string()).to_message();
798        assert!(cmd.is_ok());
799    }
800
801    #[test]
802    fn test_scroll_line_up_action() {
803        let cmd = ScrollLineUpAction::new().to_message();
804        assert!(cmd.is_ok());
805    }
806
807    #[test]
808    fn test_create_marker_action() {
809        let cmd = CreateMarkerAction::new().to_message();
810        assert!(cmd.is_ok());
811    }
812
813    #[test]
814    fn test_send_key_action() {
815        let cmd = SendKeyAction::new("ctrl+c".to_string()).to_message();
816        assert!(cmd.is_ok());
817    }
818
819    #[test]
820    fn test_send_text_action() {
821        let cmd = SendTextAction::new("hello".to_string()).to_message();
822        assert!(cmd.is_ok());
823    }
824
825    #[test]
826    fn test_kitten_action() {
827        let cmd = KittenAction::new("hints".to_string()).to_message();
828        assert!(cmd.is_ok());
829    }
830
831    #[test]
832    fn test_signal_child_action() {
833        let cmd = SignalChildAction::new("SIGTERM".to_string()).to_message();
834        assert!(cmd.is_ok());
835    }
836
837    #[test]
838    fn test_clear_terminal_action() {
839        let cmd = ClearTerminalAction::new("reset".to_string()).to_message();
840        assert!(cmd.is_ok());
841    }
842
843    #[test]
844    fn test_set_background_opacity_action() {
845        let cmd = SetBackgroundOpacityAction::new(0.8).to_message();
846        assert!(cmd.is_ok());
847    }
848}