use crate::{ ai::blocklist::InputType, appearance::Appearance, context_chips::spacing, features::FeatureFlag, settings::{AppEditorSettings, InputModeSettings}, terminal::{ block_list_viewport::InputMode, input::{InputAction, InputDropTargetData}, settings::TerminalSettings, view::TerminalAction, }, themes::theme::color::internal_colors, }; use settings::Setting; use warpui::{ elements::{ Border, ChildView, Container, CornerRadius, DropTarget, Element, Flex, Hoverable, ParentElement, Radius, SavePosition, Stack, }, AppContext, SingletonEntity, }; use super::{ common::{ add_command_xray_overlay, add_input_suggestions_overlays, add_vim_status_to_stack, add_voltron_overlay, add_workflow_info_overlay, maybe_add_buy_credits_banner, wrap_input_with_terminal_padding_and_focus_handler, }, Input, }; impl Input { /// Renders the universal input. This is used when `FeatureFlag::AgentView` is disabled and the /// user has 'universal' input type selected in settings. pub(super) fn render_universal_developer_input(&self, app: &AppContext) -> Box { let appearance = Appearance::as_ref(app); let theme = appearance.theme(); let menu_positioning = self.menu_positioning(app); let model = self.model.lock(); // We should likely rework this stack to not need to use `with_constrain_absolute_children`, // by reworking the positioning of the children to not depend on this. let mut stack = Stack::new().with_constrain_absolute_children(); let mut prompt_row = Stack::new(); let prompt_elements = self .prompt_render_helper .render_universal_developer_input_prompt(&model, appearance, app); prompt_row.add_child(prompt_elements); let vim_state = self.editor.as_ref(app).vim_state(app); let app_editor_settings = AppEditorSettings::as_ref(app); let show_vim_status = vim_state.is_some() && *app_editor_settings.vim_status_bar.value(); let input_mode = *InputModeSettings::as_ref(app).input_mode.value(); // For Universal Developer Input, ignore compact mode setting let is_compact_mode = false; let mut column = Flex::column(); if matches!(input_mode, InputMode::PinnedToBottom | InputMode::Waterfall) { if let Some(banner) = self.render_input_banner(appearance, app, input_mode, is_compact_mode) { column.add_child( Container::new(banner) .with_margin_top(spacing::UDI_CHIP_MARGIN) .finish(), ); } } column.add_child(prompt_row.finish()); let ai_input_model = self.ai_input_model.as_ref(app); if FeatureFlag::ImageAsContext.is_enabled() && matches!(ai_input_model.input_type(), InputType::AI) { if let Some(images) = self.render_attachment_chips(appearance) { column.add_child( Container::new(images) .with_margin_top(spacing::UDI_CHIP_MARGIN) .finish(), ); } } let terminal_spacing = TerminalSettings::as_ref(app) .terminal_input_spacing(appearance.line_height_ratio(), app); column.add_child( Container::new(self.render_input_box(show_vim_status, appearance, app)) .with_margin_top( terminal_spacing.prompt_to_editor_padding * spacing::UDI_PROMPT_BOTTOM_PADDING_FACTOR, ) .finish(), ); column.add_child(ChildView::new(&self.universal_developer_input_button_bar).finish()); if matches!(input_mode, InputMode::PinnedToTop) { if let Some(banner) = self.render_input_banner(appearance, app, input_mode, is_compact_mode) { column.add_child( Container::new(banner) .with_margin_bottom(spacing::UDI_CHIP_MARGIN) .finish(), ); } } if let Some(vim_state) = vim_state.as_ref() { if show_vim_status { add_vim_status_to_stack( &mut stack, vim_state, appearance, true, // use adjusted padding for UDI ); } } stack.add_child(wrap_input_with_terminal_padding_and_focus_handler( self.is_active_session(app), column.finish(), true, // use adjusted padding for UDI )); if let Some(selected_workflow_state) = self.workflows_state.selected_workflow_state.as_ref() { if selected_workflow_state.should_show_more_info_view { add_workflow_info_overlay( &mut stack, selected_workflow_state, self.size_info(app).pane_height_px().as_f32(), menu_positioning, ); } } if self.is_voltron_open && self.is_pane_focused(app) { add_voltron_overlay(&mut stack, &self.voltron_view, menu_positioning); } if self.is_pane_focused(app) { add_input_suggestions_overlays(self, &mut stack, appearance, menu_positioning, app); } if let Some(token_description) = &self.command_x_ray_description { add_command_xray_overlay( self, &mut stack, token_description, appearance, menu_positioning, app, ); } maybe_add_buy_credits_banner( &mut stack, &self.buy_credits_banner, self.is_pane_focused(app), self.terminal_view_id, self.is_input_at_top(&model, app), app, ); // If the file tree is enabled, don't include the top margin for UDI so that the UDI is flush with the // file tree. let margin_top = if FeatureFlag::FileTree.is_enabled() && self.is_input_at_top(&model, app) { 0. } else { 6. }; // Wrap the stack in a container with background and border styling based on focus let mut container = Container::new( SavePosition::new(stack.finish(), &self.status_free_input_save_position_id()).finish(), ) .with_margin_bottom(6.) .with_margin_left(6.) .with_margin_right(6.) .with_margin_top(margin_top) .with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.))); // Apply styling based on focus state if self.is_pane_focused(app) { // Focused: show background container = container .with_background(internal_colors::fg_overlay_1(theme)) .with_border(Border::all(1.).with_border_fill(theme.outline())); } else { // Unfocused: no background container = container.with_border(Border::all(1.).with_border_fill(theme.outline())); } let drop_target = DropTarget::new( container.finish(), InputDropTargetData::new(self.weak_view_handle.clone()), ) .finish(); let input = Hoverable::new(self.hoverable_handle.clone(), |_| drop_target) .on_hover(|is_hovered, ctx, _app, _position| { ctx.dispatch_typed_action(InputAction::SetUDIHovered(is_hovered)); }) .on_middle_click(|ctx, _app, _position| { ctx.dispatch_typed_action(TerminalAction::MiddleClickOnInput) }) .finish(); let mut column = Flex::column(); if input_mode.is_pinned_to_top() { column.add_child(input); column.add_child(ChildView::new(&self.agent_status_view).finish()); } else { column.add_child(ChildView::new(&self.agent_status_view).finish()); column.add_child(input); } SavePosition::new(column.finish(), &self.save_position_id()).finish() } }