镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
18b57e7
Agent: unify hover feedback and transitions
matthewrball Oct 28, 2026
dac283e
Agent: keep hover changes compatible with sidebar polish
matthewrball Oct 28, 2026
f829b66
Agent: ease Projects control visibility
matthewrball Oct 28, 2026
21a7c62
Agent: carry hover transitions onto master
matthewrball Nov 8, 2026
0769e9f
Agent: shared motion tokens, Tween and paint-offset primitives
matthewrball Nov 8, 2026
c3e0980
Agent: cover animated values and paint offsets with tests
matthewrball Nov 8, 2026
7000cc8
Agent: menus open from their trigger
matthewrball Nov 8, 2026
359b450
Agent: switches slide, dialogs rise, tooltips fade
matthewrball Nov 8, 2026
c9fd2c9
Agent: screens and Settings sections fade in
matthewrball Nov 8, 2026
8cb70fa
Agent: sidebar status and folding animate
matthewrball Nov 8, 2026
1b1f79c
Agent: live transcript rows rise in once as they arrive
matthewrball Nov 8, 2026
d0a5d69
Agent: queued messages rise in above the composer
matthewrball Nov 8, 2026
5818352
Agent: multi-line fields grow and shrink smoothly
matthewrball Nov 8, 2026
c8fcf99
Agent: composer expands and shrinks smoothly
matthewrball Nov 8, 2026
dfe93fe
Agent: Send and Stop cross-fade; attached files settle in
matthewrball Nov 8, 2026
25d7c76
Agent: settle the merged motion slices
matthewrball Nov 8, 2026
f0ab28e
Agent: new rows open at the tail and the sent message lands in place
matthewrball Nov 8, 2026
d068a56
Agent: describe how arriving rows open in the motion doc
matthewrball Nov 8, 2026
a5c0a0e
Agent: the composer glides between the empty state and the thread
matthewrball Nov 8, 2026
3e0f31f
Agent: decide a menu's entrance by the input that opened it
matthewrball Nov 8, 2026
8edbf38
Agent: the sent message opens its slot instead of jumping the transcript
matthewrball Nov 8, 2026
88cdef5
Agent: screen and Settings fades cover the content instead of fading it
matthewrball Nov 8, 2026
045e355
Agent: paint the swap cover over the content's bounds
matthewrball Nov 8, 2026
4099642
Agent: open the first send's slot in a new task, and fix review nits
matthewrball Nov 8, 2026
09fed4c
Agent: a run error or failed send takes the slot's place, and questio…
matthewrball Nov 8, 2026
323e43d
Agent: the custom amount field unfolds and folds back
matthewrball Nov 8, 2026
12b6cd5
Agent: the empty state's heading rides with the composer instead of b…
matthewrball Nov 8, 2026
ad8f708
Agent: whatever comes with the composer stretches while it shrinks back
matthewrball Nov 8, 2026
f850a08
Agent: the empty-state pane clips while the composer resizes
matthewrball Nov 9, 2026
a587cda
Merge master into #1515
matthewrball Nov 11, 2026
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
13 changes: 13 additions & 0 deletions apps/maple-agent/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,19 @@ popup before a nested one). The popup handles the rest:
A text field inside a menu that goes away calls `Popup::refocus`, so the
menu keeps the keyboard. Menu keys live in the keymap's `Menu` context.

## Motion

Animate through `ui/motion.rs` only; [motion](docs/motion.md) holds the
tokens and rules. Motion never delays input, retargets from the value on
screen, closes menus and dialogs at once, and settles immediately under
reduced motion. Streaming text, typing, and keyboard navigation do not
animate. Rows inside `gpui::list` time their entrance from view state, not
element state, so they never replay when scrolled back into view. Never put
opacity on an ancestor of a cached view: its replayed paint keeps the
opacity of its first frame. Paint a cover over it instead, as `swap_in`
does, unless its bounds or content mask change on every frame of the fade
and again at rest, as in the sidebar collapse.

## Accessibility

GPUI only reports elements with both an id and a role, and it builds the tree
Expand Down
94 changes: 92 additions & 2 deletions apps/maple-agent/app/src/desktop.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2383,6 +2383,20 @@ impl Render for MapleApp {
cx.refresh_windows();
}
let titlebar = self.titlebar.clone();
// The screen fades in when it changes kind; the first screen and a
// screen that only re-renders draw at once.
let screen_kind = if self.updater.restart_path().is_some() {
"restart"
} else {
match &self.screen {
Screen::Restoring => "restoring",
Screen::StoreGate(_) => "store-gate",
Screen::Login(_) => "login",
Screen::Chat(_) => "chat",
Screen::Settings(_) => "settings",
Screen::PlanGate(_) => "plan-gate",
}
};
let root = div()
.relative()
.size_full()
Expand All @@ -2397,7 +2411,7 @@ impl Render for MapleApp {
// on transcript/composer via `typography::chat_reading`.
.text_sm()
.child(titlebar)
.child(
.child(ui::motion::swap_in(
div()
.flex_1()
.min_h_0()
Expand Down Expand Up @@ -2442,7 +2456,10 @@ impl Render for MapleApp {
Screen::PlanGate(gate) => gate.clone().into_any_element(),
}
}),
);
"maple-screen",
screen_kind,
gpui::rgb(ui::theme::bg_app()),
));
#[cfg(target_os = "linux")]
let root = root.children(self.restart_confirmation.clone());
root
Expand Down Expand Up @@ -5323,6 +5340,79 @@ mod tests {
assert!(return_to_window(true, cx));
}

/// Settings and back brings the chat's sidebar back with its task rows.
/// The screen fade covers the whole window, so a regression there would
/// show as a sidebar whose rows never return.
#[gpui::test]
fn settings_and_back_keeps_the_sidebar_task_rows(cx: &mut gpui::TestAppContext) {
struct Host(Entity<MapleApp>);
impl Render for Host {
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
div().size_full().child(self.0.clone())
}
}
let settings = crate::settings::SETTINGS_IO_LOCK.lock();
let app = project_shell_locked(cx, &settings);
let chat = app.read_with(cx, |app, _| app.local_chat.clone().expect("local chat"));
chat.update(cx, |chat, cx| {
chat.seed_sessions_for_test(
vec![maple_agent::agent::AgentSessionSummary {
web_enabled: true,
permission_mode: Default::default(),
state: maple_agent::agent::AgentTaskState::Active,
acp: false,
id: "s1".into(),
title: "First task".into(),
project_root: "/same/project".into(),
created_ms: 0,
updated_ms: 0,
message_count: 1,
model: None,
}],
cx,
)
});
let (_, cx) = cx.add_window_view(|_, _| Host(app.clone()));
cx.simulate_resize(size(px(1100.), px(850.)));
cx.run_until_parked();
assert!(
cx.debug_bounds("session-s1").is_some(),
"the task row shows"
);

app.update(cx, |app, cx| app.show_settings(Section::General, cx));
cx.run_until_parked();
assert!(
cx.debug_bounds("session-s1").is_none(),
"Settings replaces the chat"
);
let snapshot = app.read_with(cx, |app, cx| match &app.screen {
Screen::Settings(screen) => screen.read(cx).settings_snapshot(),
_ => panic!("expected Settings"),
});
let start = std::time::Instant::now();
ui::motion::set_test_now(Some(start));
app.update(cx, |app, cx| app.close_settings(snapshot, cx));
cx.run_until_parked();
// Let the screen fade and anything the sidebar eases settle.
for step in 1..=4 {
ui::motion::set_test_now(Some(start + ui::motion::PANEL * step));
cx.update(|window, cx| {
window.simulate_next_frame(cx);
});
cx.run_until_parked();
}
// A cached view replays its last paint without recording debug
// bounds, so draw every view afresh before looking for the row.
cx.update(|_, cx| cx.refresh_windows());
cx.run_until_parked();
assert!(
cx.debug_bounds("session-s1").is_some(),
"the task row is back after Settings"
);
ui::motion::set_test_now(None);
}

#[gpui::test]
fn a_free_guest_gets_its_account_id_in_the_gate(cx: &mut gpui::TestAppContext) {
let settings = crate::settings::SETTINGS_IO_LOCK.lock();
Expand Down
Loading
Loading