Name session checkpoints by their session title

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Georg Bauer
2026-07-26 09:25:37 +02:00
parent 1f27270d66
commit 76a5dd5b26
3 changed files with 27 additions and 3 deletions

View File

@@ -552,8 +552,7 @@ impl App {
false false
} }
#[cfg(target_os = "macos")] pub(super) fn session_title(&self, session_id: i32) -> Option<String> {
fn session_title(&self, session_id: i32) -> Option<String> {
self.projects self.projects
.iter() .iter()
.flat_map(|project| &project.sessions) .flat_map(|project| &project.sessions)

View File

@@ -417,9 +417,16 @@ impl App {
let mut rows = column![].spacing(7); let mut rows = column![].spacing(7);
for entry in &usage.entries { for entry in &usage.entries {
let path = entry.path.clone(); let path = entry.path.clone();
let label = entry
.session
.and_then(|id| self.session_title(id))
.map_or_else(
|| entry.name.clone(),
|title| format!("{}: {}", entry.name, shorten(&title)),
);
rows = rows.push( rows = rows.push(
row![ row![
text(entry.name.clone()).size(12), text(label).size(12),
Space::with_width(Length::Fill), Space::with_width(Length::Fill),
text(format!( text(format!(
"{} · {} old", "{} · {} old",
@@ -499,6 +506,15 @@ const AGE_COLORS: [Color; 5] = [
Color::from_rgb(0.45, 0.45, 0.48), Color::from_rgb(0.45, 0.45, 0.48),
]; ];
/// Keeps a long session title from crowding out the size and the action.
fn shorten(title: &str) -> String {
const LIMIT: usize = 44;
if title.chars().count() <= LIMIT {
return title.to_owned();
}
title.chars().take(LIMIT - 1).chain(['…']).collect()
}
fn portion(bytes: u64, capacity: u64) -> u16 { fn portion(bytes: u64, capacity: u64) -> u16 {
((bytes.saturating_mul(1000) / capacity.max(1)) as u16).max(1) ((bytes.saturating_mul(1000) / capacity.max(1)) as u16).max(1)
} }

View File

@@ -694,6 +694,8 @@ const CACHE_ENTRY_ROWS: usize = 12;
pub(crate) struct KvCacheEntry { pub(crate) struct KvCacheEntry {
pub(crate) path: PathBuf, pub(crate) path: PathBuf,
pub(crate) name: String, pub(crate) name: String,
/// Set for session checkpoints, so the view can name the conversation.
pub(crate) session: Option<i32>,
pub(crate) bytes: u64, pub(crate) bytes: u64,
pub(crate) age_seconds: u64, pub(crate) age_seconds: u64,
} }
@@ -762,6 +764,10 @@ pub(crate) fn kv_cache_report(root: &Path, budget_bytes: u64) -> KvCacheReport {
} }
report.entries.push(KvCacheEntry { report.entries.push(KvCacheEntry {
name: entry_name(&path, transient), name: entry_name(&path, transient),
session: (!transient)
.then(|| path.file_stem().and_then(|value| value.to_str()))
.flatten()
.and_then(|stem| stem.parse().ok()),
bytes: bytes.saturating_add( bytes: bytes.saturating_add(
fs::metadata(path.with_extension("meta")).map_or(0, |index| index.len()), fs::metadata(path.with_extension("meta")).map_or(0, |index| index.len()),
), ),
@@ -924,6 +930,9 @@ mod tests {
assert_eq!(report.entries.len(), 2); assert_eq!(report.entries.len(), 2);
assert_eq!(report.entries[0].bytes, 140); assert_eq!(report.entries[0].bytes, 140);
assert_eq!(report.entries[1].name, "Session 7"); assert_eq!(report.entries[1].name, "Session 7");
// The session id survives on the row, so the view can add its title.
assert_eq!(report.entries[1].session, Some(7));
assert_eq!(report.entries[0].session, None);
fs::remove_dir_all(root).unwrap(); fs::remove_dir_all(root).unwrap();
} }