Skip to content

Commit 160b6c5

Browse files
Remove Full Terminal Agent model callout (warpdotdev#13157)
## Description Removes the one-time **"Now using Full Terminal Agent's default model."** callout that was shown in the agent input footer the first time a user entered full-terminal / agent-in-control mode, plus the private cloud-synced setting that recorded whether the callout had been shown. Per the reporter, the callout is confusing to users and should no longer be surfaced. What was removed: - The `render_ftu_callout` helper and its render branch in `app/src/ai/blocklist/agent_view/agent_input_footer/mod.rs`, the `render_ftu_callout`/`ftu_callout_close_button` fields, the `update_ftu_callout_render_state` logic and its subscriptions, the `DismissFtuModelCallout` action + handler, and the now-unused imports. - The `ftu_model_callout_dismissed` (`FtuModelCalloutDismissed`) setting in `app/src/settings/ai.rs` — the private, cloud-synced flag that tracked whether the callout had been shown. The generic `CalloutTriangle*` icons in `warp_core` are intentionally left in place (they are shared, reusable UI assets with other unused variants already present). ## Linked Issue N/A — requested directly via the factory-client bug-triage Slack thread (linked below). ## Testing - `./script/format` — clean. - `cargo clippy -p warp --bin warp --tests` — clean, no warnings. - `cargo build --bin warp` — succeeds. - UI verification: launched the built app and confirmed the agent input footer renders cleanly — the model selector chip (`auto (genius)`) and the surrounding controls render normally with no model callout bubble overlapping the footer. Note: this is a pure deletion of a transient, once-per-user callout whose trigger state (agent taking control of a long-running command, behind a feature flag) is impractical to reliably script end-to-end; correctness of the removal is covered by the clean compile/clippy plus the footer-render verification above. No regression test is added since there is no remaining behavior to assert. ### Screenshots / Videos Verified the agent input footer renders with no callout bubble (see Slack thread). ## Agent Mode - [x] Warp Agent Mode - This PR was created via Warp's AI Agent Mode <!-- CHANGELOG-NONE --> <!-- factory-client: {"source":"factory-client","slack_channel":"C0BCE7AELJ2","slack_thread_ts":"1782681422.161339","slack_permalink":"https://warpdev.slack.com/archives/C0BCE7AELJ2/p1782681422161339?thread_ts=1782681422.161339&cid=C0BCE7AELJ2","oz_run_id":"019f1017-d1c6-7b4f-94d7-94ce46f98ec7","repo":"warpdotdev/warp","ci_fix_attempts":1} --> _Conversation: https://staging.warp.dev/conversation/975d46b4-29a8-41e8-aa99-5cb3bad3fa3e_ _Run: https://oz.staging.warp.dev/runs/019f1017-d1c6-7b4f-94d7-94ce46f98ec7_ _This PR was generated with [Oz](https://warp.dev/oz)._ --------- Co-authored-by: Oz <oz-agent@warp.dev>
1 parent 178fe89 commit 160b6c5

2 files changed

Lines changed: 9 additions & 196 deletions

File tree

  • app/src
    • settings

app/src/ai/blocklist/agent_view/agent_input_footer/mod.rs

Lines changed: 9 additions & 181 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,9 @@ use chrono::{DateTime, Local};
1616
use parking_lot::FairMutex;
1717
use pathfinder_color::ColorU;
1818
use pathfinder_geometry::vector::{vec2f, Vector2F};
19-
use settings::{Setting, ToggleableSetting};
19+
#[cfg(feature = "voice_input")]
20+
use settings::Setting;
21+
use settings::ToggleableSetting;
2022
#[cfg(not(target_family = "wasm"))]
2123
use tokio::fs;
2224
use toolbar_item::AgentToolbarItemKind;
@@ -31,11 +33,10 @@ use warp_core::ui::color::ContrastingColor;
3133
use warp_core::ui::theme::color::internal_colors;
3234
use warp_core::ui::theme::{AnsiColorIdentifier, Fill};
3335
use warpui::elements::{
34-
Border, ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius,
35-
CrossAxisAlignment, DispatchEventResult, Element, Empty, EventHandler, Expanded, Flex,
36-
MainAxisAlignment, MainAxisSize, OffsetPositioning, ParentAnchor, ParentElement,
37-
ParentOffsetBounds, PositionedElementAnchor, PositionedElementOffsetBounds, Radius, Shrinkable,
38-
Stack, Text, Wrap, WrapFill, WrapFillEntireRun, DEFAULT_UI_LINE_HEIGHT_RATIO,
36+
ChildAnchor, ChildView, Clipped, ConstrainedBox, Container, CornerRadius, CrossAxisAlignment,
37+
DispatchEventResult, Element, Empty, EventHandler, Flex, MainAxisAlignment, MainAxisSize,
38+
OffsetPositioning, ParentAnchor, ParentElement, ParentOffsetBounds, Radius, Shrinkable, Stack,
39+
Wrap, WrapFill, WrapFillEntireRun,
3940
};
4041
use warpui::r#async::{SpawnedFutureHandle, Timer};
4142
use warpui::{
@@ -86,7 +87,6 @@ use crate::terminal::input::models::InlineModelSelectorTab;
8687
use crate::terminal::input::{HandoffComposeState, MenuPositioningProvider};
8788
#[cfg(not(target_family = "wasm"))]
8889
use crate::terminal::local_shell::LocalShellState;
89-
use crate::terminal::model_events::ModelEvent;
9090
use crate::terminal::profile_model_selector::{ProfileModelSelector, ProfileModelSelectorEvent};
9191
use crate::terminal::session_settings::{
9292
SessionSettings, SessionSettingsChangedEvent, ToolbarChipSelection,
@@ -102,8 +102,7 @@ use crate::terminal::ShellLaunchData;
102102
use crate::terminal::{CLIAgent, TerminalModel};
103103
use crate::ui_components::icons::Icon;
104104
use crate::view_components::action_button::{
105-
ActionButton, ActionButtonTheme, AdjoinedSide, ButtonSize, KeystrokeSource, NakedTheme,
106-
TooltipAlignment,
105+
ActionButton, ActionButtonTheme, AdjoinedSide, ButtonSize, KeystrokeSource, TooltipAlignment,
107106
};
108107
use crate::view_components::DismissibleToast;
109108
#[cfg(not(target_family = "wasm"))]
@@ -201,7 +200,6 @@ pub struct AgentInputFooter {
201200
stop_remote_control_button: ViewHandle<ActionButton>,
202201
context_window_button: ViewHandle<ActionButton>,
203202
model_selector: ViewHandle<ProfileModelSelector>,
204-
ftu_callout_close_button: ViewHandle<ActionButton>,
205203
environment_selector: Option<ViewHandle<EnvironmentSelector>>,
206204
handoff_environment_selector: ViewHandle<EnvironmentSelector>,
207205
prompt_alert: ViewHandle<PromptAlertView>,
@@ -215,7 +213,6 @@ pub struct AgentInputFooter {
215213
display_chip_config: DisplayChipConfig,
216214

217215
terminal_model: Arc<FairMutex<TerminalModel>>,
218-
render_ftu_callout: bool,
219216

220217
// CLI agent-specific buttons (rendered when a CLI agent session is active).
221218
file_explorer_button: ViewHandle<ActionButton>,
@@ -733,11 +730,6 @@ impl AgentInputFooter {
733730
ctx.notify()
734731
}
735732
});
736-
ctx.subscribe_to_model(&display_chip_config.model_events, |me, _, event, ctx| {
737-
if let ModelEvent::AgentTaggedInChanged { .. } = event {
738-
me.update_ftu_callout_render_state(ctx);
739-
}
740-
});
741733
ctx.subscribe_to_model(
742734
&display_chip_config.agent_view_controller,
743735
|me, _, _, ctx| {
@@ -782,7 +774,6 @@ impl AgentInputFooter {
782774
{
783775
return;
784776
}
785-
me.update_ftu_callout_render_state(ctx);
786777

787778
match event {
788779
BlocklistAIHistoryEvent::StartedNewConversation { .. }
@@ -862,7 +853,6 @@ impl AgentInputFooter {
862853
prompt_alert,
863854
terminal_model,
864855
handoff_compose_state,
865-
render_ftu_callout: false,
866856
left_display_chips: vec![],
867857
right_display_chips: vec![],
868858
cli_display_chips: vec![],
@@ -873,14 +863,6 @@ impl AgentInputFooter {
873863
cli_voice_input_state: CLIVoiceInputState::default(),
874864
#[cfg(feature = "voice_input")]
875865
cli_transcription_handle: None,
876-
ftu_callout_close_button: ctx.add_typed_action_view(|_ctx| {
877-
ActionButton::new("", NakedTheme)
878-
.with_icon(Icon::X)
879-
.with_size(ButtonSize::XSmall)
880-
.on_click(|ctx| {
881-
ctx.dispatch_typed_action(AgentInputFooterAction::DismissFtuModelCallout);
882-
})
883-
}),
884866
v2_model_selector,
885867
prompt_cache_expiry_timer_handle: None,
886868
prompt_cache_expired: false,
@@ -889,7 +871,6 @@ impl AgentInputFooter {
889871
me.sync_remote_control_button(ctx);
890872
me.update_context_window_button(ctx);
891873
me.update_display_chips(&prompt, ctx);
892-
me.update_ftu_callout_render_state(ctx);
893874
me
894875
}
895876

@@ -1647,36 +1628,6 @@ impl AgentInputFooter {
16471628
self.model_selector.as_ref(app).is_open()
16481629
}
16491630

1650-
fn update_ftu_callout_render_state(&mut self, ctx: &mut ViewContext<Self>) {
1651-
let ftu_dismissed = *AISettings::as_ref(ctx).ftu_model_callout_dismissed;
1652-
if !self.render_ftu_callout && ftu_dismissed {
1653-
return;
1654-
}
1655-
1656-
let showing_ftu_model_picker = FeatureFlag::InlineMenuHeaders.is_enabled()
1657-
&& self
1658-
.terminal_model
1659-
.lock()
1660-
.block_list()
1661-
.active_block()
1662-
.is_agent_in_control_or_tagged_in();
1663-
if showing_ftu_model_picker && !ftu_dismissed {
1664-
if !self.render_ftu_callout {
1665-
self.render_ftu_callout = true;
1666-
ctx.notify();
1667-
}
1668-
AISettings::handle(ctx).update(ctx, |settings, ctx| {
1669-
// This setting actually indicates whether we've shown the ftu callout at all,
1670-
// but it originally tracked whether the user manually dismissed the callout and
1671-
// we don't want to resurface the callout to folks who have already dismissed.
1672-
let _ = settings.ftu_model_callout_dismissed.set_value(true, ctx);
1673-
});
1674-
} else if !showing_ftu_model_picker && self.render_ftu_callout {
1675-
self.render_ftu_callout = false;
1676-
ctx.notify();
1677-
}
1678-
}
1679-
16801631
fn handle_profile_model_selector_event(
16811632
&mut self,
16821633
event: &ProfileModelSelectorEvent,
@@ -1694,11 +1645,6 @@ impl AgentInputFooter {
16941645
ctx.emit(AgentInputFooterEvent::OpenSettings(*section));
16951646
}
16961647
ProfileModelSelectorEvent::ToggleInlineModelSelector => {
1697-
if self.render_ftu_callout {
1698-
self.render_ftu_callout = false;
1699-
ctx.notify();
1700-
}
1701-
17021648
let initial_tab = if self
17031649
.terminal_model
17041650
.lock()
@@ -2357,121 +2303,10 @@ impl View for AgentInputFooter {
23572303
container = container.with_padding_right(16.);
23582304
}
23592305

2360-
// If the model chip has switched to show the ftu model options
2361-
// (and this is the first time this has happened)
2362-
// we show a little callout explaining the change.
2363-
let showing_ftu_model_picker = FeatureFlag::InlineMenuHeaders.is_enabled()
2364-
&& terminal_model
2365-
.block_list()
2366-
.active_block()
2367-
.is_agent_in_control_or_tagged_in();
2368-
2369-
if showing_ftu_model_picker && self.render_ftu_callout {
2370-
let mut stack = Stack::new();
2371-
stack.add_child(container.finish());
2372-
stack.add_positioned_overlay_child(
2373-
render_ftu_callout(&self.ftu_callout_close_button, app),
2374-
OffsetPositioning::offset_from_save_position_element(
2375-
"profile_model_selector_model_button",
2376-
vec2f(8., -8.),
2377-
PositionedElementOffsetBounds::WindowByPosition,
2378-
PositionedElementAnchor::TopRight,
2379-
ChildAnchor::BottomRight,
2380-
),
2381-
);
2382-
stack.finish()
2383-
} else {
2384-
container.finish()
2385-
}
2306+
container.finish()
23862307
}
23872308
}
23882309

2389-
/// Render a message bubble calling out that the model has switched now that we're in FTU mode.
2390-
/// This callout is dismissable and does not re-appear once you've dismissed it once.
2391-
fn render_ftu_callout(
2392-
close_button: &ViewHandle<ActionButton>,
2393-
app: &AppContext,
2394-
) -> Box<dyn Element> {
2395-
let appearance = Appearance::as_ref(app);
2396-
let theme = appearance.theme();
2397-
let background = theme.background().blend(&theme.accent().with_opacity(50));
2398-
let text_color = internal_colors::text_main(theme, background.into_solid());
2399-
2400-
let callout_box = ConstrainedBox::new(
2401-
Container::new(
2402-
Flex::row()
2403-
.with_cross_axis_alignment(CrossAxisAlignment::Start)
2404-
.with_spacing(8.)
2405-
.with_child(
2406-
Expanded::new(
2407-
1.,
2408-
Text::new(
2409-
"Now using Full Terminal Agent's default model.",
2410-
appearance.ui_font_family(),
2411-
appearance.monospace_font_size() - 2.,
2412-
)
2413-
.with_color(text_color)
2414-
.with_line_height_ratio(DEFAULT_UI_LINE_HEIGHT_RATIO)
2415-
.with_selectable(false)
2416-
.finish(),
2417-
)
2418-
.finish(),
2419-
)
2420-
.with_child(
2421-
Container::new(ChildView::new(close_button).finish())
2422-
.with_margin_top(-3.)
2423-
.finish(),
2424-
)
2425-
.finish(),
2426-
)
2427-
.with_vertical_padding(12.)
2428-
.with_horizontal_padding(16.)
2429-
.with_background(background)
2430-
.with_border(Border::all(1.).with_border_fill(theme.accent()))
2431-
.with_corner_radius(CornerRadius::with_all(Radius::Pixels(8.)))
2432-
.finish(),
2433-
)
2434-
.with_width(348.)
2435-
.finish();
2436-
2437-
// The way that we render the little triangle in the bottom of the message bubble
2438-
// is by rendering two triangle icons (a filled triangle and an outlined triangle) and then
2439-
// stacking them on top of each other below the message bubble. I don't think there's a simpler
2440-
// way to do this with our UI framework.
2441-
let triangle_stack = Stack::new()
2442-
.with_child(
2443-
ConstrainedBox::new(
2444-
Icon::CalloutTriangleBorderDown
2445-
.to_warpui_icon(Fill::Solid(theme.accent().into_solid()))
2446-
.finish(),
2447-
)
2448-
.with_width(24.)
2449-
.with_height(24.)
2450-
.finish(),
2451-
)
2452-
.with_child(
2453-
ConstrainedBox::new(
2454-
Icon::CalloutTriangleFillDown
2455-
.to_warpui_icon(background)
2456-
.finish(),
2457-
)
2458-
.with_width(24.)
2459-
.with_height(24.)
2460-
.finish(),
2461-
);
2462-
2463-
Flex::column()
2464-
.with_main_axis_size(MainAxisSize::Min)
2465-
.with_child(callout_box)
2466-
.with_child(
2467-
Container::new(triangle_stack.finish())
2468-
.with_margin_left(300.)
2469-
.with_margin_top(-3.)
2470-
.finish(),
2471-
)
2472-
.finish()
2473-
}
2474-
24752310
#[derive(Debug, Clone)]
24762311
pub enum AgentInputFooterAction {
24772312
#[cfg(feature = "voice_input")]
@@ -2482,7 +2317,6 @@ pub enum AgentInputFooterAction {
24822317
ToggleFileExplorer,
24832318
ToggleRichInput,
24842319
ToggleAutodetectionSetting,
2485-
DismissFtuModelCallout,
24862320
InstallPlugin,
24872321
UpdatePlugin,
24882322
OpenPluginInstallInstructionsPane,
@@ -2570,12 +2404,6 @@ impl TypedActionView for AgentInputFooter {
25702404
.toggle_and_save_value(ctx));
25712405
});
25722406
}
2573-
AgentInputFooterAction::DismissFtuModelCallout => {
2574-
if self.render_ftu_callout {
2575-
self.render_ftu_callout = false;
2576-
ctx.notify();
2577-
}
2578-
}
25792407
AgentInputFooterAction::InstallPlugin => {
25802408
#[cfg(not(target_family = "wasm"))]
25812409
{

app/src/settings/ai.rs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1419,21 +1419,6 @@ define_settings_group!(AISettings, settings: [
14191419
private: true,
14201420
}
14211421

1422-
// This is not a user-visible setting - it tracks whether the FTU model picker callout
1423-
// has been shown to the user. We set this to `true` as soon as the callout is first
1424-
// displayed (not when it's dismissed), so it never re-appears.
1425-
//
1426-
// Note: this setting was originally named "dismissed" but we now use it to mean "shown".
1427-
// We kept the same setting key so that users who already dismissed the callout on an
1428-
// older client don't see it again.
1429-
ftu_model_callout_dismissed: FtuModelCalloutDismissed {
1430-
type: bool,
1431-
default: false,
1432-
supported_platforms: SupportedPlatforms::ALL,
1433-
sync_to_cloud: SyncToCloud::Globally(RespectUserSyncSetting::Yes),
1434-
private: true,
1435-
}
1436-
14371422
// Whether the ambient agent trial widget has been dismissed by the user.
14381423
//
14391424
// Not a user-visible setting - we model it as a setting so we can track state.

0 commit comments

Comments
 (0)