first pass of merging in warp (doesn't build)

This commit is contained in:
Ryan Ward
2026-07-01 16:08:58 -05:00
parent 2f64909469
commit 4770ac06b5
3662 changed files with 414574 additions and 89772 deletions
+335 -47
View File
@@ -1,46 +1,53 @@
use std::path::{Path, PathBuf};
use ai::api_keys::{ApiKeyManager, ApiKeyManagerEvent};
use itertools::Itertools;
use regex::Regex;
use thousands::Separable;
use galaxy_core::ui::theme::color::internal_colors;
use warpui::elements::{
Align, Border, ChildView, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CrossAxisAlignment, Expanded, Flex, Highlight, MouseStateHandle, ParentElement,
PartialClickableElement, ScrollbarWidth, Text,
};
use warpui::fonts::Properties;
use warpui::platform::Cursor;
use warpui::ui_components::slider::SliderStateHandle;
use warpui::ui_components::switch::SwitchStateHandle;
use warpui::{
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use crate::ai::blocklist::BlocklistAIPermissions;
use crate::ai::execution_profiles::model_menu_items::available_model_menu_items;
use crate::ai::execution_profiles::profiles::{
AIExecutionProfilesModel, AIExecutionProfilesModelEvent, ClientProfileId,
};
use crate::ai::execution_profiles::{
profiles::{AIExecutionProfilesModel, AIExecutionProfilesModelEvent, ClientProfileId},
AIExecutionProfile, ActionPermission, WriteToPtyPermission,
AIExecutionProfile, AIExecutionProfileAppExt as _, ActionPermission, RunAgentsPermission,
WriteToPtyPermission,
};
use crate::ai::llms::{
DisableReason, LLMContextWindow, LLMId, LLMInfo, LLMPreferences, LLMPreferencesEvent,
};
use crate::ai::llms::{DisableReason, LLMId, LLMInfo, LLMPreferences, LLMPreferencesEvent};
use crate::ai::paths::host_native_absolute_path;
use crate::editor::InteractionState;
use crate::editor::{EditorView, Event as EditorEvent, SingleLineEditorOptions};
use crate::editor::{
EditorView, Event as EditorEvent, InteractionState, SingleLineEditorOptions, TextOptions,
};
use crate::pane_group::focus_state::PaneFocusHandle;
use crate::settings::{AISettings, AgentModeCommandExecutionPredicate};
use crate::pane_group::pane::view;
use crate::pane_group::{BackingView, PaneConfiguration, PaneEvent};
use crate::settings::{AISettings, AISettingsChangedEvent, AgentModeCommandExecutionPredicate};
use crate::ui_components::icons::Icon;
use crate::view_components::action_button::{ActionButton, DangerSecondaryTheme};
use crate::view_components::dropdown::DropdownAction;
use crate::view_components::{
action_button::{ActionButton, DangerSecondaryTheme},
Dropdown, DropdownItem, FilterableDropdown, SubmittableTextInput, SubmittableTextInputEvent,
};
use crate::workspace::WorkspaceAction;
use crate::workspaces::user_workspaces::UserWorkspacesEvent;
use crate::TemplatableMCPServerManager;
use crate::UserWorkspaces;
use crate::{
pane_group::{pane::view, BackingView, PaneConfiguration, PaneEvent},
Appearance,
};
use ai::api_keys::{ApiKeyManager, ApiKeyManagerEvent};
use galaxy_core::ui::theme::color::internal_colors;
use galaxyui::fonts::Properties;
use galaxyui::platform::Cursor;
use galaxyui::ui_components::switch::SwitchStateHandle;
use itertools::Itertools;
use regex::Regex;
use galaxyui::{
elements::{
Align, Border, ChildView, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox,
Container, CrossAxisAlignment, Expanded, Flex, Highlight, MouseStateHandle, ParentElement,
PartialClickableElement, ScrollbarWidth, Text,
},
AppContext, Element, Entity, ModelHandle, SingletonEntity, TypedActionView, View, ViewContext,
ViewHandle,
};
use std::path::{Path, PathBuf};
use crate::{Appearance, TemplatableMCPServerManager, UserWorkspaces};
const MODEL_MENU_WIDTH: f32 = 480.;
@@ -119,10 +126,10 @@ struct TooltipMouseStateHandles {
write_to_pty_tooltip_mouse_state: MouseStateHandle,
computer_use_tooltip_mouse_state: MouseStateHandle,
ask_user_question_tooltip_mouse_state: MouseStateHandle,
run_agents_tooltip_mouse_state: MouseStateHandle,
call_mcp_servers_tooltip_mouse_state: MouseStateHandle,
// Separate mouse state handles for text input editors (for workspace override tooltips)
command_allowlist_editor_tooltip_mouse_state: MouseStateHandle,
command_denylist_editor_tooltip_mouse_state: MouseStateHandle,
directory_allowlist_editor_tooltip_mouse_state: MouseStateHandle,
mcp_allowlist_editor_tooltip_mouse_state: MouseStateHandle,
mcp_denylist_editor_tooltip_mouse_state: MouseStateHandle,
@@ -145,6 +152,15 @@ pub enum ExecutionProfileEditorViewAction {
SetBaseModel {
id: LLMId,
},
/// Fired continuously while the user drags the context window slider.
ContextWindowSliderDragged {
value: u32,
},
/// Fired when the user commits a new context window value (slider drop,
/// track click, or input box commit).
SetContextWindowSize {
value: u32,
},
SetCodingModel {
id: LLMId,
},
@@ -177,6 +193,9 @@ pub enum ExecutionProfileEditorViewAction {
SetAskUserQuestion {
permission: super::AskUserQuestionPermission,
},
SetRunAgents {
permission: RunAgentsPermission,
},
AddToCommandAllowlist {
predicate: AgentModeCommandExecutionPredicate,
},
@@ -222,6 +241,10 @@ pub struct ExecutionProfileEditorView {
focus_handle: Option<PaneFocusHandle>,
clipped_scroll_state: ClippedScrollStateHandle,
base_model_dropdown: ViewHandle<FilterableDropdown<ExecutionProfileEditorViewAction>>,
context_window_slider_state: SliderStateHandle,
context_window_editor: ViewHandle<EditorView>,
last_synced_context_window_editor_value: Option<u32>,
dragged_context_window_value: Option<u32>,
coding_model_dropdown: ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
full_terminal_use_model_dropdown:
ViewHandle<FilterableDropdown<ExecutionProfileEditorViewAction>>,
@@ -233,11 +256,13 @@ pub struct ExecutionProfileEditorView {
call_mcp_servers_dropdown: ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
computer_use_dropdown: ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
ask_user_question_dropdown: ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
run_agents_dropdown: ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
command_allowlist_editor: ViewHandle<SubmittableTextInput>,
command_denylist_editor: ViewHandle<SubmittableTextInput>,
directory_allowlist_editor: ViewHandle<SubmittableTextInput>,
command_allowlist_mouse_state_handles: Vec<MouseStateHandle>,
command_denylist_mouse_state_handles: Vec<MouseStateHandle>,
command_denylist_tooltip_mouse_state_handles: Vec<MouseStateHandle>,
directory_allowlist_mouse_state_handles: Vec<MouseStateHandle>,
mcp_allowlist_dropdown: ViewHandle<FilterableDropdown<ExecutionProfileEditorViewAction>>,
mcp_allowlist_mouse_state_handles: Vec<MouseStateHandle>,
@@ -451,6 +476,34 @@ impl ExecutionProfileEditorView {
dropdown
});
let run_agents_dropdown = ctx.add_typed_action_view(|ctx| {
let mut dropdown = Dropdown::new(ctx);
dropdown.set_items(
vec![
DropdownItem::new(
"Never",
ExecutionProfileEditorViewAction::SetRunAgents {
permission: RunAgentsPermission::NeverAllow,
},
),
DropdownItem::new(
"Always allow",
ExecutionProfileEditorViewAction::SetRunAgents {
permission: RunAgentsPermission::AlwaysAllow,
},
),
DropdownItem::new(
"Always ask",
ExecutionProfileEditorViewAction::SetRunAgents {
permission: RunAgentsPermission::AlwaysAsk,
},
),
],
ctx,
);
dropdown
});
let mcp_allowlist_dropdown = ctx.add_typed_action_view(|ctx| {
let mut dropdown = FilterableDropdown::new(ctx);
dropdown.set_menu_header_to_static("Select MCP servers");
@@ -484,6 +537,27 @@ impl ExecutionProfileEditorView {
dropdown.set_menu_width(MODEL_MENU_WIDTH, ctx);
dropdown
});
// Initialize the context window editor buffer with the profile's
// persisted limit (or the active model's max as a sensible default).
// The slider's current position is derived from the profile on each
// render, so no local Cell is needed.
let initial_context_window_value = initial_context_window_display_value(&profile_data, ctx);
let context_window_slider_state = SliderStateHandle::default();
let context_window_editor = ctx.add_typed_action_view(|ctx| {
let options = SingleLineEditorOptions {
text: TextOptions {
font_size_override: Some(Appearance::as_ref(ctx).ui_font_size()),
..Default::default()
},
..Default::default()
};
let mut editor = EditorView::single_line(options, ctx);
editor.set_buffer_text(&initial_context_window_value.separate_with_commas(), ctx);
editor
});
let last_synced_context_window_editor_value = Some(initial_context_window_value);
let coding_model_dropdown = ctx.add_typed_action_view(|ctx| {
let mut dropdown = Dropdown::new(ctx);
dropdown.set_top_bar_max_width(MODEL_MENU_WIDTH);
@@ -578,6 +652,10 @@ impl ExecutionProfileEditorView {
focus_handle: None,
clipped_scroll_state: Default::default(),
base_model_dropdown,
context_window_slider_state,
context_window_editor,
last_synced_context_window_editor_value,
dragged_context_window_value: None,
coding_model_dropdown,
full_terminal_use_model_dropdown,
computer_use_model_dropdown,
@@ -588,11 +666,17 @@ impl ExecutionProfileEditorView {
call_mcp_servers_dropdown,
computer_use_dropdown,
ask_user_question_dropdown,
run_agents_dropdown,
command_allowlist_editor,
command_denylist_editor,
directory_allowlist_editor,
command_allowlist_mouse_state_handles,
command_denylist_mouse_state_handles,
command_denylist_tooltip_mouse_state_handles: profile_data
.command_denylist
.iter()
.map(|_| Default::default())
.collect(),
directory_allowlist_mouse_state_handles,
mcp_allowlist_dropdown,
mcp_allowlist_mouse_state_handles,
@@ -612,6 +696,10 @@ impl ExecutionProfileEditorView {
}
});
ctx.subscribe_to_view(&view.context_window_editor, |view, _, event, ctx| {
view.handle_context_window_editor_event(event, ctx);
});
ctx.subscribe_to_view(&view.command_allowlist_editor, |view, _, event, ctx| {
if let SubmittableTextInputEvent::Submit(s) = event {
let predicate = match AgentModeCommandExecutionPredicate::new_regex(s) {
@@ -671,7 +759,7 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&me.base_model_dropdown,
current_permissions.base_model.clone(),
|prefs| prefs.get_base_llm_choices_for_agent_mode().collect_vec(),
|prefs, app| prefs.get_base_llm_choices_for_agent_mode(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetBaseModel { id },
|prefs| prefs.get_default_base_model().id.clone(),
&me.upgrade_footer_mouse_state,
@@ -685,7 +773,7 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&me.full_terminal_use_model_dropdown,
current_permissions.cli_agent_model.clone(),
|prefs| prefs.get_cli_agent_llm_choices().collect_vec(),
|prefs, app| prefs.get_cli_agent_llm_choices(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetFullTerminalUseModel { id },
|prefs| prefs.get_default_cli_agent_model().id.clone(),
&me.upgrade_footer_mouse_state,
@@ -694,23 +782,25 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&me.computer_use_model_dropdown,
current_permissions.computer_use_model.clone(),
|prefs| prefs.get_computer_use_llm_choices().collect_vec(),
|prefs, _| prefs.get_computer_use_llm_choices().collect_vec(),
|id| ExecutionProfileEditorViewAction::SetComputerUseModel { id },
|prefs| prefs.get_default_computer_use_model().id.clone(),
&me.upgrade_footer_mouse_state,
ctx,
);
me.sync_context_window_editor(ctx, false);
}
LLMPreferencesEvent::UpdatedActiveAgentModeLLM => {
Self::refresh_filterable_model_dropdown(
&me.base_model_dropdown,
current_permissions.base_model.clone(),
|prefs| prefs.get_base_llm_choices_for_agent_mode().collect_vec(),
|prefs, app| prefs.get_base_llm_choices_for_agent_mode(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetBaseModel { id },
|prefs| prefs.get_default_base_model().id.clone(),
&me.upgrade_footer_mouse_state,
ctx,
);
me.sync_context_window_editor(ctx, false);
}
LLMPreferencesEvent::UpdatedActiveCodingLLM => {
Self::refresh_coding_model_dropdown(
@@ -732,7 +822,7 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&me.base_model_dropdown,
current_permissions.base_model.clone(),
|prefs| prefs.get_base_llm_choices_for_agent_mode().collect_vec(),
|prefs, app| prefs.get_base_llm_choices_for_agent_mode(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetBaseModel { id },
|prefs| prefs.get_default_base_model().id.clone(),
&me.upgrade_footer_mouse_state,
@@ -743,6 +833,7 @@ impl ExecutionProfileEditorView {
current_permissions.coding_model.clone(),
ctx,
);
me.sync_context_window_editor(ctx, false);
ctx.notify();
},
);
@@ -761,6 +852,15 @@ impl ExecutionProfileEditorView {
ctx.subscribe_to_model(&workspace, |me, workspace, event, ctx| {
if let UserWorkspacesEvent::TeamsChanged = event {
Self::update_all_editor_interaction_states(me, workspace, ctx);
me.update_mouse_state_handles(ctx);
ctx.notify();
}
});
ctx.subscribe_to_model(&AISettings::handle(ctx), |me, _, event, ctx| {
if let AISettingsChangedEvent::IsAnyAIEnabled { .. } = event {
let workspace = UserWorkspaces::handle(ctx);
Self::update_all_editor_interaction_states(me, workspace, ctx);
me.sync_context_window_editor(ctx, true);
ctx.notify();
}
});
@@ -795,6 +895,12 @@ impl ExecutionProfileEditorView {
.map(|_| Default::default())
.collect();
self.command_denylist_tooltip_mouse_state_handles = current_permissions
.command_denylist
.iter()
.map(|_| Default::default())
.collect();
self.directory_allowlist_mouse_state_handles = current_permissions
.directory_allowlist
.iter()
@@ -826,12 +932,13 @@ impl ExecutionProfileEditorView {
let computer_use_disabled = !ai_settings.is_computer_use_permissions_editable(ctx);
let ask_user_question_disabled =
!ai_settings.is_ask_user_question_permissions_editable(ctx);
let run_agents_disabled = !ai_settings.is_run_agents_permissions_editable(ctx);
let mcp_disabled = !ai_settings.is_mcp_permission_editable(ctx);
Self::refresh_filterable_model_dropdown(
&self.base_model_dropdown,
current_permissions.base_model.clone(),
|prefs| prefs.get_base_llm_choices_for_agent_mode().collect_vec(),
|prefs, app| prefs.get_base_llm_choices_for_agent_mode(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetBaseModel { id },
|prefs| prefs.get_default_base_model().id.clone(),
&self.upgrade_footer_mouse_state,
@@ -845,7 +952,7 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&self.full_terminal_use_model_dropdown,
current_permissions.cli_agent_model.clone(),
|prefs| prefs.get_cli_agent_llm_choices().collect_vec(),
|prefs, app| prefs.get_cli_agent_llm_choices(app).collect_vec(),
|id| ExecutionProfileEditorViewAction::SetFullTerminalUseModel { id },
|prefs| prefs.get_default_cli_agent_model().id.clone(),
&self.upgrade_footer_mouse_state,
@@ -854,7 +961,7 @@ impl ExecutionProfileEditorView {
Self::refresh_filterable_model_dropdown(
&self.computer_use_model_dropdown,
current_permissions.computer_use_model.clone(),
|prefs| prefs.get_computer_use_llm_choices().collect_vec(),
|prefs, _| prefs.get_computer_use_llm_choices().collect_vec(),
|id| ExecutionProfileEditorViewAction::SetComputerUseModel { id },
|prefs| prefs.get_default_computer_use_model().id.clone(),
&self.upgrade_footer_mouse_state,
@@ -903,6 +1010,12 @@ impl ExecutionProfileEditorView {
ask_user_question_disabled,
ctx,
);
Self::refresh_run_agents_dropdown_menu(
&self.run_agents_dropdown,
current_permissions.run_agents,
run_agents_disabled,
ctx,
);
Self::refresh_mcp_dropdown(
&self.mcp_allowlist_dropdown,
|uuid| ExecutionProfileEditorViewAction::AddToMCPAllowlist { id: uuid },
@@ -919,6 +1032,7 @@ impl ExecutionProfileEditorView {
);
Self::update_profile_name_editor(&self.profile_name_editor, &current_permissions, ctx);
self.sync_context_window_editor(ctx, false);
}
fn refresh_execution_profile_dropdown_menu(
@@ -1022,6 +1136,31 @@ impl ExecutionProfileEditorView {
ctx.notify();
}
fn refresh_run_agents_dropdown_menu(
menu: &ViewHandle<Dropdown<ExecutionProfileEditorViewAction>>,
current_permission: RunAgentsPermission,
disabled: bool,
ctx: &mut ViewContext<Self>,
) {
menu.update(ctx, |menu, ctx| {
if !disabled {
menu.set_enabled(ctx);
} else {
menu.set_disabled(ctx);
}
let active = match current_permission {
RunAgentsPermission::NeverAllow | RunAgentsPermission::Unknown => 0,
RunAgentsPermission::AlwaysAllow => 1,
RunAgentsPermission::AlwaysAsk => 2,
};
menu.set_selected_by_index(active, ctx);
ctx.notify();
});
ctx.notify();
}
fn refresh_filterable_model_dropdown<G, A, D>(
menu: &ViewHandle<FilterableDropdown<ExecutionProfileEditorViewAction>>,
profile_model: Option<LLMId>,
@@ -1031,7 +1170,7 @@ impl ExecutionProfileEditorView {
upgrade_mouse_state: &MouseStateHandle,
ctx: &mut ViewContext<Self>,
) where
G: FnOnce(&LLMPreferences) -> Vec<&LLMInfo>,
G: for<'a> FnOnce(&'a LLMPreferences, &AppContext) -> Vec<&'a LLMInfo>,
A: Fn(LLMId) -> ExecutionProfileEditorViewAction,
D: FnOnce(&LLMPreferences) -> LLMId,
{
@@ -1046,7 +1185,7 @@ impl ExecutionProfileEditorView {
let llm_prefs = LLMPreferences::handle(ctx);
let llm_prefs = llm_prefs.as_ref(ctx);
let choices = get_choices(llm_prefs);
let choices = get_choices(llm_prefs, ctx);
let has_upgrade_gated_models = choices
.iter()
@@ -1054,7 +1193,7 @@ impl ExecutionProfileEditorView {
let items = available_model_menu_items(
choices,
|llm| create_action(llm.id.clone()).into(),
|llm| DropdownAction::select_action_and_close(create_action(llm.id.clone())),
None,
None,
false,
@@ -1097,13 +1236,15 @@ impl ExecutionProfileEditorView {
}
let choices = LLMPreferences::as_ref(ctx)
.get_coding_llm_choices()
.get_coding_llm_choices(ctx)
.collect_vec();
let items = available_model_menu_items(
choices,
|llm| {
ExecutionProfileEditorViewAction::SetCodingModel { id: llm.id.clone() }.into()
DropdownAction::select_action_and_close(
ExecutionProfileEditorViewAction::SetCodingModel { id: llm.id.clone() },
)
},
None,
None,
@@ -1225,7 +1366,7 @@ impl ExecutionProfileEditorView {
Self::update_editor_interaction_state(
view.command_denylist_editor.as_ref(ctx).editor().clone(),
is_any_ai_enabled && !ai_autonomy_settings.has_override_for_execute_commands_denylist(),
is_any_ai_enabled,
ctx,
);
@@ -1256,10 +1397,112 @@ impl ExecutionProfileEditorView {
}
});
}
fn configurable_context_window(&self, app: &AppContext) -> Option<LLMContextWindow> {
let profile =
BlocklistAIPermissions::as_ref(app).permissions_profile_for_id(app, self.profile_id);
profile.configurable_context_window(app)
}
fn current_context_window_display_value(&self, app: &AppContext) -> Option<u32> {
let profile =
BlocklistAIPermissions::as_ref(app).permissions_profile_for_id(app, self.profile_id);
profile.context_window_display_value(app)
}
fn handle_context_window_editor_event(
&mut self,
event: &EditorEvent,
ctx: &mut ViewContext<Self>,
) {
match event {
EditorEvent::Blurred | EditorEvent::Enter => {
if !AISettings::as_ref(ctx).is_any_ai_enabled(ctx) {
self.sync_context_window_editor(ctx, true);
return;
}
let Some(cw) = self.configurable_context_window(ctx) else {
return;
};
let buffer_text = self.context_window_editor.as_ref(ctx).buffer_text(ctx);
let cleaned: String = buffer_text
.chars()
.filter(|c| !c.is_whitespace() && *c != ',')
.collect();
if let Ok(parsed) = cleaned.parse::<u32>() {
let clamped = parsed.clamp(cw.min, cw.max);
if Some(clamped) != self.current_context_window_display_value(ctx) {
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles_model, ctx| {
profiles_model.set_context_window_limit(
self.profile_id,
Some(clamped),
ctx,
);
});
}
}
self.sync_context_window_editor(ctx, true);
ctx.notify();
}
_ => {}
}
}
fn sync_context_window_editor(&mut self, ctx: &mut ViewContext<Self>, force: bool) {
self.dragged_context_window_value = None;
let Some(value) = self.current_context_window_display_value(ctx) else {
self.last_synced_context_window_editor_value = None;
self.context_window_slider_state.reset_offset();
ctx.notify();
return;
};
let formatted = value.separate_with_commas();
let should_update = if force {
true
} else {
match self.last_synced_context_window_editor_value {
Some(last_value) => {
self.context_window_editor.as_ref(ctx).buffer_text(ctx)
== last_value.separate_with_commas()
}
None => true,
}
};
if should_update {
self.context_window_editor.update(ctx, |editor, ctx| {
if editor.buffer_text(ctx) != formatted {
editor.system_reset_buffer_text(&formatted, ctx);
}
});
self.last_synced_context_window_editor_value = Some(value);
self.context_window_slider_state.reset_offset();
ctx.notify();
}
}
}
fn initial_context_window_display_value(
profile_data: &AIExecutionProfile,
app: &AppContext,
) -> u32 {
profile_data
.context_window_display_value(app)
.unwrap_or_else(|| {
LLMPreferences::as_ref(app)
.get_default_base_model()
.context_window
.default_max
})
}
mod ui_helpers;
#[cfg(test)]
#[path = "mod_tests.rs"]
mod tests;
impl View for ExecutionProfileEditorView {
fn ui_name() -> &'static str {
"ExecutionProfileEditorView"
@@ -1278,7 +1521,7 @@ impl View for ExecutionProfileEditorView {
&self.profile_name_editor,
profile_data.is_default_profile,
))
.with_child(render_models_section(appearance, self))
.with_child(render_models_section(appearance, self, app))
.with_child(render_permissions_section(
appearance,
self,
@@ -1323,9 +1566,48 @@ impl TypedActionView for ExecutionProfileEditorView {
ctx.emit(ExecutionProfileEditorViewEvent::Pane(PaneEvent::Close));
}
ExecutionProfileEditorViewAction::SetBaseModel { id } => {
// Changing the base model resets any persisted context window
// override — the new model may have a different range (or not
// be configurable at all). The user can pick a new value for
// the new model if they want one.
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles_model, ctx| {
profiles_model.set_base_model(self.profile_id, Some(id.clone()), ctx);
profiles_model.set_context_window_limit(self.profile_id, None, ctx);
});
self.sync_context_window_editor(ctx, true);
ctx.notify();
}
ExecutionProfileEditorViewAction::ContextWindowSliderDragged { value } => {
if !AISettings::as_ref(ctx).is_any_ai_enabled(ctx) {
self.sync_context_window_editor(ctx, true);
return;
}
// Transient drag update: reflect the current slider position
// in the input box without persisting to the profile yet.
// Persistence happens on SetContextWindowSize (drop / commit).
if self.configurable_context_window(ctx).is_some() {
self.dragged_context_window_value = Some(*value);
let formatted = value.separate_with_commas();
self.context_window_editor.update(ctx, |editor, ctx| {
editor.system_reset_buffer_text(&formatted, ctx);
});
ctx.notify();
}
}
ExecutionProfileEditorViewAction::SetContextWindowSize { value } => {
self.dragged_context_window_value = None;
if !AISettings::as_ref(ctx).is_any_ai_enabled(ctx) {
self.sync_context_window_editor(ctx, true);
return;
}
let Some(cw) = self.configurable_context_window(ctx) else {
return;
};
let clamped = (*value).clamp(cw.min, cw.max);
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles_model, ctx| {
profiles_model.set_context_window_limit(self.profile_id, Some(clamped), ctx);
});
self.sync_context_window_editor(ctx, true);
ctx.notify();
}
ExecutionProfileEditorViewAction::SetCodingModel { id } => {
@@ -1388,6 +1670,12 @@ impl TypedActionView for ExecutionProfileEditorView {
});
ctx.notify();
}
ExecutionProfileEditorViewAction::SetRunAgents { permission } => {
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles_model, ctx| {
profiles_model.set_run_agents(self.profile_id, *permission, ctx);
});
ctx.notify();
}
ExecutionProfileEditorViewAction::AddToCommandAllowlist { predicate } => {
AIExecutionProfilesModel::handle(ctx).update(ctx, |profiles_model, ctx| {
profiles_model.add_to_command_allowlist(self.profile_id, predicate, ctx);