Make the Rust client the sole app

This commit is contained in:
iris committed 2026-09-11 01:18:24 -04:00
1 parent a8602c1626
commit d8bb1699a8
230 files changed
+762 -27300

No files matched your search

+102
View File
@@ -0,0 +1,102 @@
use iris::prelude::*;
use winit::{dpi::PhysicalSize, window::WindowAttributes};
fn ime_argv() -> Option<f32> {
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
if arg == "--ime" {
return args.next()?.parse().ok();
}
}
None
}
fn message_argv() -> Option<String> {
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
if arg == "--message" {
return Some(args.next()?.replace("\\n", "\n"));
}
}
None
}
fn typed_argv() -> Option<String> {
let mut args = std::env::args().skip(1);
while let Some(arg) = args.next() {
if arg == "--typed" {
return Some(args.next()?.replace("\\n", "\n"));
}
}
None
}
fn main() {
DesktopApp::<Client>::run();
}
#[derive(DesktopUiState)]
pub struct Client {
ui_state: DesktopUiState,
#[allow(dead_code)]
screen: Option<ai_app::ui::TranscriptScreen>,
}
impl DesktopAppState for Client {
fn window_attributes() -> WindowAttributes {
WindowAttributes::default()
.with_title("iris transcript (bench fixture)")
.with_inner_size(PhysicalSize::new(
ai_app::ui::fixture::PHONE_WIDTH,
ai_app::ui::fixture::PHONE_HEIGHT,
))
}
fn new(
mut ui_state: DesktopUiState,
rsc: &mut DesktopRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
let screen = match ai_app::ui::fixture::open(rsc, &mut ui_state) {
Ok(opened) => {
if let Some(message) = message_argv() {
opened.screen.composer.field.edit(rsc).set(&message);
}
if let Some(text) = typed_argv() {
let field = opened.screen.composer.field;
let redraw = rsc.tasks.redraw_handle();
rsc.spawn_task(async move |mut ctx| {
for ch in text.chars() {
tokio::time::sleep(std::time::Duration::from_millis(100)).await;
ctx.update(move |state: &mut Client, rsc| {
state.set_focus(Some(field));
let end = rsc[field].text().len();
let mut edit = field.edit(rsc);
if edit.text.caret().is_none() {
edit.set_cursor_byte(end);
}
edit.insert(&ch.to_string());
});
redraw.request_redraw();
}
});
}
if let Some(inset) = ime_argv() {
opened.screen.composer.set_bottom_inset(rsc, inset);
}
Some(opened.screen)
}
Err(message) => {
let text = wtext(format!("Couldn't fold the bench fixture: {message}"))
.color(PaintId::WHITE)
.wrap(true)
.pad(dp(16))
.add_strong(rsc)
.any();
ui_state.set_root(rsc, text);
None
}
};
Self { ui_state, screen }
}
}
+208
View File
@@ -0,0 +1,208 @@
use ai_app::client::QuestionOption;
use ai_app::client::transcript_fold::{QuestionCard, TranscriptItem, TranscriptRow as FoldedRow};
use iris::prelude::*;
fn main() {
DesktopApp::<Client>::run();
}
#[derive(DesktopUiState)]
pub struct Client {
ui_state: DesktopUiState,
#[allow(dead_code)]
screen: ai_app::ui::TranscriptScreen,
}
fn msg(seq: u64, from_user: bool, text: &str) -> FoldedRow {
FoldedRow::Single(if from_user {
TranscriptItem::UserMsg {
seq,
text: text.to_string(),
attachments: Vec::new(),
}
} else {
TranscriptItem::AssistantMsg {
seq,
text: text.to_string(),
settled: true,
}
})
}
fn tool_call(id: &str, tool: &str, input: &str, result: Option<(&str, bool)>) -> TranscriptItem {
tool_call_in("run1", id, tool, input, result)
}
fn tool_call_in(
run: &str,
id: &str,
tool: &str,
input: &str,
result: Option<(&str, bool)>,
) -> TranscriptItem {
TranscriptItem::ToolRun {
seq: 3,
id: id.into(),
run_id: run.into(),
tool: tool.into(),
input: input.into(),
output: result.map(|(out, _)| out.to_string()).unwrap_or_default(),
done: result.is_some(),
failed: result.is_some_and(|(_, failed)| failed),
asks: Vec::new(),
images: Vec::new(),
}
}
fn asking(id: &str, tool: &str, input: &str) -> TranscriptItem {
let mut call = tool_call_in("run2", id, tool, input, None);
if let TranscriptItem::ToolRun { asks, .. } = &mut call {
asks.push(QuestionCard {
seq: 9,
id: format!("{id}-q"),
prompt: "Allow this command?".into(),
header: None,
options: vec![
QuestionOption {
label: "Allow".into(),
description: None,
preview: None,
},
QuestionOption {
label: "Deny".into(),
description: None,
preview: None,
},
],
multi_select: false,
answers: Vec::new(),
});
}
call
}
fn long_output() -> String {
(0..200)
.map(|i| format!("test ai_app::ui::case_{i} ... ok"))
.collect::<Vec<_>>()
.join("\n")
}
fn synthetic_rows() -> Vec<FoldedRow> {
vec![
msg(
1,
true,
"Can you show me a **bold** word, some *italic* text, and `inline code`?",
),
msg(
2,
false,
"# Sure\n\nHere's a [link to the repo](https://example.com/ai-app-2) and a fenced block:\n\n```rust\nfn main() {\n println!(\"hi\");\n}\n```",
),
FoldedRow::Tools(vec![
tool_call(
"t1",
"Read",
r#"{"file_path": "src/main.rs"}"#,
Some(("fn main() {}\n", false)),
),
tool_call(
"t2",
"Bash",
r#"{"command": "cargo build --release", "timeout": 480000, "description": "Build it"}"#,
Some((
"error: could not compile `iris`\nCaused by: linker not found",
true,
)),
),
tool_call("t3", "Grep", r#"{"pattern": "fn fold_event"}"#, None),
]),
FoldedRow::Single(tool_call(
"t5",
"Bash",
r#"{"command": "cargo test -p transcript-ui -- --nocapture"}"#,
Some((&long_output(), false)),
)),
msg(6, true, "Looks good, thanks!"),
msg(7, false, BLOCK_SAMPLER),
]
}
const BLOCK_SAMPLER: &str = "\
## What changed
Iris **fold** render measure session window anchor context transcript \
iris measure iris scroll call transcript layout *cursor* context, and a \
[bench](https://example.com/bench) link.
```rust
fn fold_event(items: Vec<Item>, seq: u64) -> Vec<Item> {
let mut out = items;
out.push(Item::new(seq));
out
}
```
| column | value |
|---|---|
| a | measure place draw tool call token context window anchor |
- one bullet
- another, with `inline code`
- nested one level
1. first numbered
2. second numbered
> A quoted line, to show the bar and the indent.
";
impl DesktopAppState for Client {
fn new(
mut ui_state: DesktopUiState,
rsc: &mut DesktopRsc<Self>,
_: Proxy<Self::Event>,
) -> Self {
let mut screen = ai_app::ui::build(rsc, &mut ui_state, synthetic_rows());
screen.push_row(
rsc,
&FoldedRow::Single(TranscriptItem::CommandRow {
seq: 8,
text: "clear".into(),
}),
);
screen.push_row(
rsc,
&FoldedRow::Tools(vec![
tool_call_in(
"run2",
"t6",
"Read",
r#"{"file_path": "docs/RUST.md"}"#,
Some(("# Moving the app to Rust\n", false)),
),
tool_call_in(
"run2",
"t7",
"Bash",
r#"{"command": "cargo clippy --workspace --all-targets"}"#,
Some(("error: unused variable `x`", true)),
),
tool_call_in("run2", "t8", "Glob", r#"{"pattern": "**/*.rs"}"#, None),
asking(
"t9",
"Bash",
r#"{"command": "rm -rf target", "timeout": 120000, "description": "Clear the build"}"#,
),
]),
);
screen.set_session_working(rsc, true);
if std::env::var_os("IRIS_TOOLS_EXPANDED").is_some() {
assert!(
screen.expand_tail_tools(rsc, true),
"the newest row must be the tool run this flag is about"
);
}
Self { ui_state, screen }
}
}