Skip to main content

koda_cli/widgets/
session_menu.rs

1//! Session picker dropdown — thin wrapper around the generic dropdown.
2//!
3//! Appears when the user types `/sessions` with no args.
4
5use super::dropdown::DropdownItem;
6
7/// A session item for the dropdown.
8#[derive(Clone, Debug)]
9pub struct SessionItem {
10    pub id: String,
11    pub short_id: String,
12    pub created_at: String,
13    pub message_count: i64,
14    pub total_tokens: i64,
15    pub is_current: bool,
16    pub title: Option<String>,
17}
18
19impl DropdownItem for SessionItem {
20    fn label(&self) -> &str {
21        &self.short_id
22    }
23    fn description(&self) -> String {
24        let mut desc = if let Some(title) = &self.title {
25            let t: String = title.chars().take(30).collect();
26            format!("{t}  {} msgs", self.message_count)
27        } else {
28            format!(
29                "{}  {} msgs  {}k tok",
30                self.created_at,
31                self.message_count,
32                self.total_tokens / 1000
33            )
34        };
35        if self.is_current {
36            desc.push_str(" \u{25c0} current");
37        }
38        desc
39    }
40    fn matches_filter(&self, filter: &str) -> bool {
41        let f = filter.to_lowercase();
42        self.id.to_lowercase().contains(&f)
43            || self.created_at.to_lowercase().contains(&f)
44            || self
45                .title
46                .as_deref()
47                .is_some_and(|t| t.to_lowercase().contains(&f))
48    }
49}
50
51#[cfg(test)]
52mod tests {
53    use super::*;
54
55    #[test]
56    fn current_shows_marker() {
57        let item = SessionItem {
58            id: "abc12345".into(),
59            short_id: "abc12345".into(),
60            created_at: "2026-03-08".into(),
61            message_count: 5,
62            total_tokens: 12000,
63            is_current: true,
64            title: None,
65        };
66        assert!(item.description().contains('\u{25c0}'));
67    }
68
69    #[test]
70    fn filter_by_id_and_date() {
71        let item = SessionItem {
72            id: "abc12345".into(),
73            short_id: "abc12345".into(),
74            created_at: "2026-03-08".into(),
75            message_count: 5,
76            total_tokens: 12000,
77            is_current: false,
78            title: Some("refactor auth module".into()),
79        };
80        assert!(item.matches_filter("abc"));
81        assert!(item.matches_filter("2026"));
82        assert!(item.matches_filter("refactor"));
83        assert!(!item.matches_filter("xyz"));
84    }
85}