T25a Generative: prompt and settings
PromptEnhancer offers a fuller prompt with the words it adds washed, split at punctuation; Use this takes it into the field, Keep mine lets it go. StylePresetPicker and AspectRatioPicker are rows of Tab stops, the chosen one in accent; a ratio chip draws its shape. SeedInput keeps digits up to u32::MAX and rolls one with a die. NegativePromptInput and ImageGenerationPanel point to forms::TagInput and forms::Form. theme.generative() holds the chapter's sizes. The gallery's preset pictures are the atrium photo in five styles.
This commit is contained in:
1 parent
173e89ed03
commit
66345b7e24
22 files changed
+1169
-10
No files matched your search
@@ -0,0 +1,216 @@
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use gpui::{
|
||||
AppContext as _, Context, Entity, IntoElement, KeyBinding, KeyUpEvent, Keystroke,
|
||||
ParentElement, Render, SharedString, Styled, TestAppContext, VisualTestContext, Window, div,
|
||||
px,
|
||||
};
|
||||
|
||||
use crate::{
|
||||
forms::{self, TextInput},
|
||||
generative::{AspectRatioPicker, PromptEnhancer, SeedInput, StylePreset, StylePresetPicker},
|
||||
primitives::FocusNext,
|
||||
theme::Theme,
|
||||
};
|
||||
|
||||
fn settle(cx: &mut VisualTestContext) {
|
||||
cx.run_until_parked();
|
||||
cx.update(|window, _| window.refresh());
|
||||
cx.run_until_parked();
|
||||
}
|
||||
|
||||
/// Presses and releases `key`, a frame apart.
|
||||
fn press(key: &str, cx: &mut VisualTestContext) {
|
||||
cx.simulate_keystrokes(key);
|
||||
settle(cx);
|
||||
cx.simulate_event(KeyUpEvent {
|
||||
keystroke: Keystroke::parse(key).expect("a key"),
|
||||
});
|
||||
settle(cx);
|
||||
}
|
||||
|
||||
fn tab(times: usize, cx: &mut VisualTestContext) {
|
||||
for _ in 0..times {
|
||||
cx.update(|window, _| window.focus_next());
|
||||
}
|
||||
}
|
||||
|
||||
fn setup(cx: &mut TestAppContext) {
|
||||
cx.update(|cx| {
|
||||
Theme::init(cx);
|
||||
forms::bind_keys(cx);
|
||||
cx.bind_keys([KeyBinding::new("tab", FocusNext, None)]);
|
||||
});
|
||||
}
|
||||
|
||||
/// A prompt with its enhancer, a suggestion the test sets, and what it heard.
|
||||
struct Enhancing {
|
||||
field: Entity<TextInput>,
|
||||
suggestion: Option<SharedString>,
|
||||
heard: Vec<String>,
|
||||
}
|
||||
|
||||
impl Render for Enhancing {
|
||||
fn render(&mut self, _: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
|
||||
let (asked, resolved) = (cx.entity(), cx.entity());
|
||||
let enhancer = PromptEnhancer::new("enhancer", &self.field)
|
||||
.on_enhance(move |prompt, _, cx| {
|
||||
asked.update(cx, |view, _| view.heard.push(format!("asked {prompt}")))
|
||||
})
|
||||
.on_resolve(move |taken, _, cx| {
|
||||
resolved.update(cx, |view, _| {
|
||||
view.heard.push(format!("taken {taken}"));
|
||||
view.suggestion = None;
|
||||
})
|
||||
});
|
||||
let enhancer = match &self.suggestion {
|
||||
Some(text) => enhancer.suggestion(text.clone()),
|
||||
None => enhancer,
|
||||
};
|
||||
div().w(px(420.0)).child(enhancer)
|
||||
}
|
||||
}
|
||||
|
||||
fn enhancing(cx: &mut TestAppContext) -> (Entity<Enhancing>, &mut VisualTestContext) {
|
||||
setup(cx);
|
||||
let (view, cx) = cx.add_window_view(|window, cx| Enhancing {
|
||||
field: cx.new(|cx| TextInput::new(window, cx)),
|
||||
suggestion: None,
|
||||
heard: Vec::new(),
|
||||
});
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
(view, cx)
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn enhance_asks_with_the_trimmed_prompt_and_use_this_takes_the_offer(cx: &mut TestAppContext) {
|
||||
let (view, cx) = enhancing(cx);
|
||||
tab(1, cx);
|
||||
cx.simulate_input(" a cat ");
|
||||
tab(1, cx);
|
||||
press("enter", cx);
|
||||
view.update(cx, |view, cx| {
|
||||
view.suggestion = Some("a fluffy cat asleep".into());
|
||||
cx.notify();
|
||||
});
|
||||
settle(cx);
|
||||
tab(1, cx);
|
||||
press("enter", cx);
|
||||
view.read_with(cx, |view, cx| {
|
||||
assert_eq!(view.heard, ["asked a cat", "taken true"]);
|
||||
assert_eq!(view.field.read(cx).text(), "a fluffy cat asleep");
|
||||
});
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn keep_mine_leaves_the_prompt_as_written(cx: &mut TestAppContext) {
|
||||
let (view, cx) = enhancing(cx);
|
||||
tab(1, cx);
|
||||
cx.simulate_input("a cat");
|
||||
view.update(cx, |view, cx| {
|
||||
view.suggestion = Some("a fluffy cat".into());
|
||||
cx.notify();
|
||||
});
|
||||
settle(cx);
|
||||
tab(3, cx);
|
||||
press("enter", cx);
|
||||
view.read_with(cx, |view, cx| {
|
||||
assert_eq!(view.heard, ["taken false"]);
|
||||
assert_eq!(view.field.read(cx).text(), "a cat");
|
||||
});
|
||||
}
|
||||
|
||||
/// Three presets, two ratios, a seed, and every pick heard.
|
||||
struct Picks {
|
||||
seed: Entity<TextInput>,
|
||||
style: SharedString,
|
||||
heard: Rc<RefCell<Vec<String>>>,
|
||||
}
|
||||
|
||||
impl Render for Picks {
|
||||
fn render(&mut self, _: &mut Window, _: &mut Context<Self>) -> impl IntoElement {
|
||||
let (styled, shaped) = (self.heard.clone(), self.heard.clone());
|
||||
let preset = |key: &str| StylePreset {
|
||||
key: key.to_string().into(),
|
||||
name: key.to_string().into(),
|
||||
picture: None,
|
||||
};
|
||||
div()
|
||||
.w(px(420.0))
|
||||
.child(
|
||||
StylePresetPicker::new(
|
||||
"styles",
|
||||
[preset("a"), preset("b"), preset("c")],
|
||||
self.style.clone(),
|
||||
)
|
||||
.on_select(move |key, _, _| styled.borrow_mut().push(format!("style {key}"))),
|
||||
)
|
||||
.child(
|
||||
AspectRatioPicker::new("ratios", [(1, 1), (4, 3)], (1, 1)).on_select(
|
||||
move |ratio, _, _| {
|
||||
shaped
|
||||
.borrow_mut()
|
||||
.push(format!("ratio {}:{}", ratio.0, ratio.1))
|
||||
},
|
||||
),
|
||||
)
|
||||
.child(SeedInput::new("seed", &self.seed))
|
||||
}
|
||||
}
|
||||
|
||||
fn picks(
|
||||
cx: &mut TestAppContext,
|
||||
) -> (
|
||||
Entity<Picks>,
|
||||
Rc<RefCell<Vec<String>>>,
|
||||
&mut VisualTestContext,
|
||||
) {
|
||||
setup(cx);
|
||||
let heard = Rc::new(RefCell::new(Vec::new()));
|
||||
let store = heard.clone();
|
||||
let (view, cx) = cx.add_window_view(|window, cx| Picks {
|
||||
seed: cx.new(|cx| TextInput::new(window, cx)),
|
||||
style: "a".into(),
|
||||
heard: store,
|
||||
});
|
||||
cx.update(|window, _| window.activate_window());
|
||||
settle(cx);
|
||||
(view, heard, cx)
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn tabs_reach_each_tile_and_chip_and_enter_picks_it(cx: &mut TestAppContext) {
|
||||
let (_, heard, cx) = picks(cx);
|
||||
tab(1, cx);
|
||||
press("enter", cx);
|
||||
tab(1, cx);
|
||||
press("enter", cx);
|
||||
tab(3, cx);
|
||||
press("enter", cx);
|
||||
assert_eq!(
|
||||
*heard.borrow(),
|
||||
["style b", "ratio 4:3"],
|
||||
"the chosen tile stays quiet"
|
||||
);
|
||||
}
|
||||
|
||||
#[gpui::test]
|
||||
fn a_seed_keeps_digits_and_the_die_rolls_one(cx: &mut TestAppContext) {
|
||||
let (view, _, cx) = picks(cx);
|
||||
tab(6, cx);
|
||||
cx.simulate_input("12a3");
|
||||
view.read_with(cx, |view, cx| {
|
||||
assert_eq!(SeedInput::read(&view.seed, cx), Some(123))
|
||||
});
|
||||
tab(1, cx);
|
||||
press("enter", cx);
|
||||
view.read_with(cx, |view, cx| {
|
||||
let text = view.seed.read(cx).text().to_string();
|
||||
assert_ne!(text, "123", "the die rolls a new seed");
|
||||
assert_eq!(
|
||||
SeedInput::read(&view.seed, cx).map(|seed| seed.to_string()),
|
||||
Some(text)
|
||||
);
|
||||
});
|
||||
}
|
||||
Reference in new issue
Block a user