Skip to content

Commit 2cc7a9f

Browse files
authored
Hide command for third-party harness cloud agents. (warpdotdev#10448)
## Description <!-- Please remember to add your design buddy onto the PR for review, if it contains any UI changes! --> First stab at an alternative UI for hiding third-party harness cloud agent commands. Significant inspiration taken from how we're showing setup commands. Currently gated behind a separate feature flag, and only touches the cloud agent path for block rendering--this should not impact other block-rendering codepaths. ## Testing <!-- How did you test this change? What automated tests did you add? If you didn't add any new tests, what's your justification for not adding any? Manual testing is required for changes that can be manually tested, and almost all changes can be manually tested. If your change can be manually tested, please include screenshots or a screen recording that show it working end to end. You can run the app locally using `./script/run` - see WARP.md for more details on how to get set up. --> - [x] I have manually tested my changes locally with `./script/run` ### Screenshots / Videos <!-- Attach screenshots or a short video demonstrating the change, where appropriate. Remove this section if it is not relevant to your PR. --> Loom: https://www.loom.com/share/2e61d8636b4f46ef91351ab5c8cacca3 ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode
1 parent 1711d59 commit 2cc7a9f

7 files changed

Lines changed: 329 additions & 115 deletions

File tree

app/src/terminal/block_list_element.rs

Lines changed: 122 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -2512,129 +2512,136 @@ impl BlockListElement {
25122512
Self::draw_border_between_blocks(border_origin, block_grid_params, ctx);
25132513
}
25142514

2515-
let prompt_height_offset = cell_size_height * block.padding_top().as_f64() as f32;
2515+
let cursor_visible = block.is_mode_set(TermMode::SHOW_CURSOR);
2516+
let command_origin = if !block.should_hide_command_grid() {
2517+
let prompt_height_offset = cell_size_height * block.padding_top().as_f64() as f32;
25162518

2517-
*grid_origin += vec2f(0., prompt_height_offset);
2519+
*grid_origin += vec2f(0., prompt_height_offset);
25182520

2519-
let prompt_origin = snackbar_header
2520-
.and_then(|header| header.header_rect())
2521-
.map_or(*grid_origin, |r| {
2522-
let y = r.origin().y() + prompt_height_offset + block_banner_height;
2523-
vec2f(grid_origin.x(), y)
2524-
});
2521+
let prompt_origin = snackbar_header
2522+
.and_then(|header| header.header_rect())
2523+
.map_or(*grid_origin, |r| {
2524+
let y = r.origin().y() + prompt_height_offset + block_banner_height;
2525+
vec2f(grid_origin.x(), y)
2526+
});
25252527

2526-
let cursor_visible = block.is_mode_set(TermMode::SHOW_CURSOR);
2527-
// Draw prompt
2528-
if let Some(label_element) = label_element {
2529-
label_element.paint(prompt_origin, ctx, app);
2530-
} else {
2531-
let size_info = &block_grid_params.grid_render_params.size_info;
2532-
if block.should_display_rprompt(size_info) {
2533-
let rprompt_origin = prompt_origin + block.rprompt_render_offset(size_info);
2534-
block.rprompt_grid().draw(
2535-
rprompt_origin,
2536-
element_origin,
2537-
glyphs,
2538-
COMMAND_ALPHA,
2539-
None,
2540-
None,
2541-
hovered_secret,
2542-
None::<std::iter::Empty<&RangeInclusive<IndexPoint>>>,
2543-
None,
2544-
Properties::default(),
2545-
block_grid_params,
2546-
None,
2547-
image_metadata,
2548-
ctx,
2549-
app,
2550-
);
2528+
// Draw prompt
2529+
if let Some(label_element) = label_element {
2530+
label_element.paint(prompt_origin, ctx, app);
2531+
} else {
2532+
let size_info = &block_grid_params.grid_render_params.size_info;
2533+
if block.should_display_rprompt(size_info) {
2534+
let rprompt_origin = prompt_origin + block.rprompt_render_offset(size_info);
2535+
block.rprompt_grid().draw(
2536+
rprompt_origin,
2537+
element_origin,
2538+
glyphs,
2539+
COMMAND_ALPHA,
2540+
None,
2541+
None,
2542+
hovered_secret,
2543+
None::<std::iter::Empty<&RangeInclusive<IndexPoint>>>,
2544+
None,
2545+
Properties::default(),
2546+
block_grid_params,
2547+
None,
2548+
image_metadata,
2549+
ctx,
2550+
app,
2551+
);
2552+
}
25512553
}
2552-
}
2553-
2554-
// If Warp prompt (non-PS1) is being used, the command is drawn below the prompt,
2555-
// hence we account for the prompt's vertical offset.
2556-
let prompt_vertical_offset_px = if !block.honor_ps1() {
2557-
cell_size_height * (block.command_padding_top() + block.prompt_height()).as_f64() as f32
2558-
} else {
2559-
// Otherwise, the prompt/command are drawn together, in a single grid. Hence, we haven't
2560-
// drawn the prompt above and we do not account for the offset.
2561-
0.0
2562-
};
25632554

2564-
*grid_origin += vec2f(0.0, prompt_vertical_offset_px);
2555+
// If Warp prompt (non-PS1) is being used, the command is drawn below the prompt,
2556+
// hence we account for the prompt's vertical offset.
2557+
let prompt_vertical_offset_px = if !block.honor_ps1() {
2558+
cell_size_height
2559+
* (block.command_padding_top() + block.prompt_height()).as_f64() as f32
2560+
} else {
2561+
// Otherwise, the prompt/command are drawn together, in a single grid. Hence, we haven't
2562+
// drawn the prompt above and we do not account for the offset.
2563+
0.0
2564+
};
25652565

2566-
// Determine command_origin based on snackbar_header.
2567-
let command_origin = if snackbar_header.is_some() {
2568-
prompt_origin + vec2f(0.0, prompt_vertical_offset_px)
2569-
} else {
2570-
*grid_origin
2571-
};
2566+
*grid_origin += vec2f(0.0, prompt_vertical_offset_px);
25722567

2573-
// Update grid_origin and draw command.
2574-
let command_grid_properties = Properties::default();
2575-
block.prompt_and_command_grid().draw(
2576-
command_origin,
2577-
element_origin,
2578-
glyphs,
2579-
COMMAND_ALPHA,
2580-
highlighted_url
2581-
.filter(|url| url.is_in_command_content() && url.block_index == block_index)
2582-
.map(|url| &url.inner),
2583-
link_tool_tip
2584-
.filter(|url| url.is_in_command_content() && url.block_index == block_index)
2585-
.map(|url| &url.inner),
2586-
hovered_secret,
2587-
block_list_find_run
2588-
.map(|run| run.matches_for_block_grid(block_index, GridType::PromptAndCommand)),
2589-
block_list_find_run
2590-
.and_then(|run| run.focused_match())
2591-
.and_then(|focused_match| match focused_match {
2592-
BlockListMatch::CommandBlock(m)
2593-
if m.block_index == block_index
2594-
&& m.grid_type == GridType::PromptAndCommand =>
2595-
{
2596-
Some(&m.range)
2597-
}
2598-
_ => None,
2599-
}),
2600-
command_grid_properties,
2601-
block_grid_params,
2602-
cursor_visible.then(|| block.prompt_and_command_grid().cursor_style().shape),
2603-
image_metadata,
2604-
ctx,
2605-
app,
2606-
);
2568+
// Determine command_origin based on snackbar_header.
2569+
let command_origin = if snackbar_header.is_some() {
2570+
prompt_origin + vec2f(0.0, prompt_vertical_offset_px)
2571+
} else {
2572+
*grid_origin
2573+
};
26072574

2608-
// Only render the cursor in the command grid if the command grid is active and if it's
2609-
// long running. This is to avoid jitter where a cursor just flickers while the pty is
2610-
// initializing.
2611-
if block.is_active_and_long_running()
2612-
&& block.is_command_grid_active()
2613-
// Check if the "hide cursor" escape sequence is present.
2614-
&& block.is_mode_set(TermMode::SHOW_CURSOR)
2615-
{
2616-
block.prompt_and_command_grid().draw_cursor(
2575+
// Update grid_origin and draw command.
2576+
let command_grid_properties = Properties::default();
2577+
block.prompt_and_command_grid().draw(
26172578
command_origin,
2618-
&block_grid_params.grid_render_params,
2579+
element_origin,
2580+
glyphs,
2581+
COMMAND_ALPHA,
2582+
highlighted_url
2583+
.filter(|url| url.is_in_command_content() && url.block_index == block_index)
2584+
.map(|url| &url.inner),
2585+
link_tool_tip
2586+
.filter(|url| url.is_in_command_content() && url.block_index == block_index)
2587+
.map(|url| &url.inner),
2588+
hovered_secret,
2589+
block_list_find_run
2590+
.map(|run| run.matches_for_block_grid(block_index, GridType::PromptAndCommand)),
2591+
block_list_find_run
2592+
.and_then(|run| run.focused_match())
2593+
.and_then(|focused_match| match focused_match {
2594+
BlockListMatch::CommandBlock(m)
2595+
if m.block_index == block_index
2596+
&& m.grid_type == GridType::PromptAndCommand =>
2597+
{
2598+
Some(&m.range)
2599+
}
2600+
_ => None,
2601+
}),
2602+
command_grid_properties,
2603+
block_grid_params,
2604+
cursor_visible.then(|| block.prompt_and_command_grid().cursor_style().shape),
2605+
image_metadata,
26192606
ctx,
2620-
terminal_view_id,
2621-
None,
2622-
block_grid_params
2623-
.grid_render_params
2624-
.warp_theme
2625-
.cursor()
2626-
.into(),
26272607
app,
26282608
);
2629-
}
26302609

2631-
// Update grid_origin & draw output
2632-
*grid_origin += vec2f(
2633-
0.,
2634-
cell_size_height
2635-
* (block.padding_middle() + block.prompt_and_command_grid().len().into_lines())
2636-
.as_f64() as f32,
2637-
);
2610+
// Only render the cursor in the command grid if the command grid is active and if it's
2611+
// long running. This is to avoid jitter where a cursor just flickers while the pty is
2612+
// initializing.
2613+
if block.is_active_and_long_running()
2614+
&& block.is_command_grid_active()
2615+
// Check if the "hide cursor" escape sequence is present.
2616+
&& block.is_mode_set(TermMode::SHOW_CURSOR)
2617+
{
2618+
block.prompt_and_command_grid().draw_cursor(
2619+
command_origin,
2620+
&block_grid_params.grid_render_params,
2621+
ctx,
2622+
terminal_view_id,
2623+
None,
2624+
block_grid_params
2625+
.grid_render_params
2626+
.warp_theme
2627+
.cursor()
2628+
.into(),
2629+
app,
2630+
);
2631+
}
2632+
2633+
// Update grid_origin & draw output
2634+
*grid_origin += vec2f(
2635+
0.,
2636+
cell_size_height
2637+
* (block.padding_middle() + block.prompt_and_command_grid().len().into_lines())
2638+
.as_f64() as f32,
2639+
);
2640+
2641+
command_origin
2642+
} else {
2643+
*grid_origin
2644+
};
26382645

26392646
let block_middle_lines =
26402647
block.padding_middle() + block.prompt_and_command_number_of_rows().into_lines();
@@ -4346,7 +4353,11 @@ impl Element for BlockListElement {
43464353
}
43474354
}
43484355

4349-
draw_border_above_block = true;
4356+
// Don't draw a border below session headers (i.e. above the next block).
4357+
draw_border_above_block = !matches!(
4358+
self.rich_content_metadata.get(view_id),
4359+
Some(RichContentMetadata::HarnessSessionHeader)
4360+
);
43504361

43514362
grid_origin += vec2f(0., *height_px);
43524363
}

app/src/terminal/model/block.rs

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,9 @@ pub struct Block {
399399
/// If `true`, the output grid should not be rendered.
400400
should_hide_output_grid: bool,
401401

402+
/// If `true`, the prompt+command grid should not be rendered.
403+
should_hide_command_grid: bool,
404+
402405
/// [`Self::linefeed`] may discard some linefeeds at the beginning of the prompt. Doing so will
403406
/// alter the row numbers for [`Self::goto`] and [`Self::goto_line`] when ConPTY is involved. We
404407
/// track the count of discarded newlines here in order to correct the row number.
@@ -1007,6 +1010,7 @@ impl Block {
10071010
has_received_user_input: false,
10081011
hidden: false,
10091012
should_hide_output_grid: false,
1013+
should_hide_command_grid: false,
10101014
leading_linefeeds_ignored: 0,
10111015
is_ai_ugc_telemetry_enabled,
10121016
restored_block_was_local: None,
@@ -1489,6 +1493,14 @@ impl Block {
14891493
self.should_hide_output_grid = should_hide;
14901494
}
14911495

1496+
pub fn should_hide_command_grid(&self) -> bool {
1497+
self.should_hide_command_grid
1498+
}
1499+
1500+
pub fn set_should_hide_command_grid(&mut self, should_hide: bool) {
1501+
self.should_hide_command_grid = should_hide;
1502+
}
1503+
14921504
/// Returns true iff this block should be used as a scrollback block
14931505
/// in a shared session context. Note the active block is included in scrollback to get the active prompt.
14941506
pub fn is_scrollback_block_for_shared_session(
@@ -1515,9 +1527,11 @@ impl Block {
15151527
Lines::zero()
15161528
} else {
15171529
self.block_banner_height()
1518-
+ self.padding_top()
1519-
+ self.prompt_and_command_height()
1520-
+ self.padding_middle()
1530+
+ if self.should_hide_command_grid {
1531+
Lines::zero()
1532+
} else {
1533+
self.padding_top() + self.prompt_and_command_height() + self.padding_middle()
1534+
}
15211535
+ if self.should_hide_output_grid {
15221536
Lines::zero()
15231537
} else {
@@ -1940,7 +1954,7 @@ impl Block {
19401954
/// In the case of combined grid: for Warp prompt, this includes the height of both the Warp prompt
19411955
/// AND combined grid; for PS1, this is just the combined grid (PS1 is included there).
19421956
pub fn prompt_and_command_height(&self) -> Lines {
1943-
if !self.ready_to_render() {
1957+
if !self.ready_to_render() || self.should_hide_command_grid {
19441958
Lines::zero()
19451959
} else if self.header_grid.honor_ps1 {
19461960
// No padding between prompt and command in the case of PS1 (combined grid).

app/src/terminal/view/ambient_agent/block.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
mod entry;
2+
mod harness_session_header;
23
mod setup_command;
34
mod setup_command_text;
45

56
pub use entry::*;
7+
pub use harness_session_header::*;
68
pub use setup_command::*;
79
pub use setup_command_text::*;
810

0 commit comments

Comments
 (0)