stynx_code_tui/dialogs/
session_list.rs1use std::time::{SystemTime, UNIX_EPOCH};
2
3use crate::state::{AppState, DialogOption, SelectKind};
4
5fn human_age(seconds_ago: u64) -> String {
6 match seconds_ago {
7 0..=59 => "just now".into(),
8 60..=3599 => format!("{}m ago", seconds_ago / 60),
9 3600..=86_399 => format!("{}h ago", seconds_ago / 3600),
10 _ => format!("{}d ago", seconds_ago / 86_400),
11 }
12}
13
14pub fn open_session_list(state: &mut AppState) {
15 let now = SystemTime::now()
16 .duration_since(UNIX_EPOCH)
17 .map(|d| d.as_secs())
18 .unwrap_or(0);
19
20 let mut entries: Vec<DialogOption> = Vec::new();
21
22 let mut pinned: Vec<_> = state
23 .sidebar
24 .sessions
25 .iter()
26 .filter(|s| s.pinned)
27 .collect();
28 pinned.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
29 for s in pinned {
30 let opt = DialogOption::new(&s.id, &s.title)
31 .with_category("Pinned")
32 .with_footer(human_age(now.saturating_sub(s.updated_at)));
33 entries.push(opt);
34 }
35
36 let mut today: Vec<_> = state
37 .sidebar
38 .sessions
39 .iter()
40 .filter(|s| !s.pinned)
41 .collect();
42 today.sort_by(|a, b| b.updated_at.cmp(&a.updated_at));
43 for s in today {
44 let opt = DialogOption::new(&s.id, &s.title)
45 .with_category("Recent")
46 .with_footer(human_age(now.saturating_sub(s.updated_at)));
47 entries.push(opt);
48 }
49
50 if entries.is_empty() {
51 entries.push(
52 DialogOption::new("__empty__", "No sessions yet")
53 .with_description("Start a conversation to populate this list."),
54 );
55 }
56
57 let current = if state.sidebar.session_id.is_empty() {
58 None
59 } else {
60 Some(state.sidebar.session_id.clone())
61 };
62
63 state.modal.open_select(
64 SelectKind::SessionList,
65 "Sessions",
66 entries,
67 current,
68 Some("^D delete ^R rename".to_string()),
69 );
70}