1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
|
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<String, Error>;
view! {
#[root]
#[name = "button"]
gtk::Button {
connect_clicked => Input::ButtonPressed,
#[watch]
set_sensitive: !model.busy,
// TRANSLATORS: please keep the newline and `<b>` tags
set_tooltip_markup: Some(gettext("<b>Smart Summary</b>\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<Self>
) -> ComponentParts<Self> {
let model = Self::default();
let widgets = view_output!();
ComponentParts { model, widgets }
}
fn update(
&mut self,
msg: Self::Input,
sender: ComponentSender<Self>,
_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<Self>, _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));
},
}
}
}
|