use adw::prelude::*; use relm4::{gtk, prelude::{AsyncComponent, AsyncComponentParts}, AsyncComponentSender}; #[derive(Debug, Default)] pub(crate) struct SmartSummaryButton { busy: bool, } #[derive(Debug, thiserror::Error)] pub(crate) enum Error { } #[derive(Debug)] pub(crate) enum Input { ButtonPressed, Text(String) } #[derive(Debug)] pub(crate) enum Output { Start, Chunk(String), Done, Error(Error) } #[relm4::component(pub(crate) async)] impl AsyncComponent for SmartSummaryButton { type Input = Input; type Output = Output; type Init = (); type CommandOutput = (); view! { #[root] #[name = "button"] gtk::Button { connect_clicked => Input::ButtonPressed, #[watch] set_sensitive: !model.busy, set_tooltip_markup: Some("Smart Summary\nAsk a language model for a single-sentence summary."), if model.busy { gtk::Spinner { set_spinning: true } } else { gtk::Label { set_markup: "✨" } } } } async fn init( _init: Self::Init, root: Self::Root, sender: AsyncComponentSender ) -> AsyncComponentParts { let model = Self::default(); let widgets = view_output!(); AsyncComponentParts { model, widgets } } async fn update_with_view( &mut self, widgets: &mut Self::Widgets, msg: Self::Input, sender: AsyncComponentSender, _root: &Self::Root ) { match msg { Input::ButtonPressed => if let Ok(()) = sender.output(Output::Start) { self.busy = true; log::debug!("Requesting text to summarize from parent component..."); self.update_view(widgets, sender.clone()); }, Input::Text(text) => { log::debug!("Would generate summary for the following text:\n{}", text); tokio::time::sleep(std::time::Duration::from_millis(450)).await; for i in ["I'", "m ", "sorry,", " I", " am ", "unable", " to", " ", "write", " you ", "a summary.", " I", " am", " not ", "really ", "an ", "LLM."] { tokio::time::sleep(std::time::Duration::from_millis(100)).await; let _ = sender.output(Output::Chunk(i.to_string())); } log::debug!("Done with the summary."); self.busy = false; let _ = sender.output(Output::Done); self.update_view(widgets, sender.clone()); } } } }