diff --git a/AgentCompany-Rust/crates/app_ui/src/app_root/inbox.rs b/AgentCompany-Rust/crates/app_ui/src/app_root/inbox.rs index 579885bc..5e3d6a1b 100644 --- a/AgentCompany-Rust/crates/app_ui/src/app_root/inbox.rs +++ b/AgentCompany-Rust/crates/app_ui/src/app_root/inbox.rs @@ -1,4 +1,6 @@ use super::*; +use gpui::{Animation, AnimationExt, pulsating_between}; +use std::time::Duration; impl AppRoot { pub(super) fn render_inbox(&self) -> impl IntoElement + '_ { @@ -192,7 +194,7 @@ fn inbox_filters( } fn filter_chip(label: &'static str, count: usize, active: bool) -> impl IntoElement { - div() + let chip = div() .px_4() .py_2() .rounded_full() @@ -207,7 +209,19 @@ fn filter_chip(label: &'static str, count: usize, active: bool) -> impl IntoElem } else { theme::text_secondary() }) - .child(format!("{} {}", label, count)) + .child(format!("{} {}", label, count)); + if active { + chip.with_animation( + ("inbox-filter", label.len()), + Animation::new(Duration::from_secs(2)) + .repeat() + .with_easing(pulsating_between(0.82, 1.0)), + |element, delta| element.opacity(delta), + ) + .into_any_element() + } else { + chip.into_any_element() + } } fn inbox_group(label: &'static str, rows: impl Iterator) -> impl IntoElement { diff --git a/AgentCompany-Rust/crates/app_ui/src/app_root/onboarding.rs b/AgentCompany-Rust/crates/app_ui/src/app_root/onboarding.rs index 57c51d46..42b68f85 100644 --- a/AgentCompany-Rust/crates/app_ui/src/app_root/onboarding.rs +++ b/AgentCompany-Rust/crates/app_ui/src/app_root/onboarding.rs @@ -1,4 +1,6 @@ use super::*; +use gpui::{Animation, AnimationExt, pulsating_between}; +use std::time::Duration; impl AppRoot { pub(super) fn render_onboarding(&self, cx: &mut Context) -> impl IntoElement + '_ { @@ -151,7 +153,7 @@ fn onboarding_steps() -> impl IntoElement { } fn step_chip(label: &'static str, complete: bool) -> impl IntoElement { - div() + let chip = div() .px_4() .py_2() .rounded_full() @@ -166,7 +168,19 @@ fn step_chip(label: &'static str, complete: bool) -> impl IntoElement { } else { theme::text_secondary() }) - .child(label) + .child(label); + if complete { + chip.with_animation( + ("step-chip", label.len()), + Animation::new(Duration::from_secs(3)) + .repeat() + .with_easing(pulsating_between(0.78, 1.0)), + |element, delta| element.opacity(delta), + ) + .into_any_element() + } else { + chip.into_any_element() + } } fn readiness_panel() -> impl IntoElement { @@ -222,7 +236,14 @@ fn success_badge() -> impl IntoElement { .w(px(32.0)) .h(px(32.0)) .rounded_full() - .bg(theme::accent_teal()), + .bg(theme::accent_teal()) + .with_animation( + "success-badge-inner", + Animation::new(Duration::from_secs(2)) + .repeat() + .with_easing(pulsating_between(0.72, 1.0)), + |element, delta| element.opacity(delta), + ), ) } diff --git a/AgentCompany-Rust/crates/app_ui/src/app_root/runs.rs b/AgentCompany-Rust/crates/app_ui/src/app_root/runs.rs index f8169b52..ce766375 100644 --- a/AgentCompany-Rust/crates/app_ui/src/app_root/runs.rs +++ b/AgentCompany-Rust/crates/app_ui/src/app_root/runs.rs @@ -1,4 +1,6 @@ use super::*; +use gpui::{Animation, AnimationExt, pulsating_between}; +use std::time::Duration; impl AppRoot { pub(super) fn render_runs(&self, cx: &mut Context) -> impl IntoElement + '_ { @@ -319,7 +321,7 @@ fn run_workspace_name(snapshot: &domain::ProfileSnapshot, run: &domain::Run) -> } fn run_status_badge(status: &str) -> impl IntoElement { - div() + let badge = div() .px_3() .py_2() .rounded_full() @@ -334,7 +336,20 @@ fn run_status_badge(status: &str) -> impl IntoElement { "failed" | "timed_out" | "cancelled" | "interrupted" => theme::danger(), _ => theme::text_secondary(), }) - .child(status.to_uppercase()) + .child(status.to_uppercase()); + if status == "running" { + badge + .with_animation( + ("run-badge", 1usize), + Animation::new(Duration::from_secs(2)) + .repeat() + .with_easing(pulsating_between(0.8, 1.0)), + |element, delta| element.opacity(delta), + ) + .into_any_element() + } else { + badge.into_any_element() + } } fn info_tile(label: &'static str, value: String) -> impl IntoElement { diff --git a/AgentCompany-Rust/prd.json b/AgentCompany-Rust/prd.json index 8519e678..444ec6ad 100644 --- a/AgentCompany-Rust/prd.json +++ b/AgentCompany-Rust/prd.json @@ -98,8 +98,8 @@ "Verify in native app" ], "priority": 7, - "passes": false, - "notes": "High-value remaining parity layer." + "passes": true, + "notes": "Completed with GPUI-native animation wrappers on onboarding success states, active inbox filters, and running run status emphasis." }, { "id": "US-008", diff --git a/AgentCompany-Rust/progress.txt b/AgentCompany-Rust/progress.txt index 4a0b9e0e..27cf1837 100644 --- a/AgentCompany-Rust/progress.txt +++ b/AgentCompany-Rust/progress.txt @@ -9,3 +9,4 @@ Started: 2026-04-19 - Standing constraint: all code is written directly in the current context; no subagents or agent teams are used. - Completed real settings/workspace state mutations in native Rust path: theme, locale, execution mode, and primary workspace. - Added native_settings_workspace_e2e.rs to verify mutation persistence across reopen. +- Completed first native motion/state parity layer using GPUI animation wrappers for onboarding, inbox, and runs.