use super::super::render;
use super::fixture::{World, world};
use super::input;
use crate::cli_outbound::Cli;
mod fold_line;
pub(super) struct Frames {
ctx: egui::Context,
lernie: Cli,
bl: Cli,
bz: Cli,
}
impl Frames {
pub(super) fn new() -> Self {
Self {
ctx: egui::Context::default(),
lernie: Cli::new("yog-absent-lernie"),
bl: Cli::new("yog-absent-bl"),
bz: Cli::new("yog-absent-bz"),
}
}
pub(super) fn run(&self, world: &mut World, t: f64) -> egui::FullOutput {
self.ctx.run(
egui::RawInput {
time: Some(t),
..input()
},
|ctx| {
render(
ctx,
&mut world.model,
&mut world.state,
&self.lernie,
&self.bl,
&self.bz,
);
},
)
}
pub(super) fn settle(&self, world: &mut World, t: f64) {
self.run(world, t);
self.run(world, t);
world.drain(&mut |world| {
self.run(world, t);
});
for i in 0..4 {
world.settle();
self.run(world, t + f64::from(i) * 0.01);
}
}
pub(super) fn panel(&self, id: &str) -> egui::Rect {
egui::containers::panel::PanelState::load(&self.ctx, egui::Id::new(id))
.expect("the panel stores its rect")
.rect
}
}
pub(super) fn deposit(world: &World, name: &str, at: &str, body: &str) {
let dir = world.ws.join("inbox/c-1");
std::fs::create_dir_all(&dir).unwrap();
std::fs::write(
dir.join(name),
format!("---\nfrom: user\ndeposited_at: {at}\n---\n{body}"),
)
.unwrap();
}
pub(super) fn quick(mut world: World) -> World {
std::fs::write(
world.model.state_root().join("cadence.yaml"),
"cadence:\n watcher:\n debounce_ms: 0\n",
)
.unwrap();
world.model.after_lernie_verb();
world.converge();
world
}
pub(super) fn converge_ws(world: &mut World) {
let ws = world.ws.clone();
world.model.mark_dirty([ws]);
world.converge();
}
pub(super) fn drain(world: &World) {
let dir = world.ws.join("inbox/c-1");
for entry in std::fs::read_dir(&dir).unwrap().flatten() {
std::fs::remove_file(entry.path()).unwrap();
}
}
fn center_y(painted: &[crate::paint_probe::Painted], needle: &str) -> f32 {
painted
.iter()
.find(|(text, _)| text.contains(needle))
.unwrap_or_else(|| panic!("{needle:?} not painted"))
.1
.center()
.y
}
#[test]
fn pending_items_stack_above_the_draft_with_fold_arrows_and_ram_folds() {
let mut world = quick(world());
let ws = world.ws.clone();
world.model.focus_agent(&ws, "c-1");
deposit(&world, "user-002.md", "t1", "second note\nmore detail");
converge_ws(&mut world);
let frames = Frames::new();
frames.settle(&mut world, 0.0);
let painted = crate::paint_probe::painted_of(&frames.run(&mut world, 0.1));
let first = center_y(&painted, "✉ user · t0");
let second = center_y(&painted, "✉ user · t1");
let box_hint = center_y(&painted, "message hello");
assert!(first < second, "oldest first: {first} !< {second}");
assert!(
second < box_hint,
"the draft is last: {second} !< {box_hint}"
);
let arrow_on = |y: f32| {
painted
.iter()
.any(|(text, rect)| text == "▶" && (rect.center().y - y).abs() < 3.0)
};
assert!(arrow_on(first), "the first pending row folds");
assert!(arrow_on(second), "the second pending row folds");
assert!(!arrow_on(box_hint), "the input has no arrow");
let text = crate::paint_probe::text_of(&frames.run(&mut world, 0.2));
assert!(text.contains("second note"), "folded preview:\n{text}");
assert!(!text.contains("more detail"), "folded body stays shut");
world
.state
.composer
.folds
.insert("inbox/c-1/user-002.md".to_owned());
let open = crate::paint_probe::text_of(&frames.run(&mut world, 0.3));
assert!(open.contains("more detail"), "the fold opens:\n{open}");
}
#[test]
fn the_in_flight_strip_sits_above_the_fold_line() {
let mut world = quick(world());
let ws = world.ws.clone();
world.model.focus_agent(&ws, "c-1");
let _lock = std::fs::File::open(ws.join("inbox/c-1")).unwrap();
let _writer = std::fs::OpenOptions::new()
.append(true)
.open(ws.join("steps/c-1/001/response.json"))
.unwrap();
converge_ws(&mut world);
let frames = Frames::new();
frames.settle(&mut world, 0.0);
let text = crate::paint_probe::text_of(&frames.run(&mut world, 0.1));
assert!(
text.contains("a model call is streaming"),
"the strip is up:\n{text}"
);
let strip = frames.panel("flight-strip");
let composer = frames.panel("composer");
assert!(
strip.bottom() <= composer.top() + 1.0,
"the strip sits above the fold line: strip {} !≤ composer {}",
strip.bottom(),
composer.top()
);
}