Skip to main content

AppOptions

Struct AppOptions 

Source
pub struct AppOptions { /* private fields */ }
Expand description

Configuration for AppOptions::start.

Defaults to environment detection without probing, Ctrl-C to quit, and base-tree UiEvent::Cancel to shut down. A cancel from inside a visible modal overlay dismisses that layer instead of quitting.

Implementations§

Source§

impl AppOptions

Source

pub fn new() -> Self

Creates the default application configuration.

Examples found in repository?
examples/companies.rs (line 193)
192async fn main() -> io::Result<()> {
193	let mut app = AppOptions::new()
194		.mouse()
195		.probe(Duration::from_millis(150))
196		.graphics_with(forced_from_args)
197		.start(|env| build_ui(env.viewport, env.ctx))
198		.await?;
199	if app.caps().graphics != Graphics::Cells {
200		for (index, provider) in PROVIDERS.iter().enumerate() {
201			let png = tokio::fs::read(format!("{ASSET_DIR}/{}.png", provider.id)).await?;
202			app.renderer_mut().register_image(
203				u32::try_from(index + 1).expect("provider count fits image IDs"),
204				png,
205			)?;
206		}
207		app.ui_mut().invalidate(SCROLL_ID);
208	}
209	show_stats(&mut app, None);
210	let mut chosen: Option<String> = None;
211	while let Some(event) = app.next().await? {
212		match event {
213			AppEvent::Resized(viewport) => {
214				app.ui_mut().set_height(SCROLL_ID, scroll_height(viewport));
215			},
216			AppEvent::Pressed(id) => chosen = Some(id.to_string()),
217			_ => {},
218		}
219		show_stats(&mut app, chosen.as_deref());
220	}
221	Ok(())
222}
More examples
Hide additional examples
examples/tml.rs (line 49)
42async fn main() -> io::Result<()> {
43	let path = std::env::args()
44		.nth(1)
45		.unwrap_or_else(|| "example.tml".into());
46	let source = std::fs::read_to_string(&path)?;
47
48	let mut ctx = None;
49	let mut app = AppOptions::new()
50		.quit([Key::Ctrl('c'), Key::Char('q'), Key::Esc])
51		.start(|env| {
52			let ui = build(&source, env.viewport.width, &env.ctx);
53			ctx = Some(env.ctx);
54			ui
55		})
56		.await?;
57	let ctx = ctx.expect("start ran the builder");
58
59	let handle = app.handle();
60	tokio::spawn(async move {
61		let mut seen = modified(path.as_ref());
62		loop {
63			tokio::time::sleep(Duration::from_millis(150)).await;
64			let stamp = modified(path.as_ref());
65			if stamp == seen {
66				continue;
67			}
68			seen = stamp;
69			let Ok(source) = std::fs::read_to_string(&path) else {
70				continue;
71			};
72			let ctx = ctx.clone();
73			handle.update(move |ui| *ui = build(&source, ui.frame().size().width, &ctx));
74		}
75	});
76
77	while app.next().await?.is_some() {}
78	Ok(())
79}
examples/gallery/main.rs (line 113)
112async fn main() -> io::Result<()> {
113	let mut app = AppOptions::new()
114		.mouse()
115		.quit([Key::Ctrl('c'), Key::Ctrl('q')])
116		.start(|env| build_ui(env.viewport, env.ctx))
117		.await?;
118	// The picker tab opens with the first model's details, like the chat
119	// overlay does.
120	picker::show_detail_on(app.ui_mut(), Some(0));
121
122	let mut synced = String::new();
123	let mut lab = anim::Lab::new();
124	let mut layers = Layers::default();
125	let mut next_step = tokio::time::Instant::now() + anim::AUTOPLAY_STEP;
126
127	loop {
128		let event = tokio::select! {
129			event = app.next() => match event? {
130				Some(event) => event,
131				None => break,
132			},
133			() = tokio::time::sleep_until(next_step) => {
134				if lab.autoplay && active_tab(app.ui()) == "Anim" {
135					lab.advance(app.ui_mut());
136				}
137				next_step += anim::AUTOPLAY_STEP;
138				continue;
139			},
140		};
141		match event {
142			AppEvent::Resized(viewport) => {
143				for pane in render::PANE_IDS {
144					app.ui_mut().set_height(pane, render::pane_height(viewport));
145				}
146			},
147			AppEvent::Key(key) => match active_tab(app.ui()).as_str() {
148				"Anim" => lab.handle_key(key, app.ui_mut()),
149				"Overlay" => match key {
150					Key::Ctrl('k') if layers.picker.is_none() => {
151						layers.picker = Some(overlay::show_picker(app.ui_mut()));
152					},
153					Key::Ctrl('g') => match layers.help.take() {
154						Some(id) => {
155							app.ui_mut().close_overlay(id);
156						},
157						None => layers.help = Some(overlay::show_help(app.ui_mut())),
158					},
159					_ => {},
160				},
161				_ => {},
162			},
163			// The Overlay tab's modal select committed a model.
164			AppEvent::Changed { id, value } if id == "model" => {
165				if let Some(overlay) = layers.picker.take() {
166					let label = overlay::MODELS
167						.iter()
168						.find(|(short, ..)| *short == value)
169						.map_or(value.as_str(), |(_, label, _)| label);
170					app.ui_mut().set_text("status", format!("model: {label}"));
171					app.ui_mut().close_overlay(overlay);
172				}
173			},
174			// The Picker tab's select moved: mirror the chat picker's
175			// facts-and-chips detail line.
176			AppEvent::Highlighted { id, value } if id == "models" => {
177				picker::show_detail_on(app.ui_mut(), value.as_str().parse().ok());
178			},
179			AppEvent::Filtered { id, value, .. } if id == "models" => {
180				let model = value.and_then(|value| value.as_str().parse().ok());
181				picker::show_detail_on(app.ui_mut(), model);
182			},
183			AppEvent::OverlayClosed(id) => {
184				if layers.picker == Some(id) {
185					layers.picker = None;
186				}
187				if layers.help == Some(id) {
188					layers.help = None;
189				}
190			},
191			_ => {},
192		}
193		render::sync_preview(app.ui_mut(), &mut synced);
194		// Reserve the Overlay tab's chords only while it is showing, so the
195		// focused composer can't spend Ctrl+K on kill-line — and the Live
196		// tab's editor keeps it.
197		let chords: &[Key] = if active_tab(app.ui()) == "Overlay" {
198			&[Key::Ctrl('k'), Key::Ctrl('g')]
199		} else {
200			&[]
201		};
202		app.set_hotkeys(chords.iter().copied());
203	}
204	Ok(())
205}
Source

pub const fn probe(self, timeout: Duration) -> Self

Runs the startup capability probe with this timeout.

Examples found in repository?
examples/companies.rs (line 195)
192async fn main() -> io::Result<()> {
193	let mut app = AppOptions::new()
194		.mouse()
195		.probe(Duration::from_millis(150))
196		.graphics_with(forced_from_args)
197		.start(|env| build_ui(env.viewport, env.ctx))
198		.await?;
199	if app.caps().graphics != Graphics::Cells {
200		for (index, provider) in PROVIDERS.iter().enumerate() {
201			let png = tokio::fs::read(format!("{ASSET_DIR}/{}.png", provider.id)).await?;
202			app.renderer_mut().register_image(
203				u32::try_from(index + 1).expect("provider count fits image IDs"),
204				png,
205			)?;
206		}
207		app.ui_mut().invalidate(SCROLL_ID);
208	}
209	show_stats(&mut app, None);
210	let mut chosen: Option<String> = None;
211	while let Some(event) = app.next().await? {
212		match event {
213			AppEvent::Resized(viewport) => {
214				app.ui_mut().set_height(SCROLL_ID, scroll_height(viewport));
215			},
216			AppEvent::Pressed(id) => chosen = Some(id.to_string()),
217			_ => {},
218		}
219		show_stats(&mut app, chosen.as_deref());
220	}
221	Ok(())
222}
Source

pub fn graphics_with( self, forced: impl FnOnce(&TerminalCaps) -> Option<Graphics> + Send + 'static, ) -> Self

Resolves an optional forced graphics tier after capability detection.

Examples found in repository?
examples/companies.rs (line 196)
192async fn main() -> io::Result<()> {
193	let mut app = AppOptions::new()
194		.mouse()
195		.probe(Duration::from_millis(150))
196		.graphics_with(forced_from_args)
197		.start(|env| build_ui(env.viewport, env.ctx))
198		.await?;
199	if app.caps().graphics != Graphics::Cells {
200		for (index, provider) in PROVIDERS.iter().enumerate() {
201			let png = tokio::fs::read(format!("{ASSET_DIR}/{}.png", provider.id)).await?;
202			app.renderer_mut().register_image(
203				u32::try_from(index + 1).expect("provider count fits image IDs"),
204				png,
205			)?;
206		}
207		app.ui_mut().invalidate(SCROLL_ID);
208	}
209	show_stats(&mut app, None);
210	let mut chosen: Option<String> = None;
211	while let Some(event) = app.next().await? {
212		match event {
213			AppEvent::Resized(viewport) => {
214				app.ui_mut().set_height(SCROLL_ID, scroll_height(viewport));
215			},
216			AppEvent::Pressed(id) => chosen = Some(id.to_string()),
217			_ => {},
218		}
219		show_stats(&mut app, chosen.as_deref());
220	}
221	Ok(())
222}
Source

pub const fn cursor_style(self, style: CursorStyle) -> Self

Uses style while the application owns the terminal.

Source

pub const fn mouse(self) -> Self

Enables inline mouse reporting (click, drag, motion, wheel) for the whole session.

Off by default: an inline app leaves the mouse to the terminal so native text selection and scrollback keep working, matching the coding agent. Opt in for pointer-driven screens.

Examples found in repository?
examples/companies.rs (line 194)
192async fn main() -> io::Result<()> {
193	let mut app = AppOptions::new()
194		.mouse()
195		.probe(Duration::from_millis(150))
196		.graphics_with(forced_from_args)
197		.start(|env| build_ui(env.viewport, env.ctx))
198		.await?;
199	if app.caps().graphics != Graphics::Cells {
200		for (index, provider) in PROVIDERS.iter().enumerate() {
201			let png = tokio::fs::read(format!("{ASSET_DIR}/{}.png", provider.id)).await?;
202			app.renderer_mut().register_image(
203				u32::try_from(index + 1).expect("provider count fits image IDs"),
204				png,
205			)?;
206		}
207		app.ui_mut().invalidate(SCROLL_ID);
208	}
209	show_stats(&mut app, None);
210	let mut chosen: Option<String> = None;
211	while let Some(event) = app.next().await? {
212		match event {
213			AppEvent::Resized(viewport) => {
214				app.ui_mut().set_height(SCROLL_ID, scroll_height(viewport));
215			},
216			AppEvent::Pressed(id) => chosen = Some(id.to_string()),
217			_ => {},
218		}
219		show_stats(&mut app, chosen.as_deref());
220	}
221	Ok(())
222}
More examples
Hide additional examples
examples/gallery/main.rs (line 114)
112async fn main() -> io::Result<()> {
113	let mut app = AppOptions::new()
114		.mouse()
115		.quit([Key::Ctrl('c'), Key::Ctrl('q')])
116		.start(|env| build_ui(env.viewport, env.ctx))
117		.await?;
118	// The picker tab opens with the first model's details, like the chat
119	// overlay does.
120	picker::show_detail_on(app.ui_mut(), Some(0));
121
122	let mut synced = String::new();
123	let mut lab = anim::Lab::new();
124	let mut layers = Layers::default();
125	let mut next_step = tokio::time::Instant::now() + anim::AUTOPLAY_STEP;
126
127	loop {
128		let event = tokio::select! {
129			event = app.next() => match event? {
130				Some(event) => event,
131				None => break,
132			},
133			() = tokio::time::sleep_until(next_step) => {
134				if lab.autoplay && active_tab(app.ui()) == "Anim" {
135					lab.advance(app.ui_mut());
136				}
137				next_step += anim::AUTOPLAY_STEP;
138				continue;
139			},
140		};
141		match event {
142			AppEvent::Resized(viewport) => {
143				for pane in render::PANE_IDS {
144					app.ui_mut().set_height(pane, render::pane_height(viewport));
145				}
146			},
147			AppEvent::Key(key) => match active_tab(app.ui()).as_str() {
148				"Anim" => lab.handle_key(key, app.ui_mut()),
149				"Overlay" => match key {
150					Key::Ctrl('k') if layers.picker.is_none() => {
151						layers.picker = Some(overlay::show_picker(app.ui_mut()));
152					},
153					Key::Ctrl('g') => match layers.help.take() {
154						Some(id) => {
155							app.ui_mut().close_overlay(id);
156						},
157						None => layers.help = Some(overlay::show_help(app.ui_mut())),
158					},
159					_ => {},
160				},
161				_ => {},
162			},
163			// The Overlay tab's modal select committed a model.
164			AppEvent::Changed { id, value } if id == "model" => {
165				if let Some(overlay) = layers.picker.take() {
166					let label = overlay::MODELS
167						.iter()
168						.find(|(short, ..)| *short == value)
169						.map_or(value.as_str(), |(_, label, _)| label);
170					app.ui_mut().set_text("status", format!("model: {label}"));
171					app.ui_mut().close_overlay(overlay);
172				}
173			},
174			// The Picker tab's select moved: mirror the chat picker's
175			// facts-and-chips detail line.
176			AppEvent::Highlighted { id, value } if id == "models" => {
177				picker::show_detail_on(app.ui_mut(), value.as_str().parse().ok());
178			},
179			AppEvent::Filtered { id, value, .. } if id == "models" => {
180				let model = value.and_then(|value| value.as_str().parse().ok());
181				picker::show_detail_on(app.ui_mut(), model);
182			},
183			AppEvent::OverlayClosed(id) => {
184				if layers.picker == Some(id) {
185					layers.picker = None;
186				}
187				if layers.help == Some(id) {
188					layers.help = None;
189				}
190			},
191			_ => {},
192		}
193		render::sync_preview(app.ui_mut(), &mut synced);
194		// Reserve the Overlay tab's chords only while it is showing, so the
195		// focused composer can't spend Ctrl+K on kill-line — and the Live
196		// tab's editor keeps it.
197		let chords: &[Key] = if active_tab(app.ui()) == "Overlay" {
198			&[Key::Ctrl('k'), Key::Ctrl('g')]
199		} else {
200			&[]
201		};
202		app.set_hotkeys(chords.iter().copied());
203	}
204	Ok(())
205}
Source

pub fn quit(self, chords: impl IntoIterator<Item = Key>) -> Self

Replaces the quit chords checked before input routing.

Examples found in repository?
examples/tml.rs (line 50)
42async fn main() -> io::Result<()> {
43	let path = std::env::args()
44		.nth(1)
45		.unwrap_or_else(|| "example.tml".into());
46	let source = std::fs::read_to_string(&path)?;
47
48	let mut ctx = None;
49	let mut app = AppOptions::new()
50		.quit([Key::Ctrl('c'), Key::Char('q'), Key::Esc])
51		.start(|env| {
52			let ui = build(&source, env.viewport.width, &env.ctx);
53			ctx = Some(env.ctx);
54			ui
55		})
56		.await?;
57	let ctx = ctx.expect("start ran the builder");
58
59	let handle = app.handle();
60	tokio::spawn(async move {
61		let mut seen = modified(path.as_ref());
62		loop {
63			tokio::time::sleep(Duration::from_millis(150)).await;
64			let stamp = modified(path.as_ref());
65			if stamp == seen {
66				continue;
67			}
68			seen = stamp;
69			let Ok(source) = std::fs::read_to_string(&path) else {
70				continue;
71			};
72			let ctx = ctx.clone();
73			handle.update(move |ui| *ui = build(&source, ui.frame().size().width, &ctx));
74		}
75	});
76
77	while app.next().await?.is_some() {}
78	Ok(())
79}
More examples
Hide additional examples
examples/gallery/main.rs (line 115)
112async fn main() -> io::Result<()> {
113	let mut app = AppOptions::new()
114		.mouse()
115		.quit([Key::Ctrl('c'), Key::Ctrl('q')])
116		.start(|env| build_ui(env.viewport, env.ctx))
117		.await?;
118	// The picker tab opens with the first model's details, like the chat
119	// overlay does.
120	picker::show_detail_on(app.ui_mut(), Some(0));
121
122	let mut synced = String::new();
123	let mut lab = anim::Lab::new();
124	let mut layers = Layers::default();
125	let mut next_step = tokio::time::Instant::now() + anim::AUTOPLAY_STEP;
126
127	loop {
128		let event = tokio::select! {
129			event = app.next() => match event? {
130				Some(event) => event,
131				None => break,
132			},
133			() = tokio::time::sleep_until(next_step) => {
134				if lab.autoplay && active_tab(app.ui()) == "Anim" {
135					lab.advance(app.ui_mut());
136				}
137				next_step += anim::AUTOPLAY_STEP;
138				continue;
139			},
140		};
141		match event {
142			AppEvent::Resized(viewport) => {
143				for pane in render::PANE_IDS {
144					app.ui_mut().set_height(pane, render::pane_height(viewport));
145				}
146			},
147			AppEvent::Key(key) => match active_tab(app.ui()).as_str() {
148				"Anim" => lab.handle_key(key, app.ui_mut()),
149				"Overlay" => match key {
150					Key::Ctrl('k') if layers.picker.is_none() => {
151						layers.picker = Some(overlay::show_picker(app.ui_mut()));
152					},
153					Key::Ctrl('g') => match layers.help.take() {
154						Some(id) => {
155							app.ui_mut().close_overlay(id);
156						},
157						None => layers.help = Some(overlay::show_help(app.ui_mut())),
158					},
159					_ => {},
160				},
161				_ => {},
162			},
163			// The Overlay tab's modal select committed a model.
164			AppEvent::Changed { id, value } if id == "model" => {
165				if let Some(overlay) = layers.picker.take() {
166					let label = overlay::MODELS
167						.iter()
168						.find(|(short, ..)| *short == value)
169						.map_or(value.as_str(), |(_, label, _)| label);
170					app.ui_mut().set_text("status", format!("model: {label}"));
171					app.ui_mut().close_overlay(overlay);
172				}
173			},
174			// The Picker tab's select moved: mirror the chat picker's
175			// facts-and-chips detail line.
176			AppEvent::Highlighted { id, value } if id == "models" => {
177				picker::show_detail_on(app.ui_mut(), value.as_str().parse().ok());
178			},
179			AppEvent::Filtered { id, value, .. } if id == "models" => {
180				let model = value.and_then(|value| value.as_str().parse().ok());
181				picker::show_detail_on(app.ui_mut(), model);
182			},
183			AppEvent::OverlayClosed(id) => {
184				if layers.picker == Some(id) {
185					layers.picker = None;
186				}
187				if layers.help == Some(id) {
188					layers.help = None;
189				}
190			},
191			_ => {},
192		}
193		render::sync_preview(app.ui_mut(), &mut synced);
194		// Reserve the Overlay tab's chords only while it is showing, so the
195		// focused composer can't spend Ctrl+K on kill-line — and the Live
196		// tab's editor keeps it.
197		let chords: &[Key] = if active_tab(app.ui()) == "Overlay" {
198			&[Key::Ctrl('k'), Key::Ctrl('g')]
199		} else {
200			&[]
201		};
202		app.set_hotkeys(chords.iter().copied());
203	}
204	Ok(())
205}
Source

pub fn hotkeys(self, chords: impl IntoIterator<Item = Key>) -> Self

Reserves chords for the host, checked after the quit chords and before widget routing, and surfaced as AppEvent::Key.

Use this for scene-level shortcuts that must win over a focused widget’s own binding — Ctrl+K opening a switcher while a text input would otherwise kill to end of line. Chords stay reserved until App::set_hotkeys replaces them, so scope them to the screen that needs them rather than reserving them globally.

Source

pub const fn hold_alt(self) -> Self

Starts with the alternate screen held: the very first frame paints there and the inline transcript stays untouched underneath until App::hold_alt releases it. Fullscreen opening scenes — a welcome screen, a picker-first flow — use this so the main buffer never flashes frame one. A Ui whose initial overlay stack is visible holds automatically without this option.

Source

pub const fn keep_on_cancel(self) -> Self

Keeps running when the base tree yields UiEvent::Cancel.

A cancel from inside a visible modal overlay is unaffected: it always dismisses that layer and surfaces AppEvent::OverlayClosed.

Source

pub async fn start(self, build: impl FnOnce(AppEnv) -> Ui + Send) -> Result<App>

Negotiates, enters the terminal, builds the Ui, paints the first frame, and returns the running host.

§Errors

Propagates terminal, input, capability, and renderer failures.

Examples found in repository?
examples/companies.rs (line 197)
192async fn main() -> io::Result<()> {
193	let mut app = AppOptions::new()
194		.mouse()
195		.probe(Duration::from_millis(150))
196		.graphics_with(forced_from_args)
197		.start(|env| build_ui(env.viewport, env.ctx))
198		.await?;
199	if app.caps().graphics != Graphics::Cells {
200		for (index, provider) in PROVIDERS.iter().enumerate() {
201			let png = tokio::fs::read(format!("{ASSET_DIR}/{}.png", provider.id)).await?;
202			app.renderer_mut().register_image(
203				u32::try_from(index + 1).expect("provider count fits image IDs"),
204				png,
205			)?;
206		}
207		app.ui_mut().invalidate(SCROLL_ID);
208	}
209	show_stats(&mut app, None);
210	let mut chosen: Option<String> = None;
211	while let Some(event) = app.next().await? {
212		match event {
213			AppEvent::Resized(viewport) => {
214				app.ui_mut().set_height(SCROLL_ID, scroll_height(viewport));
215			},
216			AppEvent::Pressed(id) => chosen = Some(id.to_string()),
217			_ => {},
218		}
219		show_stats(&mut app, chosen.as_deref());
220	}
221	Ok(())
222}
More examples
Hide additional examples
examples/tml.rs (lines 51-55)
42async fn main() -> io::Result<()> {
43	let path = std::env::args()
44		.nth(1)
45		.unwrap_or_else(|| "example.tml".into());
46	let source = std::fs::read_to_string(&path)?;
47
48	let mut ctx = None;
49	let mut app = AppOptions::new()
50		.quit([Key::Ctrl('c'), Key::Char('q'), Key::Esc])
51		.start(|env| {
52			let ui = build(&source, env.viewport.width, &env.ctx);
53			ctx = Some(env.ctx);
54			ui
55		})
56		.await?;
57	let ctx = ctx.expect("start ran the builder");
58
59	let handle = app.handle();
60	tokio::spawn(async move {
61		let mut seen = modified(path.as_ref());
62		loop {
63			tokio::time::sleep(Duration::from_millis(150)).await;
64			let stamp = modified(path.as_ref());
65			if stamp == seen {
66				continue;
67			}
68			seen = stamp;
69			let Ok(source) = std::fs::read_to_string(&path) else {
70				continue;
71			};
72			let ctx = ctx.clone();
73			handle.update(move |ui| *ui = build(&source, ui.frame().size().width, &ctx));
74		}
75	});
76
77	while app.next().await?.is_some() {}
78	Ok(())
79}
examples/gallery/main.rs (line 116)
112async fn main() -> io::Result<()> {
113	let mut app = AppOptions::new()
114		.mouse()
115		.quit([Key::Ctrl('c'), Key::Ctrl('q')])
116		.start(|env| build_ui(env.viewport, env.ctx))
117		.await?;
118	// The picker tab opens with the first model's details, like the chat
119	// overlay does.
120	picker::show_detail_on(app.ui_mut(), Some(0));
121
122	let mut synced = String::new();
123	let mut lab = anim::Lab::new();
124	let mut layers = Layers::default();
125	let mut next_step = tokio::time::Instant::now() + anim::AUTOPLAY_STEP;
126
127	loop {
128		let event = tokio::select! {
129			event = app.next() => match event? {
130				Some(event) => event,
131				None => break,
132			},
133			() = tokio::time::sleep_until(next_step) => {
134				if lab.autoplay && active_tab(app.ui()) == "Anim" {
135					lab.advance(app.ui_mut());
136				}
137				next_step += anim::AUTOPLAY_STEP;
138				continue;
139			},
140		};
141		match event {
142			AppEvent::Resized(viewport) => {
143				for pane in render::PANE_IDS {
144					app.ui_mut().set_height(pane, render::pane_height(viewport));
145				}
146			},
147			AppEvent::Key(key) => match active_tab(app.ui()).as_str() {
148				"Anim" => lab.handle_key(key, app.ui_mut()),
149				"Overlay" => match key {
150					Key::Ctrl('k') if layers.picker.is_none() => {
151						layers.picker = Some(overlay::show_picker(app.ui_mut()));
152					},
153					Key::Ctrl('g') => match layers.help.take() {
154						Some(id) => {
155							app.ui_mut().close_overlay(id);
156						},
157						None => layers.help = Some(overlay::show_help(app.ui_mut())),
158					},
159					_ => {},
160				},
161				_ => {},
162			},
163			// The Overlay tab's modal select committed a model.
164			AppEvent::Changed { id, value } if id == "model" => {
165				if let Some(overlay) = layers.picker.take() {
166					let label = overlay::MODELS
167						.iter()
168						.find(|(short, ..)| *short == value)
169						.map_or(value.as_str(), |(_, label, _)| label);
170					app.ui_mut().set_text("status", format!("model: {label}"));
171					app.ui_mut().close_overlay(overlay);
172				}
173			},
174			// The Picker tab's select moved: mirror the chat picker's
175			// facts-and-chips detail line.
176			AppEvent::Highlighted { id, value } if id == "models" => {
177				picker::show_detail_on(app.ui_mut(), value.as_str().parse().ok());
178			},
179			AppEvent::Filtered { id, value, .. } if id == "models" => {
180				let model = value.and_then(|value| value.as_str().parse().ok());
181				picker::show_detail_on(app.ui_mut(), model);
182			},
183			AppEvent::OverlayClosed(id) => {
184				if layers.picker == Some(id) {
185					layers.picker = None;
186				}
187				if layers.help == Some(id) {
188					layers.help = None;
189				}
190			},
191			_ => {},
192		}
193		render::sync_preview(app.ui_mut(), &mut synced);
194		// Reserve the Overlay tab's chords only while it is showing, so the
195		// focused composer can't spend Ctrl+K on kill-line — and the Live
196		// tab's editor keeps it.
197		let chords: &[Key] = if active_tab(app.ui()) == "Overlay" {
198			&[Key::Ctrl('k'), Key::Ctrl('g')]
199		} else {
200			&[]
201		};
202		app.set_hotkeys(chords.iter().copied());
203	}
204	Ok(())
205}

Trait Implementations§

Source§

impl Default for AppOptions

Source§

fn default() -> Self

Returns the “default value” for a type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> Downcast for T
where T: Any,

Source§

fn into_any(self: Box<T>) -> Box<dyn Any>

Convert Box<dyn Trait> (where Trait: Downcast) to Box<dyn Any>. Box<dyn Any> can then be further downcast into Box<ConcreteType> where ConcreteType implements Trait.
Source§

fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>

Convert Rc<Trait> (where Trait: Downcast) to Rc<Any>. Rc<Any> can then be further downcast into Rc<ConcreteType> where ConcreteType implements Trait.
Source§

fn as_any(&self) -> &(dyn Any + 'static)

Convert &Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &Any’s vtable from &Trait’s.
Source§

fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)

Convert &mut Trait (where Trait: Downcast) to &Any. This is needed since Rust cannot generate &mut Any’s vtable from &mut Trait’s.
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> IntoEither for T

Source§

fn into_either(self, into_left: bool) -> Either<Self, Self>

Converts self into a Left variant of Either<Self, Self> if into_left is true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

fn into_either_with<F>(self, into_left: F) -> Either<Self, Self>
where F: FnOnce(&Self) -> bool,

Converts self into a Left variant of Either<Self, Self> if into_left(&self) returns true. Converts self into a Right variant of Either<Self, Self> otherwise. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.