From 995dedbe8245bfff51e8e8af800c010917ef9d67 Mon Sep 17 00:00:00 2001 From: starlight Date: Tue, 18 Mar 2025 16:00:57 +1300 Subject: [PATCH] make the thing --- .vscode/launch.json | 4 +- README.md | 4 +- package.json | 2 +- src-tauri/Cargo.toml | 11 +- src-tauri/capabilities/default.json | 4 +- src-tauri/src/config.rs | 42 +------ src-tauri/src/lib.rs | 168 +++++++--------------------- src-tauri/src/main.rs | 2 +- src-tauri/tauri.conf.json | 6 +- src/app.html | 2 +- src/routes/+page.svelte | 135 +++++----------------- src/routes/Key.svelte | 14 --- static/global.css | 69 +++++------- 13 files changed, 122 insertions(+), 341 deletions(-) delete mode 100644 src/routes/Key.svelte diff --git a/.vscode/launch.json b/.vscode/launch.json index 0826a47..7cefad4 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,9 +8,9 @@ "type": "lldb", "request": "launch", "name": "Debug", - "program": "${workspaceFolder}/src-tauri/target/debug/keydisplay", + "program": "${workspaceFolder}/src-tauri/target/debug/copybot", "args": [], "cwd": "${workspaceFolder}" } ] -} \ No newline at end of file +} diff --git a/README.md b/README.md index 7c103bd..cfdf957 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,3 @@ -# keydisplay +# copybot -ligma balls +you may need to install libxdo-dev, check https://github.com/Enigo-rs/Enigo for instructions diff --git a/package.json b/package.json index a25f1c8..e6d213e 100644 --- a/package.json +++ b/package.json @@ -1,5 +1,5 @@ { - "name": "keydisplay", + "name": "copybot", "version": "0.1.0", "description": "", "type": "module", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index de8df49..8cf650a 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,9 +1,10 @@ [package] -name = "keydisplay" +name = "copybot" version = "0.1.0" -description = "A Tauri App" -authors = ["starlight"] edition = "2021" +repository = "https://git.stardust.wtf/starlight/copybot" +license = "GPL-2.0-only" +authors = ["starlight"] # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -11,7 +12,7 @@ edition = "2021" # The `_lib` suffix may seem redundant but it is necessary # to make the lib name unique and wouldn't conflict with the bin name. # This seems to be only an issue on Windows, see https://github.com/rust-lang/cargo/issues/8519 -name = "keydisplay_lib" +name = "copybot_lib" crate-type = ["staticlib", "cdylib", "rlib"] [build-dependencies] @@ -27,3 +28,5 @@ dirs = "5.0" thiserror = "2.0.11" tauri-plugin-fs = { version = "2.0.0", features = ["watch"] } rdev = { version = "0.5.3", features = ["serde", "serialize"] } +fastrand = "2.3.0" +enigo = "0.3.0" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index 504a3b1..52d5b3e 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -11,7 +11,7 @@ "fs:default", { "identifier": "fs:allow-app-read-recursive", - "allow": [{"path": "$CONFIG/keydisplay"}] + "allow": [{"path": "$CONFIG/copybot"}] } ] -} \ No newline at end of file +} diff --git a/src-tauri/src/config.rs b/src-tauri/src/config.rs index faf023a..976c070 100644 --- a/src-tauri/src/config.rs +++ b/src-tauri/src/config.rs @@ -1,7 +1,6 @@ use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::path::PathBuf; use std::fs; +use std::path::PathBuf; use thiserror::Error; #[derive(Error, Debug)] @@ -22,8 +21,9 @@ pub struct Config { // theme = grey / night / day / catppuccin_mocha pub default: bool, pub theme: String, - pub listen: ListenConfig, - pub display: DisplayConfig, + // bind = "Delete" + pub bind: String, + pub shift_enter_newline: bool, } // this is hack as fvck @@ -38,44 +38,14 @@ impl Default for Config { } } -// [listen] -#[derive(Debug, Serialize, Deserialize)] -pub struct ListenConfig { - // keys = ["KeyZ", "KeyX"] - pub keys: Vec, - // mouse = ["LeftButton", "RightButton"] - pub mouse: Vec, -} - -// [display] -#[derive(Debug, Serialize, Deserialize)] -pub struct DisplayConfig { - // "instant" or "ease" - pub press: String, - // display mouse buttons as a key or as an svg of a mouse - pub mouse: String, - // multiply default key length for the key by the float value - pub size: Vec>, - // which keys to linebreak after - pub r#break: Vec, -} - impl Config { // Provide default values pub fn default() -> Self { Self { default: true, theme: "grey".to_string(), - listen: ListenConfig { - keys: vec!["KeyZ".to_string(), "KeyX".to_string(), "MetaLeft".to_string()], - mouse: vec!["Left".to_string(), "Right".to_string()], - }, - display: DisplayConfig { - press: "ease".to_string(), - mouse: "key".to_string(), - size: vec![HashMap::new()], - r#break: vec![], - }, + bind: "Delete".to_string(), + shift_enter_newline: true, } } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index 011be58..896f33b 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -1,9 +1,13 @@ use config::Config; // Learn more about Tauri commands at https://tauri.app/develop/calling-rust/ -use std::{collections::HashMap, sync::Mutex}; -use tauri::{ipc::Channel, Manager, State}; -use rdev::{Event, EventType, listen}; +use enigo::{ + Direction::{Press, Release}, + Enigo, Key, Keyboard, +}; +use rdev::{listen, Event, EventType}; +use std::sync::Mutex; use std::thread; +use tauri::{ipc::Channel, Manager, State}; mod config; @@ -21,14 +25,9 @@ pub fn run() { update_config, get_default, get_theme, - get_keys, - get_mouse_buttons, - get_mouse_display, - get_press_display, - get_size_display, - get_breaks, + get_bind, start_listener, - label_from_keycode + paste_text ]) .setup(|app| { let config = config::Config::load_or_create(get_config_path()).unwrap(); @@ -39,9 +38,7 @@ pub fn run() { let themes = vec!["grey", "night", "day", "catppuccin_mocha"]; if themes.contains(&theme.as_str()) { println!("Setting theme to: {}", theme); - app.manage(Mutex::new(AppState { - config: config, - })); + app.manage(Mutex::new(AppState { config: config })); } else { println!( "{}", @@ -49,7 +46,6 @@ pub fn run() { ); } - Ok(()) }) .run(tauri::generate_context!()) @@ -62,7 +58,7 @@ pub fn run() { #[tauri::command] fn get_config_path() -> std::path::PathBuf { let config_dir = dirs::config_dir().expect("Config directory not found"); - config_dir.join("keydisplay") + config_dir.join("copybot") } // update the app state with new config #[tauri::command] @@ -72,7 +68,7 @@ fn update_config(state: State<'_, Mutex>) { } #[tauri::command] fn get_default(state: State<'_, Mutex>) -> Result { - let default = state.lock().unwrap().config.default.clone(); + let default = state.lock().unwrap().config.default; Ok(default) } #[tauri::command] @@ -81,126 +77,48 @@ fn get_theme(state: State<'_, Mutex>) -> Result { Ok(theme) } #[tauri::command] -fn get_keys(state: State<'_, Mutex>) -> Result, Vec> { - let keys = state.lock().unwrap().config.listen.keys.clone(); - Ok(keys) -} -#[tauri::command] -fn get_mouse_buttons(state: State<'_, Mutex>) -> Result, Vec> { - let mouse_buttons = state.lock().unwrap().config.listen.mouse.clone(); - Ok(mouse_buttons) -} -#[tauri::command] -fn get_press_display(state: State<'_, Mutex>) -> Result { - let press_display = state.lock().unwrap().config.display.press.clone(); - Ok(press_display) -} -#[tauri::command] -fn get_size_display(state: State<'_, Mutex>) -> Result>, Vec>> { - let size_display = state.lock().unwrap().config.display.size.clone(); - Ok(size_display) -} -#[tauri::command] -fn get_mouse_display(state: State<'_, Mutex>) -> Result { - let mouse_display = state.lock().unwrap().config.display.mouse.clone(); - Ok(mouse_display) -} -#[tauri::command] -fn get_breaks(state: State<'_, Mutex>) -> Result, Vec> { - let breaks = state.lock().unwrap().config.display.r#break.clone(); - Ok(breaks) +fn get_bind(state: State<'_, Mutex>) -> Result { + let bind = state.lock().unwrap().config.bind.clone(); + Ok(bind) } + // Input events #[tauri::command] -fn start_listener(keys: Vec, m_buttons: Vec, channel: Channel) { +fn start_listener(bind: String, channel: Channel) { thread::spawn(move || { - println!("Started listening for keys: {:?} and buttons: {:?}", keys, m_buttons); - listen(move |event| { - match event.event_type { - EventType::KeyPress(key) | EventType::KeyRelease(key) => { - if keys.contains(&serde_json::to_string(&key).unwrap().replace("\"", "")) { channel.send(event).unwrap() }; - }, - EventType::ButtonPress(button) | EventType::ButtonRelease(button) => { - if m_buttons.contains(&serde_json::to_string(&button).unwrap().replace("\"", "")) { channel.send(event).unwrap() }; - }, - EventType::MouseMove { x, y } => (), - EventType::Wheel { delta_x, delta_y } => (), + println!("Started listening for bind: {:?}", bind); + listen(move |event| match event.event_type { + EventType::KeyPress(key) | EventType::KeyRelease(key) => { + if bind == serde_json::to_string(&key).unwrap().replace("\"", "") { + channel.send(event).unwrap() + }; } + _ => (), }) }); } -// Other rust #[tauri::command] -fn label_from_keycode(code: &str) -> &str { - match code { - // Keyboard - // Fine as-is - "Alt"|"AltGr"|"End"|"Home"|"Pause" => code, - // All alphabetical keys - c if c.starts_with("Key") - && c.len() == 4 - && c.chars().nth(3).map_or(false, |c| c.is_ascii_alphabetic()) => &c[3..], - // Number row - c if c.starts_with("Num") - && c.len() == 4 - && c.chars().nth(3).map_or(false, |c| c.is_numeric()) => &c[3..], - // All F keys - c if c.starts_with("F") - && c.len() == 2 - && c.chars().nth(1).map_or(false, |c| c.is_numeric()) => c, - // Numpad numbers - c if c.starts_with("Kp") - && c.len() == 3 - && c.chars().nth(2).map_or(false, |c| c.is_numeric()) => &c[2..], - // Individual mappings - "Backspace" => "🡐", - "CapsLock" => "Caps", - "ControlLeft"|"ControlRight" => "Ctrl", - "Delete"|"KpDelete" => "Del", - "DownArrow" => "⯆", - "Escape" => "Esc", - "LeftArrow" => "⯇", - "MetaLeft"|"MetaRight" => "Super", - "PageDown" => "PgDn", - "PageUp" => "PgUp", - "Return" => "⮠", - "RightArrow" => "⯈", - "ShiftLeft"|"ShiftRight" => "Shift", - // needs an obscure blank character (U+E002D) - css acts weird if its a space or anything the program interprets as one - "Space" => "󠀯", - "Tab" => "⇌", - "UpArrow" => "⯅", - "PrintScreen" => "PrtSc", - "ScrollLock" => "ScrLk", - "NumLock" => "Num", - "BackQuote" => "`", - "Minus" => "-", - "Equal" => "=", - "LeftBracket" => "(", - "RightBracket" => ")", - "SemiColon" => ";", - "Quote" => "\"", - "BackSlash"|"IntlBackslash" => "\\", - "Comma" => ",", - "Dot" => ".", - "Slash"|"KpDivide" => "/", - "Insert" => "Ins", - "KpReturn" => "↲", - "KpPlus" => "+", - "KpMultiply" => "*", - "Function" => "Fn", +fn paste_text(state: State<'_, Mutex>, text: String) { + println!("{}", text); + let mut enigo = Enigo::new(&enigo::Settings::default()).unwrap(); - // Mouse - "Left" => "M1", - "Right" => "M2", - "Middle" => "M3", - - &_ => { - println!("Error creating frontend label for keycode: {}, displaying as Unknown", code); - "Unknown" - }, + if state.lock().unwrap().config.shift_enter_newline { + for c in text.chars() { + if c == '\n' { + // Press Shift+Enter combination + enigo.key(Key::Shift, Press).unwrap(); + enigo.key(Key::Return, Press).unwrap(); + enigo.key(Key::Return, Release).unwrap(); + enigo.key(Key::Shift, Release).unwrap(); + } else { + // Type regular characters normally + enigo.text(&c.to_string()).unwrap(); + } + } + } else { + enigo.text(text.as_str()).unwrap(); } -} \ No newline at end of file +} diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 59f52d8..2a61d19 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -4,5 +4,5 @@ mod config; fn main() { - keydisplay_lib::run() + copybot_lib::run() } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a7ac764..3792ff7 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,8 +1,8 @@ { "$schema": "https://schema.tauri.app/config/2", - "productName": "keydisplay", + "productName": "COPYBOT", "version": "0.1.0", - "identifier": "whatisthisanandroidapp.keydisplay.starlight", + "identifier": "whatisthisanandroidapp.copybot.starlight", "build": { "beforeDevCommand": "yarn dev", "devUrl": "http://localhost:1420", @@ -12,7 +12,7 @@ "app": { "windows": [ { - "title": "keydisplay", + "title": "copybot", "width": 800, "height": 600 } diff --git a/src/app.html b/src/app.html index e7244c0..410113b 100644 --- a/src/app.html +++ b/src/app.html @@ -4,7 +4,7 @@ - keydisplay + copybot %sveltekit.head% diff --git a/src/routes/+page.svelte b/src/routes/+page.svelte index be933e8..d7674c1 100644 --- a/src/routes/+page.svelte +++ b/src/routes/+page.svelte @@ -1,6 +1,5 @@
{#if default_config} -
Docs for configuring this program are at https://git.stardust.wtf/starlight/keydisplay/src/branch/main/README.md
You can disable this annoying notice by removing the default = true line from {config_path} :)
+
Docs for configuring this program are at https://git.stardust.wtf/starlight/copybot/src/branch/main/README.md
You can disable this annoying notice by removing the default = true line from {config_path} :)
{/if} -
- {#each key_elements as key (key.code)} - - - {#if breaks.has(key.code)} -
- {/if} - {/each} -
-
- {#each mouse_button_elements as button (button.code)} - - - {#if breaks.has(button.code)} -
- {/if} - {/each} -
-
\ No newline at end of file +

copybot

+ + +

Current bind: {bind}

+

Set to current textarea content: {content == content_tmp}

+ diff --git a/src/routes/Key.svelte b/src/routes/Key.svelte deleted file mode 100644 index 0296711..0000000 --- a/src/routes/Key.svelte +++ /dev/null @@ -1,14 +0,0 @@ - - - -
-{label} -
\ No newline at end of file diff --git a/static/global.css b/static/global.css index 9cd1298..9ee6223 100644 --- a/static/global.css +++ b/static/global.css @@ -32,30 +32,30 @@ img { :root, [data-selected-theme="grey"] { --color-app-background: #2F2F2F; - --color-key-background: #363636; - --color-key-background-pressed: #212121; + --color-app-background-2: #4D4D4D; --color-text: #fff; + --color-button-pressed: #212121; --color-accent: #737373; } [data-selected-theme="night"] { --color-app-background: #000; - --color-key-background: #0f0f0f; - --color-key-background-pressed: #212121; + --color-app-background-2: #1B1B1B; --color-text: #eee; + --color-button-pressed: #212121; --color-accent: #525151; } [data-selected-theme="day"] { --color-app-background: #e0e0e0; - --color-key-background: #fff; - --color-key-background-pressed: #eee; + --color-app-background-2: #f0f0f0; --color-text: #000; + --color-button-pressed: #000; --color-accent: #fff; } [data-selected-theme="catppuccin_mocha"] { --color-app-background: #181825; - --color-key-background: #1e1e2e; - --color-key-background-pressed: #313244; + --color-app-background-2: #1e1e2e; --color-text: #cdd6f4; + --color-button-pressed: #313244; --color-accent: #f5e0dc; } @@ -87,22 +87,28 @@ button { border: 1px solid transparent; padding: 0.6em 1.2em; font-size: 1em; - font-weight: 500; + font-weight: 400; font-family: inherit; - color: #0f0f0f; - background-color: #ffffff; + color: var(--color-text); + background-color: var(--color-app-background-2); transition: border-color 0.25s; box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2); cursor: pointer; outline: none; } +button#set_text { + margin-top: 5px; + width: 25vw; + margin-inline: auto; +} + button:hover { border-color: #396cd8; } button:active { border-color: #396cd8; - background-color: #e8e8e8; + background-color: var(--color-button-pressed); } a { @@ -125,34 +131,13 @@ div#default_config { overflow-wrap: break-word; } -div#keys, -div#mouse_buttons { - /* horizontal spacing between keys is dependent on font-size of the parent div? */ - font-size: 0; +textarea { + background-color: var(--color-app-background-2); + border: 3px solid var(--color-accent); + border-radius: 5px; + font-size: 16px; + width: 80vw; + margin-top: 5px; + margin-inline: auto; + color: var(--color-text); } - -.key { - width: 100px; - height: 100px; - display: inline-flex; - align-items: center; - justify-content: center; - font-size: 20px; - margin: 5px; - border: 5px solid var(--color-accent); - border-radius: 5px; - transition: var(--pressed-transition); - background-color: var(--color-key-background); - cursor: pointer; - user-select: none; -} - -[data-press-display="ease"] { - --pressed-transition: background-color 0.1s ease, transform 0.1s ease; -} - -.pressed { - background-color: var(--color-key-background-pressed) !important; - transform: scale(0.95); - box-shadow: 0 2px 5px rgba(0,0,0,0.2); -} \ No newline at end of file