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.context_total = None;
27 self.push_viewport_reset();
28 self.cleanup_incognito_images();
29 self.push_status("new chat — send a message to start it".to_string());
30 }
31
32 pub fn switch_to_session_by_id(&mut self, id: &str) -> Result<()> {
36 let Some(s) = self
37 .db
38 .get_session(id)?
39 .or_else(|| self.sessions_cache.iter().find(|s| s.id == id).cloned())
40 else {
41 self.push_status(format!("session not found: {id}"));
42 return Ok(());
43 };
44 self.messages = self.db.load_messages(&s.id)?;
45 self.unread.remove(&s.id);
46 self.notifications.retain(|n| n.session_id != s.id);
47 self.push_status(format!("switched to: {}", s.title));
48 self.current_model = Some(s.model.clone());
49 self.web_mode = s.web_mode;
50 self.session = Some(s);
51 self.backfill_compaction_row();
52 self.restore_survey_gate_prompt();
53 self.refresh_toolbox();
54 self.context_total = None;
55 self.push_viewport_reset();
58 self.cleanup_incognito_images();
59 Ok(())
60 }
61}
62
63pub fn session_score(s: &Session, needle: &str) -> Option<i32> {
65 use crate::app::fuzzy_score;
66 let mut best = fuzzy_score(&s.title, needle);
67 let upd = |best: &mut Option<i32>, cand: Option<i32>| {
68 if let Some(c) = cand {
69 *best = Some(best.map_or(c, |b| b.max(c)));
70 }
71 };
72 if let Some(slug) = &s.slug {
73 upd(&mut best, fuzzy_score(slug, needle).map(|v| v + 2));
74 }
75 upd(&mut best, fuzzy_score(&s.id, needle));
76 best
77}
78
79pub fn parse_topic(text: &str) -> Option<(String, String)> {
82 let start = text.find('{')?;
83 let end = text.rfind('}')?;
84 let json = text.get(start..=end)?;
85 let v: serde_json::Value = serde_json::from_str(json).ok()?;
86 let topic = v.get("topic").and_then(|t| t.as_str())?.trim();
87 if topic.is_empty() {
88 return None;
89 }
90 let raw_slug = v.get("id").and_then(|s| s.as_str()).unwrap_or(topic);
91 Some((topic.to_string(), slugify(raw_slug)))
92}
93
94pub fn slugify(s: &str) -> String {
96 let slug = s
97 .to_lowercase()
98 .split(|c: char| !c.is_ascii_alphanumeric())
99 .filter(|w| !w.is_empty())
100 .take(5)
101 .collect::<Vec<_>>()
102 .join("-");
103 if slug.is_empty() {
104 "chat".to_string()
105 } else {
106 slug
107 }
108}