gn_cli/commands/
picker.rs1use anyhow::Result;
2use crossterm::{
3 cursor,
4 event::{self, Event, KeyCode},
5 execute,
6 terminal::{self, ClearType},
7};
8use gn_core::note::Note;
9use std::io::{stdout, Write};
10
11pub fn pick_note<'a>(title: &str, notes: &'a [Note]) -> Result<Option<&'a Note>> {
14 if notes.is_empty() {
15 return Ok(None);
16 }
17 if notes.len() == 1 {
18 return Ok(Some(¬es[0]));
19 }
20
21 terminal::enable_raw_mode()?;
22 let mut stdout = stdout();
23 execute!(stdout, cursor::Hide)?;
24
25 let mut selected = notes.len() - 1; let render = |stdout: &mut std::io::Stdout, sel: usize| -> Result<()> {
28 execute!(stdout, cursor::MoveToColumn(0), terminal::Clear(ClearType::FromCursorDown))?;
29 println!("\x1b[1;36m? {}\x1b[0m \x1b[90m(Use ↑/↓ arrows, Enter to select, Esc to cancel)\x1b[0m\r", title);
30
31 let start = if sel > 5 { sel - 5 } else { 0 };
32 let end = (start + 8).min(notes.len());
33
34 for i in start..end {
35 let n = ¬es[i];
36 let id_short = &n.id.to_string()[..8];
37 let file = n.file.as_deref().unwrap_or("-");
38 let line = n.line_start.unwrap_or(0);
39 let author = n.author.split('<').next().unwrap_or(&n.author).trim();
40 let body_preview = n.body.replace('\n', " ");
41 let body_short = if body_preview.len() > 38 {
42 format!("{}...", &body_preview[..35])
43 } else {
44 body_preview
45 };
46
47 if i == sel {
48 println!(
49 " \x1b[36m❯ [{}] {:<8} {:<18} {:<12} \"{}\"\x1b[0m\r",
50 i + 1,
51 id_short,
52 format!("{}:{}", file, line),
53 author,
54 body_short
55 );
56 } else {
57 println!(
58 " [{}] {:<8} {:<18} {:<12} \"{}\"\r",
59 i + 1,
60 id_short,
61 format!("{}:{}", file, line),
62 author,
63 body_short
64 );
65 }
66 }
67 stdout.flush()?;
68 Ok(())
69 };
70
71 render(&mut stdout, selected)?;
72
73 let result = loop {
74 if let Event::Key(key) = event::read()? {
75 match key.code {
76 KeyCode::Up => {
77 if selected > 0 {
78 selected -= 1;
79 let lines_printed = ((selected + 1 - (if selected > 5 { selected - 5 } else { 0 })) + 2) as u16;
81 execute!(stdout, cursor::MoveUp(lines_printed.min(10)))?;
82 render(&mut stdout, selected)?;
83 }
84 }
85 KeyCode::Down => {
86 if selected + 1 < notes.len() {
87 selected += 1;
88 let lines_printed = ((selected - (if selected > 5 { selected - 5 } else { 0 })) + 2) as u16;
89 execute!(stdout, cursor::MoveUp(lines_printed.min(10)))?;
90 render(&mut stdout, selected)?;
91 }
92 }
93 KeyCode::Enter => {
94 break Ok(Some(¬es[selected]));
95 }
96 KeyCode::Esc | KeyCode::Char('q') => {
97 break Ok(None);
98 }
99 _ => {}
100 }
101 }
102 };
103
104 execute!(stdout, cursor::Show)?;
105 terminal::disable_raw_mode()?;
106 println!();
107 result
108}