Skip to content

Commit

Permalink
Refactor hotkey
Browse files Browse the repository at this point in the history
  • Loading branch information
AurevoirXavier committed Jul 8, 2024
1 parent ec1a79b commit e7231d2
Show file tree
Hide file tree
Showing 17 changed files with 304 additions and 227 deletions.
3 changes: 3 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

6 changes: 5 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,8 @@ eframe = { version = "0.28", features = ["persistence"] }
egui_extras = { version = "0.28", features = ["svg"] }
enigo = { version = "0.2" }
futures = { version = "0.3" }
global-hotkey = { version = "0.5" }
global-hotkey = { version = "0.5", features = ["serde"] }
parking_lot = { version = "0.12" }
reqwew = { version = "0.2" }
serde = { version = "1.0", features = ["derive"] }
thiserror = { version = "1.0" }
Expand All @@ -48,6 +49,9 @@ tracing-appender = { version = "0.2" }
tracing-subscriber = { version = "0.3", features = ["env-filter"] }
# llm_utils = { version = "0.0.6", optional = true }

[target.'cfg(all(unix, not(target_os = "macos")))'.dependencies]
xkeysym = { version = "0.2" }

[target.'cfg(target_os = "macos")'.dependencies]
objc2-app-kit = { version = "0.2", features = ["NSApplication", "NSResponder", "NSRunningApplication", "NSWindow"] }
objc2-foundation = { version = "0.2" }
Expand Down
7 changes: 1 addition & 6 deletions src/air.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,12 +99,7 @@ impl App for AiR {
}

fn on_exit(&mut self, _: Option<&GlowContext>) {
self.services.quoter.abort();
self.services.hotkey.abort();

if let Some(rt) = self.services.rt.take() {
rt.shutdown_background();
}
self.services.abort();
}
}

Expand Down
3 changes: 3 additions & 0 deletions src/component.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,9 @@ use crate::prelude::*;
#[derive(Debug)]
pub struct Components {
pub setting: Setting,
// Keyboard didn't implement `Send`, can't use it between threads.
// pub keyboard: Arc<Mutex<Keyboard>>,
// TODO?: move the lock to somewhere else.
pub openai: Arc<Mutex<OpenAi>>,
#[cfg(feature = "tokenizer")]
pub tokenizer: Tokenizer,
Expand Down
24 changes: 10 additions & 14 deletions src/component/function.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// std
use std::borrow::Cow;
// self
use super::setting::Translation;
use super::setting::Chat;

#[derive(Debug)]
pub enum Function {
Expand All @@ -9,20 +11,14 @@ pub enum Function {
TranslateDirectly,
}
impl Function {
pub fn prompt(&self, setting: &Translation) -> String {
pub fn is_directly(&self) -> bool {
matches!(self, Self::RewriteDirectly | Self::TranslateDirectly)
}

pub fn prompt<'a>(&'a self, setting: &'a Chat) -> Cow<str> {
match self {
Self::Rewrite | Self::RewriteDirectly =>
"As an English professor, assist me in refining this text. \
Amend any grammatical errors and enhance the language to sound more like a native speaker.\
Provide the refined text only, without any other things."
.into(),
Self::Translate | Self::TranslateDirectly => format!(
"As a language professor, assist me in translate this text from {} to {}. \
Amend any grammatical errors and enhance the language to sound more like a native speaker.\
Provide the translated text only, without any other things.",
setting.source.as_str(),
setting.target.as_str()
),
Self::Rewrite | Self::RewriteDirectly => setting.rewrite.prompt(),
Self::Translate | Self::TranslateDirectly => setting.translation.prompt(),
}
}
}
22 changes: 12 additions & 10 deletions src/component/keyboard.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,30 @@
// crates.io
use enigo::{Direction, Enigo, Key, Keyboard as _, Settings};
#[cfg(all(unix, not(target_os = "macos")))] use xkeysym::key::c;
// self
use crate::prelude::*;

#[derive(Debug)]
pub struct Keyboard {
pub enigo: Enigo,
}
pub struct Keyboard(pub Enigo);
impl Keyboard {
pub fn init() -> Result<Self> {
let enigo = Enigo::new(&Settings::default()).map_err(EnigoError::NewCon)?;

Ok(Self { enigo })
Ok(Self(Enigo::new(&Settings::default()).map_err(EnigoError::NewCon)?))
}

pub fn copy(&mut self) -> Result<()> {
self.enigo.key(Key::Other(0x37), Direction::Press).map_err(EnigoError::Input)?;
self.enigo.key(Key::Other(0x08), Direction::Click).map_err(EnigoError::Input)?;
self.enigo.key(Key::Other(0x37), Direction::Release).map_err(EnigoError::Input)?;
self.0.key(Key::Meta, Direction::Press).map_err(EnigoError::Input)?;
// TODO: create a `CGKeyCode` table for macOS in `build.rs`.
#[cfg(target_os = "macos")]
self.0.key(Key::Other(0x08), Direction::Click).map_err(EnigoError::Input)?;
// TODO: Windows.
#[cfg(all(unix, not(target_os = "macos")))]
self.0.key(Key::Other(c), Direction::Click).map_err(EnigoError::Input)?;
self.0.key(Key::Meta, Direction::Release).map_err(EnigoError::Input)?;

Ok(())
}

pub fn text(&mut self, text: &str) -> Result<()> {
Ok(self.enigo.text(text).map_err(EnigoError::Input)?)
Ok(self.0.text(text).map_err(EnigoError::Input)?)
}
}
86 changes: 65 additions & 21 deletions src/component/setting.rs
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// std
use std::{fs, path::PathBuf};
use std::{borrow::Cow, fs, path::PathBuf};
// crates.io
use app_dirs2::AppDataType;
use async_openai::config::OPENAI_API_BASE;
use eframe::egui::WidgetText;
use global_hotkey::hotkey::{Code, HotKey, Modifiers};
use serde::{Deserialize, Serialize};
// self
use super::openai::Model;
Expand All @@ -14,7 +14,8 @@ use crate::{prelude::*, APP_INFO};
pub struct Setting {
pub general: General,
pub ai: Ai,
pub translation: Translation,
pub chat: Chat,
pub hotkeys: Hotkeys,
}
impl Setting {
pub fn path() -> Result<PathBuf> {
Expand Down Expand Up @@ -79,16 +80,58 @@ impl Default for Ai {
}
}

// TODO: add a super type for all the settings.
// TODO?: implement a `Prompt` trait.
#[derive(Clone, Debug, Default, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Chat {
pub rewrite: Rewrite,
pub translation: Translation,
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Rewrite {
pub prompt: String,
}
impl Rewrite {
pub fn prompt(&self) -> Cow<str> {
Cow::Borrowed(&self.prompt)
}
}
impl Default for Rewrite {
fn default() -> Self {
Self {
prompt: "As language professor, assist me in refining this text. \
Amend any grammatical errors and enhance the language to sound more like a native speaker.\
Just provide the refined text only, without any other things."
.into(),
}
}
}
#[derive(Clone, Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Translation {
pub source: Language,
pub target: Language,
pub prompt: String,
pub a: Language,
pub b: Language,
}
impl Translation {
pub fn prompt(&self) -> Cow<str> {
Cow::Owned(format!(
"Assist me in translate this text between {} and {}. {}",
self.a.as_str(),
self.b.as_str(),
self.prompt
))
}
}
impl Default for Translation {
fn default() -> Self {
Self { source: Language::ZhCn, target: Language::EnGb }
Self {
prompt: "As a language professor, amend any grammatical errors and enhance the language to sound more like a native speaker. \
Provide the translated text only, without any other things.".into(),
a: Language::ZhCn,
b: Language::EnGb,
}
}
}
// https://www.alchemysoftware.com/livedocs/ezscript/Topics/Catalyst/Language.htm
Expand All @@ -100,21 +143,22 @@ pub enum Language {
// English (United Kingdom).
EnGb,
}
impl Language {
pub fn all() -> [Self; 2] {
[Self::ZhCn, Self::EnGb]
}

pub fn as_str(&self) -> &'static str {
match self {
Self::ZhCn => "zh-CN",
Self::EnGb => "en-GB",
}
}
#[derive(Debug, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")]
pub struct Hotkeys {
pub rewrite: HotKey,
pub rewrite_directly: HotKey,
pub translate: HotKey,
pub translate_directly: HotKey,
}
#[allow(clippy::from_over_into)]
impl Into<WidgetText> for &Language {
fn into(self) -> WidgetText {
self.as_str().into()
impl Default for Hotkeys {
fn default() -> Self {
Self {
rewrite: HotKey::new(Some(Modifiers::CONTROL), Code::KeyT),
rewrite_directly: HotKey::new(Some(Modifiers::CONTROL), Code::KeyY),
translate: HotKey::new(Some(Modifiers::CONTROL), Code::KeyU),
translate_directly: HotKey::new(Some(Modifiers::CONTROL), Code::KeyI),
}
}
}
3 changes: 2 additions & 1 deletion src/component/tokenizer.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// std
use std::sync::{Arc, RwLock};
use std::sync::Arc;
// crates.io
use llm_utils::tokenizer::LlmTokenizer;
use parking_lot::RwLock;

// TODO: get rid of the `Arc<RwLock<_>>` wrapper.
#[derive(Debug)]
Expand Down
2 changes: 0 additions & 2 deletions src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,6 @@
pub enum Error {
#[error(transparent)]
Io(#[from] std::io::Error),
#[error(transparent)]
TryRecv(#[from] std::sync::mpsc::TryRecvError),

#[error(transparent)]
AppDirs2(#[from] app_dirs2::AppDirsError),
Expand Down
11 changes: 3 additions & 8 deletions src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ mod prelude {
}

// std
use std::panic;
#[cfg(not(feature = "dev"))] use std::panic;
// crates.io
use app_dirs2::{AppDataType, AppInfo};
use tracing_appender::rolling::{RollingFileAppender, Rotation};
Expand Down Expand Up @@ -57,12 +57,7 @@ fn main() {

subscriber.init();

panic::set_hook(Box::new(|p| {
if let Some(p) = p.payload().downcast_ref::<&str>() {
tracing::error!("{p}");
} else {
tracing::error!("panic occurred");
}
}));
#[cfg(not(feature = "dev"))]
panic::set_hook(Box::new(|p| tracing::error!("{p}")));
air::launch().unwrap();
}
19 changes: 17 additions & 2 deletions src/service.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
mod hotkey;
use hotkey::Hotkey;

mod keyboard;
use keyboard::Keyboard;

mod quoter;
use quoter::Quoter;

Expand All @@ -12,16 +15,28 @@ use crate::{component::Components, prelude::*, state::State};

#[derive(Debug)]
pub struct Services {
pub keyboard: Keyboard,
pub rt: Option<Runtime>,
pub quoter: Quoter,
pub hotkey: Hotkey,
}
impl Services {
pub fn init(ctx: &Context, components: &Components, state: &State) -> Result<Self> {
let keyboard = Keyboard::init();
let rt = Runtime::new()?;
let quoter = Quoter::init(&rt, state.chat.quote.clone());
let hotkey = Hotkey::init(ctx, &rt, components, state)?;
let hotkey = Hotkey::init(ctx, keyboard.clone(), &rt, components, state)?;

Ok(Self { keyboard, rt: Some(rt), quoter, hotkey })
}

pub fn abort(&mut self) {
self.keyboard.abort();
self.quoter.abort();
self.hotkey.abort();

Ok(Self { rt: Some(rt), quoter, hotkey })
if let Some(rt) = self.rt.take() {
rt.shutdown_background();
}
}
}
Loading

0 comments on commit e7231d2

Please sign in to comment.