summary refs log tree commit diff
path: root/src/components/smart_summary.rs
blob: 9da67afb7cf94fb43b1cb1d5efb277f18a648cc0 (plain) (blame)
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));
            },
        }
    }
}