T24e: Agent, environment
- SandboxStatus: a dot and a word (a ring while it starts or works), what it uses of what it has as meters, and how long it has run; the header wraps - McpServerList over lists::ListItem: a dot per state, what it runs or why it failed, its tools once connected, and a switch - ToolRegistry: tools grouped by source, found by every word of the owner's query, each Allow, Ask or Deny; rows wrap at 280px - MemoryPanel: a line to keep a memory, Enter or Remember, blank words kept out; each memory with its source, when, and Forget - CheckpointList over data_display::Timeline, newest first, the current point marked, Rewind on the rest - gallery: every control wired to state
This commit is contained in:
1 parent
8550c086b0
commit
cbdcf85cec
9 files changed
+1245
-5
No files matched your search
@@ -0,0 +1,170 @@
|
||||
use gpui::{
|
||||
AppContext as _, Context, Entity, IntoElement, KeyBinding, ParentElement, Render, SharedString,
|
||||
Styled, TestAppContext, VisualTestContext, Window, div, px,
|
||||
};
|
||||
use jiff::Timestamp;
|
||||
|
||||
use super::{press, settle};
|
||||
use crate::{
|
||||
agent::{
|
||||
Checkpoint, CheckpointList, McpServer, McpServerList, Memory, MemoryPanel, SandboxState,
|
||||
SandboxStatus, ServerState,
|
||||
},
|
||||
forms::{self, TextInput},
|
||||
primitives::FocusNext,
|
||||
theme::Theme,
|
||||
};
|
||||
|
||||
fn setup(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
Theme::init(cx);
|
||||
forms::bind_keys(cx);
|
||||
cx.bind_keys([KeyBinding::new("tab", FocusNext, None)]);
|
||||
});
|
||||
}
|
||||
|
||||
/// A memory panel over a field, and the words it kept.
|
||||
struct Memories {
|
||||
field: Entity<TextInput>,
|
||||
kept: Vec<String>,
|
||||
}
|
||||
|
||||
impl Render for Memories {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
let memory = Memory {
|
||||
key: "tone".into(),
|
||||
text: "Prefers plain words".into(),
|
||||
source: None,
|
||||
at: Timestamp::UNIX_EPOCH,
|
||||
};
|
||||
div().w(px(360.0)).child(
|
||||
MemoryPanel::new("memory", &self.field, [memory])
|
||||
.on_add(move |text, _, cx| {
|
||||
view.update(cx, |memories, _| memories.kept.push(text.to_string()))
|
||||
})
|
||||
.on_forget(|_, _, _| {}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn memories(cx: &mut TestAppContext) -> (Entity<Memories>, &mut VisualTestContext) {
|
||||
setup(cx);
|
||||
let (view, cx) = cx.add_window_view(|window, cx| Memories {
|
||||
field: cx.new(|cx| TextInput::new(window, cx)),
|
||||
kept: Vec::new(),
|
||||
});
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
(view, cx)
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn enter_keeps_a_memory_and_empties_the_field(cx: &mut TestAppContext) {
|
||||
let (view, cx) = memories(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
cx.simulate_input(" ");
|
||||
press("enter", cx);
|
||||
cx.simulate_input("Lifts stay under 20%");
|
||||
press("enter", cx);
|
||||
view.read_with(cx, |memories, cx| {
|
||||
assert_eq!(
|
||||
memories.kept,
|
||||
["Lifts stay under 20%"],
|
||||
"blank words are kept out"
|
||||
);
|
||||
assert_eq!(memories.field.read(cx).text(), "");
|
||||
});
|
||||
}
|
||||
|
||||
/// Two servers, and the switches thrown.
|
||||
struct Servers {
|
||||
thrown: Vec<(SharedString, bool)>,
|
||||
}
|
||||
|
||||
impl Render for Servers {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
let server = |key: &str, state| McpServer {
|
||||
key: key.to_string().into(),
|
||||
name: key.to_string().into(),
|
||||
detail: "npx server".into(),
|
||||
state,
|
||||
tools: 3,
|
||||
};
|
||||
div().w(px(360.0)).child(
|
||||
McpServerList::new(
|
||||
"servers",
|
||||
[
|
||||
server("files", ServerState::Connected),
|
||||
server("web", ServerState::Off),
|
||||
],
|
||||
)
|
||||
.on_toggle(move |key, on, _, cx| {
|
||||
view.update(cx, |servers, _| servers.thrown.push((key.clone(), on)))
|
||||
}),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_servers_switch_turns_it_off_and_on(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let (view, cx) = cx.add_window_view(|_, _| Servers { thrown: Vec::new() });
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
press("space", cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
press("space", cx);
|
||||
let thrown = view.read_with(cx, |servers, _| servers.thrown.clone());
|
||||
assert_eq!(thrown, [("files".into(), false), ("web".into(), true)]);
|
||||
}
|
||||
|
||||
/// Three checkpoints standing on the middle one, and the rewinds asked for.
|
||||
struct Points {
|
||||
asked: Vec<usize>,
|
||||
}
|
||||
|
||||
impl Render for Points {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let view = cx.entity();
|
||||
let point = |key: &str| Checkpoint {
|
||||
key: key.to_string().into(),
|
||||
label: key.to_string().into(),
|
||||
at: Timestamp::UNIX_EPOCH,
|
||||
files: 2,
|
||||
};
|
||||
div().w(px(360.0)).child(
|
||||
CheckpointList::new(
|
||||
"points",
|
||||
[point("first"), point("second"), point("third")],
|
||||
1,
|
||||
)
|
||||
.on_rewind(move |ix, _, cx| view.update(cx, |points, _| points.asked.push(ix))),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn every_checkpoint_but_the_current_rewinds(cx: &mut TestAppContext) {
|
||||
setup(cx);
|
||||
let (view, cx) = cx.add_window_view(|_, _| Points { asked: Vec::new() });
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
press("enter", cx);
|
||||
cx.update(|window, _| window.focus_next());
|
||||
press("enter", cx);
|
||||
assert_eq!(
|
||||
view.read_with(cx, |points, _| points.asked.clone()),
|
||||
[2, 0],
|
||||
"newest first, the current skipped"
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
#[should_panic(expected = "memory 5 of 4")]
|
||||
fn memory_past_its_limit_fails_loud() {
|
||||
let _ = SandboxStatus::new("box", "python", SandboxState::Ready).memory(5, 4);
|
||||
}
|
||||
@@ -4,6 +4,7 @@ use gpui::{
|
||||
};
|
||||
|
||||
mod changes;
|
||||
mod environment;
|
||||
mod previews;
|
||||
|
||||
use super::{AgentProgress, Permission, PermissionPrompt, ToolApprovalDialog};
|
||||
|
||||
Reference in new issue
Block a user