diff --git a/migrations/20260726100000_add_project_collapsed/down.sql b/migrations/20260726100000_add_project_collapsed/down.sql new file mode 100644 index 0000000..0f9ea31 --- /dev/null +++ b/migrations/20260726100000_add_project_collapsed/down.sql @@ -0,0 +1 @@ +ALTER TABLE projects DROP COLUMN collapsed; diff --git a/migrations/20260726100000_add_project_collapsed/up.sql b/migrations/20260726100000_add_project_collapsed/up.sql new file mode 100644 index 0000000..7ebd689 --- /dev/null +++ b/migrations/20260726100000_add_project_collapsed/up.sql @@ -0,0 +1,2 @@ +ALTER TABLE projects ADD COLUMN collapsed BOOLEAN NOT NULL DEFAULT FALSE + CHECK (collapsed IN (FALSE, TRUE)); diff --git a/src/app.rs b/src/app.rs index 6d29397..6426042 100644 --- a/src/app.rs +++ b/src/app.rs @@ -178,7 +178,7 @@ pub(crate) enum Message { ProjectNameChanged(String), ConfirmProject, CancelProject, - SelectProject(i32), + ToggleProject(i32), DeleteProject(i32), CreateSession(i32), DiscardSession(i32), @@ -718,20 +718,22 @@ impl App { self.project_name_input.clear(); self.error = None; } - Message::SelectProject(project_id) => { - if self.generating { - self.error = - Some("Stop the active generation before changing sessions.".into()); + // Sidebar-only: collapsing a project leaves the open chat alone. + Message::ToggleProject(project_id) => { + let Some(item) = self + .projects + .iter_mut() + .find(|item| item.project.id == project_id) + else { return Task::none(); + }; + let collapsed = !item.project.collapsed; + item.project.collapsed = collapsed; + if let Some(database) = &mut self.database + && let Err(error) = database.set_project_collapsed(project_id, collapsed) + { + self.error = Some(error); } - self.remember_project(project_id); - self.selected_session = None; - self.conversation.clear(); - self.composer.clear(); - self.context_used = 0; - self.context_limit = self.preferences.context_tokens.max(0) as u32; - self.tokens_per_second = None; - self.error = None; } Message::DeleteProject(project_id) => { if self.generating && self.selected_project == Some(project_id) { diff --git a/src/app/projects.rs b/src/app/projects.rs index 3d27003..f87de3b 100644 --- a/src/app/projects.rs +++ b/src/app/projects.rs @@ -195,6 +195,7 @@ mod tests { id, name: name.into(), path: format!("/tmp/{name}"), + collapsed: false, } } diff --git a/src/app/view.rs b/src/app/view.rs index d62edd0..29b679b 100644 --- a/src/app/view.rs +++ b/src/app/view.rs @@ -155,12 +155,16 @@ impl App { projects = projects.push( row![ button( - row![icon(ICON_FOLDER, 17), text(&project.name).size(14)] - .spacing(9) - .align_y(Alignment::Center), + row![ + text(if project.collapsed { "›" } else { "▾" }).size(12), + icon(ICON_FOLDER, 17), + text(&project.name).size(14) + ] + .spacing(9) + .align_y(Alignment::Center), ) .width(Length::Fill) - .on_press(Message::SelectProject(project.id)) + .on_press(Message::ToggleProject(project.id)) .style(button::text), button(icon(ICON_NEW_SESSION, 15)) .on_press(Message::CreateSession(project.id)) @@ -173,6 +177,10 @@ impl App { .align_y(Alignment::Center), ); + if project.collapsed { + continue; + } + for session in item .sessions .iter() diff --git a/src/database.rs b/src/database.rs index eaa7c4e..2c49c42 100644 --- a/src/database.rs +++ b/src/database.rs @@ -257,6 +257,8 @@ pub struct Project { pub id: i32, pub name: String, pub path: String, + /// Sidebar state: hides this project's session rows. + pub collapsed: bool, } #[derive(Insertable)] @@ -536,6 +538,18 @@ impl Database { .map_err(|error| error.to_string()) } + pub fn set_project_collapsed( + &mut self, + project_id: i32, + collapsed: bool, + ) -> Result<(), String> { + diesel::update(projects::table.find(project_id)) + .set(projects::collapsed.eq(collapsed)) + .execute(&mut self.connection) + .map(|_| ()) + .map_err(|error| error.to_string()) + } + pub fn delete_project(&mut self, project_id: i32) -> Result<(), String> { self.connection .transaction(|connection| { @@ -863,6 +877,9 @@ mod tests { database.set_sidebar_collapsed(true).unwrap(); database.set_last_project(Some(project.id)).unwrap(); + assert!(!loaded[0].project.collapsed); + database.set_project_collapsed(project.id, true).unwrap(); + assert!(database.load_projects().unwrap()[0].project.collapsed); database.delete_project(project.id).unwrap(); assert!(database.load_projects().unwrap().is_empty()); diff --git a/src/schema.rs b/src/schema.rs index 5383906..ec57583 100644 --- a/src/schema.rs +++ b/src/schema.rs @@ -15,6 +15,7 @@ diesel::table! { id -> Integer, name -> Text, path -> Text, + collapsed -> Bool, } }