This commit is contained in:
2026-08-05 01:08:26 -05:00
parent c321e17708
commit 2015498831
4 changed files with 110 additions and 96 deletions
+53 -68
View File
@@ -22,7 +22,7 @@ use galaxy_core::ui::theme::color::internal_colors;
use galaxy_util::local_or_remote_path::LocalOrRemotePath; use galaxy_util::local_or_remote_path::LocalOrRemotePath;
use galaxyui::elements::new_scrollable::SingleAxisConfig; use galaxyui::elements::new_scrollable::SingleAxisConfig;
use galaxyui::elements::{ use galaxyui::elements::{
Align, Border, ChildAnchor, ChildView, ConstrainedBox, Container, CornerRadius, Align, Border, ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius,
CrossAxisAlignment, Empty, Expanded, Fill, Flex, FormattedTextElement, Hoverable, CrossAxisAlignment, Empty, Expanded, Fill, Flex, FormattedTextElement, Hoverable,
MainAxisAlignment, MainAxisSize, NewScrollable, OffsetPositioning, ParentAnchor, ParentElement, MainAxisAlignment, MainAxisSize, NewScrollable, OffsetPositioning, ParentAnchor, ParentElement,
ParentOffsetBounds, Radius, Shrinkable, Stack, Text, Wrap, ParentOffsetBounds, Radius, Shrinkable, Stack, Text, Wrap,
@@ -85,18 +85,22 @@ use crate::ai::blocklist::inline_action::ask_user_question_view::AskUserQuestion
use crate::ai::blocklist::inline_action::aws_bedrock_credentials_error::AwsBedrockCredentialsErrorView; use crate::ai::blocklist::inline_action::aws_bedrock_credentials_error::AwsBedrockCredentialsErrorView;
use crate::ai::blocklist::inline_action::create_or_edit_document::CreateOrEditDocumentAction; use crate::ai::blocklist::inline_action::create_or_edit_document::CreateOrEditDocumentAction;
use crate::ai::blocklist::inline_action::inline_action_header::{ use crate::ai::blocklist::inline_action::inline_action_header::{
HeaderConfig, InteractionMode, INLINE_ACTION_HEADER_VERTICAL_PADDING, ExpandedConfig, HeaderConfig, InteractionMode, INLINE_ACTION_HEADER_VERTICAL_PADDING,
INLINE_ACTION_HORIZONTAL_PADDING, INLINE_ACTION_HORIZONTAL_PADDING,
}; };
use crate::ai::blocklist::inline_action::inline_action_icons::{self, icon_size}; use crate::ai::blocklist::inline_action::inline_action_icons::{self, icon_size};
use crate::ai::blocklist::inline_action::requested_action::{ use crate::ai::blocklist::inline_action::requested_action::{
render_requested_action_body_text, render_requested_action_row_for_text, RenderableAction, render_requested_action_body_text, render_requested_action_row_for_text, RenderableAction,
}; };
use crate::ai::blocklist::inline_action::requested_command::RequestedCommand; use crate::ai::blocklist::inline_action::requested_command::{
format_command_text, RequestedCommand, REQUESTED_COMMAND_BODY_VERTICAL_PADDING,
VIEWING_COMMAND_DETAIL_MESSAGE,
};
use crate::ai::blocklist::inline_action::run_agents_card_view::RunAgentsCardView; use crate::ai::blocklist::inline_action::run_agents_card_view::RunAgentsCardView;
use crate::ai::blocklist::inline_action::search_codebase::SearchCodebaseView; use crate::ai::blocklist::inline_action::search_codebase::SearchCodebaseView;
use crate::ai::blocklist::inline_action::suggested_unit_tests::SuggestedUnitTestsView; use crate::ai::blocklist::inline_action::suggested_unit_tests::SuggestedUnitTestsView;
use crate::ai::blocklist::inline_action::summarization::SummarizationView; use crate::ai::blocklist::inline_action::summarization::SummarizationView;
use crate::ai::blocklist::inline_action::tool_pane::render_tool_pane_shell;
use crate::ai::blocklist::inline_action::web_fetch::WebFetchView; use crate::ai::blocklist::inline_action::web_fetch::WebFetchView;
use crate::ai::blocklist::inline_action::web_search::WebSearchView; use crate::ai::blocklist::inline_action::web_search::WebSearchView;
use crate::ai::blocklist::keyboard_navigable_buttons::KeyboardNavigableButtons; use crate::ai::blocklist::keyboard_navigable_buttons::KeyboardNavigableButtons;
@@ -1287,7 +1291,6 @@ fn render_runtime_activity(
let state = props.collapsible_block_states.get(&output_message.id)?; let state = props.collapsible_block_states.get(&output_message.id)?;
let appearance = Appearance::as_ref(app); let appearance = Appearance::as_ref(app);
let theme = appearance.theme(); let theme = appearance.theme();
let text_color = blended_colors::text_main(theme, theme.background());
let output = activity let output = activity
.output .output
.as_deref() .as_deref()
@@ -1296,56 +1299,51 @@ fn render_runtime_activity(
state.expansion_state, state.expansion_state,
CollapsibleExpansionState::Expanded { .. } CollapsibleExpansionState::Expanded { .. }
); );
let mut content = Flex::column().with_cross_axis_alignment(CrossAxisAlignment::Stretch); let icon = match activity.status.as_ref() {
Some(RuntimeActivityStatus::Pending) => icons::pending_icon(appearance),
let title = Text::new( Some(RuntimeActivityStatus::InProgress) => icons::yellow_running_icon(appearance),
activity.title.clone(), Some(RuntimeActivityStatus::Completed) => inline_action_icons::green_check_icon(appearance),
appearance.monospace_font_family(), Some(RuntimeActivityStatus::Failed) => inline_action_icons::red_x_icon(appearance),
appearance.monospace_font_size(), Some(RuntimeActivityStatus::Other(_)) | None => icons::gray_circle_icon(appearance),
)
.with_color(text_color)
.with_selectable(false)
.finish();
if output.is_some() {
let chevron = if is_expanded {
Icon::ChevronDown
} else {
Icon::ChevronRight
}; };
let icon_sz = icon_size(app); let title = if is_expanded {
VIEWING_COMMAND_DETAIL_MESSAGE.to_owned()
} else {
format_command_text(&activity.title)
};
let mut header = HeaderConfig::new(title, app)
.with_selectable_text()
.with_icon(icon)
.with_corner_radius_override(if is_expanded && output.is_some() {
CornerRadius::with_top(Radius::Pixels(8.))
} else {
CornerRadius::with_all(Radius::Pixels(8.))
});
if !is_expanded {
header = header.with_font_family(appearance.monospace_font_family());
}
if output.is_some() {
let message_id = output_message.id.clone(); let message_id = output_message.id.clone();
let mouse_state = state.expansion_toggle_mouse_state.clone(); let command = activity.title.clone();
let header = Hoverable::new(mouse_state, move |_| { let expansion =
Flex::row() ExpandedConfig::new(is_expanded, state.expansion_toggle_mouse_state.clone())
.with_cross_axis_alignment(CrossAxisAlignment::Center) .with_toggle_callback(move |ctx| {
.with_child(Shrinkable::new(1., title).finish())
.with_child(
Container::new(
ConstrainedBox::new(chevron.to_galaxyui_icon(text_color.into()).finish())
.with_width(icon_sz)
.with_height(icon_sz)
.finish(),
)
.with_margin_left(6.)
.finish(),
)
.finish()
})
.with_cursor(Cursor::PointingHand)
.on_click(move |ctx, _, _| {
ctx.dispatch_typed_action(AIBlockAction::ToggleCollapsibleBlockExpanded( ctx.dispatch_typed_action(AIBlockAction::ToggleCollapsibleBlockExpanded(
message_id.clone(), message_id.clone(),
)); ));
})
.with_right_click_callback(move |ctx| {
ctx.dispatch_typed_action(AIBlockAction::StoreRightClickedCommand {
command: command.clone(),
}); });
content.add_child( });
Flex::row() header = header.with_interaction_mode(InteractionMode::ManuallyExpandable(expansion));
.with_child(Shrinkable::new(1., header.finish()).finish())
.finish(),
);
} else {
content.add_child(title);
} }
let mut content = Flex::column()
.with_cross_axis_alignment(CrossAxisAlignment::Stretch)
.with_child(Clipped::new(header.render(app)).finish());
if let Some(output) = output { if let Some(output) = output {
let body = render_requested_action_body_text( let body = render_requested_action_body_text(
output.into(), output.into(),
@@ -1364,31 +1362,18 @@ fn render_runtime_activity(
is_streaming, is_streaming,
320., 320.,
) { ) {
content.add_child(Container::new(scrollable).with_margin_top(12.).finish()); content.add_child(
Container::new(scrollable)
.with_horizontal_padding(INLINE_ACTION_HORIZONTAL_PADDING)
.with_vertical_padding(REQUESTED_COMMAND_BODY_VERTICAL_PADDING)
.with_background(theme.background())
.with_corner_radius(CornerRadius::with_bottom(Radius::Pixels(8.)))
.finish(),
);
} }
} }
let icon = match activity.status.as_ref() { Some(render_tool_pane_shell(content.finish(), false, false, app))
Some(RuntimeActivityStatus::Completed) => {
inline_action_icons::green_check_icon(appearance).finish()
}
Some(RuntimeActivityStatus::Failed) => inline_action_icons::red_x_icon(appearance).finish(),
Some(RuntimeActivityStatus::Pending)
| Some(RuntimeActivityStatus::InProgress)
| Some(RuntimeActivityStatus::Other(_))
| None => galaxyui::elements::Icon::new(
Icon::ClockRefresh.into(),
internal_colors::neutral_5(appearance.theme()),
)
.finish(),
};
Some(
RenderableAction::new_with_element(content.finish(), app)
.with_icon(icon)
.render(app)
.finish(),
)
} }
fn should_render_stopped_output(props: Props, app: &AppContext) -> bool { fn should_render_stopped_output(props: Props, app: &AppContext) -> bool {
@@ -16,5 +16,6 @@ pub(super) mod search_codebase;
pub(crate) mod search_results_common; pub(crate) mod search_results_common;
pub(crate) mod suggested_unit_tests; pub(crate) mod suggested_unit_tests;
pub(super) mod summarization; pub(super) mod summarization;
pub(crate) mod tool_pane;
pub(super) mod web_fetch; pub(super) mod web_fetch;
pub(super) mod web_search; pub(super) mod web_search;
@@ -36,13 +36,13 @@ use crate::ai::blocklist::block::cli_controller::{
use crate::ai::blocklist::block::view_impl::output::action_icon; use crate::ai::blocklist::block::view_impl::output::action_icon;
use crate::ai::blocklist::block::view_impl::{ use crate::ai::blocklist::block::view_impl::{
render_autonomy_checkbox_setting_speedbump_footer, render_citation, render_citation_chips, render_autonomy_checkbox_setting_speedbump_footer, render_citation, render_citation_chips,
CONTENT_HORIZONTAL_PADDING, CONTENT_ITEM_VERTICAL_MARGIN,
}; };
use crate::ai::blocklist::block::{AIBlockAction, AutonomySettingSpeedbump}; use crate::ai::blocklist::block::{AIBlockAction, AutonomySettingSpeedbump};
use crate::ai::blocklist::inline_action::inline_action_header::{ use crate::ai::blocklist::inline_action::inline_action_header::{
ExpandedConfig, HeaderConfig, InteractionMode, RightClickConfig, ExpandedConfig, HeaderConfig, InteractionMode, RightClickConfig,
INLINE_ACTION_HORIZONTAL_PADDING, INLINE_ACTION_HORIZONTAL_PADDING,
}; };
use crate::ai::blocklist::inline_action::tool_pane::render_tool_pane_shell;
use crate::ai::blocklist::model::{AIBlockModel, AIBlockModelHelper}; use crate::ai::blocklist::model::{AIBlockModel, AIBlockModelHelper};
use crate::ai::blocklist::{ use crate::ai::blocklist::{
AIBlock, BlocklistAIActionEvent, BlocklistAIActionModel, BlocklistAIHistoryModel, AIBlock, BlocklistAIActionEvent, BlocklistAIActionModel, BlocklistAIHistoryModel,
@@ -1600,14 +1600,9 @@ impl View for RequestedCommandView {
content.add_child(Clipped::new(footer).finish()); content.add_child(Clipped::new(footer).finish());
} }
let border_color = if action_status let has_highlighted_border = action_status
.as_ref() .as_ref()
.is_some_and(|status| status.is_blocked()) .is_some_and(|status| status.is_blocked());
{
theme.accent()
} else {
theme.surface_2()
};
// If the requested command is expanded above a terminal block or // If the requested command is expanded above a terminal block or
// the next exchange flows directly after, remove bottom margin for // the next exchange flows directly after, remove bottom margin for
@@ -1637,21 +1632,12 @@ impl View for RequestedCommandView {
})) }))
&& !is_input_pinned_to_top); && !is_input_pinned_to_top);
let container = Container::new(content.finish()) let container = render_tool_pane_shell(
.with_margin_left(if action_status.is_some_and(|status| status.is_blocked()) { content.finish(),
CONTENT_HORIZONTAL_PADDING has_highlighted_border,
} else { should_remove_bottom_margin,
CONTENT_HORIZONTAL_PADDING + icon_size(app) + 16. app,
}) );
.with_margin_right(CONTENT_HORIZONTAL_PADDING)
.with_margin_bottom(if should_remove_bottom_margin {
0.
} else {
CONTENT_ITEM_VERTICAL_MARGIN
})
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.with_border(Border::all(1.).with_border_fill(border_color))
.finish();
let mut root_stack = Stack::new(); let mut root_stack = Stack::new();
root_stack.add_child(container); root_stack.add_child(container);
@@ -0,0 +1,42 @@
use galaxy_core::ui::appearance::Appearance;
use warpui::elements::{Border, Container, CornerRadius, ParentElement, Radius};
use warpui::{AppContext, Element, SingletonEntity};
use super::inline_action_icons::icon_size;
use crate::ai::blocklist::block::view_impl::{
CONTENT_HORIZONTAL_PADDING, CONTENT_ITEM_VERTICAL_MARGIN,
};
/// Renders the shared outer shell used by native and runtime-owned tool panes.
///
/// Callers own execution and body content. This function owns the pane geometry
/// and theme treatment so display-only runtimes cannot drift from native tools.
pub(crate) fn render_tool_pane_shell(
content: Box<dyn Element>,
has_highlighted_border: bool,
should_remove_bottom_margin: bool,
app: &AppContext,
) -> Box<dyn Element> {
let theme = Appearance::as_ref(app).theme();
let border_color = if has_highlighted_border {
theme.accent()
} else {
theme.surface_2()
};
Container::new(content)
.with_margin_left(if has_highlighted_border {
CONTENT_HORIZONTAL_PADDING
} else {
CONTENT_HORIZONTAL_PADDING + icon_size(app) + 16.
})
.with_margin_right(CONTENT_HORIZONTAL_PADDING)
.with_margin_bottom(if should_remove_bottom_margin {
0.
} else {
CONTENT_ITEM_VERTICAL_MARGIN
})
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
.with_border(Border::all(1.).with_border_fill(border_color))
.finish()
}