Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 9 additions & 9 deletions app/src/ai/agent_management/agent_management_model.rs
Original file line number Diff line number Diff line change
Expand Up @@ -422,9 +422,7 @@ impl AgentNotificationsModel {
branch: Option<String>,
ctx: &mut ModelContext<Self>,
) {
if !*AISettings::as_ref(ctx).show_agent_notifications {
return;
}
let show_agent_notifications = *AISettings::as_ref(ctx).show_agent_notifications;

let is_visible = is_terminal_view_visible(terminal_view_id, ctx);
let item = NotificationItem::new(
Expand All @@ -438,12 +436,14 @@ impl AgentNotificationsModel {
artifacts,
branch,
);
send_telemetry_from_ctx!(
TelemetryEvent::AgentNotificationShown {
agent_variant: agent.into(),
},
ctx
);
if show_agent_notifications {
send_telemetry_from_ctx!(
TelemetryEvent::AgentNotificationShown {
agent_variant: agent.into(),
},
ctx
);
}

let id = item.id;
self.notifications.push(item);
Expand Down
51 changes: 49 additions & 2 deletions app/src/ai/agent_management/agent_management_model_tests.rs
Original file line number Diff line number Diff line change
@@ -1,20 +1,27 @@
use settings::Setting as _;
use warp_core::features::FeatureFlag;
use warpui::{App, EntityId, ModelHandle};
use warpui::{App, EntityId, ModelHandle, SingletonEntity};

use super::AgentNotificationsModel;
use crate::ai::active_agent_views_model::ActiveAgentViewsModel;
use crate::ai::agent::conversation::AIConversationId;
use crate::ai::agent_management::notifications::{
NotificationCategory, NotificationFilter, NotificationOrigin, NotificationSourceAgent,
};
use crate::ai::artifacts::Artifact;
use crate::ai::blocklist::BlocklistAIHistoryEvent;
use crate::settings::AISettings;
use crate::terminal::cli_agent_sessions::CLIAgentSessionsModel;
use crate::BlocklistAIHistoryModel;
use crate::test_util::settings::initialize_settings_for_tests;
use crate::{report_if_error, BlocklistAIHistoryModel};

fn setup_app(
app: &mut App,
) -> (
ModelHandle<BlocklistAIHistoryModel>,
ModelHandle<AgentNotificationsModel>,
) {
initialize_settings_for_tests(app);
let history = app.add_singleton_model(|_| BlocklistAIHistoryModel::new(vec![], &[]));
app.add_singleton_model(|_| CLIAgentSessionsModel::new());
app.add_singleton_model(|_| ActiveAgentViewsModel::new());
Expand Down Expand Up @@ -97,6 +104,46 @@ fn multiple_artifacts_accumulated_across_turns() {
});
}

#[test]
fn add_notification_tracks_unread_activity_when_in_app_notifications_are_hidden() {
App::test((), |mut app| async move {
let _guard = FeatureFlag::HOANotifications.override_enabled(true);
let (_history, notifications) = setup_app(&mut app);

AISettings::handle(&app).update(&mut app, |settings, ctx| {
report_if_error!(settings.show_agent_notifications.set_value(false, ctx));
});

let conversation_id = AIConversationId::new();
let terminal_view_id = EntityId::new();
notifications.update(&mut app, |model, ctx| {
model.add_notification(
"Agent task".to_owned(),
"Task completed.".to_owned(),
NotificationCategory::Complete,
NotificationSourceAgent::Oz { is_ambient: false },
NotificationOrigin::Conversation(conversation_id),
terminal_view_id,
vec![],
None,
ctx,
);
});

notifications.read(&app, |model, _| {
assert_eq!(
model
.notifications()
.filtered_count(NotificationFilter::All),
1
);
assert!(model
.notifications()
.has_unread_for_terminal_view(terminal_view_id));
});
});
}

#[test]
fn flush_drains_pending_artifacts() {
App::test((), |mut app| async move {
Expand Down
10 changes: 0 additions & 10 deletions app/src/workspace/header_toolbar_editor.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@ use crate::chip_configurator::{
ChipConfiguratorAction, ChipConfiguratorLayout, ChipEditorModalConfig, ChipEditorMouseHandles,
ChipEditorSectionsConfig, ConfigurableItem, ControlItemRenderer,
};
use crate::settings::AISettings;
use crate::workspace::header_toolbar_item::HeaderToolbarItemKind;
use crate::workspace::tab_settings::{
HeaderToolbarChipSelection, TabSettings, TabSettingsChangedEvent,
Expand Down Expand Up @@ -176,15 +175,6 @@ fn sync_show_hide_settings<V: View>(
.set_value(code_review_placed, ctx));
});
}

let notifications_placed = placed.contains(&&HeaderToolbarItemKind::NotificationsMailbox);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This deletion seems incorrect? Think this should still be propagating the setting flag

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why should disabling the notifications mailbox have any effect on whether we show in app notifications? that's one of the bugs i'm trying to fix. maybe i'm misunderstanding the code?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I guess that’s fair. U could still independently disable this setting if u wantsd

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i think that's the right behavior. it was unexpected to me that disabling the inbox turned off notifications entirely

if *AISettings::as_ref(ctx).show_agent_notifications != notifications_placed {
AISettings::handle(ctx).update(ctx, |settings, ctx| {
report_if_error!(settings
.show_agent_notifications
.set_value(notifications_placed, ctx));
});
}
}

impl HeaderToolbarInlineEditor {
Expand Down
39 changes: 34 additions & 5 deletions app/src/workspace/view/vertical_tabs.rs
Original file line number Diff line number Diff line change
Expand Up @@ -782,6 +782,7 @@ struct VerticalTabsSummaryData {
primary_labels: Vec<VerticalTabsSummaryPrimaryLabel>,
working_directories: Vec<String>,
branch_entries: Vec<VerticalTabsSummaryBranchEntry>,
has_unread_activity: bool,
}

impl TabGroupColorMode {
Expand Down Expand Up @@ -2434,7 +2435,10 @@ fn has_unread_activity(typed: &TypedPane<'_>, app: &AppContext) -> bool {
return false;
};
let terminal_view = terminal_pane.terminal_view(app);
let terminal_view_id = terminal_view.as_ref(app).id();
has_unread_activity_for_terminal_view(terminal_view.as_ref(app).id(), app)
}

fn has_unread_activity_for_terminal_view(terminal_view_id: EntityId, app: &AppContext) -> bool {
AgentNotificationsModel::as_ref(app)
.notifications()
.has_unread_for_terminal_view(terminal_view_id)
Expand Down Expand Up @@ -2713,6 +2717,7 @@ fn build_vertical_tabs_summary_data(
let mut working_directories = Vec::new();
let mut working_directory_seen = HashMap::new();
let mut branch_entries = Vec::new();
let mut has_unread_activity = false;

for pane_id in visible_pane_ids {
let Some(pane) = pane_group.pane_by_id(*pane_id) else {
Expand All @@ -2731,6 +2736,8 @@ fn build_vertical_tabs_summary_data(
TypedPane::Terminal(terminal_pane) => {
let terminal_view = terminal_pane.terminal_view(app);
let terminal_view = terminal_view.as_ref(app);
has_unread_activity |=
has_unread_activity_for_terminal_view(terminal_view.id(), app);
let title_text = terminal_view.terminal_title_from_shell();
let working_directory = resolved_terminal_working_directory(terminal_view, app);
let working_directory_text = working_directory
Expand Down Expand Up @@ -2826,6 +2833,7 @@ fn build_vertical_tabs_summary_data(
primary_labels,
working_directories,
branch_entries: coalesce_summary_branch_entries(branch_entries),
has_unread_activity,
}
}

Expand Down Expand Up @@ -3637,6 +3645,9 @@ fn render_summary_tab_item(

// Title region. A custom-title or rename override short-circuits the per-label list and
// renders as a single line (no prefix slot, no overflow line).
let mut title_region = Flex::column()
.with_main_axis_size(MainAxisSize::Min)
.with_cross_axis_alignment(CrossAxisAlignment::Start);
if let Some(title_override) = render_title_override(
&props,
12.,
Expand All @@ -3645,9 +3656,9 @@ fn render_summary_tab_item(
appearance,
app,
) {
text_col.add_child(title_override);
title_region.add_child(title_override);
} else if summary.primary_labels.is_empty() {
text_col.add_child(render_text_line(
title_region.add_child(render_text_line(
&props.title,
main_text_color,
ClipConfig::end(),
Expand All @@ -3668,7 +3679,7 @@ fn render_summary_tab_item(
main_text_color,
appearance,
);
text_col.add_child(if idx == 0 {
title_region.add_child(if idx == 0 {
line
} else {
Container::new(line)
Expand All @@ -3680,7 +3691,7 @@ fn render_summary_tab_item(
let hidden_label_count =
summary_overflow_count(summary.primary_labels.len(), MAX_VISIBLE_PRIMARY_LABELS);
if hidden_label_count > 0 {
text_col.add_child(
title_region.add_child(
Container::new(render_summary_overflow_line(
hidden_label_count,
sub_text_color,
Expand All @@ -3691,6 +3702,24 @@ fn render_summary_tab_item(
);
}
}
let title_region = title_region.finish();
if summary.has_unread_activity {
text_col.add_child(
Flex::row()
.with_main_axis_size(MainAxisSize::Max)
.with_main_axis_alignment(MainAxisAlignment::SpaceBetween)
.with_cross_axis_alignment(CrossAxisAlignment::Center)
.with_child(Shrinkable::new(1., title_region).finish())
.with_child(
Container::new(render_title_indicator(theme))
.with_margin_left(4.)
.finish(),
)
.finish(),
);
} else {
text_col.add_child(title_region);
}

// Working-directory region.
let visible_directory_count = summary
Expand Down
1 change: 1 addition & 0 deletions app/src/workspace/view/vertical_tabs_tests.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,7 @@ fn summary_search_fragments_include_hidden_overflow_values() {
pull_request_label: Some("#789".to_string()),
},
],
has_unread_activity: false,
};

let fragments = summary_search_text_fragments(&summary, Some("Custom tab"));
Expand Down
Loading