summary refs log tree commit diff
diff options
context:
space:
mode:
authorVika <vika@fireburn.ru>2024-09-04 21:06:01 +0300
committerVika <vika@fireburn.ru>2024-09-04 21:06:01 +0300
commit9ca1f9c49e2ed15f226000e5cb46342dfc72f5dd (patch)
tree92e6ac5dc02160ea9d1eb05cfe6132c37bc9e1f2
parenta26610044483ddd323479d748af401382b8df210 (diff)
Preferences dialog
-rw-r--r--icons.toml2
-rw-r--r--po/bowl.pot17
-rw-r--r--po/ru.po17
-rw-r--r--src/components/preferences.rs90
-rw-r--r--src/lib.rs14
5 files changed, 138 insertions, 2 deletions
diff --git a/icons.toml b/icons.toml
index e594704..61797c1 100644
--- a/icons.toml
+++ b/icons.toml
@@ -4,4 +4,4 @@
 
 # List of icon names you found (shipped with this crate)
 # Note: the file ending `-symbolic.svg` isn't part of the icon name.
-icons = ["menu"]
+icons = ["menu", "magic-wand"]
diff --git a/po/bowl.pot b/po/bowl.pot
index 92da214..8034d1d 100644
--- a/po/bowl.pot
+++ b/po/bowl.pot
@@ -179,3 +179,20 @@ msgstr ""
 #: data/xyz.vikanezrimaya.kittybox.Bowl.gschema.xml.in:48
 msgid "Append this to the prompt after the article text."
 msgstr ""
+
+#: src/components/preferences.rs:21
+msgid "Language Models"
+msgstr ""
+
+#: src/components/preferences.rs:22
+msgid "Settings for the language model integrations."
+msgstr ""
+
+#: src/components/preferences.rs:26
+msgid "General"
+msgstr ""
+
+#: src/components/preferences.rs:33
+msgid "Smart Summary"
+msgstr ""
+
diff --git a/po/ru.po b/po/ru.po
index 8ce26e5..b6df217 100644
--- a/po/ru.po
+++ b/po/ru.po
@@ -192,3 +192,20 @@ msgstr "Суффикс вводной Умной Выжимки"
 #: data/xyz.vikanezrimaya.kittybox.Bowl.gschema.xml.in:48
 msgid "Append this to the prompt after the article text."
 msgstr "Что приписывается к вводной после текста статьи."
+
+#: src/components/preferences.rs:21
+msgid "Language Models"
+msgstr "Языковые модели"
+
+#: src/components/preferences.rs:22
+msgid "Settings for the language model integrations."
+msgstr "Настройки интеграции языковых моделей."
+
+#: src/components/preferences.rs:26
+msgid "General"
+msgstr "Общее"
+
+#: src/components/preferences.rs:33
+msgid "Smart Summary"
+msgstr "Умная Выжимка"
+
diff --git a/src/components/preferences.rs b/src/components/preferences.rs
new file mode 100644
index 0000000..9bbc313
--- /dev/null
+++ b/src/components/preferences.rs
@@ -0,0 +1,90 @@
+use gettextrs::*;
+use gio::prelude::*;
+use adw::prelude::*;
+use relm4::prelude::*;
+
+pub struct Preferences {
+    settings: gio::Settings,
+}
+
+#[relm4::component(pub)]
+impl Component for Preferences {
+    type CommandOutput = ();
+    type Input = Option<gtk::Widget>;
+    type Output = ();
+    type Init = ();
+
+    view! {
+        #[root]
+        adw::PreferencesDialog {
+            add = &adw::PreferencesPage {
+                set_title: &gettext("Language Models"),
+                set_description: &gettext("Settings for the language model integrations."),
+                set_icon_name: Some("magic-wand"),
+
+                adw::PreferencesGroup {
+                    set_title: &gettext("General"),
+
+                    #[name = "llm_endpoint"]
+                    adw::EntryRow {},
+                },
+
+                adw::PreferencesGroup {
+                    set_title: &gettext("Smart Summary"),
+
+                    #[name = "smart_summary_model"] adw::EntryRow {},
+                    #[name = "smart_summary_system_prompt"] adw::EntryRow {},
+                    #[name = "smart_summary_prompt_prefix"] adw::EntryRow {},
+                    #[name = "smart_summary_prompt_suffix"] adw::EntryRow {},
+                }
+            }
+        }
+    }
+
+    fn init(
+        _: Self::Init,
+        root: Self::Root,
+        _sender: ComponentSender<Self>,
+    ) -> ComponentParts<Self> {
+        let model = Self {
+            settings: gio::Settings::new(crate::APPLICATION_ID),
+        };
+
+        model.settings.delay();
+        let schema = model.settings.settings_schema().unwrap();
+
+        let widgets = view_output!();
+
+        for (row, key) in [
+            (&widgets.llm_endpoint, "llm-endpoint"),
+            (&widgets.smart_summary_model, "smart-summary-model"),
+            (&widgets.smart_summary_system_prompt, "smart-summary-system-prompt"),
+            (&widgets.smart_summary_prompt_prefix, "smart-summary-prompt-prefix"),
+            (&widgets.smart_summary_prompt_suffix, "smart-summary-prompt-suffix"),
+        ] {
+            model.settings.bind(key, row, "text")
+                .get()
+                .set()
+                .build();
+            row.set_title(&gettext(schema.key(key).summary().unwrap()));
+        }
+
+        root.connect_closed(glib::clone!(
+            #[strong(rename_to = settings)]
+            model.settings,
+            move |_| {
+                settings.apply()
+            }
+        ));
+
+        ComponentParts { model, widgets }
+    }
+
+    fn update(&mut self, msg: Self::Input, _sender: ComponentSender<Self>, root: &Self::Root) {
+        root.present(msg.as_ref());
+    }
+
+    fn shutdown(&mut self, _: &mut Self::Widgets, _: relm4::Sender<()>) {
+        self.settings.apply()
+    }
+}
diff --git a/src/lib.rs b/src/lib.rs
index 30cba6f..6421560 100644
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -19,6 +19,9 @@ pub mod components {
 
     pub mod signin;
     pub use signin::{SignIn, Output as SignInOutput};
+
+    pub mod preferences;
+    pub use preferences::Preferences;
 }
 
 use components::{post_editor::Post, PostEditorInput};
@@ -274,8 +277,17 @@ impl AsyncComponent for App {
         let about_action: RelmAction<AboutAction> = RelmAction::new_stateless(move |_| {
             App::about().present(weak_window.upgrade().as_ref());
         });
+        let weak_window = window.downgrade();
         let preferences_action: RelmAction<PreferencesAction> = RelmAction::new_stateless(move |_| {
-            log::warn!("Ain't implemented yet!");
+            // This could be built as an action that sends an input to open preferences.
+            //
+            // But I find this an acceptable alternative.
+            let mut prefs = components::Preferences::builder()
+                .launch(())
+                .detach();
+
+            prefs.emit(weak_window.upgrade().map(|w| w.upcast()));
+            prefs.detach_runtime();
         });
         let sign_out_action: RelmAction<SignOutAction> = RelmAction::new_stateless(move |_| {
             input_sender.emit(Input::SignOut)