1use crate::demo_script::DemoAction;
2use crossterm::event::{KeyCode, KeyModifiers};
3use std::time::Duration;
4
5#[derive(Clone, Debug)]
6pub struct DemoComponent {
7 pub id: &'static str, pub name: &'static str, pub description: &'static str, pub actions: Vec<DemoAction>, }
12
13pub fn get_component(id: &str) -> Option<DemoComponent> {
15 match id {
16 "basic_navigation" => Some(basic_navigation_component()),
18 "word_navigation" => Some(word_navigation_component()),
19 "paragraph_navigation" => Some(paragraph_navigation_component()),
20 "search_navigation" => Some(search_navigation_component()),
21
22 "select_word" => Some(select_word_component()),
24 "select_paragraph" => Some(select_paragraph_component()),
25 "select_line" => Some(select_line_component()),
26 "visual_char_mode" => Some(visual_char_mode_component()),
27
28 "yank_line" => Some(yank_line_component()),
30 "yank_selection" => Some(yank_selection_component()),
31 "highlight_selection" => Some(highlight_selection_component()),
32 "clear_highlights" => Some(clear_highlights_component()),
33
34 "execute_ls" => Some(execute_ls_component()),
36 "execute_cat" => Some(execute_cat_component()),
37 "execute_grep" => Some(execute_grep_component()),
38 "yank_and_execute" => Some(yank_and_execute_component()),
39 "execute_cat_with_paste" => Some(execute_cat_with_paste_component()),
40
41 "simple_jjj_navigation" => Some(simple_jjj_navigation_component()),
43 "search_cargo" => Some(search_cargo_component()),
44 "advanced_navigation" => Some(advanced_navigation_component()),
45 "multi_select" => Some(multi_select_component()),
46 "split_view" => Some(split_view_component()),
47
48 "intro_message" => Some(intro_message_component()),
50 "final_message" => Some(final_message_component()),
51 "final_message_short" => Some(final_message_short_component()),
52
53 _ => None,
54 }
55}
56
57pub fn list_all_components() -> Vec<DemoComponent> {
59 vec![
60 basic_navigation_component(),
62 word_navigation_component(),
63 paragraph_navigation_component(),
64 search_navigation_component(),
65 select_word_component(),
67 select_paragraph_component(),
68 select_line_component(),
69 visual_char_mode_component(),
70 yank_line_component(),
72 yank_selection_component(),
73 highlight_selection_component(),
74 clear_highlights_component(),
75 execute_ls_component(),
77 execute_cat_component(),
78 execute_grep_component(),
79 yank_and_execute_component(),
80 execute_cat_with_paste_component(),
81 simple_jjj_navigation_component(),
83 search_cargo_component(),
84 advanced_navigation_component(),
85 multi_select_component(),
86 split_view_component(),
87 intro_message_component(),
89 final_message_component(),
90 final_message_short_component(),
91 ]
92}
93
94fn basic_navigation_component() -> DemoComponent {
97 use DemoAction::*;
98
99 DemoComponent {
100 id: "basic_navigation",
101 name: "Basic Navigation",
102 description: "Basic j/k/h/l movements",
103 actions: vec![
104 ShowHint(
105 "navigate with vim keys\nj=down k=up h=left l=right".to_string(),
106 Duration::from_millis(3000),
107 ),
108 Wait(Duration::from_millis(500)),
109 Key(KeyCode::Char('j')),
110 Wait(Duration::from_millis(300)),
111 Key(KeyCode::Char('j')),
112 Wait(Duration::from_millis(300)),
113 Key(KeyCode::Char('k')),
114 Wait(Duration::from_millis(300)),
115 Key(KeyCode::Char('l')),
116 Wait(Duration::from_millis(300)),
117 Key(KeyCode::Char('h')),
118 Wait(Duration::from_millis(500)),
119 ],
120 }
121}
122
123fn word_navigation_component() -> DemoComponent {
124 use DemoAction::*;
125
126 DemoComponent {
127 id: "word_navigation",
128 name: "Word Navigation",
129 description: "w/b/e word movements",
130 actions: vec![
131 ShowHint(
132 "jump between words\nw=next b=back e=end".to_string(),
133 Duration::from_millis(3000),
134 ),
135 Wait(Duration::from_millis(500)),
136 Key(KeyCode::Char('w')),
137 Wait(Duration::from_millis(300)),
138 Key(KeyCode::Char('w')),
139 Wait(Duration::from_millis(300)),
140 Key(KeyCode::Char('b')),
141 Wait(Duration::from_millis(300)),
142 Key(KeyCode::Char('e')),
143 Wait(Duration::from_millis(500)),
144 ],
145 }
146}
147
148fn paragraph_navigation_component() -> DemoComponent {
149 use DemoAction::*;
150
151 DemoComponent {
152 id: "paragraph_navigation",
153 name: "Paragraph Navigation",
154 description: "{ } paragraph jumps",
155 actions: vec![
156 ShowHint(
157 "jump between paragraphs\n{ = previous } = next".to_string(),
158 Duration::from_millis(3000),
159 ),
160 Wait(Duration::from_millis(500)),
161 Key(KeyCode::Char('}')),
162 Wait(Duration::from_millis(500)),
163 Key(KeyCode::Char('}')),
164 Wait(Duration::from_millis(500)),
165 Key(KeyCode::Char('{')),
166 Wait(Duration::from_millis(500)),
167 ],
168 }
169}
170
171fn search_navigation_component() -> DemoComponent {
172 use DemoAction::*;
173
174 DemoComponent {
175 id: "search_navigation",
176 name: "Search Navigation",
177 description: "/ search and n/N navigation",
178 actions: vec![
179 ShowHint(
180 "search for text\n/ to search, n/N to navigate".to_string(),
181 Duration::from_millis(3000),
182 ),
183 Wait(Duration::from_millis(500)),
184 Key(KeyCode::Char('/')),
185 Wait(Duration::from_millis(250)),
186 TypeString("reader".to_string(), Duration::from_millis(100)),
187 Wait(Duration::from_millis(250)),
188 Key(KeyCode::Enter),
189 Wait(Duration::from_millis(500)),
190 Key(KeyCode::Char('n')),
191 Wait(Duration::from_millis(500)),
192 Key(KeyCode::Char('N')),
193 Wait(Duration::from_millis(500)),
194 ],
195 }
196}
197
198fn select_word_component() -> DemoComponent {
201 use DemoAction::*;
202
203 DemoComponent {
204 id: "select_word",
205 name: "Select Word",
206 description: "viw to select word",
207 actions: vec![
208 ShowHint(
209 "select entire words\nwith text objects".to_string(),
210 Duration::from_millis(3000),
211 ),
212 Wait(Duration::from_millis(500)),
213 VimMotion("viw".to_string()),
214 Wait(Duration::from_millis(1000)),
215 Key(KeyCode::Esc),
216 Wait(Duration::from_millis(500)),
217 ],
218 }
219}
220
221fn select_paragraph_component() -> DemoComponent {
222 use DemoAction::*;
223
224 DemoComponent {
225 id: "select_paragraph",
226 name: "Select Paragraph",
227 description: "vip to select paragraph",
228 actions: vec![
229 ShowHint(
230 "select entire paragraphs".to_string(),
231 Duration::from_millis(3500),
232 ),
233 Wait(Duration::from_millis(500)),
234 VimMotion("vip".to_string()),
235 Wait(Duration::from_millis(2000)),
236 ],
237 }
238}
239
240fn select_line_component() -> DemoComponent {
241 use DemoAction::*;
242
243 DemoComponent {
244 id: "select_line",
245 name: "Select Line",
246 description: "V line selection",
247 actions: vec![
248 ShowHint(
249 "select entire lines\nwith visual line mode".to_string(),
250 Duration::from_millis(3000),
251 ),
252 Wait(Duration::from_millis(500)),
253 Key(KeyCode::Char('V')),
254 Wait(Duration::from_millis(500)),
255 Key(KeyCode::Char('j')),
256 Wait(Duration::from_millis(500)),
257 Key(KeyCode::Char('j')),
258 Wait(Duration::from_millis(1000)),
259 ],
260 }
261}
262
263fn visual_char_mode_component() -> DemoComponent {
264 use DemoAction::*;
265
266 DemoComponent {
267 id: "visual_char_mode",
268 name: "Visual Character Mode",
269 description: "v + character selection",
270 actions: vec![
271 ShowHint(
272 "precise character selection\nwith visual mode".to_string(),
273 Duration::from_millis(3000),
274 ),
275 Wait(Duration::from_millis(500)),
276 Key(KeyCode::Char('v')),
277 Wait(Duration::from_millis(500)),
278 Key(KeyCode::Char('w')),
279 Wait(Duration::from_millis(300)),
280 Key(KeyCode::Char('w')),
281 Wait(Duration::from_millis(300)),
282 Key(KeyCode::Char('e')),
283 Wait(Duration::from_millis(1000)),
284 ],
285 }
286}
287
288fn yank_line_component() -> DemoComponent {
291 use DemoAction::*;
292
293 DemoComponent {
294 id: "yank_line",
295 name: "Yank Line",
296 description: "yy to yank line",
297 actions: vec![
298 ShowHint(
299 "copy from command output".to_string(),
300 Duration::from_millis(3000),
301 ),
302 Wait(Duration::from_millis(500)),
303 Key(KeyCode::Char('y')),
304 Wait(Duration::from_millis(250)),
305 Key(KeyCode::Char('y')),
306 Wait(Duration::from_millis(1000)),
307 ],
308 }
309}
310
311fn yank_selection_component() -> DemoComponent {
312 use DemoAction::*;
313
314 DemoComponent {
315 id: "yank_selection",
316 name: "Yank Selection",
317 description: "y to yank selection",
318 actions: vec![
319 ShowHint(
320 "copy selected text\nwith y command".to_string(),
321 Duration::from_millis(3000),
322 ),
323 Wait(Duration::from_millis(500)),
324 Key(KeyCode::Char('y')),
325 Wait(Duration::from_millis(1000)),
326 ],
327 }
328}
329
330fn highlight_selection_component() -> DemoComponent {
331 use DemoAction::*;
332
333 DemoComponent {
334 id: "highlight_selection",
335 name: "Highlight Selection",
336 description: ":h to highlight",
337 actions: vec![
338 ShowHint(
339 "highlight selected text".to_string(),
340 Duration::from_millis(3500),
341 ),
342 Wait(Duration::from_millis(500)),
343 Key(KeyCode::Char(':')),
344 Wait(Duration::from_millis(250)),
345 Key(KeyCode::Char('h')),
346 Wait(Duration::from_millis(500)),
347 Key(KeyCode::Enter),
348 Wait(Duration::from_millis(1500)),
349 ],
350 }
351}
352
353fn clear_highlights_component() -> DemoComponent {
354 use DemoAction::*;
355
356 DemoComponent {
357 id: "clear_highlights",
358 name: "Clear Highlights",
359 description: ":ch to clear highlights",
360 actions: vec![
361 ShowHint(
362 "clear all highlights\nwith :ch command".to_string(),
363 Duration::from_millis(3000),
364 ),
365 Wait(Duration::from_millis(500)),
366 Key(KeyCode::Char(':')),
367 Wait(Duration::from_millis(250)),
368 Key(KeyCode::Char('c')),
369 Wait(Duration::from_millis(250)),
370 Key(KeyCode::Char('h')),
371 Wait(Duration::from_millis(500)),
372 Key(KeyCode::Enter),
373 Wait(Duration::from_millis(1000)),
374 ],
375 }
376}
377
378fn execute_ls_component() -> DemoComponent {
381 use DemoAction::*;
382
383 DemoComponent {
384 id: "execute_ls",
385 name: "Execute ls Command",
386 description: "List files with :!ls",
387 actions: vec![
388 ShowHint("execute commands".to_string(), Duration::from_millis(3500)),
389 Wait(Duration::from_millis(500)),
390 Key(KeyCode::Char(':')),
391 Wait(Duration::from_millis(250)),
392 Key(KeyCode::Char('!')),
393 Wait(Duration::from_millis(250)),
394 Key(KeyCode::Char('l')),
395 Wait(Duration::from_millis(250)),
396 Key(KeyCode::Char('s')),
397 Wait(Duration::from_millis(500)),
398 Key(KeyCode::Enter),
399 Wait(Duration::from_millis(1000)),
400 ],
401 }
402}
403
404fn execute_cat_component() -> DemoComponent {
405 use DemoAction::*;
406
407 DemoComponent {
408 id: "execute_cat",
409 name: "Execute cat Command",
410 description: ":!cat filename",
411 actions: vec![
412 ShowHint(
413 "view file contents\nwith :!cat command".to_string(),
414 Duration::from_millis(3500),
415 ),
416 Wait(Duration::from_millis(500)),
417 Key(KeyCode::Char(':')),
418 Wait(Duration::from_millis(250)),
419 Key(KeyCode::Char('!')),
420 Wait(Duration::from_millis(250)),
421 TypeString("cat README.md".to_string(), Duration::from_millis(100)),
422 Wait(Duration::from_millis(500)),
423 Key(KeyCode::Enter),
424 Wait(Duration::from_millis(1500)),
425 ],
426 }
427}
428
429fn execute_grep_component() -> DemoComponent {
430 use DemoAction::*;
431
432 DemoComponent {
433 id: "execute_grep",
434 name: "Execute grep Command",
435 description: ":!grep pattern file",
436 actions: vec![
437 ShowHint(
438 "search file contents\nwith :!grep command".to_string(),
439 Duration::from_millis(3500),
440 ),
441 Wait(Duration::from_millis(500)),
442 Key(KeyCode::Char(':')),
443 Wait(Duration::from_millis(250)),
444 Key(KeyCode::Char('!')),
445 Wait(Duration::from_millis(250)),
446 TypeString("grep TODO *.md".to_string(), Duration::from_millis(100)),
447 Wait(Duration::from_millis(500)),
448 Key(KeyCode::Enter),
449 Wait(Duration::from_millis(1500)),
450 ],
451 }
452}
453
454fn yank_and_execute_component() -> DemoComponent {
455 use DemoAction::*;
456
457 DemoComponent {
458 id: "yank_and_execute",
459 name: "Yank and Execute",
460 description: "Yank then paste in command",
461 actions: vec![
462 ShowHint(
463 "paste yanked text\ninto commands with Ctrl+V".to_string(),
464 Duration::from_millis(3500),
465 ),
466 Wait(Duration::from_millis(500)),
467 Key(KeyCode::Char(':')),
468 Wait(Duration::from_millis(250)),
469 Key(KeyCode::Char('!')),
470 Wait(Duration::from_millis(250)),
471 Key(KeyCode::Char('c')),
472 Wait(Duration::from_millis(250)),
473 Key(KeyCode::Char('a')),
474 Wait(Duration::from_millis(250)),
475 Key(KeyCode::Char('t')),
476 Wait(Duration::from_millis(250)),
477 Key(KeyCode::Char(' ')),
478 Wait(Duration::from_millis(250)),
479 KeyWithModifiers(KeyCode::Char('v'), KeyModifiers::CONTROL),
480 Wait(Duration::from_millis(500)),
481 Key(KeyCode::Enter),
482 Wait(Duration::from_millis(2000)),
483 ],
484 }
485}
486
487fn intro_message_component() -> DemoComponent {
490 use DemoAction::*;
491
492 DemoComponent {
493 id: "intro_message",
494 name: "Intro Message",
495 description: "Opening message",
496 actions: vec![Wait(Duration::from_millis(2000))],
497 }
498}
499
500fn final_message_component() -> DemoComponent {
501 use DemoAction::*;
502
503 DemoComponent {
504 id: "final_message",
505 name: "Final Message",
506 description: "Closing with github link",
507 actions: vec![
508 ShowHint(
509 "hygg - simplifying the way you read\n\ngithub.com/kruseio/hygg"
510 .to_string(),
511 Duration::from_millis(5000),
512 ),
513 Wait(Duration::from_millis(4000)),
514 ],
515 }
516}
517
518fn final_message_short_component() -> DemoComponent {
519 use DemoAction::*;
520
521 DemoComponent {
522 id: "final_message_short",
523 name: "Final Message Short",
524 description: "Short closing message",
525 actions: vec![
526 ShowHint(
527 "hygg - simplifying the way you read\n\ngithub.com/kruseio/hygg"
528 .to_string(),
529 Duration::from_millis(3000),
530 ),
531 Wait(Duration::from_millis(2000)),
532 ],
533 }
534}
535
536fn simple_jjj_navigation_component() -> DemoComponent {
539 use DemoAction::*;
540
541 DemoComponent {
542 id: "simple_jjj_navigation",
543 name: "Simple JJJ Navigation",
544 description: "Just 3 j movements down",
545 actions: vec![
546 ShowHint(
547 "navigate to the file listing\nusing j to move down".to_string(),
548 Duration::from_millis(3000),
549 ),
550 Wait(Duration::from_millis(500)),
551 Key(KeyCode::Char('j')),
552 Wait(Duration::from_millis(300)),
553 Key(KeyCode::Char('j')),
554 Wait(Duration::from_millis(300)),
555 Key(KeyCode::Char('j')),
556 Wait(Duration::from_millis(500)),
557 ],
558 }
559}
560
561fn search_cargo_component() -> DemoComponent {
562 use DemoAction::*;
563
564 DemoComponent {
565 id: "search_cargo",
566 name: "Search in command output",
567 description: "Search in command output",
568 actions: vec![
569 ShowHint(
570 "search in command output".to_string(),
571 Duration::from_millis(3000),
572 ),
573 Wait(Duration::from_millis(500)),
574 Key(KeyCode::Char('/')),
575 Wait(Duration::from_millis(250)),
576 TypeString("toml".to_string(), Duration::from_millis(100)),
577 Wait(Duration::from_millis(500)),
578 Key(KeyCode::Enter),
579 Wait(Duration::from_millis(1000)),
580 ],
581 }
582}
583
584fn execute_cat_with_paste_component() -> DemoComponent {
587 use DemoAction::*;
588
589 DemoComponent {
590 id: "execute_cat_with_paste",
591 name: "Execute cat with Paste",
592 description: ":!cat with Ctrl+V to paste yanked text",
593 actions: vec![
594 ShowHint(
595 "paste into next command".to_string(),
596 Duration::from_millis(3500),
597 ),
598 Wait(Duration::from_millis(500)),
599 Key(KeyCode::Char(':')),
600 Wait(Duration::from_millis(250)),
601 Key(KeyCode::Char('!')),
602 Wait(Duration::from_millis(250)),
603 Key(KeyCode::Char('c')),
604 Wait(Duration::from_millis(100)),
605 Key(KeyCode::Char('a')),
606 Wait(Duration::from_millis(100)),
607 Key(KeyCode::Char('t')),
608 Wait(Duration::from_millis(100)),
609 Key(KeyCode::Char(' ')),
610 Wait(Duration::from_millis(250)),
611 KeyWithModifiers(KeyCode::Char('v'), KeyModifiers::CONTROL),
612 Wait(Duration::from_millis(500)),
613 Key(KeyCode::Enter),
614 Wait(Duration::from_millis(2000)),
615 ],
616 }
617}
618
619fn advanced_navigation_component() -> DemoComponent {
620 use DemoAction::*;
621
622 DemoComponent {
623 id: "advanced_navigation",
624 name: "Advanced Navigation",
625 description: "gg/G/Ctrl-f/Ctrl-b movements",
626 actions: vec![
627 ShowHint(
628 "advanced navigation\ngg=top G=bottom Ctrl-f/b=page".to_string(),
629 Duration::from_millis(3500),
630 ),
631 Wait(Duration::from_millis(500)),
632 VimMotion("gg".to_string()),
633 Wait(Duration::from_millis(500)),
634 Key(KeyCode::Char('G')),
635 Wait(Duration::from_millis(500)),
636 KeyWithModifiers(KeyCode::Char('f'), KeyModifiers::CONTROL),
637 Wait(Duration::from_millis(500)),
638 KeyWithModifiers(KeyCode::Char('b'), KeyModifiers::CONTROL),
639 Wait(Duration::from_millis(500)),
640 ],
641 }
642}
643
644fn multi_select_component() -> DemoComponent {
645 use DemoAction::*;
646
647 DemoComponent {
648 id: "multi_select",
649 name: "Multi Select",
650 description: "Multiple visual selections demo",
651 actions: vec![
652 ShowHint(
653 "select multiple sections\nwith visual mode".to_string(),
654 Duration::from_millis(3000),
655 ),
656 Wait(Duration::from_millis(500)),
657 Key(KeyCode::Char('v')),
658 Wait(Duration::from_millis(300)),
659 Key(KeyCode::Char('}')),
660 Wait(Duration::from_millis(500)),
661 Key(KeyCode::Char('}')),
662 Wait(Duration::from_millis(500)),
663 Key(KeyCode::Esc),
664 Wait(Duration::from_millis(500)),
665 ],
666 }
667}
668
669fn split_view_component() -> DemoComponent {
670 use DemoAction::*;
671
672 DemoComponent {
673 id: "split_view",
674 name: "Split View",
675 description: "Demo split view after command execution",
676 actions: vec![
677 ShowHint(
678 "command output appears\nin a split view".to_string(),
679 Duration::from_millis(3000),
680 ),
681 Wait(Duration::from_millis(500)),
682 Key(KeyCode::Char('j')),
685 Wait(Duration::from_millis(300)),
686 Key(KeyCode::Char('k')),
687 Wait(Duration::from_millis(500)),
688 ],
689 }
690}
691
692#[cfg(test)]
693mod tests {
694 use super::*;
695
696 #[test]
697 fn test_get_component_valid() {
698 let component = get_component("basic_navigation");
699 assert!(component.is_some());
700 let component = component.unwrap();
701 assert_eq!(component.id, "basic_navigation");
702 assert_eq!(component.name, "Basic Navigation");
703 }
704
705 #[test]
706 fn test_get_component_invalid() {
707 let component = get_component("nonexistent_component");
708 assert!(component.is_none());
709 }
710
711 #[test]
712 fn test_list_all_components_count() {
713 let components = list_all_components();
714 assert!(components.len() >= 22);
716 }
717
718 #[test]
719 fn test_component_actions_not_empty() {
720 let component = get_component("select_paragraph").unwrap();
721 assert!(!component.actions.is_empty());
722 }
723
724 #[test]
725 fn test_new_components_exist() {
726 assert!(get_component("simple_jjj_navigation").is_some());
727 assert!(get_component("search_cargo").is_some());
728 assert!(get_component("execute_cat_with_paste").is_some());
729 assert!(get_component("advanced_navigation").is_some());
730 assert!(get_component("multi_select").is_some());
731 assert!(get_component("split_view").is_some());
732 }
733}