journal-tail: show timestamps and extract meaningful titles
Sort key normalization ensures consistent ordering across entries with different date formats (content dates vs key dates). Title extraction skips date-only lines, finds ## headers or falls back to first content line truncated at 70 chars. Also fixed: cargo bin had stale binary shadowing local bin install.
This commit is contained in:
parent
7264bdc39c
commit
6096acb312
1 changed files with 47 additions and 9 deletions
56
src/main.rs
56
src/main.rs
|
|
@ -1188,14 +1188,26 @@ fn cmd_journal_tail(args: &[String]) -> Result<(), String> {
|
|||
let date_re = regex::Regex::new(r"(\d{4}-\d{2}-\d{2}[T ]\d{2}:\d{2})").unwrap();
|
||||
let key_date_re = regex::Regex::new(r"^journal\.md#j-(\d{4}-\d{2}-\d{2}[t-]\d{2}-\d{2})").unwrap();
|
||||
|
||||
let extract_sort_key = |node: &capnp_store::Node| -> String {
|
||||
// Try content header first (## 2026-02-28T23:11)
|
||||
if let Some(caps) = date_re.captures(&node.content) {
|
||||
return caps[1].to_string();
|
||||
let normalize_date = |s: &str| -> String {
|
||||
// Normalize to YYYY-MM-DDTHH:MM for consistent sorting
|
||||
let s = s.replace('t', "T");
|
||||
// Key dates use dashes everywhere: 2026-02-28-23-11
|
||||
// Content dates use dashes and colons: 2026-02-28T23:11
|
||||
// Normalize: first 10 chars keep dashes, rest convert dashes to colons
|
||||
if s.len() >= 16 {
|
||||
format!("{}T{}", &s[..10], s[11..].replace('-', ":"))
|
||||
} else {
|
||||
s
|
||||
}
|
||||
// Try key (journal.md#j-2026-02-28t23-11-...)
|
||||
};
|
||||
let extract_sort_key = |node: &capnp_store::Node| -> String {
|
||||
// Try key first (journal.md#j-2026-02-28t23-11-...)
|
||||
if let Some(caps) = key_date_re.captures(&node.key) {
|
||||
return caps[1].replace('t', "T").replace('-', ":");
|
||||
return normalize_date(&caps[1]);
|
||||
}
|
||||
// Try content header (## 2026-02-28T23:11)
|
||||
if let Some(caps) = date_re.captures(&node.content) {
|
||||
return normalize_date(&caps[1]);
|
||||
}
|
||||
// Fallback: use node timestamp
|
||||
format!("{:.0}", node.timestamp)
|
||||
|
|
@ -1206,11 +1218,37 @@ fn cmd_journal_tail(args: &[String]) -> Result<(), String> {
|
|||
.collect();
|
||||
journal.sort_by_key(|n| extract_sort_key(n));
|
||||
|
||||
// Show last N
|
||||
// Show last N — each entry: [timestamp] ## Title
|
||||
let skip = if journal.len() > n { journal.len() - n } else { 0 };
|
||||
for node in journal.iter().skip(skip) {
|
||||
println!("{}", node.content);
|
||||
println!();
|
||||
let ts = extract_sort_key(node);
|
||||
// Find a meaningful title: first ## header, or first non-date non-empty line
|
||||
let mut title = String::new();
|
||||
for line in node.content.lines() {
|
||||
let stripped = line.trim();
|
||||
if stripped.is_empty() { continue; }
|
||||
// Skip date-only lines like "## 2026-03-01T01:22"
|
||||
if date_re.is_match(stripped) && stripped.len() < 25 { continue; }
|
||||
if stripped.starts_with("## ") {
|
||||
title = stripped[3..].to_string();
|
||||
break;
|
||||
} else if stripped.starts_with("# ") {
|
||||
title = stripped[2..].to_string();
|
||||
break;
|
||||
} else {
|
||||
// Use first content line, truncated
|
||||
title = if stripped.len() > 70 {
|
||||
format!("{}...", &stripped[..67])
|
||||
} else {
|
||||
stripped.to_string()
|
||||
};
|
||||
break;
|
||||
}
|
||||
}
|
||||
if title.is_empty() {
|
||||
title = node.key.clone();
|
||||
}
|
||||
println!("[{}] {}", ts, title);
|
||||
}
|
||||
|
||||
Ok(())
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue