Polish provider selection and cycle all permission modes
This commit is contained in:
@@ -970,11 +970,12 @@ fn render_permission_mode_button(
|
||||
) -> Box<dyn Element> {
|
||||
use crate::ai::execution_profiles::PermissionMode;
|
||||
let is_active = matches!(props.mode, PermissionMode::GalaxyDecides | PermissionMode::AllowAll);
|
||||
let is_locked = props.is_locked || props.mode == PermissionMode::ReadOnly;
|
||||
let icon = if is_active {
|
||||
Icon::FastForwardFilled
|
||||
} else {
|
||||
Icon::FastForward
|
||||
let is_locked = props.is_locked;
|
||||
let (icon, next_mode) = match props.mode {
|
||||
PermissionMode::ReadOnly => (Icon::Eye, PermissionMode::AskFirst),
|
||||
PermissionMode::AskFirst => (Icon::MessageChatSquare, PermissionMode::GalaxyDecides),
|
||||
PermissionMode::GalaxyDecides => (Icon::GalaxyLogo, PermissionMode::AllowAll),
|
||||
PermissionMode::AllowAll => (Icon::CheckCircleBroken, PermissionMode::ReadOnly),
|
||||
};
|
||||
let icon_size = get_icon_size(appearance);
|
||||
let icon = Container::new(
|
||||
@@ -988,20 +989,28 @@ fn render_permission_mode_button(
|
||||
)
|
||||
.finish();
|
||||
|
||||
let content = Flex::row()
|
||||
.with_cross_axis_alignment(CrossAxisAlignment::Center)
|
||||
.with_spacing(6.)
|
||||
.with_child(icon)
|
||||
.with_child(
|
||||
Text::new(props.mode.label(), appearance.ui_font_family(), get_keybinding_font_size(appearance))
|
||||
.with_color(appearance.theme().active_ui_text_color().into())
|
||||
.finish(),
|
||||
)
|
||||
.finish();
|
||||
let tooltip_text = if is_locked {
|
||||
"Permission mode is managed by the profile"
|
||||
} else if is_active {
|
||||
"Galaxy Decides · switch to Ask First"
|
||||
format!("{} · permission mode is managed by the profile", props.mode.label())
|
||||
} else {
|
||||
"Ask First · switch to Galaxy Decides"
|
||||
format!("{} · switch to {}", props.mode.label(), next_mode.label())
|
||||
};
|
||||
|
||||
render_warping_indicator_button(
|
||||
props.button_handle.clone(),
|
||||
appearance,
|
||||
icon,
|
||||
content,
|
||||
props.keystroke,
|
||||
tooltip_text.to_string(),
|
||||
tooltip_text,
|
||||
is_active,
|
||||
is_locked,
|
||||
move |ctx| {
|
||||
|
||||
@@ -201,8 +201,8 @@ pub struct ExecutionProfileEditorView {
|
||||
command_denylist_editor: ViewHandle<SubmittableTextInput>,
|
||||
command_allowlist_editor: ViewHandle<SubmittableTextInput>,
|
||||
command_allowlist_mouse_state_handles: Vec<MouseStateHandle>,
|
||||
orchestration_provider_switches: Vec<(String, SwitchStateHandle)>,
|
||||
all_providers_switch: SwitchStateHandle,
|
||||
orchestration_provider_checkboxes: Vec<(String, MouseStateHandle)>,
|
||||
all_providers_checkbox: MouseStateHandle,
|
||||
command_denylist_mouse_state_handles: Vec<MouseStateHandle>,
|
||||
command_denylist_tooltip_mouse_state_handles: Vec<MouseStateHandle>,
|
||||
mcp_denylist_dropdown: ViewHandle<FilterableDropdown<ExecutionProfileEditorViewAction>>,
|
||||
@@ -364,8 +364,8 @@ impl ExecutionProfileEditorView {
|
||||
command_denylist_editor,
|
||||
command_allowlist_editor,
|
||||
command_allowlist_mouse_state_handles: Vec::new(),
|
||||
orchestration_provider_switches: Vec::new(),
|
||||
all_providers_switch: Default::default(),
|
||||
orchestration_provider_checkboxes: Vec::new(),
|
||||
all_providers_checkbox: Default::default(),
|
||||
command_denylist_mouse_state_handles,
|
||||
command_denylist_tooltip_mouse_state_handles: profile_data
|
||||
.command_denylist
|
||||
@@ -555,16 +555,16 @@ impl ExecutionProfileEditorView {
|
||||
|
||||
self.command_allowlist_mouse_state_handles = current_permissions.command_allowlist.iter().map(|_| Default::default()).collect();
|
||||
let mut names: Vec<_> = LLMPreferences::as_ref(app).get_base_llm_choices_for_agent_mode(app)
|
||||
.filter(|model| !crate::ai::custom_model_routers::is_custom_router_id(model.id.as_str()))
|
||||
.filter(|model| !matches!(model.id.as_str(), "none" | "placeholder") && !crate::ai::custom_model_routers::is_custom_router_id(model.id.as_str()))
|
||||
.filter_map(|model| model.description.clone()).collect();
|
||||
names.extend(AISettings::as_ref(app).openai_providers.value().iter().map(|provider| provider.name.clone()));
|
||||
if let Some(included) = ¤t_permissions.orchestration_providers { names.extend(included.iter().cloned()); }
|
||||
names.retain(|name| !name.trim().is_empty());
|
||||
names.sort(); names.dedup();
|
||||
self.orchestration_provider_switches.retain(|(name, _)| names.contains(name));
|
||||
for name in names { if !self.orchestration_provider_switches.iter().any(|(existing, _)| existing == &name) {
|
||||
self.orchestration_provider_switches.push((name, Default::default()));
|
||||
self.orchestration_provider_checkboxes.retain(|(name, _)| names.contains(name));
|
||||
for name in names { if !self.orchestration_provider_checkboxes.iter().any(|(existing, _)| existing == &name) {
|
||||
self.orchestration_provider_checkboxes.push((name, Default::default()));
|
||||
}}
|
||||
self.orchestration_provider_switches.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
self.orchestration_provider_checkboxes.sort_by(|a, b| a.0.cmp(&b.0));
|
||||
|
||||
self.command_denylist_mouse_state_handles = current_permissions
|
||||
.command_denylist
|
||||
|
||||
@@ -9,6 +9,7 @@ use warpui::elements::{
|
||||
};
|
||||
use warpui::fonts::{Properties, Weight};
|
||||
use warpui::ui_components::components::{Coords, UiComponent, UiComponentStyles};
|
||||
use warpui::ui_components::text::Span;
|
||||
use warpui::{AppContext, Element, SingletonEntity, ViewHandle};
|
||||
|
||||
use super::{ExecutionProfileEditorView, ExecutionProfileEditorViewAction};
|
||||
@@ -627,23 +628,61 @@ fn render_command_allowlist_section(view: &ExecutionProfileEditorView, profile:
|
||||
.with_child(render_input_list(None, items, Some(&view.command_allowlist_editor), appearance)).finish()
|
||||
}
|
||||
|
||||
fn render_orchestration_providers(view: &ExecutionProfileEditorView, profile: &AIExecutionProfile, appearance: &Appearance) -> Box<dyn Element> {
|
||||
let mut column = Flex::column().with_child(create_section_header("Orchestration providers", "Only included providers can supply models for sub-agents and code reviews. All providers includes providers added later.", appearance));
|
||||
fn render_orchestration_providers(
|
||||
view: &ExecutionProfileEditorView,
|
||||
profile: &AIExecutionProfile,
|
||||
appearance: &Appearance,
|
||||
) -> Box<dyn Element> {
|
||||
let mut column = Flex::column().with_child(create_section_header(
|
||||
"Orchestration providers",
|
||||
"Choose which providers can supply models for sub-agents and code reviews.",
|
||||
appearance,
|
||||
));
|
||||
let all = profile.orchestration_providers.is_none();
|
||||
let names: Vec<_> = view.orchestration_provider_switches.iter().map(|(name, _)| name.clone()).collect();
|
||||
let selected = if all { Some(names.clone()) } else { None };
|
||||
let switch = appearance.ui_builder().switch(view.all_providers_switch.clone()).check(all).build()
|
||||
.on_click(move |ctx, _, _| ctx.dispatch_typed_action(ExecutionProfileEditorViewAction::SetOrchestrationProviders { providers: selected.clone() })).finish();
|
||||
column.add_child(Flex::row().with_main_axis_alignment(MainAxisAlignment::SpaceBetween).with_child(Text::new("All providers", appearance.ui_font_family(), 13.).with_color(appearance.theme().active_ui_text_color().into()).finish()).with_child(switch).finish());
|
||||
for (name, state) in &view.orchestration_provider_switches {
|
||||
let included = profile.includes_orchestration_provider(name);
|
||||
let mut next = profile.orchestration_providers.clone().unwrap_or_else(|| names.clone());
|
||||
if included { next.retain(|provider| provider != name); } else { next.push(name.clone()); }
|
||||
let switch = appearance.ui_builder().switch(state.clone()).check(included).build()
|
||||
.on_click(move |ctx, _, _| ctx.dispatch_typed_action(ExecutionProfileEditorViewAction::SetOrchestrationProviders { providers: Some(next.clone()) })).finish();
|
||||
column.add_child(Container::new(Flex::row().with_main_axis_alignment(MainAxisAlignment::SpaceBetween).with_child(Text::new(name.clone(), appearance.ui_font_family(), 13.).with_color(appearance.theme().active_ui_text_color().into()).finish()).with_child(switch).finish()).with_margin_top(8.).finish());
|
||||
let names: Vec<_> = view.orchestration_provider_checkboxes
|
||||
.iter()
|
||||
.map(|(name, _)| name.clone())
|
||||
.collect();
|
||||
let selected = if all { Some(names) } else { None };
|
||||
let checkbox = appearance.ui_builder()
|
||||
.checkbox(view.all_providers_checkbox.clone(), Some(14.))
|
||||
.with_label(Span::new("All providers", Default::default()))
|
||||
.check(all)
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(ExecutionProfileEditorViewAction::SetOrchestrationProviders {
|
||||
providers: selected.clone(),
|
||||
});
|
||||
})
|
||||
.finish();
|
||||
column.add_child(Container::new(checkbox).with_margin_top(12.).finish());
|
||||
|
||||
if !all {
|
||||
let mut providers = Flex::column();
|
||||
for (name, state) in &view.orchestration_provider_checkboxes {
|
||||
let included = profile.includes_orchestration_provider(name);
|
||||
let mut next = profile.orchestration_providers.clone().unwrap_or_default();
|
||||
if included {
|
||||
next.retain(|provider| provider != name);
|
||||
} else {
|
||||
next.push(name.clone());
|
||||
}
|
||||
let checkbox = appearance.ui_builder()
|
||||
.checkbox(state.clone(), Some(14.))
|
||||
.with_label(Span::new(name.clone(), Default::default()))
|
||||
.check(included)
|
||||
.build()
|
||||
.on_click(move |ctx, _, _| {
|
||||
ctx.dispatch_typed_action(ExecutionProfileEditorViewAction::SetOrchestrationProviders {
|
||||
providers: Some(next.clone()),
|
||||
});
|
||||
})
|
||||
.finish();
|
||||
providers.add_child(Container::new(checkbox).with_margin_top(12.).finish());
|
||||
}
|
||||
column.add_child(Container::new(providers.finish()).with_margin_left(24.).finish());
|
||||
}
|
||||
Container::new(column.finish()).with_margin_top(16.).finish()
|
||||
Container::new(column.finish()).with_margin_top(16.).with_margin_bottom(12.).finish()
|
||||
}
|
||||
|
||||
fn display_mcp_name(uuid: &Uuid, app: &AppContext) -> String {
|
||||
|
||||
@@ -27437,15 +27437,12 @@ impl TypedActionView for TerminalView {
|
||||
ToggleAutoexecuteMode => {
|
||||
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles, ctx| {
|
||||
let profile = profiles.active_profile(Some(self.view_id), ctx);
|
||||
use crate::ai::execution_profiles::PermissionMode;
|
||||
let next = match profile.data().permission_mode {
|
||||
crate::ai::execution_profiles::PermissionMode::ReadOnly => return,
|
||||
crate::ai::execution_profiles::PermissionMode::AskFirst => {
|
||||
crate::ai::execution_profiles::PermissionMode::GalaxyDecides
|
||||
}
|
||||
crate::ai::execution_profiles::PermissionMode::GalaxyDecides
|
||||
| crate::ai::execution_profiles::PermissionMode::AllowAll => {
|
||||
crate::ai::execution_profiles::PermissionMode::AskFirst
|
||||
}
|
||||
PermissionMode::ReadOnly => PermissionMode::AskFirst,
|
||||
PermissionMode::AskFirst => PermissionMode::GalaxyDecides,
|
||||
PermissionMode::GalaxyDecides => PermissionMode::AllowAll,
|
||||
PermissionMode::AllowAll => PermissionMode::ReadOnly,
|
||||
};
|
||||
profiles.set_permission_mode(*profile.id(), next, ctx);
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user