nexus_core/app/
sessions.rs1use anyhow::Result;
2
3use super::App;
4use crate::db::Session;
5
6impl App {
7 pub fn activate_notification(&mut self, index: usize) -> Result<()> {
8 let Some(notification) = self.notifications.remove(index) else {
9 return Ok(());
10 };
11 self.switch_to_session_by_id(¬ification.session_id)
12 }
13
14 pub fn new_session(&mut self) {
21 self.session = None;
22 self.messages.clear();
23 self.refresh_memory_snapshot();
24 self.context_total = None;
28 self.push_viewport_reset();
29 self.cleanup_incognito_images();
30 self.push_status("new chat — send a message to start it".to_string());
31 }
32
33 pub fn switch_to_session_by_id(&mut self, id: &str) -> Result<()> {
37 let Some(s) = self
38 .db
39 .get_session(id)?
40 .or_else(|| self.sessions_cache.iter().find(|s| s.id == id).cloned())
41 else {
42 self.push_status(format!("session not found: {id}"));
43 return Ok(());
44 };
45 self.messages = self.db.load_messages(&s.id)?;
46 self.unread.remove(&s.id);
47 self.notifications.retain(|n| n.session_id != s.id);
48 self.push_status(format!("switched to: {}", s.title));
49 self.current_model = Some(s.model.clone());
50 self.web_mode = s.web_mode;
51 self.session = Some(s);
52 self.refresh_memory_snapshot();
53 self.backfill_compaction_row();
54 self.restore_survey_gate_prompt();
55 self.refresh_toolbox();
56 self.context_total = None;
57 self.push_viewport_reset();
60 self.cleanup_incognito_images();
61 self.maybe_compact();
65 Ok(())
66 }
67}
68
69pub fn session_score(s: &Session, needle: &str) -> Option<i32> {
71 use crate::app::fuzzy_score;
72 let mut best = fuzzy_score(&s.title, needle);
73 let upd = |best: &mut Option<i32>, cand: Option<i32>| {
74 if let Some(c) = cand {
75 *best = Some(best.map_or(c, |b| b.max(c)));
76 }
77 };
78 if let Some(slug) = &s.slug {
79 upd(&mut best, fuzzy_score(slug, needle).map(|v| v + 2));
80 }
81 upd(&mut best, fuzzy_score(&s.id, needle));
82 best
83}
84
85pub fn parse_topic(text: &str) -> Option<(String, String)> {
88 let start = text.find('{')?;
89 let end = text.rfind('}')?;
90 let json = text.get(start..=end)?;
91 let v: serde_json::Value = serde_json::from_str(json).ok()?;
92 let topic = v.get("topic").and_then(|t| t.as_str())?.trim();
93 if topic.is_empty() {
94 return None;
95 }
96 let raw_slug = v.get("id").and_then(|s| s.as_str()).unwrap_or(topic);
97 Some((topic.to_string(), slugify(raw_slug)))
98}
99
100pub fn slugify(s: &str) -> String {
102 let slug = s
103 .to_lowercase()
104 .split(|c: char| !c.is_ascii_alphanumeric())
105 .filter(|w| !w.is_empty())
106 .take(5)
107 .collect::<Vec<_>>()
108 .join("-");
109 if slug.is_empty() {
110 "chat".to_string()
111 } else {
112 slug
113 }
114}