use adw::prelude::*; use gettextrs::*; use relm4::{gtk, prelude::{Component, ComponentParts}, ComponentSender}; #[derive(Debug, Default)] pub(crate) struct SmartSummaryButton { busy: bool, } #[derive(Debug, thiserror::Error)] pub(crate) enum Error { } #[derive(Debug)] pub(crate) enum Input { #[doc(hidden)] ButtonPressed, Text(String), Cancel, } #[derive(Debug)] pub(crate) enum Output { Start, Chunk(String), Done, Error(Error) } #[relm4::component(pub(crate))] impl Component for SmartSummaryButton { type Input = Input; type Output = Output; type Init = (); type CommandOutput = Result; view! { #[root] #[name = "button"] gtk::Button { connect_clicked => Input::ButtonPressed, #[watch] set_sensitive: !model.busy, // TRANSLATORS: please keep the newline and `` tags set_tooltip_markup: Some(gettext("Smart Summary\nAsk a language model for a single-sentence summary.")).as_deref(), if model.busy { gtk::Spinner { set_spinning: true } } else { gtk::Label { set_markup: "✨" } } } } fn init( _init: Self::Init, root: Self::Root, sender: ComponentSender ) -> ComponentParts { let model = Self::default(); let widgets = view_output!(); ComponentParts { model, widgets } } fn update( &mut self, msg: Self::Input, sender: ComponentSender, _root: &Self::Root ) { match msg { Input::Cancel => { self.busy = false; log::debug!("Parent component asked us to cancel."); }, Input::ButtonPressed => if let Ok(()) = sender.output(Output::Start) { self.busy = true; log::debug!("Requesting text to summarize from parent component..."); }, Input::Text(text) => { log::debug!("Would generate summary for the following text:\n{}", text); sender.command(|sender, shutdown| shutdown.register(async move { 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; if sender.send(Ok(i.to_string())).is_err() { return }; } log::debug!("Done with the summary."); let _ = sender.send(Ok(String::new())); }).drop_on_shutdown()); } } } fn update_cmd(&mut self, msg: Self::CommandOutput, sender: ComponentSender, _root: &Self::Root) { match msg { Ok(chunk) if chunk.is_empty() => { self.busy = false; let _ = sender.output(Output::Done); }, Err(err) => { self.busy = false; let _ = sender.output(Output::Error(err)); } Ok(chunk) => { let _ = sender.output(Output::Chunk(chunk)); }, } } }