Skip to main content

App

Struct App 

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

Running retained-UI terminal host.

Implementations§

Source§

impl App

Source

pub const fn ui(&self) -> &Ui

Borrows the retained UI.

Examples found in repository?
examples/gallery/main.rs (line 134)
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 ui_mut(&mut self) -> &mut Ui

Mutably borrows the retained UI between host events.

Examples found in repository?
examples/companies.rs (line 164)
156fn show_stats(app: &mut App, chosen: Option<&str>) {
157	let caps = app.caps();
158	let stats = app.last_stats();
159	let pixels = caps.cell_px.map_or_else(
160		|| "cell-px ?".to_owned(),
161		|(width, height)| format!("cell-px {width}×{height}"),
162	);
163	let login = chosen.map_or(String::new(), |id| format!("login: {id} · "));
164	app.ui_mut().set_text(
165		HUD_ID,
166		format!(
167			"{login}{} · {} · {} · repaint: {} cells",
168			graphics_label(caps.graphics),
169			caps.id,
170			pixels,
171			stats.changed_cells,
172		),
173	);
174}
175
176fn forced_from_args(caps: &TerminalCaps) -> Option<Graphics> {
177	let mut forced = None;
178	for argument in std::env::args().skip(1) {
179		forced = match argument.as_str() {
180			"--cells" => Some(Graphics::Cells),
181			"--kitty" if caps.kitty_placeholders => Some(Graphics::KittyPlaceholders),
182			"--kitty" => Some(Graphics::KittyDirect),
183			"--kitty-placeholders" => Some(Graphics::KittyPlaceholders),
184			"--sixel" => Some(Graphics::Sixel),
185			_ => forced,
186		};
187	}
188	forced
189}
190
191#[tokio::main]
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 120)
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 set_hotkeys(&mut self, chords: impl IntoIterator<Item = Key>)

Replaces the reserved host chords, scoping AppOptions::hotkeys to the screen that is actually showing.

Examples found in repository?
examples/gallery/main.rs (line 202)
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 handle(&self) -> UiHandle

Creates a remote that can update or stop this host.

Examples found in repository?
examples/tml.rs (line 59)
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}
Source

pub const fn renderer_mut(&mut self) -> &mut Renderer<TtyOut>

Mutably borrows the renderer for image registration and output policy.

Examples found in repository?
examples/companies.rs (line 202)
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 terminal_mut(&mut self) -> &mut Terminal

Mutably borrows the terminal for titles, progress, and appearance hooks.

Source

pub const fn caps(&self) -> TerminalCaps

Returns the resolved terminal capabilities.

Examples found in repository?
examples/companies.rs (line 157)
156fn show_stats(app: &mut App, chosen: Option<&str>) {
157	let caps = app.caps();
158	let stats = app.last_stats();
159	let pixels = caps.cell_px.map_or_else(
160		|| "cell-px ?".to_owned(),
161		|(width, height)| format!("cell-px {width}×{height}"),
162	);
163	let login = chosen.map_or(String::new(), |id| format!("login: {id} · "));
164	app.ui_mut().set_text(
165		HUD_ID,
166		format!(
167			"{login}{} · {} · {} · repaint: {} cells",
168			graphics_label(caps.graphics),
169			caps.id,
170			pixels,
171			stats.changed_cells,
172		),
173	);
174}
175
176fn forced_from_args(caps: &TerminalCaps) -> Option<Graphics> {
177	let mut forced = None;
178	for argument in std::env::args().skip(1) {
179		forced = match argument.as_str() {
180			"--cells" => Some(Graphics::Cells),
181			"--kitty" if caps.kitty_placeholders => Some(Graphics::KittyPlaceholders),
182			"--kitty" => Some(Graphics::KittyDirect),
183			"--kitty-placeholders" => Some(Graphics::KittyPlaceholders),
184			"--sixel" => Some(Graphics::Sixel),
185			_ => forced,
186		};
187	}
188	forced
189}
190
191#[tokio::main]
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 viewport(&self) -> Size

Returns the latest settled terminal geometry.

Source

pub const fn last_stats(&self) -> PaintStats

Returns statistics from the most recent present or rebuild.

Examples found in repository?
examples/companies.rs (line 158)
156fn show_stats(app: &mut App, chosen: Option<&str>) {
157	let caps = app.caps();
158	let stats = app.last_stats();
159	let pixels = caps.cell_px.map_or_else(
160		|| "cell-px ?".to_owned(),
161		|(width, height)| format!("cell-px {width}×{height}"),
162	);
163	let login = chosen.map_or(String::new(), |id| format!("login: {id} · "));
164	app.ui_mut().set_text(
165		HUD_ID,
166		format!(
167			"{login}{} · {} · {} · repaint: {} cells",
168			graphics_label(caps.graphics),
169			caps.id,
170			pixels,
171			stats.changed_cells,
172		),
173	);
174}
Source

pub const fn set_stable_rows(&mut self, rows: u16)

Sets the immutable leading-row boundary used for presentation.

Source

pub const fn hold_alt(&mut self, hold: bool)

Requests or releases a persistent alternate-screen hold.

Fullscreen scenes — a welcome screen, a pager — hold the alternate screen for their lifetime: frames paint there with mouse tracking active while the inline transcript stays untouched underneath. Every visible modal overlay holds it automatically; this covers scenes without one. Non-modal layers (crate::OverlayOptions::non_modal) never hold: they composite into the live inline viewport while the document keeps committing to native scrollback. Release restores the main screen, rebuilding history only when geometry changed while held. Takes effect on the next App::next call.

Source

pub async fn next(&mut self) -> Result<Option<AppEvent>>

Flushes pending damage, waits for one host event, and routes it.

None means shutdown; subsequent calls continue returning None.

§Errors

Propagates terminal input, geometry, and renderer failures.

Examples found in repository?
examples/companies.rs (line 211)
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 77)
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 129)
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 Drop for App

Source§

fn drop(&mut self)

Executes the destructor for this type. Read more
Source§

fn pin_drop(self: Pin<&mut Self>)

🔬This is a nightly-only experimental API. (pin_ergonomics)
Execute the destructor for this type, but different to Drop::drop, it requires self to be pinned. Read more

Auto Trait Implementations§

§

impl !Freeze for App

§

impl !RefUnwindSafe for App

§

impl !Send for App

§

impl !Sync for App

§

impl !UnwindSafe for App

§

impl Unpin for App

§

impl UnsafeUnpin for App

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.