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
impl AppOptions
Sourcepub fn new() -> Self
pub fn new() -> Self
Creates the default application configuration.
Examples found in repository?
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
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}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}Sourcepub const fn probe(self, timeout: Duration) -> Self
pub const fn probe(self, timeout: Duration) -> Self
Runs the startup capability probe with this timeout.
Examples found in repository?
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}Sourcepub fn graphics_with(
self,
forced: impl FnOnce(&TerminalCaps) -> Option<Graphics> + Send + 'static,
) -> Self
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?
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}Sourcepub const fn cursor_style(self, style: CursorStyle) -> Self
pub const fn cursor_style(self, style: CursorStyle) -> Self
Uses style while the application owns the terminal.
Sourcepub const fn mouse(self) -> Self
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?
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
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}Sourcepub fn quit(self, chords: impl IntoIterator<Item = Key>) -> Self
pub fn quit(self, chords: impl IntoIterator<Item = Key>) -> Self
Replaces the quit chords checked before input routing.
Examples found in repository?
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
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}Sourcepub fn hotkeys(self, chords: impl IntoIterator<Item = Key>) -> Self
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.
Sourcepub const fn hold_alt(self) -> Self
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.
Sourcepub const fn keep_on_cancel(self) -> Self
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.
Sourcepub async fn start(self, build: impl FnOnce(AppEnv) -> Ui + Send) -> Result<App>
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?
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
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}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§
Auto Trait Implementations§
impl !RefUnwindSafe for AppOptions
impl !Sync for AppOptions
impl !UnwindSafe for AppOptions
impl Freeze for AppOptions
impl Send for AppOptions
impl Unpin for AppOptions
impl UnsafeUnpin for AppOptions
Blanket Implementations§
Source§impl<T> BorrowMut<T> for Twhere
T: ?Sized,
impl<T> BorrowMut<T> for Twhere
T: ?Sized,
Source§fn borrow_mut(&mut self) -> &mut T
fn borrow_mut(&mut self) -> &mut T
Source§impl<T> Downcast for Twhere
T: Any,
impl<T> Downcast for Twhere
T: Any,
Source§fn into_any(self: Box<T>) -> Box<dyn Any>
fn into_any(self: Box<T>) -> Box<dyn Any>
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>
fn into_any_rc(self: Rc<T>) -> Rc<dyn Any>
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)
fn as_any(&self) -> &(dyn Any + 'static)
&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)
fn as_any_mut(&mut self) -> &mut (dyn Any + 'static)
&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> IntoEither for T
impl<T> IntoEither for T
Source§fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
fn into_either(self, into_left: bool) -> Either<Self, Self> ⓘ
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 moreSource§fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
fn into_either_with<F>(self, into_left: F) -> Either<Self, Self> ⓘ
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