koda_cli/widgets/
session_menu.rs1use super::dropdown::DropdownItem;
6
7#[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}