Run chats independently

This commit is contained in:
Georg Bauer
2026-07-27 19:12:59 +02:00
parent 22a939751e
commit 7d865df20f
6 changed files with 401 additions and 37 deletions

View File

@@ -30,6 +30,7 @@ impl App {
self.a2ui_image_requests.insert(url.clone());
self.a2ui_image_loading = true;
let request_url = url.clone();
let session_id = self.selected_session;
Task::perform(
async move {
let mut response = ureq::get(&request_url)
@@ -48,7 +49,7 @@ impl App {
.read_to_vec()
.map_err(|error| error.to_string())
},
move |result| Message::A2uiImageLoaded(url, result),
move |result| Message::A2uiImageLoaded(session_id, url, result),
)
}
@@ -159,17 +160,11 @@ impl App {
/// Opens an unsaved session on a project and selects it. Nothing reaches the
/// database until the first chat turn is stored by [`App::persist_session`].
pub(super) fn create_session(&mut self, project_id: i32) {
if self.generating {
self.error = Some("Stop the active generation before creating a session.".into());
return;
}
if self.database.is_none() {
return;
}
#[cfg(target_os = "macos")]
self.stop_agent_jobs();
#[cfg(target_os = "macos")]
self.tool_cards.clear();
self.leave_current_chat();
let title = draft_title(&self.projects, project_id);
self.drafts.entry(project_id).or_insert(title);
self.remember_project(project_id);

View File

@@ -101,7 +101,8 @@ impl App {
/// Whether a dialog covers the window. Focus moves through the whole widget
/// tree, so the layers below have to stay out of the dialog's field order.
pub(super) fn modal_open(&self) -> bool {
self.pending_project_path.is_some()
self.quit_confirmation
|| self.pending_project_path.is_some()
|| self.pending_session_delete.is_some()
|| self.session_rename.is_some()
|| self.menu_session().is_some()
@@ -161,7 +162,9 @@ impl App {
let mut layers = vec![content];
#[cfg(target_os = "macos")]
if let Some((prompt, _)) = &self.pending_tool_approval {
if self.quit_confirmation {
layers.push(self.quit_confirmation_panel());
} else if let Some((prompt, _)) = &self.pending_tool_approval {
layers.push(self.tool_approval_panel(prompt));
} else if let Some(path) = &self.pending_project_path {
layers.push(self.project_dialog(path));
@@ -177,7 +180,9 @@ impl App {
layers.push(panel);
}
#[cfg(not(target_os = "macos"))]
if let Some(path) = &self.pending_project_path {
if self.quit_confirmation {
layers.push(self.quit_confirmation_panel());
} else if let Some(path) = &self.pending_project_path {
layers.push(self.project_dialog(path));
} else if let Some((_, title)) = &self.session_rename {
layers.push(self.rename_dialog(title));
@@ -197,6 +202,38 @@ impl App {
.into()
}
fn quit_confirmation_panel(&self) -> Element<'_, Message> {
let active = self.active_chat_count();
let dialog = container(
column![
text("Quit while chats are active?").size(22),
text(format!(
"{active} active chat{} will be stopped.",
if active == 1 { "" } else { "s" }
))
.size(13),
row![
Space::new().width(Length::Fill),
action_button("Cancel").on_press(Message::CancelQuit),
danger_button("Quit").on_press(Message::ConfirmQuit),
]
.spacing(8),
]
.spacing(12),
)
.padding(22)
.width(460)
.style(overview_style);
opaque(
container(dialog)
.center_x(Length::Fill)
.center_y(Length::Fill)
.style(|_| {
container::Style::default().background(Color::from_rgba8(0, 0, 0, 0.68))
}),
)
}
#[cfg(target_os = "macos")]
fn tool_approval_panel<'a>(
&'a self,
@@ -408,6 +445,9 @@ impl App {
label = label.push(icon(ICON_PIN, 12));
}
label = label.push(text(&session.title).size(13));
if self.session_is_active(session.id) {
label = label.push(text("").size(10).color(app_theme().palette().success));
}
row![
Space::new().width(26),
button(label)