use super::*;
use crate::entities::chat::ChatSummary;
use crate::entities::profile::{CharacterNames, Profile};
use crate::entities::self_model::SelfModel;
use crate::features::chat_search::SearchGroup;
use crate::features::chat_search_sort::SortMode;
use crate::features::tools::mcp::McpSnapshot;
use crate::screens::awaited_chat::AwaitedChat;
use crate::shared::config::AppConfig;
use crate::shared::secrets::SecretKey;
use crate::shared::server::ServerStatuses;
pub(super) fn apply_event(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
back: &mut Option<Back>,
clipboard: &mut Option<arboard::Clipboard>,
cmd_tx: &UnboundedSender<AppCommand>,
event: AppEvent,
) {
match event {
AppEvent::ServerStatus(status) => apply_server_status(screen, active, status),
AppEvent::ChatList(chats) => apply_chat_list(screen, active, chats),
AppEvent::ChatRenamed { id, title } => screen.rename_chat(id, title),
AppEvent::ChatSearchResults { query, chat_ids } => {
apply_content_search_results(active, query, chat_ids)
}
AppEvent::MessageSearchResults {
query,
groups,
total,
} => show_message_search_results(screen, active, query, groups, total),
AppEvent::ChatListError(message) => report_chat_list_error(screen, active, message),
AppEvent::CopyToClipboard(text) => deliver_clipboard(screen, active, clipboard, &text),
AppEvent::ProfileList(profiles) => screen.set_profile_list(profiles),
AppEvent::Settings {
config,
profiles,
language_locked,
mcp,
secrets_present,
} => {
refresh_settings_screens(
active,
&config,
&profiles,
&language_locked,
&mcp,
&secrets_present,
);
screen.set_settings(*config, profiles, language_locked, mcp, secrets_present);
}
AppEvent::CharacterNames(names) => screen.set_character_names(names),
AppEvent::ChatActivated {
id,
title,
messages,
draft,
feed_view,
focus,
compaction,
child,
live_turn,
} => {
clear_back_if_left(back, id);
hand_over_to_chat(active, back, id);
screen.set_child_view(child);
screen.activate_chat(id, title, &messages, &draft, feed_view, focus, compaction);
screen.set_live_turn(live_turn);
}
AppEvent::TranscriptGrew { id, messages } => screen.grow_transcript(id, &messages),
AppEvent::TranscriptLine {
generation_id,
role,
} => screen.set_transcript_line(generation_id, role),
AppEvent::TranscriptReset { id, messages } => screen.reset_transcript(id, &messages),
AppEvent::EngineModel(model) => screen.set_engine_model(model),
AppEvent::EngineSlots(slots) => apply_engine_slots(screen, active, slots),
AppEvent::ModelCatalogue { slot, models } => {
if let ActiveScreen::Settings(settings) = active {
settings.set_model_catalogue(slot, models);
}
}
AppEvent::EngineSamplingFields(fields) => {
apply_engine_sampling_fields(screen, active, fields)
}
AppEvent::UserMessage(text) => screen.push_user_message(text),
AppEvent::RestoreInput(text) => screen.restore_input(text),
AppEvent::GenerationStarted {
generation_id,
model,
continuation,
} => apply_generation_started(screen, generation_id, model, continuation),
AppEvent::Chunk {
generation_id,
text,
} => screen.push_chunk(generation_id, &text),
AppEvent::Thoughts {
generation_id,
text,
} => screen.push_thoughts(generation_id, &text),
AppEvent::TokenUsage {
generation_id,
completion,
context,
context_exact,
reasoning,
} => screen.set_token_usage(generation_id, completion, context, context_exact, reasoning),
AppEvent::ToolConfirmRequest {
generation_id,
call_id,
name,
arguments,
inputs,
} => screen.request_tool_confirm(generation_id, call_id, name, arguments, inputs),
AppEvent::ToolCallStarted {
generation_id,
call_id,
name,
arguments,
} => screen.push_tool_call_started(generation_id, call_id, name, arguments),
AppEvent::ToolCall {
generation_id,
call_id,
name,
arguments,
result,
images,
} => screen.push_tool_call(generation_id, call_id, name, arguments, result, images),
AppEvent::AssistantContinue { generation_id } => screen.continue_assistant(generation_id),
AppEvent::AssistantRewrite { generation_id } => screen.rewrite_assistant(generation_id),
AppEvent::Finished {
generation_id,
reason,
continuable,
} => screen.finish_generation(generation_id, reason, continuable),
AppEvent::ImpersonationStarted { generation_id } => {
screen.begin_impersonation(generation_id)
}
AppEvent::ImpersonationChunk {
generation_id,
text,
} => screen.push_impersonation_chunk(generation_id, &text),
AppEvent::ImpersonationFinished {
generation_id,
reason,
} => screen.finish_impersonation(generation_id, reason),
AppEvent::RagProgress(progress) => screen.set_rag_progress(progress),
AppEvent::FileProgress(progress) => screen.set_file_progress(progress),
AppEvent::ProjectProgress(progress) => screen.set_project_progress(progress),
AppEvent::Attachments(items) => screen.set_attachments(items),
AppEvent::ImageProgress(progress) => screen.set_image_progress(progress),
AppEvent::StagedImages(items) => screen.set_staged_images(items),
AppEvent::SelfModelView { model, names } => show_self_model(screen, active, *model, names),
AppEvent::WorkspaceChanges(set) => show_changes(screen, active, *set),
AppEvent::SelfModelChanged => refresh_self_model_screen(active, cmd_tx),
AppEvent::TtsActive(on) => screen.set_speaking(on),
AppEvent::BackgroundTask { kind, active: on } => apply_background_task(screen, kind, on),
AppEvent::BackgroundRuns { out } => screen.set_background_runs(out),
AppEvent::TaskList(list) => apply_task_list(active, back, *list),
AppEvent::McpImportResult(text) => report_mcp_import_result(active, text),
AppEvent::Retrying {
generation_id,
attempt,
max,
delay_secs,
} => screen.set_retrying(generation_id, attempt, max, delay_secs),
AppEvent::SubagentProgress {
generation_id,
run,
progress,
} => screen.set_subagent_progress(generation_id, run, progress),
AppEvent::Error(message) => screen.push_error(&message),
AppEvent::Notice(message) => screen.push_note(&message),
AppEvent::Compacted {
chat_id,
boundary,
summary,
folded,
} => apply_compacted(screen, chat_id, boundary, summary, folded),
}
}
fn apply_server_status(screen: &mut ChatScreen, active: &mut ActiveScreen, status: ServerStatuses) {
if let ActiveScreen::Settings(settings) = active {
settings.set_server_statuses(status.clone());
}
screen.set_server_status(status);
}
fn apply_engine_sampling_fields(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
fields: Option<std::sync::Arc<[String]>>,
) {
if let ActiveScreen::Settings(settings) = active {
settings.set_engine_sampling_fields(fields.clone());
}
screen.set_engine_sampling_fields(fields);
}
fn apply_engine_slots(screen: &mut ChatScreen, active: &mut ActiveScreen, slots: Option<u32>) {
if let ActiveScreen::Settings(settings) = active {
settings.set_engine_slots(slots);
}
screen.set_engine_slots(slots);
}
fn apply_chat_list(screen: &mut ChatScreen, active: &mut ActiveScreen, chats: Vec<ChatSummary>) {
if let ActiveScreen::ChatList(list) = active {
list.set_chats(chats.clone());
}
screen.set_chat_list(chats);
}
fn apply_content_search_results(
active: &mut ActiveScreen,
query: String,
chat_ids: Option<Vec<Uuid>>,
) {
if let ActiveScreen::ChatList(list) = active {
list.set_search_results(query, chat_ids);
}
}
fn apply_generation_started(
screen: &mut ChatScreen,
generation_id: Uuid,
model: Option<String>,
continuation: bool,
) {
if continuation {
screen.begin_continuation(generation_id, model);
} else {
screen.begin_generation(generation_id, model);
}
}
fn show_message_search_results(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
query: String,
groups: Vec<SearchGroup>,
total: usize,
) {
match active {
ActiveScreen::Search(search) => search.set_results(query, groups, total),
_ => {
*active = ActiveScreen::Search(Box::new(SearchScreen::new(
query,
groups,
total,
screen.palette(),
screen.loc(),
)))
}
}
}
fn report_chat_list_error(screen: &mut ChatScreen, active: &mut ActiveScreen, message: String) {
match active {
ActiveScreen::ChatList(list) => {
list.stop_awaiting();
list.set_error(message);
}
_ => screen.push_error(&message),
}
}
fn refresh_settings_screens(
active: &mut ActiveScreen,
config: &AppConfig,
profiles: &[Profile],
language_locked: &[Uuid],
mcp: &McpSnapshot,
secrets_present: &[SecretKey],
) {
match active {
ActiveScreen::Settings(settings) => {
settings.refresh(config.clone(), profiles.to_vec(), language_locked.to_vec());
settings.set_mcp(mcp.clone());
settings.set_secrets_present(secrets_present.to_vec());
}
other => other.set_theme(
Palette::for_theme(config.interface.theme)
.with_compat(config.interface.terminal_compat),
crate::shared::i18n::locale(config.interface.language),
),
}
}
fn clear_back_if_left(back: &mut Option<Back>, id: Uuid) {
if back.as_ref().is_some_and(|ret| ret.chat() != id) {
*back = None;
}
}
fn hand_over_to_chat(active: &mut ActiveScreen, back: &mut Option<Back>, id: Uuid) {
let arrived = match active {
ActiveScreen::ChatList(list) => {
let arrived = list.take_awaited(id);
if !arrived {
list.set_active(Some(id));
}
arrived
}
ActiveScreen::Search(results) => results.take_awaited(id),
ActiveScreen::Tasks(tasks) => tasks.take_awaited(id),
ActiveScreen::Chat
| ActiveScreen::Settings(_)
| ActiveScreen::SelfModel(_)
| ActiveScreen::Changes(_) => false,
};
if arrived {
show_chat(active, back, id);
}
}
fn show_chat(active: &mut ActiveScreen, back: &mut Option<Back>, chat: Uuid) {
match std::mem::replace(active, ActiveScreen::Chat) {
ActiveScreen::Search(screen) => *back = Some(Back::Search { screen, chat }),
ActiveScreen::Tasks(screen) => *back = Some(Back::Tasks { screen, chat }),
ActiveScreen::Chat
| ActiveScreen::ChatList(_)
| ActiveScreen::Settings(_)
| ActiveScreen::SelfModel(_)
| ActiveScreen::Changes(_) => {}
}
}
fn show_self_model(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
model: Option<SelfModel>,
names: CharacterNames,
) {
match active {
ActiveScreen::SelfModel(view) => view.set_model(model, names),
ActiveScreen::Search(_) | ActiveScreen::Changes(_) | ActiveScreen::Tasks(_) => {}
ActiveScreen::Chat | ActiveScreen::ChatList(_) | ActiveScreen::Settings(_) => {
*active = ActiveScreen::SelfModel(Box::new(SelfModelScreen::new(
model,
names,
screen.palette(),
screen.loc(),
screen.self_model_note_order(),
)))
}
}
}
fn refresh_self_model_screen(active: &ActiveScreen, cmd_tx: &UnboundedSender<AppCommand>) {
if matches!(active, ActiveScreen::SelfModel(_)) {
let _ = cmd_tx.send(AppCommand::RequestSelfModel);
}
}
fn report_mcp_import_result(active: &mut ActiveScreen, text: String) {
if let ActiveScreen::Settings(settings) = active {
settings.set_mcp_import_result(text);
}
}
fn apply_compacted(
screen: &mut ChatScreen,
chat_id: Uuid,
boundary: Uuid,
summary: String,
folded: usize,
) {
if screen.active_chat() == Some(chat_id) {
let note = screen
.loc()
.tf("ui.compact.done", &[("count", &folded.to_string())]);
screen.set_compaction(Some((boundary, summary)));
screen.push_note(¬e);
}
}
fn show_changes(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
set: crate::features::workspace_diff::ChangeSet,
) {
match active {
ActiveScreen::Changes(view) => view.set_changes(set),
ActiveScreen::Search(_) | ActiveScreen::Tasks(_) => {}
ActiveScreen::Chat
| ActiveScreen::ChatList(_)
| ActiveScreen::Settings(_)
| ActiveScreen::SelfModel(_) => {
*active = ActiveScreen::Changes(Box::new(ChangesScreen::new(
set,
screen.palette(),
screen.loc(),
)))
}
}
}
fn apply_task_list(active: &mut ActiveScreen, back: &mut Option<Back>, list: TaskList) {
if let Some(Back::Tasks { screen, .. }) = back {
screen.set_list(list.clone());
}
if let ActiveScreen::Tasks(view) = active {
view.set_list(list);
}
}
pub(super) fn dispatch_tasks(
intent: TasksIntent,
cmd_tx: &UnboundedSender<AppCommand>,
screen: &ChatScreen,
active: &mut ActiveScreen,
back: &mut Option<Back>,
) -> bool {
match intent {
TasksIntent::Close => {
*active = ActiveScreen::Chat;
false
}
TasksIntent::Quit => true,
TasksIntent::StopRun(id) => {
let _ = cmd_tx.send(AppCommand::StopSubagentRun { id });
false
}
TasksIntent::StopTask(kind) => {
let _ = cmd_tx.send(AppCommand::StopBackgroundTask { kind });
false
}
TasksIntent::OpenRun(chat) | TasksIntent::OpenParent(chat) => {
match active {
ActiveScreen::Tasks(tasks) if screen.active_chat() != Some(chat) => {
tasks.await_chat(chat)
}
_ => show_chat(active, back, chat),
}
let _ = cmd_tx.send(AppCommand::SwitchChat(chat));
false
}
}
}
pub(super) fn dispatch_changes(
intent: ChangesIntent,
cmd_tx: &UnboundedSender<AppCommand>,
active: &mut ActiveScreen,
) -> bool {
match intent {
ChangesIntent::Close => {
*active = ActiveScreen::Chat;
false
}
ChangesIntent::Quit => true,
ChangesIntent::Revert(path) => {
let _ = cmd_tx.send(AppCommand::RevertWorkspaceFile { path });
false
}
}
}
fn apply_background_task(screen: &mut ChatScreen, kind: BackgroundKind, on: bool) {
match kind {
BackgroundKind::Reflection => screen.set_reflecting(on),
BackgroundKind::Consolidation => screen.set_consolidating(on),
BackgroundKind::SelfConsolidation => screen.set_self_consolidating(on),
BackgroundKind::Compaction => screen.set_compacting(on),
}
}
pub(super) fn deliver_clipboard(
screen: &mut ChatScreen,
active: &mut ActiveScreen,
clipboard: &mut Option<arboard::Clipboard>,
text: &str,
) {
let loc = screen.loc();
let (message, failed) = copy_text(clipboard, text, screen.clipboard_osc52()).message(loc);
match active {
ActiveScreen::ChatList(list) if failed => list.set_error(message),
ActiveScreen::ChatList(list) => list.set_notice(message),
_ if failed => screen.push_error(&message),
_ => screen.push_note(&message),
}
}
pub(super) enum AnyIntent {
Chat(ChatIntent),
List(ChatListIntent),
Settings(SettingsIntent),
SelfModel(SelfModelIntent),
Changes(ChangesIntent),
Search(SearchIntent),
Tasks(TasksIntent),
}
pub(super) fn dispatch_any(
intent: AnyIntent,
cmd_tx: &UnboundedSender<AppCommand>,
screen: &mut ChatScreen,
active: &mut ActiveScreen,
back: &mut Option<Back>,
) -> bool {
match intent {
AnyIntent::Chat(i) => dispatch(i, cmd_tx, screen, active, back),
AnyIntent::List(i) => dispatch_chat_list(i, cmd_tx, screen, active),
AnyIntent::Settings(i) => dispatch_settings(i, cmd_tx, active),
AnyIntent::SelfModel(i) => dispatch_self_model(i, cmd_tx, active),
AnyIntent::Changes(i) => dispatch_changes(i, cmd_tx, active),
AnyIntent::Search(i) => dispatch_search(i, cmd_tx, screen, active, back),
AnyIntent::Tasks(i) => dispatch_tasks(i, cmd_tx, screen, active, back),
}
}
pub(super) fn dispatch(
intent: ChatIntent,
cmd_tx: &UnboundedSender<AppCommand>,
screen: &ChatScreen,
active: &mut ActiveScreen,
back: &mut Option<Back>,
) -> bool {
let command = match intent {
ChatIntent::Quit => return true,
ChatIntent::Send(text) => AppCommand::SendMessage(text),
ChatIntent::RegenerateLast => AppCommand::RegenerateLast,
ChatIntent::ContinueLast => AppCommand::ContinueLast,
ChatIntent::DeleteLastExchange => AppCommand::DeleteLastExchange,
ChatIntent::Cancel => AppCommand::Cancel,
ChatIntent::Impersonate { seed } => AppCommand::Impersonate { seed },
ChatIntent::CancelImpersonation => AppCommand::CancelImpersonation,
ChatIntent::NewChat { profile_id } => AppCommand::NewChat { profile_id },
ChatIntent::CopyChat(id) => AppCommand::CopyChat(id),
ChatIntent::RenameChat { id, title } => AppCommand::RenameChat { id, title },
ChatIntent::AutoTitleChat(id) => AppCommand::AutoRenameChat(id),
ChatIntent::CloneChat(id) => AppCommand::CloneChat(id),
ChatIntent::SetChildrenExpanded { id, expanded } => {
AppCommand::SetChildrenExpanded { id, expanded }
}
ChatIntent::StopSubagentRun { id } => AppCommand::StopSubagentRun { id },
ChatIntent::StopBackgroundTasks { kinds } => {
for kind in kinds {
let _ = cmd_tx.send(AppCommand::StopBackgroundTask { kind });
}
return false;
}
ChatIntent::ExportChat { id, format, path } => AppCommand::ExportChat { id, format, path },
ChatIntent::CreateProfile { name } => AppCommand::CreateProfile {
name,
system_message: String::new(),
},
ChatIntent::DeleteProfile(id) => AppCommand::DeleteProfile(id),
ChatIntent::UpdateProfile { id, edit } => AppCommand::UpdateProfile { id, edit },
ChatIntent::UpdateConfig(config) => AppCommand::UpdateConfig(config),
ChatIntent::ClearSelfModel => {
AppCommand::UpdateSelfModel(crate::entities::self_model::SelfModelEdit::Clear)
}
ChatIntent::SearchMessages { query } => AppCommand::SearchMessages {
query,
sort: SortMode::default(),
},
ChatIntent::OpenChatLink(id) => {
if let Some(origin) = screen.active_chat().filter(|from| *from != id) {
*back = Some(Back::Link { origin, chat: id });
}
AppCommand::SwitchChat(id)
}
ChatIntent::RagAdd { path, recursive } => AppCommand::RagAdd { path, recursive },
ChatIntent::RagDelete { path } => AppCommand::RagDelete { path },
ChatIntent::RagList => AppCommand::RagList,
ChatIntent::RagRebuild => AppCommand::RagRebuild,
ChatIntent::Reindex => AppCommand::Reindex,
ChatIntent::Compact => AppCommand::Compact,
ChatIntent::FileAttach { path } => AppCommand::FileAttach { path },
ChatIntent::FileRemove { target } => AppCommand::FileRemove { target },
ChatIntent::FileList => AppCommand::FileList,
ChatIntent::FileOpen { target } => AppCommand::FileOpen { target },
ChatIntent::FileFolder => AppCommand::FileFolder,
ChatIntent::ProjectAttach { path } => AppCommand::ProjectAttach { path },
ChatIntent::ProjectSlot { slot, action } => AppCommand::ProjectSlot { slot, action },
ChatIntent::ProjectDetach => AppCommand::ProjectDetach,
ChatIntent::ProjectStatus => AppCommand::ProjectStatus,
ChatIntent::ImageAttach { path } => AppCommand::ImageAttach { path },
ChatIntent::ImageRemove { target } => AppCommand::ImageRemove { target },
ChatIntent::ImageList => AppCommand::ImageList,
ChatIntent::Tts(scope) => AppCommand::Tts(scope),
ChatIntent::TtsStop => AppCommand::TtsStop,
ChatIntent::TtsPause => AppCommand::TtsPause,
ChatIntent::TtsResume => AppCommand::TtsResume,
ChatIntent::OpenSettings => {
if let Some((config, profiles, language_locked, mcp, secrets)) =
screen.settings_snapshot()
{
let mut settings = SettingsScreen::new(config, profiles, language_locked);
settings.set_server_statuses(screen.server_statuses());
settings.set_engine_slots(screen.engine_slots());
settings.set_engine_sampling_fields(screen.engine_sampling_fields());
settings.set_mcp(mcp);
settings.set_secrets_present(secrets);
*active = ActiveScreen::Settings(Box::new(settings));
}
return false;
}
ChatIntent::OpenChatList if back.is_some() => {
match back.take() {
Some(Back::Search { screen, .. }) => *active = ActiveScreen::Search(screen),
Some(Back::Link { origin, .. }) => {
let _ = cmd_tx.send(AppCommand::SwitchChat(origin));
}
Some(Back::Tasks { screen, .. }) => {
*active = ActiveScreen::Tasks(screen);
let _ = cmd_tx.send(AppCommand::RequestTasks);
}
None => {}
}
return false;
}
ChatIntent::OpenChatList => {
*active = ActiveScreen::ChatList(Box::new(ChatListScreen::new(
screen.chat_summaries(),
screen.active_chat(),
screen.palette(),
screen.loc(),
)));
return false;
}
ChatIntent::OpenChanges => {
let _ = cmd_tx.send(AppCommand::OpenChanges);
return false;
}
ChatIntent::OpenTasks => {
*active =
ActiveScreen::Tasks(Box::new(TasksScreen::new(screen.palette(), screen.loc())));
let _ = cmd_tx.send(AppCommand::RequestTasks);
return false;
}
ChatIntent::OpenHelp => return false,
ChatIntent::OpenSelfModel => {
let _ = cmd_tx.send(AppCommand::RequestSelfModel);
return false;
}
ChatIntent::SetMouseCapture(on) => {
let _ = if on {
execute!(stdout(), EnableMouseCapture)
} else {
execute!(stdout(), DisableMouseCapture)
};
return false;
}
ChatIntent::SetFeedView(view) => {
let _ = cmd_tx.send(AppCommand::SetFeedView(view));
return false;
}
ChatIntent::ConfirmTool {
generation_id,
call_id,
decision,
} => AppCommand::ConfirmTool {
generation_id,
call_id,
decision,
},
ChatIntent::CopyToClipboard(_) => return false,
ChatIntent::PasteImage { .. } => return false,
};
if command.works_on_the_open_chat() {
*back = None;
}
let _ = cmd_tx.send(command);
false
}
pub(super) fn dispatch_chat_list(
intent: ChatListIntent,
cmd_tx: &UnboundedSender<AppCommand>,
screen: &mut ChatScreen,
active: &mut ActiveScreen,
) -> bool {
let command = match intent {
ChatListIntent::Close => {
*active = ActiveScreen::Chat;
return false;
}
ChatListIntent::Quit => return true,
ChatListIntent::Switch(id) => {
leave_list_for(active, screen, AwaitedChat::Chat(id));
AppCommand::SwitchChat(id)
}
ChatListIntent::NewChat => {
match screen.request_new_chat() {
Some(ChatIntent::NewChat { profile_id }) => {
leave_list_for(active, screen, AwaitedChat::Created);
let _ = cmd_tx.send(AppCommand::NewChat { profile_id });
}
_ => *active = ActiveScreen::Chat,
}
return false;
}
ChatListIntent::Clone(id) => {
leave_list_for(active, screen, AwaitedChat::Created);
AppCommand::CloneChat(id)
}
ChatListIntent::Copy(id) => AppCommand::CopyChat(id),
ChatListIntent::Delete(id) => AppCommand::DeleteChat(id),
ChatListIntent::Rename { id, title } => AppCommand::RenameChat { id, title },
ChatListIntent::AutoRename(id) => AppCommand::AutoRenameChat(id),
ChatListIntent::SetChildrenExpanded { id, expanded } => {
AppCommand::SetChildrenExpanded { id, expanded }
}
ChatListIntent::SearchContent(query) => AppCommand::SearchChats(query),
ChatListIntent::SearchMessages { query, sort } => {
AppCommand::SearchMessages { query, sort }
}
ChatListIntent::OpenFirstMatch { chat, query } => {
leave_list_for(active, screen, AwaitedChat::Chat(chat));
AppCommand::OpenChatAtFirstMatch { chat, query }
}
};
let _ = cmd_tx.send(command);
false
}
fn leave_list_for(active: &mut ActiveScreen, screen: &ChatScreen, awaited: AwaitedChat) {
let already_open = match awaited {
AwaitedChat::Chat(id) => screen.active_chat() == Some(id),
AwaitedChat::Created | AwaitedChat::Nothing => false,
};
match active {
ActiveScreen::ChatList(list) if !already_open => list.await_chat(awaited),
_ => *active = ActiveScreen::Chat,
}
}
pub(super) fn dispatch_settings(
intent: SettingsIntent,
cmd_tx: &UnboundedSender<AppCommand>,
active: &mut ActiveScreen,
) -> bool {
let command = match intent {
SettingsIntent::Close => {
*active = ActiveScreen::Chat;
return false;
}
SettingsIntent::Quit => return true,
SettingsIntent::SaveConfig(config) => AppCommand::UpdateConfig(config),
SettingsIntent::SaveProfile { id, edit } => AppCommand::UpdateProfile { id, edit },
SettingsIntent::CreateProfile {
name,
system_message,
} => AppCommand::CreateProfile {
name,
system_message,
},
SettingsIntent::DeleteProfile(id) => AppCommand::DeleteProfile(id),
SettingsIntent::ConfirmMcpCatalog(server) => AppCommand::ConfirmMcpCatalog(server),
SettingsIntent::ReconnectMcpServer(server) => AppCommand::ReconnectMcpServer(server),
SettingsIntent::SetSecret { key, value } => AppCommand::SetSecret { key, value },
SettingsIntent::ImportMcpServers(path) => AppCommand::ImportMcpServers(path),
SettingsIntent::ListModels(slot) => AppCommand::ListModels(slot),
};
let _ = cmd_tx.send(command);
false
}
pub(super) fn dispatch_search(
intent: SearchIntent,
cmd_tx: &UnboundedSender<AppCommand>,
screen: &ChatScreen,
active: &mut ActiveScreen,
back: &mut Option<Back>,
) -> bool {
match intent {
SearchIntent::Quit => true,
SearchIntent::Close => {
*back = None;
let query = match active {
ActiveScreen::Search(search) => search.query().to_string(),
_ => String::new(),
};
let mut list = ChatListScreen::new(
screen.chat_summaries(),
screen.active_chat(),
screen.palette(),
screen.loc(),
);
list.restore_content_query(query.clone());
*active = ActiveScreen::ChatList(Box::new(list));
let _ = cmd_tx.send(AppCommand::SearchChats(query));
false
}
SearchIntent::OpenHit {
chat,
message,
query,
} => {
match active {
ActiveScreen::Search(results) => results.await_chat(chat),
_ => *active = ActiveScreen::Chat,
}
let _ = cmd_tx.send(AppCommand::OpenChatAt {
chat,
message,
query,
});
false
}
}
}
pub(super) fn dispatch_self_model(
intent: SelfModelIntent,
cmd_tx: &UnboundedSender<AppCommand>,
active: &mut ActiveScreen,
) -> bool {
match intent {
SelfModelIntent::Close => {
*active = ActiveScreen::Chat;
false
}
SelfModelIntent::Quit => true,
SelfModelIntent::Edit(edit) => {
let _ = cmd_tx.send(AppCommand::UpdateSelfModel(edit));
false
}
}
}