diff --git a/app/src/ai/agent_management/agent_type_selector.rs b/app/src/ai/agent_management/agent_type_selector.rs index 71504d04..bac764dc 100644 --- a/app/src/ai/agent_management/agent_type_selector.rs +++ b/app/src/ai/agent_management/agent_type_selector.rs @@ -7,8 +7,8 @@ use galaxy_core::ui::color::blend::Blend; use galaxy_core::ui::theme::color::internal_colors; use galaxyui::elements::{ Align, Border, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment, Dismiss, - DropShadow, Element, Flex, Hoverable, MainAxisAlignment, MainAxisSize, MouseStateHandle, - ParentElement, Radius, Shrinkable, Text, + DropShadow, Element, Expanded, Flex, Hoverable, MainAxisAlignment, MainAxisSize, + MouseStateHandle, ParentElement, Radius, Shrinkable, Text, }; use galaxyui::fonts::{Properties, Weight}; use galaxyui::keymap::{FixedBinding, Keystroke}; @@ -22,7 +22,7 @@ use crate::appearance::Appearance; use crate::ui_components::icons::Icon; // Modal dimensions based on Figma design. -const MODAL_WIDTH: f32 = 440.; +const MODAL_WIDTH: f32 = 680.; const DIALOG_CORNER_RADIUS: f32 = 8.; const HEADER_PADDING_TOP: f32 = 24.; @@ -40,6 +40,7 @@ const OPTIONS_VERTICAL_GAP: f32 = 8.; const AVATAR_SIZE: f32 = 48.; const AVATAR_ICON_SIZE: f32 = 24.; +const OPTION_HEIGHT: f32 = 136.; const TITLE_FONT_SIZE: f32 = 16.; const OPTION_TITLE_FONT_SIZE: f32 = 14.; @@ -292,21 +293,25 @@ impl AgentTypeSelector { ) .finish(); - Container::new( - Flex::row() - .with_cross_axis_alignment(CrossAxisAlignment::Center) - .with_spacing(OPTION_GAP) - .with_child(avatar) - .with_child(Shrinkable::new(1., text_content).finish()) - .finish(), + ConstrainedBox::new( + Container::new( + Flex::row() + .with_cross_axis_alignment(CrossAxisAlignment::Center) + .with_spacing(OPTION_GAP) + .with_child(avatar) + .with_child(Shrinkable::new(1., text_content).finish()) + .finish(), + ) + .with_padding_left(OPTION_PADDING_HORIZONTAL) + .with_padding_right(OPTION_PADDING_HORIZONTAL) + .with_padding_top(OPTION_PADDING_VERTICAL) + .with_padding_bottom(OPTION_PADDING_VERTICAL) + .with_corner_radius(CornerRadius::with_all(Radius::Pixels(OPTION_CORNER_RADIUS))) + .with_border(Border::all(1.).with_border_color(border_color)) + .with_background(background) + .finish(), ) - .with_padding_left(OPTION_PADDING_HORIZONTAL) - .with_padding_right(OPTION_PADDING_HORIZONTAL) - .with_padding_top(OPTION_PADDING_VERTICAL) - .with_padding_bottom(OPTION_PADDING_VERTICAL) - .with_corner_radius(CornerRadius::with_all(Radius::Pixels(OPTION_CORNER_RADIUS))) - .with_border(Border::all(1.).with_border_color(border_color)) - .with_background(background) + .with_height(OPTION_HEIGHT) .finish() }) .with_cursor(Cursor::PointingHand) @@ -353,11 +358,11 @@ impl AgentTypeSelector { appearance, ); - let options = Flex::column() + let options = Flex::row() .with_cross_axis_alignment(CrossAxisAlignment::Stretch) .with_spacing(OPTIONS_VERTICAL_GAP) - .with_child(cloud_agent_option) - .with_child(local_agent_option) + .with_child(Expanded::new(1., cloud_agent_option).finish()) + .with_child(Expanded::new(1., local_agent_option).finish()) .finish(); let body = Container::new(options) diff --git a/app/src/ai/execution_profiles/model_menu_items.rs b/app/src/ai/execution_profiles/model_menu_items.rs index f21cc55e..7ce85e56 100644 --- a/app/src/ai/execution_profiles/model_menu_items.rs +++ b/app/src/ai/execution_profiles/model_menu_items.rs @@ -93,6 +93,17 @@ fn make_item_fields( Icon::Aws } else if is_custom_router { Icon::Dataflow + } else if matches!(llm.provider, crate::ai::llms::LLMProvider::Acp) { + let model_id = llm.id.as_str().to_ascii_lowercase(); + if model_id.contains("claude") { + Icon::ClaudeLogo + } else if model_id.contains("gemini") { + Icon::GeminiLogo + } else if model_id.contains("codex") { + Icon::OpenAILogo + } else { + Icon::Terminal + } } else { llm.provider.icon().unwrap_or(Icon::Oz) }; diff --git a/app/src/settings_view/provider_setup_modal.rs b/app/src/settings_view/provider_setup_modal.rs index ece6242a..6ab20451 100644 --- a/app/src/settings_view/provider_setup_modal.rs +++ b/app/src/settings_view/provider_setup_modal.rs @@ -1,3 +1,5 @@ +use galaxy_cli::agent::Harness; +use galaxy_core::ui::theme::Fill; use galaxyui::clipboard::ClipboardContent; use galaxyui::elements::{ Border, ChildView, ClippedScrollStateHandle, ClippedScrollable, ConstrainedBox, Container, @@ -12,9 +14,11 @@ use galaxyui::ui_components::switch::SwitchStateHandle; use galaxyui::{ AppContext, Element, Entity, SingletonEntity, TypedActionView, View, ViewContext, ViewHandle, }; +use pathfinder_color::ColorU; #[cfg(not(target_family = "wasm"))] use crate::ai::chatgpt_auth::{ChatGPTAuthModel, ChatGPTAuthModelEvent, ChatGPTAuthState}; +use crate::ai::harness_display; use crate::ai::llms::{merge_discovered_provider_models, LLMPreferences}; use crate::appearance::Appearance; use crate::editor::{ @@ -34,6 +38,7 @@ const MODAL_WIDTH: f32 = 900.; const MODAL_HEIGHT: f32 = 700.; const BODY_HEIGHT: f32 = 630.; const INPUT_FONT_SIZE: f32 = 12.; +const MODEL_LOGO_SIZE: f32 = 20.; #[derive(Clone, Copy, Debug, PartialEq, Eq)] enum ProviderSetupStep { @@ -1539,6 +1544,49 @@ impl ProviderSetupModalBody { .finish() } + fn model_logo(&self) -> (Icon, ColorU) { + match self.provider_type { + ProviderSetupProviderType::ChatGPTSubscription + | ProviderSetupProviderType::OpenAICompatible => { + (Icon::OpenAILogo, crate::terminal::cli_agent::OPENAI_COLOR) + } + ProviderSetupProviderType::Anthropic => { + (Icon::ClaudeLogo, crate::ai::blocklist::CLAUDE_ORANGE) + } + ProviderSetupProviderType::Gemini | ProviderSetupProviderType::VertexAI => { + (Icon::GeminiLogo, crate::terminal::cli_agent::GEMINI_BLUE) + } + ProviderSetupProviderType::Bedrock => { + (Icon::BedrockLogo, ColorU::new(255, 153, 0, 255)) + } + ProviderSetupProviderType::Acp => { + let agent = self.draft_acp.agent_id.to_ascii_lowercase(); + let harness = if agent.contains("claude") { + Harness::Claude + } else if agent.contains("gemini") { + Harness::Gemini + } else if agent.contains("codex") { + Harness::Codex + } else { + Harness::Unknown + }; + ( + harness_display::icon_for(harness), + harness_display::brand_color(harness) + .unwrap_or(ColorU::new(128, 128, 128, 255)), + ) + } + } + } + + fn render_model_logo(&self) -> Box { + let (icon, color) = self.model_logo(); + ConstrainedBox::new(icon.to_galaxyui_icon(Fill::Solid(color)).finish()) + .with_width(MODEL_LOGO_SIZE) + .with_height(MODEL_LOGO_SIZE) + .finish() + } + fn render_model_table_header(&self, appearance: &Appearance) -> Box { let header = |label: &str| { Text::new(label.to_owned(), appearance.ui_font_family(), 11.) @@ -1630,6 +1678,12 @@ impl ProviderSetupModalBody { .finish(), ) .finish(); + let model_info = Flex::row() + .with_cross_axis_alignment(CrossAxisAlignment::Center) + .with_spacing(10.) + .with_child(self.render_model_logo()) + .with_child(model_info) + .finish(); let context_input = appearance .ui_builder() @@ -1689,26 +1743,33 @@ impl ProviderSetupModalBody { .models .iter() .map(|model| { - Flex::column() - .with_spacing(2.) + Flex::row() + .with_cross_axis_alignment(CrossAxisAlignment::Center) + .with_spacing(10.) + .with_child(self.render_model_logo()) .with_child( - Text::new( - model.display_name.clone(), - appearance.ui_font_family(), - INPUT_FONT_SIZE, - ) - .with_color(appearance.theme().active_ui_text_color().into()) - .finish(), - ) - .with_child( - Text::new( - model.model_id.clone(), - appearance.monospace_font_family(), - 10., - ) - .with_color(appearance.theme().nonactive_ui_text_color().into()) - .soft_wrap(true) - .finish(), + Flex::column() + .with_spacing(2.) + .with_child( + Text::new( + model.display_name.clone(), + appearance.ui_font_family(), + INPUT_FONT_SIZE, + ) + .with_color(appearance.theme().active_ui_text_color().into()) + .finish(), + ) + .with_child( + Text::new( + model.model_id.clone(), + appearance.monospace_font_family(), + 10., + ) + .with_color(appearance.theme().nonactive_ui_text_color().into()) + .soft_wrap(true) + .finish(), + ) + .finish(), ) .finish() }) @@ -1747,31 +1808,38 @@ impl ProviderSetupModalBody { .map(|value| value.name.as_str()) .collect::>() .join(", "); - Flex::column() - .with_spacing(2.) + Flex::row() + .with_cross_axis_alignment(CrossAxisAlignment::Center) + .with_spacing(10.) + .with_child(self.render_model_logo()) .with_child( - Text::new( - option.name.clone(), - appearance.ui_font_family(), - INPUT_FONT_SIZE, - ) - .with_color(appearance.theme().active_ui_text_color().into()) - .with_style(Properties::default().weight(Weight::Bold)) - .finish(), - ) - .with_child( - Text::new( - if values.is_empty() { - option.current_value.to_string() - } else { - values - }, - appearance.monospace_font_family(), - 10., - ) - .with_color(appearance.theme().nonactive_ui_text_color().into()) - .soft_wrap(true) - .finish(), + Flex::column() + .with_spacing(2.) + .with_child( + Text::new( + option.name.clone(), + appearance.ui_font_family(), + INPUT_FONT_SIZE, + ) + .with_color(appearance.theme().active_ui_text_color().into()) + .with_style(Properties::default().weight(Weight::Bold)) + .finish(), + ) + .with_child( + Text::new( + if values.is_empty() { + option.current_value.to_string() + } else { + values + }, + appearance.monospace_font_family(), + 10., + ) + .with_color(appearance.theme().nonactive_ui_text_color().into()) + .soft_wrap(true) + .finish(), + ) + .finish(), ) .finish() })