Add host cycling and saved defaults for new TUI conversations (#59007)

## What changed

- With multi-backend support enabled, show the current host before the first submission and use `leader b` to cycle through available connections. Expose the shortcut as `tui.keymap.global.change_host`.
- Preserve the conversation name and composer draft, including cursor position, attachments, pending pastes, and literal versus shell-command interpretation of `!`.
- Save explicit host choices in user-only `tui.default_hosts`, scoped to the local project and user identity. Use the saved host for new conversations, falling back to the first available connection when it is unavailable.
- Prepare the destination and check directory trust before replacing the current conversation. Block switching during voice calls and active turns, and remove replaced blank conversations from the session overview.

## Testing

Add coverage for shortcut visibility and remapping, draft and name preservation, project/user preference isolation, unavailable-host fallback, destination trust and startup failures, and voice-call guards.

GitOrigin-RevId: def7b088951336bec8fe3962954fba9b861f91c6
This commit is contained in:
Benjamin Carlsson
2026-11-02 23:19:03 +00:00
committed by copyberry
parent 857415f7f2
commit 26f73fba8f
49 changed files with 1273 additions and 118 deletions
+7
View File
@@ -35,11 +35,13 @@ pub(super) struct KeymapActionDescriptor {
#[derive(Clone, Copy, Debug)]
enum KeymapActionFeature {
FastMode,
MultiBackend,
}
#[derive(Clone, Copy, Debug, Default)]
pub(crate) struct KeymapActionFilter {
pub(crate) fast_mode_enabled: bool,
pub(crate) multi_backend_enabled: bool,
}
impl KeymapActionDescriptor {
@@ -47,6 +49,7 @@ impl KeymapActionDescriptor {
match self.required_feature {
None => true,
Some(KeymapActionFeature::FastMode) => filter.fast_mode_enabled,
Some(KeymapActionFeature::MultiBackend) => filter.multi_backend_enabled,
}
}
}
@@ -111,6 +114,7 @@ macro_rules! define_keymap_actions {
define_keymap_actions! {
global "Global" {
change_host [MultiBackend] => "Change where a new conversation runs.",
open_agents => "Open the agent command center.",
open_transcript => "Open the transcript overlay.",
find_transcript => "Find text in the full transcript.",
@@ -298,6 +302,9 @@ define_keymap_actions! {
/// parsed back into an action name, because underscores and casing are part of
/// the stable config contract.
pub(super) fn action_label(action: &str) -> String {
if action == "change_host" {
return "Change Where to Run".to_string();
}
action
.split('_')
.map(|word| {