From 5bcc084833a27ca3a3bd89cc43333663523d59aa Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Tue, 24 Dec 2024 23:29:46 +1000 Subject: [PATCH 01/97] docs(readme): update download links to version 0.3.1 --- README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index c13d915..7d75f63 100644 --- a/README.md +++ b/README.md @@ -5,31 +5,31 @@ The fixed and simple clipboard manager for both Windows and Linux. - + Windows (x64) • - + Windows (arm64) - + Linux (deb) • - + Linux (rpm) • - + Linux (AppImage) - + macOS (Silicon) • - + macOS (Intel) From a4657448e6dcbaad02089a7a8de7ad961a58f7bd Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 17:17:59 +1000 Subject: [PATCH 02/97] docs(readme): roadmap update --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 7d75f63..452991a 100644 --- a/README.md +++ b/README.md @@ -64,7 +64,7 @@ Qopy is a fixed clipboard manager designed as a simple alternative to the standa - [ ] [Setup guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md) - [ ] Sync Clipboard across devices https://github.com/0PandaDEV/Qopy/issues/8 - [ ] Settings https://github.com/0PandaDEV/Qopy/issues/2 -- [ ] Metadata for copied items https://github.com/0PandaDEV/Qopy/issues/5 +- [x] Metadata for copied items https://github.com/0PandaDEV/Qopy/issues/5 - [ ] Code highlighting https://github.com/0PandaDEV/Qopy/issues/7 - [ ] Streamshare integration https://github.com/0PandaDEV/Qopy/issues/4 - [ ] Content type filter https://github.com/0PandaDEV/Qopy/issues/16 From fc0c52228c042f5e3cbe8aa3ea8d8c5a8a12e73f Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 18:14:14 +1000 Subject: [PATCH 03/97] chore: update nuxt version to 3.15.0 in package.json --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index 5e1944c..aad552e 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ "@tauri-apps/cli": "2.1.0", "@tauri-apps/plugin-autostart": "2.2.0", "@tauri-apps/plugin-os": "2.2.0", - "nuxt": "3.14.1592", + "nuxt": "3.15.0", "overlayscrollbars": "2.10.1", "overlayscrollbars-vue": "0.5.9", "sass-embedded": "1.83.0", From f59c5bbc959f24b84c7b897c1dceb51be0ce0608 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:33:09 +1000 Subject: [PATCH 04/97] fix: windows ui bugs #21 --- app.vue | 2 ++ assets/css/index.scss | 4 ++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app.vue b/app.vue index ef6e0aa..8f75e44 100644 --- a/app.vue +++ b/app.vue @@ -69,6 +69,8 @@ html, body, #__nuxt { background-color: transparent; + width: 750px; + height: 474px; } .os-scrollbar-horizontal { diff --git a/assets/css/index.scss b/assets/css/index.scss index db473f6..63ae190 100644 --- a/assets/css/index.scss +++ b/assets/css/index.scss @@ -7,8 +7,8 @@ $text2: #ada9a1; $mutedtext: #78756f; .bg { - width: 750px; - height: 474px; + width: 100%; + height: 100%; background-color: $primary; border: 1px solid $divider; border-radius: 12px; From 4adb57d6adb0f3dc747889ebb9ccea5c86a5596f Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:34:57 +1000 Subject: [PATCH 05/97] fix: font no displayed in content --- assets/css/index.scss | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/assets/css/index.scss b/assets/css/index.scss index 63ae190..2fb355a 100644 --- a/assets/css/index.scss +++ b/assets/css/index.scss @@ -99,7 +99,7 @@ $mutedtext: #78756f; top: 53px; left: 284px; height: calc(100vh - 254px); - font-family: CommitMono Nerd Font !important; + font-family: CommitMono !important; font-size: 12px; letter-spacing: 1; border-radius: 10px; @@ -116,7 +116,7 @@ $mutedtext: #78756f; } span { - font-family: CommitMono Nerd Font !important; + font-family: CommitMono !important; } .image { From a0cbd13f53819beac2de47371a38bc168061f52c Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 20:36:47 +1000 Subject: [PATCH 06/97] chore: update qopy version to 0.3.2 in Cargo files --- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index d24fd5b..7004a95 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4075,7 +4075,7 @@ dependencies = [ [[package]] name = "qopy" -version = "0.3.1" +version = "0.3.2" dependencies = [ "active-win-pos-rs", "applications", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6045afb..d6df90d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qopy" -version = "0.3.1" +version = "0.3.2" description = "Qopy" authors = ["pandadev"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index dd53dd9..daf875d 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Qopy", - "version": "0.3.1", + "version": "0.3.2", "identifier": "net.pandadev.qopy", "build": { "frontendDist": "../dist", From 18ca53ef04c132bf5f98b51692c2077870c0d4bd Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 21:14:55 +1000 Subject: [PATCH 07/97] feat(logger): enhance logging format and add panic hook --- src-tauri/src/utils/logger.rs | 46 +++++++++++++++++++++++++++++------ 1 file changed, 39 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/utils/logger.rs b/src-tauri/src/utils/logger.rs index d2f1442..114b2c6 100644 --- a/src-tauri/src/utils/logger.rs +++ b/src-tauri/src/utils/logger.rs @@ -2,6 +2,7 @@ use chrono; use log::{LevelFilter, SetLoggerError}; use std::fs::{File, OpenOptions}; use std::io::Write; +use std::panic; pub struct FileLogger { file: File, @@ -15,14 +16,18 @@ impl log::Log for FileLogger { fn log(&self, record: &log::Record) { if self.enabled(record.metadata()) { let mut file = self.file.try_clone().expect("Failed to clone file handle"); + + // Format: timestamp [LEVEL] target: message (file:line) writeln!( file, - "{} - {}: {}", + "{} [{:<5}] {}: {} ({}:{})", chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), record.level(), - record.args() - ) - .expect("Failed to write to log file"); + record.target(), + record.args(), + record.file().unwrap_or("unknown"), + record.line().unwrap_or(0) + ).expect("Failed to write to log file"); } } @@ -35,15 +40,42 @@ pub fn init_logger(app_data_dir: &std::path::Path) -> Result<(), SetLoggerError> let logs_dir = app_data_dir.join("logs"); std::fs::create_dir_all(&logs_dir).expect("Failed to create logs directory"); + // Use .log extension for standard log files let log_path = logs_dir.join("app.log"); let file = OpenOptions::new() .create(true) - .append(true) - .open(log_path) + .append(true) // Use append mode instead of write + .open(&log_path) .expect("Failed to open log file"); + // Set up panic hook + let panic_file = file.try_clone().expect("Failed to clone file handle"); + panic::set_hook(Box::new(move |panic_info| { + let mut file = panic_file.try_clone().expect("Failed to clone file handle"); + + let location = panic_info.location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_else(|| "unknown location".to_string()); + + let message = match panic_info.payload().downcast_ref::<&str>() { + Some(s) => *s, + None => match panic_info.payload().downcast_ref::() { + Some(s) => s.as_str(), + None => "Unknown panic message", + }, + }; + + let _ = writeln!( + file, + "{} [PANIC] rust_panic: {} ({})", + chrono::Local::now().format("%Y-%m-%d %H:%M:%S"), + message, + location + ); + })); + let logger = Box::new(FileLogger { file }); unsafe { log::set_logger_racy(Box::leak(logger))? }; log::set_max_level(LevelFilter::Debug); Ok(()) -} +} \ No newline at end of file From dc34912895a3218cadee91567e6d59a54c9d9444 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 21:16:23 +1000 Subject: [PATCH 08/97] fix(release): improve changelog generation and add debug information --- .github/workflows/release.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 21b6882..5b6ecf8 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -243,7 +243,14 @@ jobs: id: release_body run: | VERSION="${{ needs.prepare.outputs.version }}" - CHANGES=$(git log $(git describe --tags --abbrev=0)..HEAD --pretty=format:"- %s") + echo "Debug: Listing tags" + git tag -l + echo "Debug: Getting latest tag" + git describe --tags --abbrev=0 || echo "No tags found" + echo "Debug: Generating changelog" + CHANGES=$(git log "$(git describe --tags --abbrev=0 2>/dev/null || echo HEAD^)" HEAD --pretty=format:"- %s" || echo "No changelog available") + echo "Debug: Changelog content:" + echo "$CHANGES" # Calculate hashes with corrected paths WINDOWS_ARM_HASH=$(sha256sum "artifacts/windows-arm64-binaries/Qopy-${VERSION}_arm64.msi" | awk '{ print $1 }') From 1417d35cd7ee4a94a9319846c843ee3d7e4e3d9c Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 21:31:26 +1000 Subject: [PATCH 09/97] fix(release): enhance changelog generation to handle missing tags --- .github/workflows/release.yml | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 5b6ecf8..f79b85d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -243,12 +243,18 @@ jobs: id: release_body run: | VERSION="${{ needs.prepare.outputs.version }}" - echo "Debug: Listing tags" - git tag -l - echo "Debug: Getting latest tag" - git describe --tags --abbrev=0 || echo "No tags found" - echo "Debug: Generating changelog" - CHANGES=$(git log "$(git describe --tags --abbrev=0 2>/dev/null || echo HEAD^)" HEAD --pretty=format:"- %s" || echo "No changelog available") + + # Get the most recent release tag (v* tags only) + LAST_TAG=$(git describe --match "v*" --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1` 2>/dev/null || echo "") + + if [ -n "$LAST_TAG" ]; then + echo "Debug: Found last release tag: $LAST_TAG" + CHANGES=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s") + else + echo "Debug: No previous release tag found, using first commit" + CHANGES=$(git log --pretty=format:"- %s") + fi + echo "Debug: Changelog content:" echo "$CHANGES" From 2fa82d758429f8fd7595f0a4cb9bd2aa0e8c2deb Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 25 Dec 2024 23:44:06 +1000 Subject: [PATCH 10/97] fix: interface unusable becasue no interaction was possible --- app.vue | 9 ++++++--- components/Noise.vue | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/app.vue b/app.vue index 8f75e44..2d69efc 100644 --- a/app.vue +++ b/app.vue @@ -1,5 +1,5 @@ - + @@ -59,6 +59,8 @@ onMounted(async () => { scroll-behavior: smooth; scrollbar-width: thin; user-select: none; + position: relative; + z-index: 1; --os-handle-bg: #ADA9A1; --os-handle-bg-hover: #78756F; @@ -66,11 +68,12 @@ onMounted(async () => { } html, -body, -#__nuxt { +body { background-color: transparent; width: 750px; height: 474px; + user-select: none !important; + pointer-events: none !important; } .os-scrollbar-horizontal { diff --git a/components/Noise.vue b/components/Noise.vue index 41bb879..67fcbff 100644 --- a/components/Noise.vue +++ b/components/Noise.vue @@ -12,7 +12,7 @@ height: 100vh; pointer-events: none; user-select: none; - z-index: 0; + z-index: -1; background-image: url('/noise.png'); background-repeat: repeat; image-rendering: pixelated; From 1a15ea777779327acf0bf8a09edd8e8180ea774e Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 26 Dec 2024 00:17:25 +1000 Subject: [PATCH 11/97] chore: update qopy version to 0.3.3 in Cargo files and configuration --- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 7004a95..64b9f84 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4075,7 +4075,7 @@ dependencies = [ [[package]] name = "qopy" -version = "0.3.2" +version = "0.3.3" dependencies = [ "active-win-pos-rs", "applications", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d6df90d..809f47b 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qopy" -version = "0.3.2" +version = "0.3.3" description = "Qopy" authors = ["pandadev"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index daf875d..2e12a87 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Qopy", - "version": "0.3.2", + "version": "0.3.3", "identifier": "net.pandadev.qopy", "build": { "frontendDist": "../dist", From d1f2b1ddd75d408b8dbce6d12843ac1771087cab Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Sun, 29 Dec 2024 10:53:26 +1000 Subject: [PATCH 12/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 452991a..b06496e 100644 --- a/README.md +++ b/README.md @@ -21,7 +21,7 @@ The fixed and simple clipboard manager for both Windows and Linux. Linux (rpm) • - + Linux (AppImage) From b9d758f3172e11cd9afc8a3d3734c56626888caf Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:00:54 +1000 Subject: [PATCH 13/97] chore: update serde to 1.0.217 and reqwest to 0.12.11 in Cargo.toml --- src-tauri/Cargo.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 809f47b..08142a1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -26,14 +26,14 @@ tauri-plugin-prevent-default = "1.0.1" tauri-plugin-global-shortcut = "2.2.0" tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.2", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } -serde = { version = "1.0.216", features = ["derive"] } +serde = { version = "1.0.217", features = ["derive"] } tokio = { version = "1.42.0", features = ["full"] } serde_json = "1.0.134" rdev = "0.5.3" rand = "0.8.5" base64 = "0.22.1" image = "0.25.5" -reqwest = { version = "0.12.9", features = ["json", "blocking"] } +reqwest = { version = "0.12.11", features = ["json", "blocking"] } url = "2.5.4" regex = "1.11.1" sha2 = "0.10.8" From a7e221cb295047a49fa3b0f0734515223ef66141 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:00:59 +1000 Subject: [PATCH 14/97] chore: update dependencies in Cargo.lock for hyper-util, reqwest, serde, tower, and related packages --- src-tauri/Cargo.lock | 52 ++++++++++++++------------------------------ 1 file changed, 16 insertions(+), 36 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 64b9f84..31fba10 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -2318,9 +2318,9 @@ dependencies = [ [[package]] name = "hyper-util" -version = "0.1.7" +version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cde7055719c54e36e95e8719f95883f22072a48ede39db7fc17a4e1d5281e9b9" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" dependencies = [ "bytes", "futures-channel", @@ -2331,7 +2331,6 @@ dependencies = [ "pin-project-lite", "socket2", "tokio", - "tower", "tower-service", "tracing", ] @@ -3844,26 +3843,6 @@ dependencies = [ "siphasher", ] -[[package]] -name = "pin-project" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" -dependencies = [ - "pin-project-internal", -] - -[[package]] -name = "pin-project-internal" -version = "1.1.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.87", -] - [[package]] name = "pin-project-lite" version = "0.2.14" @@ -4442,9 +4421,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.9" +version = "0.12.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" +checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3" dependencies = [ "base64 0.22.1", "bytes", @@ -4481,6 +4460,7 @@ dependencies = [ "tokio-native-tls", "tokio-rustls", "tokio-util", + "tower", "tower-service", "url", "wasm-bindgen", @@ -4771,9 +4751,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b9781016e935a97e8beecf0c933758c97a5520d32930e460142b4cd80c6338e" +checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" dependencies = [ "serde_derive", ] @@ -4791,9 +4771,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.216" +version = "1.0.217" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46f859dbbf73865c6627ed570e78961cd3ac92407a2d117204c49232485da55e" +checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" dependencies = [ "proc-macro2", "quote", @@ -6248,14 +6228,14 @@ dependencies = [ [[package]] name = "tower" -version = "0.4.13" +version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8fa9be0de6cf49e536ce1851f987bd21a43b771b09473c3549a6c853db37c1c" +checksum = "d039ad9159c98b70ecfd540b2573b97f7f52c3e8d9f8ad57a24b916a536975f9" dependencies = [ "futures-core", "futures-util", - "pin-project", "pin-project-lite", + "sync_wrapper", "tokio", "tower-layer", "tower-service", @@ -6263,15 +6243,15 @@ dependencies = [ [[package]] name = "tower-layer" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c20c8dbed6283a09604c3e69b4b7eeb54e298b8a600d4d5ecb5ad39de609f1d0" +checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-service" -version = "0.3.2" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bc1c9ce2b5135ac7f93c72918fc37feb872bdc6a5533a8b85eb4b86bfdae52" +checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" From 0a08ce95d21b4da06d66205fa4723c95c3fc2e66 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Mon, 30 Dec 2024 12:04:23 +1000 Subject: [PATCH 15/97] refactor: clean up main.rs by removing unused imports and simplifying main window initialization --- src-tauri/src/main.rs | 61 ++++++++++++++++++------------------------- 1 file changed, 26 insertions(+), 35 deletions(-) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index c6aed37..7f9fb79 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -7,14 +7,12 @@ mod api; mod db; mod utils; +use sqlx::sqlite::SqlitePoolOptions; use std::fs; use tauri::Manager; -use tauri::WebviewUrl; -use tauri::WebviewWindow; -use sqlx::sqlite::SqlitePoolOptions; +use tauri_plugin_aptabase::{EventTracker, InitOptions}; use tauri_plugin_autostart::MacosLauncher; use tauri_plugin_prevent_default::Flags; -use tauri_plugin_aptabase::{EventTracker, InitOptions}; fn main() { let runtime = tokio::runtime::Runtime::new().expect("Failed to create Tokio runtime"); @@ -27,19 +25,27 @@ fn main() { .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_fs::init()) .plugin(tauri_plugin_updater::Builder::default().build()) - .plugin(tauri_plugin_aptabase::Builder::new("A-SH-8937252746") - .with_options(InitOptions { - host: Some("https://aptabase.pandadev.net".to_string()), - flush_interval: None, - }) - .with_panic_hook(Box::new(|client, info, msg| { - let location = info.location().map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())).unwrap_or_else(|| "".to_string()); + .plugin( + tauri_plugin_aptabase::Builder::new("A-SH-8937252746") + .with_options(InitOptions { + host: Some("https://aptabase.pandadev.net".to_string()), + flush_interval: None, + }) + .with_panic_hook(Box::new(|client, info, msg| { + let location = info + .location() + .map(|loc| format!("{}:{}:{}", loc.file(), loc.line(), loc.column())) + .unwrap_or_else(|| "".to_string()); - let _ = client.track_event("panic", Some(serde_json::json!({ - "info": format!("{} ({})", msg, location), - }))); - })) - .build()) + let _ = client.track_event( + "panic", + Some(serde_json::json!({ + "info": format!("{} ({})", msg, location), + })), + ); + })) + .build(), + ) .plugin(tauri_plugin_autostart::init( MacosLauncher::LaunchAgent, Some(vec![]), @@ -52,7 +58,7 @@ fn main() { .setup(|app| { let app_data_dir = app.path().app_data_dir().unwrap(); utils::logger::init_logger(&app_data_dir).expect("Failed to initialize logger"); - + fs::create_dir_all(&app_data_dir).expect("Failed to create app data directory"); let db_path = app_data_dir.join("data.db"); @@ -76,22 +82,7 @@ fn main() { app_handle_clone.manage(pool); }); - let main_window = if let Some(window) = app.get_webview_window("main") { - window - } else { - WebviewWindow::builder(app.handle(), "main", WebviewUrl::App("index.html".into())) - .title("Qopy") - .resizable(false) - .fullscreen(false) - .inner_size(750.0, 474.0) - .focused(true) - .skip_taskbar(true) - .visible(false) - .decorations(false) - .transparent(true) - .always_on_top(false) - .build()? - }; + let main_window = app.get_webview_window("main"); let _ = db::database::setup(app); api::hotkeys::setup(app_handle.clone()); @@ -99,8 +90,8 @@ fn main() { api::clipboard::setup(app.handle()); let _ = api::clipboard::start_monitor(app_handle.clone()); - utils::commands::center_window_on_current_monitor(&main_window); - main_window.hide()?; + utils::commands::center_window_on_current_monitor(main_window.as_ref().unwrap()); + main_window.as_ref().map(|w| w.hide()).unwrap_or(Ok(()))?; let _ = app.track_event("app_started", None); From 14e8edd23840e25f944e9f8d8a988b5c7ef1994b Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Wed, 1 Jan 2025 16:13:50 +1000 Subject: [PATCH 16/97] docs: add linux to os path --- GET_STARTED.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GET_STARTED.md b/GET_STARTED.md index dc761bf..548a5e9 100644 --- a/GET_STARTED.md +++ b/GET_STARTED.md @@ -8,7 +8,7 @@ All the data of Qopy is stored inside of a SQLite database. |------------------|-----------------------------------------------------------------| | Windows | `C:\Users\USERNAME\AppData\Roaming\net.pandadev.qopy` | | macOS | `/Users/USERNAME/Library/Application Support/net.pandadev.qopy` | -| Linux | `` | +| Linux | `/home/USERNAME/.local/share/net.pandadev.qopy` | ## Disable Windows+V for default clipboard manager From 0f29e0af07513f59c28c1f934f1f214c6c7e0865 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 16:17:13 +1000 Subject: [PATCH 17/97] chore: migrate from pnpm to bun for dependency management in workflows and update README instructions --- .github/workflows/build.yml | 24 ++++++++++---------- .github/workflows/release.yml | 42 +++++++++++++++++------------------ README.md | 8 +++---- src-tauri/tauri.conf.json | 8 +++---- 4 files changed, 40 insertions(+), 42 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c23943..afe4e08 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,11 +56,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- - - run: npm install -g pnpm && pnpm install + ${{ runner.os }}-bun- + - run: curl -fsSL https://bun.sh/install | bash && bun install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -143,11 +143,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- - - run: npm install -g pnpm && pnpm install + ${{ runner.os }}-bun- + - run: curl -fsSL https://bun.sh/install | bash && bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -199,16 +199,16 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- + ${{ runner.os }}-bun- - name: Install dependencies run: | sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: npm install -g pnpm && pnpm install + - run: curl -fsSL https://bun.sh/install | bash && bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f79b85d..99ce4e6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,11 +57,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- - - run: npm install -g pnpm && pnpm install + ${{ runner.os }}-bun- + - run: curl -fsSL https://bun.sh/install | bash && bun install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -83,7 +83,7 @@ jobs: TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} with: args: ${{ matrix.args }} - + - name: Rename macOS Artifacts run: | mv src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/*.dmg src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.dmg @@ -125,11 +125,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- - - run: npm install -g pnpm && pnpm install + ${{ runner.os }}-bun- + - run: curl -fsSL https://bun.sh/install | bash && bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -189,16 +189,16 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.pnpm-store - key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} + path: ~/.bun + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | - ${{ runner.os }}-pnpm- + ${{ runner.os }}-bun- - name: Install dependencies run: | sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: npm install -g pnpm && pnpm install + - run: curl -fsSL https://bun.sh/install | bash && bun install - name: Generate Changelog id: changelog run: | @@ -243,10 +243,10 @@ jobs: id: release_body run: | VERSION="${{ needs.prepare.outputs.version }}" - + # Get the most recent release tag (v* tags only) LAST_TAG=$(git describe --match "v*" --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1` 2>/dev/null || echo "") - + if [ -n "$LAST_TAG" ]; then echo "Debug: Found last release tag: $LAST_TAG" CHANGES=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s") @@ -254,10 +254,10 @@ jobs: echo "Debug: No previous release tag found, using first commit" CHANGES=$(git log --pretty=format:"- %s") fi - + echo "Debug: Changelog content:" echo "$CHANGES" - + # Calculate hashes with corrected paths WINDOWS_ARM_HASH=$(sha256sum "artifacts/windows-arm64-binaries/Qopy-${VERSION}_arm64.msi" | awk '{ print $1 }') WINDOWS_64_HASH=$(sha256sum "artifacts/windows-x64-binaries/Qopy-${VERSION}_x64.msi" | awk '{ print $1 }') @@ -279,11 +279,11 @@ jobs: RELEASE_BODY=$(cat <<-EOF ## ♻️ Changelog - + $CHANGES - + ## ⬇️ Downloads - + - [Windows (x64)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_x64.msi) - ${WINDOWS_64_HASH} - [Windows (ARM64)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_arm64.msi) - ${WINDOWS_ARM_HASH} - [macOS (Silicon)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_silicon.dmg) - ${MAC_SILICON_HASH} @@ -312,4 +312,4 @@ jobs: artifacts/**/*.deb artifacts/**/*.AppImage artifacts/**/*.rpm - body: ${{ env.RELEASE_BODY }} \ No newline at end of file + body: ${{ env.RELEASE_BODY }} diff --git a/README.md b/README.md index b06496e..3d76477 100644 --- a/README.md +++ b/README.md @@ -95,13 +95,13 @@ You can use GitHub Codespaces for online development: [![][codespaces-shield]][codespaces-link] -Or to get Qopy set up on your machine, you'll need to have Rust and pnpm installed. Then, follow these steps: +Or to get Qopy set up on your machine, you'll need to have Rust and bun installed. Then, follow these steps: ```zsh git clone https://github.com/0pandadev/Qopy.git cd Qopy -pnpm i -pnpm dev +bun i +bun dev ``` > \[!TIP] @@ -113,7 +113,7 @@ pnpm dev To build for production simply execute: ```zsh -pnpm build +bun build ``` > \[!NOTE] diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2e12a87..2cfde48 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -5,8 +5,8 @@ "build": { "frontendDist": "../dist", "devUrl": "http://localhost:3000", - "beforeDevCommand": "pnpm nuxt dev", - "beforeBuildCommand": "pnpm nuxt generate" + "beforeDevCommand": "bun nuxt dev", + "beforeBuildCommand": "bun nuxt generate" }, "app": { "windows": [ @@ -51,9 +51,7 @@ "plugins": { "updater": { "pubkey": "dW50cnVzdGVkIGNvbW1lbnQ6IG1pbmlzaWduIHB1YmxpYyBrZXk6IDExNDIzNjA1QjE0NjU1OTkKUldTWlZVYXhCVFpDRWNvNmt0UE5lQmZkblEyZGZiZ2tHelJvT2YvNVpLU1RIM1RKZFQrb2tzWWwK", - "endpoints": [ - "https://qopy.pandadev.net/" - ] + "endpoints": ["https://qopy.pandadev.net/"] } }, "$schema": "../node_modules/@tauri-apps/cli/schema.json" From b3daaaa633ee00c01fcf6ba4737916133356f5c6 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:43:35 +1000 Subject: [PATCH 18/97] fix: back button not wroking on settings --- assets/css/settings.scss | 1 + pages/settings.vue | 58 ++++++++++++++++++++++++---------------- 2 files changed, 36 insertions(+), 23 deletions(-) diff --git a/assets/css/settings.scss b/assets/css/settings.scss index c3ad9d4..c7d829a 100644 --- a/assets/css/settings.scss +++ b/assets/css/settings.scss @@ -43,6 +43,7 @@ $mutedtext: #78756F; justify-content: center; height: 100vh; gap: 6px; + z-index: -1; .title { font-size: 20px; diff --git a/pages/settings.vue b/pages/settings.vue index 6fd6417..dbf51c4 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -1,9 +1,9 @@ - - + + Back - + @@ -154,29 +154,41 @@ const saveKeybind = async () => { onMounted(() => { os.value = platform(); - keyboard.down("all", (event) => { - const isMacSaveCombo = - os.value === "macos" && - (event.code === "MetaLeft" || event.code === "MetaRight") && - event.key === "Enter"; - - const isOtherOsSaveCombo = - os.value !== "macos" && - (event.code === "ControlLeft" || event.code === "ControlRight") && - event.key === "Enter"; - - if ( - (isMacSaveCombo || isOtherOsSaveCombo) && - !isKeybindInputFocused.value - ) { - event.preventDefault(); - saveKeybind(); + keyboard.down("MetaLeft+Enter", (event) => { + if (os.value === "macos" && !isKeybindInputFocused.value) { + console.log("Save on macOS") + event.preventDefault() + saveKeybind() } - }); + }) + + keyboard.down("MetaRight+Enter", (event) => { + if (os.value === "macos" && !isKeybindInputFocused.value) { + console.log("Save on macOS") + event.preventDefault() + saveKeybind() + } + }) + + keyboard.down("ControlLeft+Enter", (event) => { + if (os.value !== "macos" && !isKeybindInputFocused.value) { + console.log("Save on other OS") + event.preventDefault() + saveKeybind() + } + }) + + keyboard.down("ControlRight+Enter", (event) => { + if (os.value !== "macos" && !isKeybindInputFocused.value) { + console.log("Save on other OS") + event.preventDefault() + saveKeybind() + } + }) keyboard.down("Escape", (event) => { - const now = Date.now(); - if (!isKeybindInputFocused.value && now - lastBlurTime.value > 100) { + console.log("Escape"); + if (!isKeybindInputFocused.value) { event.preventDefault(); router.push("/"); } From 6514abcdb3789f17458e5c7e37f30ecac06288c2 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 17:43:46 +1000 Subject: [PATCH 19/97] fix: update keybind references to settings in app and tray setup --- app.vue | 4 +--- package.json | 2 +- src-tauri/src/api/tray.rs | 8 ++++---- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/app.vue b/app.vue index 2d69efc..7309924 100644 --- a/app.vue +++ b/app.vue @@ -10,15 +10,13 @@ import { app, window } from '@tauri-apps/api'; import { onMounted } from 'vue' onMounted(async () => { - await listen('change_keybind', async () => { - console.log("change_keybind"); + await listen('settings', async () => { await navigateTo('/settings') await app.show(); await window.getCurrentWindow().show(); }) await listen('main_route', async () => { - console.log("main_route"); await navigateTo('/') }) }) diff --git a/package.json b/package.json index aad552e..e605d31 100644 --- a/package.json +++ b/package.json @@ -25,4 +25,4 @@ "overrides": { "chokidar": "^3.6.0" } -} \ No newline at end of file +} diff --git a/src-tauri/src/api/tray.rs b/src-tauri/src/api/tray.rs index 8c3d9bc..7e579a4 100644 --- a/src-tauri/src/api/tray.rs +++ b/src-tauri/src/api/tray.rs @@ -22,7 +22,7 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { .enabled(false) .build(app)?]) .items(&[&MenuItemBuilder::with_id("show", "Show/Hide").build(app)?]) - .items(&[&MenuItemBuilder::with_id("keybind", "Change keybind").build(app)?]) + .items(&[&MenuItemBuilder::with_id("settings", "Settings").build(app)?]) .items(&[&MenuItemBuilder::with_id("quit", "Quit").build(app)?]) .build()?, ) @@ -44,9 +44,9 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { } window.emit("main_route", ()).unwrap(); } - "keybind" => { - let _ = _app.track_event("tray_keybind_change", None); - window.emit("change_keybind", ()).unwrap(); + "settings" => { + let _ = _app.track_event("tray_settings", None); + window.emit("settings", ()).unwrap(); } _ => (), }) From 6157eb75a19e9aa207fc2a723f92ae0a0c5b9740 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:26:37 +1000 Subject: [PATCH 20/97] feat: add 'Check for updates' option to tray menu and improve update handling --- assets/css/settings.scss | 20 ++++++++++---------- src-tauri/src/api/tray.rs | 8 ++++++++ src-tauri/src/api/updater.rs | 26 ++++++++++++++++++++++---- src-tauri/src/main.rs | 2 +- 4 files changed, 41 insertions(+), 15 deletions(-) diff --git a/assets/css/settings.scss b/assets/css/settings.scss index c3ad9d4..c593334 100644 --- a/assets/css/settings.scss +++ b/assets/css/settings.scss @@ -1,10 +1,10 @@ -$primary: #2E2D2B; -$accent: #FEB453; +$primary: #2e2d2b; +$accent: #feb453; $divider: #ffffff0d; -$text: #E5DFD5; -$text2: #ADA9A1; -$mutedtext: #78756F; +$text: #e5dfd5; +$text2: #ada9a1; +$mutedtext: #78756f; .bg { width: 750px; @@ -25,7 +25,7 @@ $mutedtext: #78756F; gap: 8px; align-items: center; - img{ + img { background-color: $divider; border-radius: 6px; padding: 8px 6px; @@ -123,7 +123,7 @@ $mutedtext: #78756F; background-color: $divider; margin-left: 8px; margin-right: 4px; - transition: all .2s; + transition: all 0.2s; } .actions { @@ -134,7 +134,7 @@ $mutedtext: #78756F; gap: 8px; border-radius: 7px; background-color: transparent; - transition: all .2s; + transition: all 0.2s; cursor: pointer; } @@ -142,8 +142,8 @@ $mutedtext: #78756F; background-color: $divider; } - &:hover .actions:hover~.divider { + &:hover .actions:hover ~ .divider { opacity: 0; } } -} \ No newline at end of file +} diff --git a/src-tauri/src/api/tray.rs b/src-tauri/src/api/tray.rs index 8c3d9bc..72d24ec 100644 --- a/src-tauri/src/api/tray.rs +++ b/src-tauri/src/api/tray.rs @@ -23,6 +23,7 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { .build(app)?]) .items(&[&MenuItemBuilder::with_id("show", "Show/Hide").build(app)?]) .items(&[&MenuItemBuilder::with_id("keybind", "Change keybind").build(app)?]) + .items(&[&MenuItemBuilder::with_id("check_updates", "Check for updates").build(app)?]) .items(&[&MenuItemBuilder::with_id("quit", "Quit").build(app)?]) .build()?, ) @@ -48,6 +49,13 @@ pub fn setup(app: &mut tauri::App) -> Result<(), Box> { let _ = _app.track_event("tray_keybind_change", None); window.emit("change_keybind", ()).unwrap(); } + "check_updates" => { + let _ = _app.track_event("tray_check_updates", None); + let app_handle = _app.app_handle().clone(); + tauri::async_runtime::spawn(async move { + crate::api::updater::check_for_updates(app_handle, true).await; + }); + } _ => (), }) .icon(icon) diff --git a/src-tauri/src/api/updater.rs b/src-tauri/src/api/updater.rs index a5e16f9..8c600fa 100644 --- a/src-tauri/src/api/updater.rs +++ b/src-tauri/src/api/updater.rs @@ -1,8 +1,9 @@ +use tauri::Manager; use tauri::{async_runtime, AppHandle}; use tauri_plugin_dialog::{DialogExt, MessageDialogButtons, MessageDialogKind}; use tauri_plugin_updater::UpdaterExt; -pub async fn check_for_updates(app: AppHandle) { +pub async fn check_for_updates(app: AppHandle, prompted: bool) { println!("Checking for updates..."); let updater = app.updater().unwrap(); @@ -18,6 +19,10 @@ pub async fn check_for_updates(app: AppHandle) { "Would you like to install it now?", ]); + let window = app.get_webview_window("main").unwrap(); + window.show().unwrap(); + window.set_focus().unwrap(); + app.dialog() .message(msg) .title("Qopy Update Available") @@ -31,7 +36,7 @@ pub async fn check_for_updates(app: AppHandle) { Ok(_) => { app.dialog() .message("Update installed successfully. The application needs to restart to apply the changes.") - .title("Qopy Update Installed") + .title("Qopy Needs to Restart") .buttons(MessageDialogButtons::OkCancelCustom(String::from("Restart"), String::from("Cancel"))) .show(move |response| { if response { @@ -50,9 +55,22 @@ pub async fn check_for_updates(app: AppHandle) { }); }); } - Ok(None) => println!("No updates available."), + Ok(None) => { + println!("No updates available."); + } Err(e) => { - println!("Failed to check for updates: {:?}", e); + if prompted { + let window = app.get_webview_window("main").unwrap(); + window.show().unwrap(); + window.set_focus().unwrap(); + + app.dialog() + .message("No updates available.") + .title("Qopy Update Check") + .show(|_| {}); + } + + println!("No updates available. {}", e.to_string()); } } } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 7f9fb79..0f2ae6a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -96,7 +96,7 @@ fn main() { let _ = app.track_event("app_started", None); tauri::async_runtime::spawn(async move { - api::updater::check_for_updates(app_handle).await; + api::updater::check_for_updates(app_handle, false).await; }); Ok(()) From 3552a3779b9427d77d87bab036eb8fa9acc4c6b3 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 18:31:12 +1000 Subject: [PATCH 21/97] fix: update bun installation steps in workflows for proper dependency setup --- .github/workflows/build.yml | 15 ++++++++++++--- .github/workflows/release.yml | 21 ++++++++++++++++++--- 2 files changed, 30 insertions(+), 6 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index afe4e08..eeb8b66 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -60,7 +60,10 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -147,7 +150,10 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -208,7 +214,10 @@ jobs: sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 99ce4e6..111c2ae 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -61,7 +61,12 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: curl -fsSL https://bun.sh/install | bash + - name: Install dependencies + run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -129,7 +134,12 @@ jobs: key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} restore-keys: | ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: curl -fsSL https://bun.sh/install | bash + - name: Install dependencies + run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -198,7 +208,12 @@ jobs: sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: curl -fsSL https://bun.sh/install | bash && bun install + - run: curl -fsSL https://bun.sh/install | bash + - name: Install dependencies + run: | + curl -fsSL https://bun.sh/install | bash + source ~/.bashrc + bun install - name: Generate Changelog id: changelog run: | From 9db390c1c2a8b41fa0c8d7c09ecc50e1784ce3a1 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 1 Jan 2025 20:41:29 +1000 Subject: [PATCH 22/97] chore: migrate from bun to pnpm for dependency management in workflows --- .github/workflows/build.yml | 33 ++++++++------------ .github/workflows/release.yml | 57 +++++++++++++---------------------- src-tauri/tauri.conf.json | 4 +-- 3 files changed, 35 insertions(+), 59 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index eeb8b66..4c23943 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -56,14 +56,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- - - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + ${{ runner.os }}-pnpm- + - run: npm install -g pnpm && pnpm install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -146,14 +143,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- - - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + ${{ runner.os }}-pnpm- + - run: npm install -g pnpm && pnpm install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -205,19 +199,16 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- + ${{ runner.os }}-pnpm- - name: Install dependencies run: | sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + - run: npm install -g pnpm && pnpm install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 111c2ae..f79b85d 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -57,16 +57,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash - - name: Install dependencies - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + ${{ runner.os }}-pnpm- + - run: npm install -g pnpm && pnpm install - name: Import Apple Developer Certificate env: APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} @@ -88,7 +83,7 @@ jobs: TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} with: args: ${{ matrix.args }} - + - name: Rename macOS Artifacts run: | mv src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/*.dmg src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.dmg @@ -130,16 +125,11 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- - - run: curl -fsSL https://bun.sh/install | bash - - name: Install dependencies - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + ${{ runner.os }}-pnpm- + - run: npm install -g pnpm && pnpm install - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} @@ -199,21 +189,16 @@ jobs: save-if: "true" - uses: actions/cache@v4 with: - path: ~/.bun - key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lockb') }} + path: ~/.pnpm-store + key: ${{ runner.os }}-pnpm-${{ hashFiles('**/pnpm-lock.yaml') }} restore-keys: | - ${{ runner.os }}-bun- + ${{ runner.os }}-pnpm- - name: Install dependencies run: | sudo apt update sudo apt install -y libwebkit2gtk-4.1-dev build-essential curl wget file libssl-dev libayatana-appindicator3-dev librsvg2-dev libasound2-dev rpm echo "PKG_CONFIG_PATH=/usr/lib/x86_64-linux-gnu/pkgconfig" >> $GITHUB_ENV - - run: curl -fsSL https://bun.sh/install | bash - - name: Install dependencies - run: | - curl -fsSL https://bun.sh/install | bash - source ~/.bashrc - bun install + - run: npm install -g pnpm && pnpm install - name: Generate Changelog id: changelog run: | @@ -258,10 +243,10 @@ jobs: id: release_body run: | VERSION="${{ needs.prepare.outputs.version }}" - + # Get the most recent release tag (v* tags only) LAST_TAG=$(git describe --match "v*" --abbrev=0 --tags `git rev-list --tags --skip=1 --max-count=1` 2>/dev/null || echo "") - + if [ -n "$LAST_TAG" ]; then echo "Debug: Found last release tag: $LAST_TAG" CHANGES=$(git log ${LAST_TAG}..HEAD --pretty=format:"- %s") @@ -269,10 +254,10 @@ jobs: echo "Debug: No previous release tag found, using first commit" CHANGES=$(git log --pretty=format:"- %s") fi - + echo "Debug: Changelog content:" echo "$CHANGES" - + # Calculate hashes with corrected paths WINDOWS_ARM_HASH=$(sha256sum "artifacts/windows-arm64-binaries/Qopy-${VERSION}_arm64.msi" | awk '{ print $1 }') WINDOWS_64_HASH=$(sha256sum "artifacts/windows-x64-binaries/Qopy-${VERSION}_x64.msi" | awk '{ print $1 }') @@ -294,11 +279,11 @@ jobs: RELEASE_BODY=$(cat <<-EOF ## ♻️ Changelog - + $CHANGES - + ## ⬇️ Downloads - + - [Windows (x64)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_x64.msi) - ${WINDOWS_64_HASH} - [Windows (ARM64)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_arm64.msi) - ${WINDOWS_ARM_HASH} - [macOS (Silicon)](https://github.com/${{ github.repository }}/releases/download/v${VERSION}/Qopy-${VERSION}_silicon.dmg) - ${MAC_SILICON_HASH} @@ -327,4 +312,4 @@ jobs: artifacts/**/*.deb artifacts/**/*.AppImage artifacts/**/*.rpm - body: ${{ env.RELEASE_BODY }} + body: ${{ env.RELEASE_BODY }} \ No newline at end of file diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2cfde48..7be281f 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -5,8 +5,8 @@ "build": { "frontendDist": "../dist", "devUrl": "http://localhost:3000", - "beforeDevCommand": "bun nuxt dev", - "beforeBuildCommand": "bun nuxt generate" + "beforeDevCommand": "pnpm nuxt dev", + "beforeBuildCommand": "pnpm nuxt generate" }, "app": { "windows": [ From 9658310689b3ba4ef42cb849f1763949dd36884a Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:53:47 +1000 Subject: [PATCH 23/97] refactor: improve keybind saving logic and error handling --- pages/settings.vue | 104 +++++++++++++++++++++++++-------------------- 1 file changed, 59 insertions(+), 45 deletions(-) diff --git a/pages/settings.vue b/pages/settings.vue index dbf51c4..cf4771e 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -10,7 +10,10 @@ Qopy - + Save @@ -23,7 +26,9 @@ - + Record a new Hotkey >(new Set()); const isKeybindInputFocused = ref(false); @@ -61,6 +67,7 @@ const lastBlurTime = ref(0); const os = ref(""); const router = useRouter(); const keyboard = useKeyboard(); +const showEmptyKeybindError = ref(false); const keyToDisplayMap: Record = { " ": "Space", @@ -115,16 +122,17 @@ const updateKeybind = () => { const onBlur = () => { isKeybindInputFocused.value = false; lastBlurTime.value = Date.now(); + showEmptyKeybindError.value = false; }; const onFocus = () => { isKeybindInputFocused.value = true; activeModifiers.clear(); keybind.value = []; + showEmptyKeybindError.value = false; }; const onKeyDown = (event: KeyboardEvent) => { - event.preventDefault(); const key = event.code; if (key === "Escape") { @@ -142,57 +150,63 @@ const onKeyDown = (event: KeyboardEvent) => { } updateKeybind(); + showEmptyKeybindError.value = false; }; const saveKeybind = async () => { - console.log("New:", keybind.value); - const oldKeybind = await invoke("get_keybind"); - console.log("Old:", oldKeybind); - await invoke("save_keybind", { keybind: keybind.value }); + if (keybind.value.length > 0) { + console.log("Saving keybind", keybind.value); + await invoke("save_keybind", { keybind: keybind }); + } else { + showEmptyKeybindError.value = true; + } }; +os.value = platform(); + onMounted(() => { - os.value = platform(); - - keyboard.down("MetaLeft+Enter", (event) => { - if (os.value === "macos" && !isKeybindInputFocused.value) { - console.log("Save on macOS") - event.preventDefault() - saveKeybind() - } - }) - - keyboard.down("MetaRight+Enter", (event) => { - if (os.value === "macos" && !isKeybindInputFocused.value) { - console.log("Save on macOS") - event.preventDefault() - saveKeybind() - } - }) - - keyboard.down("ControlLeft+Enter", (event) => { - if (os.value !== "macos" && !isKeybindInputFocused.value) { - console.log("Save on other OS") - event.preventDefault() - saveKeybind() - } - }) - - keyboard.down("ControlRight+Enter", (event) => { - if (os.value !== "macos" && !isKeybindInputFocused.value) { - console.log("Save on other OS") - event.preventDefault() - saveKeybind() - } - }) - - keyboard.down("Escape", (event) => { - console.log("Escape"); - if (!isKeybindInputFocused.value) { - event.preventDefault(); + keyboard.down([Key.Escape], (event) => { + console.log("Escape key pressed"); + if (isKeybindInputFocused.value) { + keybindInput.value?.blur(); + } else { router.push("/"); } }); + + switch (os.value) { + case "macos": + keyboard.down([Key.LeftMeta, Key.Enter], (event) => { + if (!isKeybindInputFocused.value) { + saveKeybind(); + } + }); + + keyboard.down([Key.RightMeta, Key.Enter], (event) => { + if (!isKeybindInputFocused.value) { + saveKeybind(); + } + }); + break; + + case "linux" || "windows": + keyboard.down([Key.LeftControl, Key.Enter], (event) => { + if (!isKeybindInputFocused.value) { + saveKeybind(); + } + }); + + keyboard.down([Key.RightControl, Key.Enter], (event) => { + if (!isKeybindInputFocused.value) { + saveKeybind(); + } + }); + break; + } +}); + +onUnmounted(() => { + keyboard.unregisterAll(); }); From b328b8fa9ca7a8f1dbda8bb5cff0798faab9b1d5 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:54:04 +1000 Subject: [PATCH 24/97] refactor: fix casing and spacing in settings.scss --- assets/css/settings.scss | 31 +++++++++++++++++++++---------- 1 file changed, 21 insertions(+), 10 deletions(-) diff --git a/assets/css/settings.scss b/assets/css/settings.scss index c7d829a..39bae81 100644 --- a/assets/css/settings.scss +++ b/assets/css/settings.scss @@ -1,10 +1,10 @@ -$primary: #2E2D2B; -$accent: #FEB453; +$primary: #2e2d2b; +$accent: #feb453; $divider: #ffffff0d; -$text: #E5DFD5; -$text2: #ADA9A1; -$mutedtext: #78756F; +$text: #e5dfd5; +$text2: #ada9a1; +$mutedtext: #78756f; .bg { width: 750px; @@ -25,7 +25,7 @@ $mutedtext: #78756F; gap: 8px; align-items: center; - img{ + img { background-color: $divider; border-radius: 6px; padding: 8px 6px; @@ -71,6 +71,12 @@ $mutedtext: #78756F; .keybind-input:focus { border: 1px solid rgba(255, 255, 255, 0.2); } + + &.empty-keybind { + .keybind-input { + border-color: rgba(255, 82, 82, 0.298); + } + } } .bottom-bar { @@ -124,7 +130,7 @@ $mutedtext: #78756F; background-color: $divider; margin-left: 8px; margin-right: 4px; - transition: all .2s; + transition: all 0.2s; } .actions { @@ -135,16 +141,21 @@ $mutedtext: #78756F; gap: 8px; border-radius: 7px; background-color: transparent; - transition: all .2s; + transition: all 0.2s; cursor: pointer; + + &.disabled { + pointer-events: none; + opacity: 0.5; + } } .actions:hover { background-color: $divider; } - &:hover .actions:hover~.divider { + &:hover .actions:hover ~ .divider { opacity: 0; } } -} \ No newline at end of file +} From 25f7b116f6b4b77e7adc631da3b957fc760bf109 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:54:42 +1000 Subject: [PATCH 25/97] refactor: fix casing and spacing in settings.scss refactor: improve keybind saving logic and error handling fix: update keybind references to settings in app and tray setup fix: back button not working on settings --- app.vue | 35 +++---- pages/index.vue | 153 +++++++++++++++++++----------- patches/wrdu-keyboard@3.0.0.patch | 123 ++++++++++++++++++++++++ 3 files changed, 237 insertions(+), 74 deletions(-) create mode 100644 patches/wrdu-keyboard@3.0.0.patch diff --git a/app.vue b/app.vue index 7309924..40f7e2a 100644 --- a/app.vue +++ b/app.vue @@ -1,25 +1,28 @@ - + \ No newline at end of file + diff --git a/pages/index.vue b/pages/index.vue index c902e9b..50a57c3 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -65,10 +65,17 @@ + @error=" + ($event.target as HTMLImageElement).src = + '../public/icons/Link.svg' + " /> - - + + {{ selectedItem?.content || "" }} @@ -135,9 +146,7 @@ {{ row.label }} - + {{ row.value }} @@ -158,7 +167,15 @@ import { listen } from "@tauri-apps/api/event"; import { useNuxtApp } from "#app"; import { invoke } from "@tauri-apps/api/core"; import { HistoryItem, ContentType } from "~/types/types"; -import type { InfoText, InfoImage, InfoFile, InfoLink, InfoColor, InfoCode } from "~/types/types"; +import type { + InfoText, + InfoImage, + InfoFile, + InfoLink, + InfoColor, + InfoCode, +} from "~/types/types"; +import { Key } from "wrdu-keyboard/key"; interface GroupedHistory { label: string; @@ -188,8 +205,8 @@ const imageSizes = shallowRef>({}); const lastUpdateTime = ref(Date.now()); const imageLoadError = ref(false); const imageLoading = ref(false); -const pageTitle = ref(''); -const pageOgImage = ref(''); +const pageTitle = ref(""); +const pageOgImage = ref(""); const keyboard = useKeyboard(); @@ -583,41 +600,35 @@ const setupEventListeners = async (): Promise => { searchInput.value?.blur(); }); - keyboard.down("ArrowDown", (event) => { - event.preventDefault(); + keyboard.prevent.down([Key.DownArrow], (event) => { selectNext(); }); - keyboard.down("ArrowUp", (event) => { - event.preventDefault(); + keyboard.prevent.down([Key.UpArrow], (event) => { selectPrevious(); }); - keyboard.down("Enter", (event) => { - event.preventDefault(); + keyboard.prevent.down([Key.Enter], (event) => { pasteSelectedItem(); }); - keyboard.down("Escape", (event) => { - event.preventDefault(); + keyboard.prevent.down([Key.Escape], (event) => { hideApp(); }); - keyboard.down("all", (event) => { - const isMacActionCombo = - os.value === "macos" && - (event.code === "MetaLeft" || event.code === "MetaRight") && - event.key === "k"; + switch (os.value) { + case "macos": + keyboard.prevent.down([Key.LeftMeta, Key.K], (event) => {}); - const isOtherOsActionCombo = - os.value !== "macos" && - (event.code === "ControlLeft" || event.code === "ControlRight") && - event.key === "k"; + keyboard.prevent.down([Key.RightMeta, Key.K], (event) => {}); + break; - if (isMacActionCombo || isOtherOsActionCombo) { - event.preventDefault(); - } - }); + case "linux" || "windows": + keyboard.prevent.down([Key.LeftControl, Key.K], (event) => {}); + + keyboard.prevent.down([Key.RightControl, Key.K], (event) => {}); + break; + } }; const hideApp = async (): Promise => { @@ -646,7 +657,7 @@ watch(searchQuery, () => { onMounted(async () => { try { - os.value = await platform(); + os.value = platform(); await loadHistoryChunk(); resultsContainer.value @@ -686,27 +697,33 @@ const formatFileSize = (bytes: number): string => { const fetchPageMeta = async (url: string) => { try { - const [title, ogImage] = await invoke('fetch_page_meta', { url }) as [string, string | null]; + const [title, ogImage] = (await invoke("fetch_page_meta", { url })) as [ + string, + string | null + ]; pageTitle.value = title; if (ogImage) { pageOgImage.value = ogImage; } } catch (error) { - console.error('Error fetching page meta:', error); - pageTitle.value = 'Error loading title'; + console.error("Error fetching page meta:", error); + pageTitle.value = "Error loading title"; } }; -watch(() => selectedItem.value, (newItem) => { - if (newItem?.content_type === ContentType.Link) { - pageTitle.value = 'Loading...'; - pageOgImage.value = ''; - fetchPageMeta(newItem.content); - } else { - pageTitle.value = ''; - pageOgImage.value = ''; +watch( + () => selectedItem.value, + (newItem) => { + if (newItem?.content_type === ContentType.Link) { + pageTitle.value = "Loading..."; + pageOgImage.value = ""; + fetchPageMeta(newItem.content); + } else { + pageTitle.value = ""; + pageOgImage.value = ""; + } } -}); +); const getInfo = computed(() => { if (!selectedItem.value) return null; @@ -716,7 +733,10 @@ const getInfo = computed(() => { copied: selectedItem.value.timestamp, }; - const infoMap: Record InfoText | InfoImage | InfoFile | InfoLink | InfoColor | InfoCode> = { + const infoMap: Record< + ContentType, + () => InfoText | InfoImage | InfoFile | InfoLink | InfoColor | InfoCode + > = { [ContentType.Text]: () => ({ ...baseInfo, content_type: ContentType.Text, @@ -747,20 +767,21 @@ const getInfo = computed(() => { const r = parseInt(hex.slice(1, 3), 16); const g = parseInt(hex.slice(3, 5), 16); const b = parseInt(hex.slice(5, 7), 16); - + const rNorm = r / 255; const gNorm = g / 255; const bNorm = b / 255; - + const max = Math.max(rNorm, gNorm, bNorm); const min = Math.min(rNorm, gNorm, bNorm); - let h = 0, s = 0; + let h = 0, + s = 0; const l = (max + min) / 2; if (max !== min) { const d = max - min; s = l > 0.5 ? d / (2 - max - min) : d / (max + min); - + switch (max) { case rNorm: h = (gNorm - bNorm) / d + (gNorm < bNorm ? 6 : 0); @@ -780,14 +801,16 @@ const getInfo = computed(() => { content_type: ContentType.Color, hex: hex, rgb: `rgb(${r}, ${g}, ${b})`, - hsl: `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round(l * 100)}%)`, + hsl: `hsl(${Math.round(h * 360)}, ${Math.round(s * 100)}%, ${Math.round( + l * 100 + )}%)`, }; }, [ContentType.Code]: () => ({ ...baseInfo, content_type: ContentType.Code, language: selectedItem.value!.language ?? "Unknown", - lines: selectedItem.value!.content.split('\n').length, + lines: selectedItem.value!.content.split("\n").length, }), }; @@ -799,24 +822,37 @@ const infoRows = computed(() => { const commonRows = [ { label: "Source", value: getInfo.value.source, isUrl: false }, - { label: "Content Type", value: getInfo.value.content_type.charAt(0).toUpperCase() + getInfo.value.content_type.slice(1), isUrl: false }, + { + label: "Content Type", + value: + getInfo.value.content_type.charAt(0).toUpperCase() + + getInfo.value.content_type.slice(1), + isUrl: false, + }, ]; - const typeSpecificRows: Record> = { + const typeSpecificRows: Record< + ContentType, + Array<{ label: string; value: string | number; isUrl?: boolean }> + > = { [ContentType.Text]: [ { label: "Characters", value: (getInfo.value as InfoText).characters }, { label: "Words", value: (getInfo.value as InfoText).words }, ], [ContentType.Image]: [ { label: "Dimensions", value: (getInfo.value as InfoImage).dimensions }, - { label: "Image size", value: formatFileSize((getInfo.value as InfoImage).size) }, + { + label: "Image size", + value: formatFileSize((getInfo.value as InfoImage).size), + }, ], [ContentType.File]: [ { label: "Path", value: (getInfo.value as InfoFile).path }, ], [ContentType.Link]: [ - ...((getInfo.value as InfoLink).title && (getInfo.value as InfoLink).title !== 'Loading...' - ? [{ label: "Title", value: (getInfo.value as InfoLink).title || '' }] + ...((getInfo.value as InfoLink).title && + (getInfo.value as InfoLink).title !== "Loading..." + ? [{ label: "Title", value: (getInfo.value as InfoLink).title || "" }] : []), { label: "URL", value: (getInfo.value as InfoLink).url, isUrl: true }, { label: "Characters", value: (getInfo.value as InfoLink).characters }, @@ -832,8 +868,9 @@ const infoRows = computed(() => { ], }; - const specificRows = typeSpecificRows[getInfo.value.content_type] - .filter(row => row.value !== ""); + const specificRows = typeSpecificRows[getInfo.value.content_type].filter( + (row) => row.value !== "" + ); return [ ...commonRows, diff --git a/patches/wrdu-keyboard@3.0.0.patch b/patches/wrdu-keyboard@3.0.0.patch new file mode 100644 index 0000000..e973647 --- /dev/null +++ b/patches/wrdu-keyboard@3.0.0.patch @@ -0,0 +1,123 @@ +diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store +new file mode 100644 +index 0000000000000000000000000000000000000000..fabbd951c2d14c46fd10fa167b8836d116bc0db6 +Binary files /dev/null and b/.DS_Store differ +diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts +index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d23c5f1f13 100644 +--- a/dist/runtime/keyboard.d.ts ++++ b/dist/runtime/keyboard.d.ts +@@ -1,15 +1,16 @@ +-import { Key } from './types/keys.js'; +-import { type Plugin } from '#app'; ++import { Key } from "./types/keys.js"; ++import { type Plugin } from "#app"; + type Handler = (event: KeyboardEvent) => void; + type Config = { + once?: boolean; + prevent?: boolean; + }; +-type PublicConfig = Omit; ++type PublicConfig = Omit; + type New = (keys: Key[], handler: Handler, config?: PublicConfig) => void; + export interface Keyboard { + init: () => void; + stop: () => void; ++ unregisterAll: () => void; + down: New; + up: New; + prevent: { +diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js +index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cdb37820aa 100644 +--- a/dist/runtime/keyboard.js ++++ b/dist/runtime/keyboard.js +@@ -1,13 +1,14 @@ + import { Key } from "./types/keys.js"; + import { defineNuxtPlugin } from "#app"; +-const getKeyString = (keys) => keys[0] == Key.All ? keys.sort().join("+") : "All"; ++const getKeyString = (keys) => keys.includes(Key.All) ? "All" : keys.sort().join("+"); + const handlers = { + down: {}, + up: {} + }; + const pressedKeys = /* @__PURE__ */ new Set(); + const onKeydown = (event) => { +- pressedKeys.add(event.code); ++ const key = event.code; ++ pressedKeys.add(key); + const pressedArray = Array.from(pressedKeys); + const keyString = getKeyString(pressedArray); + if (handlers.down[keyString]) { +@@ -17,13 +18,16 @@ const onKeydown = (event) => { + } + eventHandler.handler(event); + if (eventHandler.once) { +- handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler); ++ handlers.down[keyString] = handlers.down[keyString].filter( ++ (h) => h !== eventHandler ++ ); + } + }); + } + }; + const onKeyup = (event) => { +- pressedKeys.delete(event.code); ++ const key = event.code; ++ pressedKeys.delete(key); + const releasedArray = Array.from(pressedKeys); + const keyString = getKeyString(releasedArray); + if (handlers.up[keyString]) { +@@ -33,13 +37,16 @@ const onKeyup = (event) => { + } + eventHandler.handler(event); + if (eventHandler.once) { +- handlers.up[keyString] = handlers.up[keyString].filter((h) => h !== eventHandler); ++ handlers.up[keyString] = handlers.up[keyString].filter( ++ (h) => h !== eventHandler ++ ); + } + }); + } + }; + const init = () => { + stop(); ++ pressedKeys.clear(); + window.addEventListener("keydown", onKeydown); + window.addEventListener("keyup", onKeyup); + }; +@@ -47,6 +54,20 @@ const stop = () => { + window.removeEventListener("keydown", onKeydown); + window.removeEventListener("keyup", onKeyup); + }; ++const unregisterAll = () => { ++ Object.keys(handlers.down).forEach((key) => { ++ handlers.down[key].forEach((handler) => { ++ console.log(`Unregistering ${key} ${handler.handler.toString()}`); ++ }); ++ }); ++ Object.keys(handlers.up).forEach((key) => { ++ handlers.up[key].forEach((handler) => { ++ console.log(`Unregistering ${key} ${handler.handler.toString()}`); ++ }); ++ }); ++ handlers.down = {}; ++ handlers.up = {}; ++}; + const down = (keys, handler, config = {}) => { + if (keys.includes(Key.All)) { + keys = [Key.All]; +@@ -59,6 +80,7 @@ const down = (keys, handler, config = {}) => { + handlers.down[key] = []; + } + const { once = false, prevent = false } = config; ++ console.log(key, handler.toString()); + handlers.down[key].push({ handler, prevent, once }); + }; + const up = (keys, handler, config = {}) => { +@@ -84,6 +106,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => { + keyboard: { + init, + stop, ++ unregisterAll, + down: (keys, handler, config = {}) => down(keys, handler, config), + up: (keys, handler, config = {}) => up(keys, handler, config), + prevent: { From 6656af8ab1409b91d0f561d100e22dec5b108016 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 14:54:47 +1000 Subject: [PATCH 26/97] chore: Update wrdu-keyboard to version 3.0.0 --- package.json | 5 ++++- src-tauri/Cargo.lock | 2 +- src-tauri/Cargo.toml | 2 +- src-tauri/tauri.conf.json | 2 +- 4 files changed, 7 insertions(+), 4 deletions(-) diff --git a/package.json b/package.json index e605d31..de5e05e 100644 --- a/package.json +++ b/package.json @@ -20,9 +20,12 @@ "sass-embedded": "1.83.0", "uuid": "11.0.3", "vue": "3.5.13", - "wrdu-keyboard": "1.1.1" + "wrdu-keyboard": "3.0.0" }, "overrides": { "chokidar": "^3.6.0" + }, + "patchedDependencies": { + "wrdu-keyboard@3.0.0": "patches/wrdu-keyboard@3.0.0.patch" } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 31fba10..a5d7869 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4054,7 +4054,7 @@ dependencies = [ [[package]] name = "qopy" -version = "0.3.3" +version = "0.3.4" dependencies = [ "active-win-pos-rs", "applications", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 08142a1..45e9dd7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "qopy" -version = "0.3.3" +version = "0.3.4" description = "Qopy" authors = ["pandadev"] edition = "2021" diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 2cfde48..10ae2a2 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Qopy", - "version": "0.3.3", + "version": "0.3.4", "identifier": "net.pandadev.qopy", "build": { "frontendDist": "../dist", From 0c28a5b0dbc9c7e1668a515e4109eb21c691d386 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:05:02 +1000 Subject: [PATCH 27/97] refactor: improve keybind saving logic and error handling --- GET_STARTED.md | 4 +- pages/settings.vue | 71 ++++------ patches/wrdu-keyboard@3.0.0.patch | 98 +++++++------- src-tauri/src/api/hotkeys.rs | 114 ++++++++-------- src-tauri/src/db/settings.rs | 9 +- src-tauri/src/utils/keys.rs | 118 ++++++++++++++++ src-tauri/src/utils/mod.rs | 1 + types/keys.ts | 217 ++++++++++++++++++++++++++++++ 8 files changed, 478 insertions(+), 154 deletions(-) create mode 100644 src-tauri/src/utils/keys.rs create mode 100644 types/keys.ts diff --git a/GET_STARTED.md b/GET_STARTED.md index 548a5e9..a793041 100644 --- a/GET_STARTED.md +++ b/GET_STARTED.md @@ -1,6 +1,6 @@ # Get Started -The default hotkey for Qopy is Windows+V which is also the hotkey for the default clipboard manager to turn that off follow [this guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md#disable-windowsv-for-default-clipboard-manager). +The default hotkey for Qopy is Windows+V which is also the hotkey for the default clipboard manager to turn that off follow [this guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md#disable-windowsv-for-default-clipboard-manager). All the data of Qopy is stored inside of a SQLite database. @@ -12,7 +12,7 @@ All the data of Qopy is stored inside of a SQLite database. ## Disable Windows+V for default clipboard manager -https://github.com/user-attachments/assets/723f9e07-3190-46ec-9bb7-15dfc112f620 + To disable the default clipboard manager popup from windows open Command prompt and run this command diff --git a/pages/settings.vue b/pages/settings.vue index cf4771e..7a20eb1 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -33,7 +33,6 @@ @@ -44,7 +43,7 @@ class="key" :class="{ modifier: isModifier(key) }" v-for="(key, index) in keybind"> - {{ keyToDisplay(key) }} + {{ keyToLabel(key) }} @@ -58,10 +57,11 @@ import { onMounted, onUnmounted, reactive, ref } from "vue"; import { platform } from "@tauri-apps/plugin-os"; import { useRouter } from "vue-router"; import { Key } from "wrdu-keyboard/key"; +import { KeyValues, KeyLabels } from "../types/keys"; -const activeModifiers = reactive>(new Set()); +const activeModifiers = reactive>(new Set()); const isKeybindInputFocused = ref(false); -const keybind = ref([]); +const keybind = ref([]); const keybindInput = ref(null); const lastBlurTime = ref(0); const os = ref(""); @@ -69,52 +69,27 @@ const router = useRouter(); const keyboard = useKeyboard(); const showEmptyKeybindError = ref(false); -const keyToDisplayMap: Record = { - " ": "Space", - Alt: "Alt", - AltLeft: "Alt L", - AltRight: "Alt R", - ArrowDown: "↓", - ArrowLeft: "←", - ArrowRight: "→", - ArrowUp: "↑", - Control: "Ctrl", - ControlLeft: "Ctrl L", - ControlRight: "Ctrl R", - Enter: "↵", - Meta: "Meta", - MetaLeft: "Meta L", - MetaRight: "Meta R", - Shift: "⇧", - ShiftLeft: "⇧ L", - ShiftRight: "⇧ R", -}; - const modifierKeySet = new Set([ - "Alt", - "AltLeft", - "AltRight", - "Control", - "ControlLeft", - "ControlRight", - "Meta", - "MetaLeft", - "MetaRight", - "Shift", - "ShiftLeft", - "ShiftRight", + KeyValues.AltLeft, + KeyValues.AltRight, + KeyValues.ControlLeft, + KeyValues.ControlRight, + KeyValues.MetaLeft, + KeyValues.MetaRight, + KeyValues.ShiftLeft, + KeyValues.ShiftRight, ]); -const isModifier = (key: string): boolean => { +const isModifier = (key: KeyValues): boolean => { return modifierKeySet.has(key); }; -const keyToDisplay = (key: string): string => { - return keyToDisplayMap[key] || key; +const keyToLabel = (key: KeyValues): string => { + return KeyLabels[key] || key; }; const updateKeybind = () => { - const modifiers = Array.from(activeModifiers).sort(); + const modifiers = Array.from(activeModifiers); const nonModifiers = keybind.value.filter((key) => !isModifier(key)); keybind.value = [...modifiers, ...nonModifiers]; }; @@ -133,9 +108,9 @@ const onFocus = () => { }; const onKeyDown = (event: KeyboardEvent) => { - const key = event.code; + const key = event.code as KeyValues; - if (key === "Escape") { + if (key === KeyValues.Escape) { if (keybindInput.value) { keybindInput.value.blur(); } @@ -155,8 +130,7 @@ const onKeyDown = (event: KeyboardEvent) => { const saveKeybind = async () => { if (keybind.value.length > 0) { - console.log("Saving keybind", keybind.value); - await invoke("save_keybind", { keybind: keybind }); + await invoke("save_keybind", { keybind: keybind.value }); } else { showEmptyKeybindError.value = true; } @@ -165,8 +139,13 @@ const saveKeybind = async () => { os.value = platform(); onMounted(() => { + keyboard.down([Key.All], (event) => { + if (isKeybindInputFocused.value) { + onKeyDown(event); + } + }); + keyboard.down([Key.Escape], (event) => { - console.log("Escape key pressed"); if (isKeybindInputFocused.value) { keybindInput.value?.blur(); } else { diff --git a/patches/wrdu-keyboard@3.0.0.patch b/patches/wrdu-keyboard@3.0.0.patch index e973647..bc77583 100644 --- a/patches/wrdu-keyboard@3.0.0.patch +++ b/patches/wrdu-keyboard@3.0.0.patch @@ -1,6 +1,6 @@ diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store new file mode 100644 -index 0000000000000000000000000000000000000000..fabbd951c2d14c46fd10fa167b8836d116bc0db6 +index 0000000000000000000000000000000000000000..4b7e9446f3580fab3e4feaba097bcdaf98c5833c Binary files /dev/null and b/.DS_Store differ diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d23c5f1f13 100644 @@ -27,10 +27,10 @@ index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d2 up: New; prevent: { diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js -index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cdb37820aa 100644 +index e16f600258cee90d185ffc52777bed95c14bd93e..5ddec447a5dc66ffe063eb9f9dd765c9045bdaf7 100644 --- a/dist/runtime/keyboard.js +++ b/dist/runtime/keyboard.js -@@ -1,13 +1,14 @@ +@@ -1,45 +1,54 @@ import { Key } from "./types/keys.js"; import { defineNuxtPlugin } from "#app"; -const getKeyString = (keys) => keys[0] == Key.All ? keys.sort().join("+") : "All"; @@ -45,18 +45,31 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd + const key = event.code; + pressedKeys.add(key); const pressedArray = Array.from(pressedKeys); - const keyString = getKeyString(pressedArray); - if (handlers.down[keyString]) { -@@ -17,13 +18,16 @@ const onKeydown = (event) => { - } - eventHandler.handler(event); - if (eventHandler.once) { +- const keyString = getKeyString(pressedArray); +- if (handlers.down[keyString]) { +- handlers.down[keyString].forEach((eventHandler) => { +- if (eventHandler.prevent) { +- event.preventDefault(); +- } +- eventHandler.handler(event); +- if (eventHandler.once) { - handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler); -+ handlers.down[keyString] = handlers.down[keyString].filter( -+ (h) => h !== eventHandler -+ ); - } - }); +- } +- }); ++ for (const keyString of [getKeyString(pressedArray), "All"]) { ++ if (handlers.down[keyString]) { ++ handlers.down[keyString].forEach((eventHandler) => { ++ if (eventHandler.prevent) { ++ event.preventDefault(); ++ } ++ eventHandler.handler(event); ++ if (eventHandler.once) { ++ handlers.down[keyString] = handlers.down[keyString].filter( ++ (h) => h !== eventHandler ++ ); ++ } ++ }); ++ } } }; const onKeyup = (event) => { @@ -64,18 +77,31 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd + const key = event.code; + pressedKeys.delete(key); const releasedArray = Array.from(pressedKeys); - const keyString = getKeyString(releasedArray); - if (handlers.up[keyString]) { -@@ -33,13 +37,16 @@ const onKeyup = (event) => { - } - eventHandler.handler(event); - if (eventHandler.once) { +- const keyString = getKeyString(releasedArray); +- if (handlers.up[keyString]) { +- handlers.up[keyString].forEach((eventHandler) => { +- if (eventHandler.prevent) { +- event.preventDefault(); +- } +- eventHandler.handler(event); +- if (eventHandler.once) { - handlers.up[keyString] = handlers.up[keyString].filter((h) => h !== eventHandler); -+ handlers.up[keyString] = handlers.up[keyString].filter( -+ (h) => h !== eventHandler -+ ); - } - }); +- } +- }); ++ for (const keyString of [getKeyString(releasedArray), "All"]) { ++ if (handlers.up[keyString]) { ++ handlers.up[keyString].forEach((eventHandler) => { ++ if (eventHandler.prevent) { ++ event.preventDefault(); ++ } ++ eventHandler.handler(event); ++ if (eventHandler.once) { ++ handlers.up[keyString] = handlers.up[keyString].filter( ++ (h) => h !== eventHandler ++ ); ++ } ++ }); ++ } } }; const init = () => { @@ -84,36 +110,18 @@ index e16f600258cee90d185ffc52777bed95c14bd93e..e4ce2678db649ec82e5a67fcdb74f5cd window.addEventListener("keydown", onKeydown); window.addEventListener("keyup", onKeyup); }; -@@ -47,6 +54,20 @@ const stop = () => { +@@ -47,6 +56,10 @@ const stop = () => { window.removeEventListener("keydown", onKeydown); window.removeEventListener("keyup", onKeyup); }; +const unregisterAll = () => { -+ Object.keys(handlers.down).forEach((key) => { -+ handlers.down[key].forEach((handler) => { -+ console.log(`Unregistering ${key} ${handler.handler.toString()}`); -+ }); -+ }); -+ Object.keys(handlers.up).forEach((key) => { -+ handlers.up[key].forEach((handler) => { -+ console.log(`Unregistering ${key} ${handler.handler.toString()}`); -+ }); -+ }); + handlers.down = {}; + handlers.up = {}; +}; const down = (keys, handler, config = {}) => { if (keys.includes(Key.All)) { keys = [Key.All]; -@@ -59,6 +80,7 @@ const down = (keys, handler, config = {}) => { - handlers.down[key] = []; - } - const { once = false, prevent = false } = config; -+ console.log(key, handler.toString()); - handlers.down[key].push({ handler, prevent, once }); - }; - const up = (keys, handler, config = {}) => { -@@ -84,6 +106,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => { +@@ -84,6 +97,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => { keyboard: { init, stop, diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 0470e24..52a276b 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -1,48 +1,61 @@ -use tauri_plugin_aptabase::EventTracker; use crate::utils::commands::center_window_on_current_monitor; +use crate::utils::keys::KeyCode; use global_hotkey::{ hotkey::{Code, HotKey, Modifiers}, GlobalHotKeyEvent, GlobalHotKeyManager, HotKeyState, }; -use std::cell::RefCell; +use lazy_static::lazy_static; use std::str::FromStr; +use std::sync::Mutex; use tauri::{AppHandle, Listener, Manager}; +use tauri_plugin_aptabase::EventTracker; -thread_local! { - static HOTKEY_MANAGER: RefCell> = RefCell::new(None); +lazy_static! { + static ref HOTKEY_MANAGER: Mutex> = Mutex::new(None); + static ref REGISTERED_HOTKEYS: Mutex> = Mutex::new(Vec::new()); } pub fn setup(app_handle: tauri::AppHandle) { let app_handle_clone = app_handle.clone(); - let manager = GlobalHotKeyManager::new().expect("Failed to initialize hotkey manager"); - HOTKEY_MANAGER.with(|m| *m.borrow_mut() = Some(manager)); + let manager = match GlobalHotKeyManager::new() { + Ok(manager) => manager, + Err(err) => { + eprintln!("Failed to initialize hotkey manager: {:?}", err); + return; + } + }; + + { + let mut manager_guard = HOTKEY_MANAGER.lock().unwrap(); + *manager_guard = Some(manager); + } let rt = app_handle.state::(); let initial_keybind = rt .block_on(crate::db::settings::get_keybind(app_handle_clone.clone())) .expect("Failed to get initial keybind"); - let initial_shortcut = initial_keybind.join("+"); - let initial_shortcut_for_update = initial_shortcut.clone(); - let initial_shortcut_for_save = initial_shortcut.clone(); + let initial_shortcut_for_update = initial_keybind.clone(); + let initial_shortcut_for_save = initial_keybind.clone(); - if let Err(e) = register_shortcut(&initial_shortcut) { + if let Err(e) = register_shortcut(&initial_keybind) { eprintln!("Error registering initial shortcut: {:?}", e); } app_handle.listen("update-shortcut", move |event| { - let payload_str = event.payload().to_string(); + let payload_str = event.payload(); if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_update) { - HOTKEY_MANAGER.with(|manager| { - if let Some(manager) = manager.borrow().as_ref() { - let _ = manager.unregister(old_hotkey); - } - }); + let manager_guard = HOTKEY_MANAGER.lock().unwrap(); + if let Some(manager) = manager_guard.as_ref() { + let _ = manager.unregister(old_hotkey); + } } - if let Err(e) = register_shortcut(&payload_str) { + let payload: Vec = serde_json::from_str(payload_str).unwrap_or_default(); + + if let Err(e) = register_shortcut(&payload) { eprintln!("Error re-registering shortcut: {:?}", e); } }); @@ -51,14 +64,14 @@ pub fn setup(app_handle: tauri::AppHandle) { let payload_str = event.payload().to_string(); if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_save) { - HOTKEY_MANAGER.with(|manager| { - if let Some(manager) = manager.borrow().as_ref() { - let _ = manager.unregister(old_hotkey); - } - }); + let manager_guard = HOTKEY_MANAGER.lock().unwrap(); + if let Some(manager) = manager_guard.as_ref() { + let _ = manager.unregister(old_hotkey); + } } - if let Err(e) = register_shortcut(&payload_str) { + let payload: Vec = serde_json::from_str(&payload_str).unwrap_or_default(); + if let Err(e) = register_shortcut(&payload) { eprintln!("Error registering saved shortcut: {:?}", e); } }); @@ -81,48 +94,36 @@ pub fn setup(app_handle: tauri::AppHandle) { }); } -fn register_shortcut(shortcut: &str) -> Result<(), Box> { +fn register_shortcut(shortcut: &[String]) -> Result<(), Box> { let hotkey = parse_hotkey(shortcut)?; - HOTKEY_MANAGER.with(|manager| { - if let Some(manager) = manager.borrow().as_ref() { - manager.register(hotkey)?; - } + + let manager_guard = HOTKEY_MANAGER.lock().unwrap(); + if let Some(manager) = manager_guard.as_ref() { + manager.register(hotkey.clone())?; + REGISTERED_HOTKEYS.lock().unwrap().push(hotkey); Ok(()) - }) + } else { + Err("Hotkey manager not initialized".into()) + } } -fn parse_hotkey(shortcut: &str) -> Result> { +fn parse_hotkey(shortcut: &[String]) -> Result> { let mut modifiers = Modifiers::empty(); let mut code = None; - let shortcut = shortcut.replace("\"", ""); - - for part in shortcut.split('+') { - let part = part.trim().to_lowercase(); + for part in shortcut { match part.as_str() { - "ctrl" | "control" | "controlleft" => modifiers |= Modifiers::CONTROL, - "alt" | "altleft" | "optionleft" => modifiers |= Modifiers::ALT, - "shift" | "shiftleft" => modifiers |= Modifiers::SHIFT, - "super" | "meta" | "cmd" | "metaleft" => modifiers |= Modifiers::META, + "ControlLeft" => modifiers |= Modifiers::CONTROL, + "AltLeft" => modifiers |= Modifiers::ALT, + "ShiftLeft" => modifiers |= Modifiers::SHIFT, + "MetaLeft" => modifiers |= Modifiers::META, key => { - let key_code = if key.starts_with("key") { - "Key".to_string() + &key[3..].to_uppercase() - } else if key.len() == 1 && key.chars().next().unwrap().is_alphabetic() { - "Key".to_string() + &key.to_uppercase() - } else { - key.to_string() - }; - - code = Some( - Code::from_str(&key_code) - .map_err(|_| format!("Invalid key code: {}", key_code))?, - ); + code = Some(Code::from(KeyCode::from_str(key)?)); } } } - let key_code = - code.ok_or_else(|| format!("No valid key code found in shortcut: {}", shortcut))?; + let key_code = code.ok_or_else(|| "No valid key code found".to_string())?; Ok(HotKey::new(Some(modifiers), key_code)) } @@ -144,7 +145,10 @@ fn handle_hotkey_event(app_handle: &AppHandle) { center_window_on_current_monitor(&window); } - let _ = app_handle.track_event("hotkey_triggered", Some(serde_json::json!({ - "action": if window.is_visible().unwrap() { "hide" } else { "show" } - }))); + let _ = app_handle.track_event( + "hotkey_triggered", + Some(serde_json::json!({ + "action": if window.is_visible().unwrap() { "hide" } else { "show" } + })), + ); } diff --git a/src-tauri/src/db/settings.rs b/src-tauri/src/db/settings.rs index bdd6f58..7fedf8c 100644 --- a/src-tauri/src/db/settings.rs +++ b/src-tauri/src/db/settings.rs @@ -30,11 +30,8 @@ pub async fn save_keybind( pool: tauri::State<'_, SqlitePool>, keybind: Vec, ) -> Result<(), String> { - let keybind_str = keybind.join("+"); - let keybind_clone = keybind_str.clone(); - app_handle - .emit("update-shortcut", &keybind_str) + .emit("update-shortcut", &keybind) .map_err(|e| e.to_string())?; let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?; @@ -46,7 +43,7 @@ pub async fn save_keybind( .map_err(|e| e.to_string())?; let _ = app_handle.track_event("keybind_saved", Some(serde_json::json!({ - "keybind": keybind_clone + "keybind": keybind }))); Ok(()) @@ -97,7 +94,7 @@ pub async fn get_keybind(app_handle: tauri::AppHandle) -> Result, St .map_err(|e| e.to_string())?; let json = row.map(|r| r.get::("value")).unwrap_or_else(|| { - serde_json::to_string(&vec!["Meta".to_string(), "V".to_string()]) + serde_json::to_string(&vec!["MetaLeft".to_string(), "KeyV".to_string()]) .expect("Failed to serialize default keybind") }); diff --git a/src-tauri/src/utils/keys.rs b/src-tauri/src/utils/keys.rs new file mode 100644 index 0000000..fffacdd --- /dev/null +++ b/src-tauri/src/utils/keys.rs @@ -0,0 +1,118 @@ +use global_hotkey::hotkey::Code; +use std::str::FromStr; + +pub struct KeyCode(Code); + +impl FromStr for KeyCode { + type Err = String; + + fn from_str(s: &str) -> Result { + let code = match s { + "Backquote" => Code::Backquote, + "Backslash" => Code::Backslash, + "BracketLeft" => Code::BracketLeft, + "BracketRight" => Code::BracketRight, + "Comma" => Code::Comma, + "Digit0" => Code::Digit0, + "Digit1" => Code::Digit1, + "Digit2" => Code::Digit2, + "Digit3" => Code::Digit3, + "Digit4" => Code::Digit4, + "Digit5" => Code::Digit5, + "Digit6" => Code::Digit6, + "Digit7" => Code::Digit7, + "Digit8" => Code::Digit8, + "Digit9" => Code::Digit9, + "Equal" => Code::Equal, + "KeyA" => Code::KeyA, + "KeyB" => Code::KeyB, + "KeyC" => Code::KeyC, + "KeyD" => Code::KeyD, + "KeyE" => Code::KeyE, + "KeyF" => Code::KeyF, + "KeyG" => Code::KeyG, + "KeyH" => Code::KeyH, + "KeyI" => Code::KeyI, + "KeyJ" => Code::KeyJ, + "KeyK" => Code::KeyK, + "KeyL" => Code::KeyL, + "KeyM" => Code::KeyM, + "KeyN" => Code::KeyN, + "KeyO" => Code::KeyO, + "KeyP" => Code::KeyP, + "KeyQ" => Code::KeyQ, + "KeyR" => Code::KeyR, + "KeyS" => Code::KeyS, + "KeyT" => Code::KeyT, + "KeyU" => Code::KeyU, + "KeyV" => Code::KeyV, + "KeyW" => Code::KeyW, + "KeyX" => Code::KeyX, + "KeyY" => Code::KeyY, + "KeyZ" => Code::KeyZ, + "Minus" => Code::Minus, + "Period" => Code::Period, + "Quote" => Code::Quote, + "Semicolon" => Code::Semicolon, + "Slash" => Code::Slash, + "Backspace" => Code::Backspace, + "CapsLock" => Code::CapsLock, + "Delete" => Code::Delete, + "Enter" => Code::Enter, + "Space" => Code::Space, + "Tab" => Code::Tab, + "End" => Code::End, + "Home" => Code::Home, + "Insert" => Code::Insert, + "PageDown" => Code::PageDown, + "PageUp" => Code::PageUp, + "ArrowDown" => Code::ArrowDown, + "ArrowLeft" => Code::ArrowLeft, + "ArrowRight" => Code::ArrowRight, + "ArrowUp" => Code::ArrowUp, + "NumLock" => Code::NumLock, + "Numpad0" => Code::Numpad0, + "Numpad1" => Code::Numpad1, + "Numpad2" => Code::Numpad2, + "Numpad3" => Code::Numpad3, + "Numpad4" => Code::Numpad4, + "Numpad5" => Code::Numpad5, + "Numpad6" => Code::Numpad6, + "Numpad7" => Code::Numpad7, + "Numpad8" => Code::Numpad8, + "Numpad9" => Code::Numpad9, + "NumpadAdd" => Code::NumpadAdd, + "NumpadDecimal" => Code::NumpadDecimal, + "NumpadDivide" => Code::NumpadDivide, + "NumpadMultiply" => Code::NumpadMultiply, + "NumpadSubtract" => Code::NumpadSubtract, + "Escape" => Code::Escape, + "PrintScreen" => Code::PrintScreen, + "ScrollLock" => Code::ScrollLock, + "Pause" => Code::Pause, + "AudioVolumeDown" => Code::AudioVolumeDown, + "AudioVolumeMute" => Code::AudioVolumeMute, + "AudioVolumeUp" => Code::AudioVolumeUp, + "F1" => Code::F1, + "F2" => Code::F2, + "F3" => Code::F3, + "F4" => Code::F4, + "F5" => Code::F5, + "F6" => Code::F6, + "F7" => Code::F7, + "F8" => Code::F8, + "F9" => Code::F9, + "F10" => Code::F10, + "F11" => Code::F11, + "F12" => Code::F12, + _ => return Err(format!("Unknown key code: {}", s)), + }; + Ok(KeyCode(code)) + } +} + +impl From for Code { + fn from(key_code: KeyCode) -> Self { + key_code.0 + } +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index b888b1f..9554229 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -2,3 +2,4 @@ pub mod commands; pub mod favicon; pub mod types; pub mod logger; +pub mod keys; \ No newline at end of file diff --git a/types/keys.ts b/types/keys.ts new file mode 100644 index 0000000..d81454f --- /dev/null +++ b/types/keys.ts @@ -0,0 +1,217 @@ +export enum KeyValues { + Backquote = 'Backquote', + Backslash = 'Backslash', + BracketLeft = 'BracketLeft', + BracketRight = 'BracketRight', + Comma = 'Comma', + Digit0 = 'Digit0', + Digit1 = 'Digit1', + Digit2 = 'Digit2', + Digit3 = 'Digit3', + Digit4 = 'Digit4', + Digit5 = 'Digit5', + Digit6 = 'Digit6', + Digit7 = 'Digit7', + Digit8 = 'Digit8', + Digit9 = 'Digit9', + Equal = 'Equal', + KeyA = 'KeyA', + KeyB = 'KeyB', + KeyC = 'KeyC', + KeyD = 'KeyD', + KeyE = 'KeyE', + KeyF = 'KeyF', + KeyG = 'KeyG', + KeyH = 'KeyH', + KeyI = 'KeyI', + KeyJ = 'KeyJ', + KeyK = 'KeyK', + KeyL = 'KeyL', + KeyM = 'KeyM', + KeyN = 'KeyN', + KeyO = 'KeyO', + KeyP = 'KeyP', + KeyQ = 'KeyQ', + KeyR = 'KeyR', + KeyS = 'KeyS', + KeyT = 'KeyT', + KeyU = 'KeyU', + KeyV = 'KeyV', + KeyW = 'KeyW', + KeyX = 'KeyX', + KeyY = 'KeyY', + KeyZ = 'KeyZ', + Minus = 'Minus', + Period = 'Period', + Quote = 'Quote', + Semicolon = 'Semicolon', + Slash = 'Slash', + AltLeft = 'AltLeft', + AltRight = 'AltRight', + Backspace = 'Backspace', + CapsLock = 'CapsLock', + ContextMenu = 'ContextMenu', + ControlLeft = 'ControlLeft', + ControlRight = 'ControlRight', + Enter = 'Enter', + MetaLeft = 'MetaLeft', + MetaRight = 'MetaRight', + ShiftLeft = 'ShiftLeft', + ShiftRight = 'ShiftRight', + Space = 'Space', + Tab = 'Tab', + Delete = 'Delete', + End = 'End', + Home = 'Home', + Insert = 'Insert', + PageDown = 'PageDown', + PageUp = 'PageUp', + ArrowDown = 'ArrowDown', + ArrowLeft = 'ArrowLeft', + ArrowRight = 'ArrowRight', + ArrowUp = 'ArrowUp', + NumLock = 'NumLock', + Numpad0 = 'Numpad0', + Numpad1 = 'Numpad1', + Numpad2 = 'Numpad2', + Numpad3 = 'Numpad3', + Numpad4 = 'Numpad4', + Numpad5 = 'Numpad5', + Numpad6 = 'Numpad6', + Numpad7 = 'Numpad7', + Numpad8 = 'Numpad8', + Numpad9 = 'Numpad9', + NumpadAdd = 'NumpadAdd', + NumpadDecimal = 'NumpadDecimal', + NumpadDivide = 'NumpadDivide', + NumpadMultiply = 'NumpadMultiply', + NumpadSubtract = 'NumpadSubtract', + Escape = 'Escape', + PrintScreen = 'PrintScreen', + ScrollLock = 'ScrollLock', + Pause = 'Pause', + AudioVolumeDown = 'AudioVolumeDown', + AudioVolumeMute = 'AudioVolumeMute', + AudioVolumeUp = 'AudioVolumeUp', + F1 = 'F1', + F2 = 'F2', + F3 = 'F3', + F4 = 'F4', + F5 = 'F5', + F6 = 'F6', + F7 = 'F7', + F8 = 'F8', + F9 = 'F9', + F10 = 'F10', + F11 = 'F11', + F12 = 'F12', +} + +export enum KeyLabels { + Backquote = '`', + Backslash = '\\', + BracketLeft = '[', + BracketRight = ']', + Comma = ',', + Digit0 = '0', + Digit1 = '1', + Digit2 = '2', + Digit3 = '3', + Digit4 = '4', + Digit5 = '5', + Digit6 = '6', + Digit7 = '7', + Digit8 = '8', + Digit9 = '9', + Equal = '=', + KeyA = 'A', + KeyB = 'B', + KeyC = 'C', + KeyD = 'D', + KeyE = 'E', + KeyF = 'F', + KeyG = 'G', + KeyH = 'H', + KeyI = 'I', + KeyJ = 'J', + KeyK = 'K', + KeyL = 'L', + KeyM = 'M', + KeyN = 'N', + KeyO = 'O', + KeyP = 'P', + KeyQ = 'Q', + KeyR = 'R', + KeyS = 'S', + KeyT = 'T', + KeyU = 'U', + KeyV = 'V', + KeyW = 'W', + KeyX = 'X', + KeyY = 'Y', + KeyZ = 'Z', + Minus = '-', + Period = '.', + Quote = "'", + Semicolon = ';', + Slash = '/', + AltLeft = 'Alt', + AltRight = 'Alt (Right)', + Backspace = 'Backspace', + CapsLock = 'Caps Lock', + ContextMenu = 'Context Menu', + ControlLeft = 'Ctrl', + ControlRight = 'Ctrl (Right)', + Enter = 'Enter', + MetaLeft = 'Meta', + MetaRight = 'Meta (Right)', + ShiftLeft = 'Shift', + ShiftRight = 'Shift (Right)', + Space = 'Space', + Tab = 'Tab', + Delete = 'Delete', + End = 'End', + Home = 'Home', + Insert = 'Insert', + PageDown = 'Page Down', + PageUp = 'Page Up', + ArrowDown = '↓', + ArrowLeft = '←', + ArrowRight = '→', + ArrowUp = '↑', + NumLock = 'Num Lock', + Numpad0 = 'Numpad 0', + Numpad1 = 'Numpad 1', + Numpad2 = 'Numpad 2', + Numpad3 = 'Numpad 3', + Numpad4 = 'Numpad 4', + Numpad5 = 'Numpad 5', + Numpad6 = 'Numpad 6', + Numpad7 = 'Numpad 7', + Numpad8 = 'Numpad 8', + Numpad9 = 'Numpad 9', + NumpadAdd = 'Numpad +', + NumpadDecimal = 'Numpad .', + NumpadDivide = 'Numpad /', + NumpadMultiply = 'Numpad *', + NumpadSubtract = 'Numpad -', + Escape = 'Esc', + PrintScreen = 'Print Screen', + ScrollLock = 'Scroll Lock', + Pause = 'Pause', + AudioVolumeDown = 'Volume Down', + AudioVolumeMute = 'Volume Mute', + AudioVolumeUp = 'Volume Up', + F1 = 'F1', + F2 = 'F2', + F3 = 'F3', + F4 = 'F4', + F5 = 'F5', + F6 = 'F6', + F7 = 'F7', + F8 = 'F8', + F9 = 'F9', + F10 = 'F10', + F11 = 'F11', + F12 = 'F12', +} \ No newline at end of file From c872490a9576e5a79d015bc2891634f9bfa68f3f Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:10:09 +1000 Subject: [PATCH 28/97] feat: add redirect after saving keybind in settings.vue --- pages/settings.vue | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/settings.vue b/pages/settings.vue index 7a20eb1..52f84d3 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -131,6 +131,7 @@ const onKeyDown = (event: KeyboardEvent) => { const saveKeybind = async () => { if (keybind.value.length > 0) { await invoke("save_keybind", { keybind: keybind.value }); + router.push("/"); } else { showEmptyKeybindError.value = true; } From 80545504435ef5b1c55f32df6bff6318bccf3f97 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 17:10:46 +1000 Subject: [PATCH 29/97] fix: old hotkey not getting unregistered correctly --- src-tauri/src/api/hotkeys.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 52a276b..69ee336 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -12,7 +12,7 @@ use tauri_plugin_aptabase::EventTracker; lazy_static! { static ref HOTKEY_MANAGER: Mutex> = Mutex::new(None); - static ref REGISTERED_HOTKEYS: Mutex> = Mutex::new(Vec::new()); + static ref REGISTERED_HOTKEY: Mutex> = Mutex::new(None); } pub fn setup(app_handle: tauri::AppHandle) { @@ -36,9 +36,6 @@ pub fn setup(app_handle: tauri::AppHandle) { .block_on(crate::db::settings::get_keybind(app_handle_clone.clone())) .expect("Failed to get initial keybind"); - let initial_shortcut_for_update = initial_keybind.clone(); - let initial_shortcut_for_save = initial_keybind.clone(); - if let Err(e) = register_shortcut(&initial_keybind) { eprintln!("Error registering initial shortcut: {:?}", e); } @@ -46,7 +43,7 @@ pub fn setup(app_handle: tauri::AppHandle) { app_handle.listen("update-shortcut", move |event| { let payload_str = event.payload(); - if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_update) { + if let Some(old_hotkey) = REGISTERED_HOTKEY.lock().unwrap().take() { let manager_guard = HOTKEY_MANAGER.lock().unwrap(); if let Some(manager) = manager_guard.as_ref() { let _ = manager.unregister(old_hotkey); @@ -63,7 +60,7 @@ pub fn setup(app_handle: tauri::AppHandle) { app_handle.listen("save_keybind", move |event| { let payload_str = event.payload().to_string(); - if let Ok(old_hotkey) = parse_hotkey(&initial_shortcut_for_save) { + if let Some(old_hotkey) = REGISTERED_HOTKEY.lock().unwrap().take() { let manager_guard = HOTKEY_MANAGER.lock().unwrap(); if let Some(manager) = manager_guard.as_ref() { let _ = manager.unregister(old_hotkey); @@ -100,7 +97,7 @@ fn register_shortcut(shortcut: &[String]) -> Result<(), Box Date: Thu, 2 Jan 2025 18:40:55 +1000 Subject: [PATCH 30/97] fix: spacing of main ui --- assets/css/index.scss | 30 ++++++++++++++++++------------ 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/assets/css/index.scss b/assets/css/index.scss index 2fb355a..5feb44b 100644 --- a/assets/css/index.scss +++ b/assets/css/index.scss @@ -22,7 +22,7 @@ $mutedtext: #78756f; position: fixed; top: 0; left: 0; - height: 54px; + height: 56px; background-color: transparent; outline: none; border: none; @@ -35,10 +35,10 @@ $mutedtext: #78756f; .results { position: absolute; - width: 284px; - top: 53px; + width: 286px; + top: 55px; left: 0; - height: calc(100vh - 95px); + height: 417px; border-right: 1px solid $divider; display: flex; flex-direction: column; @@ -46,6 +46,7 @@ $mutedtext: #78756f; padding-bottom: 8px; overflow-y: auto; overflow-x: hidden; + z-index: 3; .result { height: 40px; @@ -59,6 +60,7 @@ $mutedtext: #78756f; overflow: hidden; text-overflow: clip; white-space: nowrap; + color: $text; } .result { @@ -96,20 +98,22 @@ $mutedtext: #78756f; .content { position: absolute; - top: 53px; - left: 284px; - height: calc(100vh - 254px); + top: 55px; + left: 285px; + height: 220px; font-family: CommitMono !important; font-size: 12px; letter-spacing: 1; border-radius: 10px; - width: calc(100vw - 286px); + width: 465px; white-space: pre-wrap; word-wrap: break-word; display: flex; flex-direction: column; align-items: center; overflow: hidden; + z-index: 2; + color: $text; &:not(:has(.image)) { padding: 8px; @@ -128,7 +132,7 @@ $mutedtext: #78756f; } .bottom-bar { - height: 40px; + height: 39px; width: calc(100vw - 2px); backdrop-filter: blur(18px); background-color: hsla(40, 3%, 16%, 0.8); @@ -215,18 +219,20 @@ $mutedtext: #78756f; display: flex; flex-direction: column; gap: 14px; - bottom: 40px; - left: 284px; + bottom: 39px; + left: 285px; height: 160px; - width: calc(100vw - 286px); + width: 465px; border-top: 1px solid $divider; background-color: $primary; padding: 14px; + z-index: 1; .title { font-family: SFRoundedSemiBold; font-size: 12px; letter-spacing: 0.6px; + color: $text; } .info-content { From 6d7874c1aef9605f189377d6959fb52ea405accb Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:41:10 +1000 Subject: [PATCH 31/97] feat: new settings ui --- assets/css/settings.scss | 133 ++++++++++++++++++++++++++++++--------- pages/settings.vue | 103 ++++++++++++++++++++++-------- 2 files changed, 179 insertions(+), 57 deletions(-) diff --git a/assets/css/settings.scss b/assets/css/settings.scss index 39bae81..70b1c53 100644 --- a/assets/css/settings.scss +++ b/assets/css/settings.scss @@ -36,49 +36,116 @@ $mutedtext: #78756f; } } -.keybind-container { - display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - height: 100vh; - gap: 6px; - z-index: -1; +p { + font-family: SFRoundedMedium; +} - .title { - font-size: 20px; - font-weight: 800; - } +.settings-container { + width: 100%; + margin-top: 26px; + position: relative; + font-size: 12px; + font-family: SFRoundedMedium; - .keybind-input { - padding: 6px; - border: 1px solid $divider; - color: $text2; + .settings { + position: absolute; + left: 50%; + transform: translateX(-50%); + margin-left: -26px; display: flex; - border-radius: 13px; - outline: none; - gap: 6px; + gap: 24px; - .key { - color: $text2; - font-family: SFRoundedMedium; - background-color: $divider; - padding: 6px 8px; - border-radius: 8px; + .names { + display: flex; + flex-direction: column; + gap: 16px; + + p { + font-family: SFRoundedSemiBold; + color: $text2; + } + } + + .actions { + display: flex; + flex-direction: column; + gap: 16px; + color: $mutedtext; + } + } +} + +.launch { + display: flex; + align-items: center; + gap: 6px; + + input[type="checkbox"] { + appearance: none; + width: 14px; + height: 14px; + background-color: transparent; + border-radius: 5px; + border: 1px solid $mutedtext; + position: relative; + cursor: pointer; + transition: background-color 0.2s; + + &:checked { + ~ .checkmark { + opacity: 1; + } } } - .keybind-input:focus { - border: 1px solid rgba(255, 255, 255, 0.2); + .checkmark { + height: 14px; + width: 14px; + position: absolute; + opacity: 0; + transition: opacity 0.2s; } - &.empty-keybind { - .keybind-input { - border-color: rgba(255, 82, 82, 0.298); - } + p { + color: $text2; } } +.keybind-input { + width: min-content; + white-space: nowrap; + padding: 6px; + border: 1px solid $divider; + color: $text2; + display: flex; + border-radius: 10px; + outline: none; + gap: 4px; + + .key { + color: $text2; + font-family: SFRoundedMedium; + background-color: $divider; + padding: 2px 6px; + border-radius: 6px; + font-size: 14px; + } +} + +.keybind-input:focus { + border: 1px solid rgba(255, 255, 255, 0.2); +} + +.empty-keybind { + border-color: rgba(255, 82, 82, 0.298); +} + +.top-bar { + width: 100%; + height: 56px; + border-bottom: 1px solid $divider; +} + .bottom-bar { height: 40px; width: calc(100vw - 2px); @@ -144,6 +211,10 @@ $mutedtext: #78756f; transition: all 0.2s; cursor: pointer; + p { + color: $text; + } + &.disabled { pointer-events: none; opacity: 0.5; diff --git a/pages/settings.vue b/pages/settings.vue index 52f84d3..d4814e0 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -1,9 +1,11 @@ - - - Back - + + + + Back + + @@ -26,26 +28,61 @@ - - Record a new Hotkey - - Click here - - - {{ keyToLabel(key) }} - - + + + + Startup + Qopy Hotkey + + + + + + + + + + + + + Launch Qopy at login + + + Click here + + + {{ keyToLabel(key) }} + + + + @@ -58,6 +95,7 @@ import { platform } from "@tauri-apps/plugin-os"; import { useRouter } from "vue-router"; import { Key } from "wrdu-keyboard/key"; import { KeyValues, KeyLabels } from "../types/keys"; +import { disable, enable } from "@tauri-apps/plugin-autostart"; const activeModifiers = reactive>(new Set()); const isKeybindInputFocused = ref(false); @@ -68,6 +106,8 @@ const os = ref(""); const router = useRouter(); const keyboard = useKeyboard(); const showEmptyKeybindError = ref(false); +const autostart = ref(false); +const { $settings } = useNuxtApp(); const modifierKeySet = new Set([ KeyValues.AltLeft, @@ -130,16 +170,25 @@ const onKeyDown = (event: KeyboardEvent) => { const saveKeybind = async () => { if (keybind.value.length > 0) { - await invoke("save_keybind", { keybind: keybind.value }); + await $settings.saveSetting("keybind", JSON.stringify(keybind.value)); router.push("/"); } else { showEmptyKeybindError.value = true; } }; +const toggleAutostart = async () => { + if (autostart.value === true) { + await enable(); + } else { + await disable(); + } + await $settings.saveSetting("autostart", autostart.value ? "true" : "false"); +}; + os.value = platform(); -onMounted(() => { +onMounted(async () => { keyboard.down([Key.All], (event) => { if (isKeybindInputFocused.value) { onKeyDown(event); @@ -183,6 +232,8 @@ onMounted(() => { }); break; } + + autostart.value = (await $settings.getSetting("autostart")) === "true"; }); onUnmounted(() => { From 3bfd72b638b4de1a78ea869653ed3da056d4ed8b Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 2 Jan 2025 18:41:28 +1000 Subject: [PATCH 32/97] feat: add autostart toggle setting --- app.vue | 9 ++++++++- pages/index.vue | 5 ----- plugins/settings.ts | 8 -------- src-tauri/src/db/database.rs | 4 ++-- src-tauri/src/db/migrations/{migration1.sql => v1.sql} | 0 src-tauri/src/db/migrations/{migration2.sql => v2.sql} | 0 src-tauri/src/db/migrations/v3.sql | 1 + 7 files changed, 11 insertions(+), 16 deletions(-) rename src-tauri/src/db/migrations/{migration1.sql => v1.sql} (100%) rename src-tauri/src/db/migrations/{migration2.sql => v2.sql} (100%) create mode 100644 src-tauri/src/db/migrations/v3.sql diff --git a/app.vue b/app.vue index 40f7e2a..a60f802 100644 --- a/app.vue +++ b/app.vue @@ -7,9 +7,11 @@ diff --git a/patches/wrdu-keyboard@3.0.0.patch b/patches/wrdu-keyboard@3.0.0.patch deleted file mode 100644 index bc77583..0000000 --- a/patches/wrdu-keyboard@3.0.0.patch +++ /dev/null @@ -1,131 +0,0 @@ -diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store -new file mode 100644 -index 0000000000000000000000000000000000000000..4b7e9446f3580fab3e4feaba097bcdaf98c5833c -Binary files /dev/null and b/.DS_Store differ -diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts -index aeae40f3d2bc3efd459cce04c29c21c43884154d..6131bab4895ebb3048a5225f366430d23c5f1f13 100644 ---- a/dist/runtime/keyboard.d.ts -+++ b/dist/runtime/keyboard.d.ts -@@ -1,15 +1,16 @@ --import { Key } from './types/keys.js'; --import { type Plugin } from '#app'; -+import { Key } from "./types/keys.js"; -+import { type Plugin } from "#app"; - type Handler = (event: KeyboardEvent) => void; - type Config = { - once?: boolean; - prevent?: boolean; - }; --type PublicConfig = Omit; -+type PublicConfig = Omit; - type New = (keys: Key[], handler: Handler, config?: PublicConfig) => void; - export interface Keyboard { - init: () => void; - stop: () => void; -+ unregisterAll: () => void; - down: New; - up: New; - prevent: { -diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js -index e16f600258cee90d185ffc52777bed95c14bd93e..5ddec447a5dc66ffe063eb9f9dd765c9045bdaf7 100644 ---- a/dist/runtime/keyboard.js -+++ b/dist/runtime/keyboard.js -@@ -1,45 +1,54 @@ - import { Key } from "./types/keys.js"; - import { defineNuxtPlugin } from "#app"; --const getKeyString = (keys) => keys[0] == Key.All ? keys.sort().join("+") : "All"; -+const getKeyString = (keys) => keys.includes(Key.All) ? "All" : keys.sort().join("+"); - const handlers = { - down: {}, - up: {} - }; - const pressedKeys = /* @__PURE__ */ new Set(); - const onKeydown = (event) => { -- pressedKeys.add(event.code); -+ const key = event.code; -+ pressedKeys.add(key); - const pressedArray = Array.from(pressedKeys); -- const keyString = getKeyString(pressedArray); -- if (handlers.down[keyString]) { -- handlers.down[keyString].forEach((eventHandler) => { -- if (eventHandler.prevent) { -- event.preventDefault(); -- } -- eventHandler.handler(event); -- if (eventHandler.once) { -- handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler); -- } -- }); -+ for (const keyString of [getKeyString(pressedArray), "All"]) { -+ if (handlers.down[keyString]) { -+ handlers.down[keyString].forEach((eventHandler) => { -+ if (eventHandler.prevent) { -+ event.preventDefault(); -+ } -+ eventHandler.handler(event); -+ if (eventHandler.once) { -+ handlers.down[keyString] = handlers.down[keyString].filter( -+ (h) => h !== eventHandler -+ ); -+ } -+ }); -+ } - } - }; - const onKeyup = (event) => { -- pressedKeys.delete(event.code); -+ const key = event.code; -+ pressedKeys.delete(key); - const releasedArray = Array.from(pressedKeys); -- const keyString = getKeyString(releasedArray); -- if (handlers.up[keyString]) { -- handlers.up[keyString].forEach((eventHandler) => { -- if (eventHandler.prevent) { -- event.preventDefault(); -- } -- eventHandler.handler(event); -- if (eventHandler.once) { -- handlers.up[keyString] = handlers.up[keyString].filter((h) => h !== eventHandler); -- } -- }); -+ for (const keyString of [getKeyString(releasedArray), "All"]) { -+ if (handlers.up[keyString]) { -+ handlers.up[keyString].forEach((eventHandler) => { -+ if (eventHandler.prevent) { -+ event.preventDefault(); -+ } -+ eventHandler.handler(event); -+ if (eventHandler.once) { -+ handlers.up[keyString] = handlers.up[keyString].filter( -+ (h) => h !== eventHandler -+ ); -+ } -+ }); -+ } - } - }; - const init = () => { - stop(); -+ pressedKeys.clear(); - window.addEventListener("keydown", onKeydown); - window.addEventListener("keyup", onKeyup); - }; -@@ -47,6 +56,10 @@ const stop = () => { - window.removeEventListener("keydown", onKeydown); - window.removeEventListener("keyup", onKeyup); - }; -+const unregisterAll = () => { -+ handlers.down = {}; -+ handlers.up = {}; -+}; - const down = (keys, handler, config = {}) => { - if (keys.includes(Key.All)) { - keys = [Key.All]; -@@ -84,6 +97,7 @@ const keyboard = defineNuxtPlugin((nuxtApp) => { - keyboard: { - init, - stop, -+ unregisterAll, - down: (keys, handler, config = {}) => down(keys, handler, config), - up: (keys, handler, config = {}) => up(keys, handler, config), - prevent: { diff --git a/patches/wrdu-keyboard@3.1.0.patch b/patches/wrdu-keyboard@3.1.0.patch new file mode 100644 index 0000000..ffc2318 --- /dev/null +++ b/patches/wrdu-keyboard@3.1.0.patch @@ -0,0 +1,190 @@ +diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store +new file mode 100644 +index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 +Binary files /dev/null and b/.DS_Store differ +diff --git a/dist/runtime/composables.d.ts b/dist/runtime/composables.d.ts +index fb2e51205317d4bf2530528da750cd18f6486022..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 +--- a/dist/runtime/composables.d.ts ++++ b/dist/runtime/composables.d.ts +@@ -1,3 +0,0 @@ +-import type { Keyboard } from './keyboard.js'; +-export * from './keyboard.js'; +-export declare const useKeyboard: () => Keyboard; +diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts +index 1cd64674456f1598acb93ed77d4f84ff4823ba40..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 +--- a/dist/runtime/keyboard.d.ts ++++ b/dist/runtime/keyboard.d.ts +@@ -1,25 +0,0 @@ +-import { Key } from './types/keys.js'; +-import { type Plugin } from '#app'; +-type Handler = (event: KeyboardEvent) => void; +-type Config = { +- once?: boolean; +- prevent?: boolean; +-}; +-type PublicConfig = Omit; +-type New = (keys: Key[], handler: Handler, config?: PublicConfig) => void; +-export interface Keyboard { +- init: () => void; +- stop: () => void; +- clear: () => void; +- down: New; +- up: New; +- prevent: { +- down: New; +- up: New; +- }; +-} +-type KeyboardPlugin = Plugin<{ +- keyboard: Keyboard; +-}>; +-declare const keyboard: KeyboardPlugin; +-export default keyboard; +diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js +index e49c3d115e570a91ad814e4fba013d61a6399637..a2e390e66b59c1af3506d7394ce6a6600f6d205e 100644 +--- a/dist/runtime/keyboard.js ++++ b/dist/runtime/keyboard.js +@@ -12,15 +12,22 @@ const onKeydown = (event) => { + const keyString = getKeyString(pressedArray); + if (handlers.down[keyString]) { + handlers.down[keyString].forEach((eventHandler) => { +- if (eventHandler.prevent) { +- event.preventDefault(); +- } ++ if (eventHandler.prevent) event.preventDefault(); + eventHandler.handler(event); + if (eventHandler.once) { + handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler); + } + }); + } ++ if (handlers.down["All"]) { ++ handlers.down["All"].forEach((eventHandler) => { ++ if (eventHandler.prevent) event.preventDefault(); ++ eventHandler.handler(event); ++ if (eventHandler.once) { ++ handlers.down["All"] = handlers.down["All"].filter((h) => h !== eventHandler); ++ } ++ }); ++ } + }; + const onKeyup = (event) => { + const releasedArray = Array.from(pressedKeys); +diff --git a/dist/runtime/types/keys.d.ts b/dist/runtime/types/keys.d.ts +index f48fa8415a91f17626f9aaa0a03f14426785744b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 +--- a/dist/runtime/types/keys.d.ts ++++ b/dist/runtime/types/keys.d.ts +@@ -1,113 +0,0 @@ +-export declare const Key: { +- Backspace: string; +- Tab: string; +- Enter: string; +- LeftShift: string; +- RightShift: string; +- LeftControl: string; +- RightControl: string; +- LeftAlt: string; +- RightAlt: string; +- Pause: string; +- CapsLock: string; +- Escape: string; +- Space: string; +- PageUp: string; +- PageDown: string; +- End: string; +- Home: string; +- LeftArrow: string; +- UpArrow: string; +- RightArrow: string; +- DownArrow: string; +- PrintScreen: string; +- Insert: string; +- Delete: string; +- Zero: string; +- One: string; +- Two: string; +- Three: string; +- Four: string; +- Five: string; +- Six: string; +- Seven: string; +- Eight: string; +- Nine: string; +- A: string; +- B: string; +- C: string; +- D: string; +- E: string; +- F: string; +- G: string; +- H: string; +- I: string; +- J: string; +- K: string; +- L: string; +- M: string; +- N: string; +- O: string; +- P: string; +- Q: string; +- R: string; +- S: string; +- T: string; +- U: string; +- V: string; +- W: string; +- X: string; +- Y: string; +- Z: string; +- LeftMeta: string; +- RightMeta: string; +- ContextMenu: string; +- NumpadZero: string; +- NumpadOne: string; +- NumpadTwo: string; +- NumpadThree: string; +- NumpadFour: string; +- NumpadFive: string; +- NumpadSix: string; +- NumpadSeven: string; +- NumpadEight: string; +- NumpadNine: string; +- NumpadMultiply: string; +- NumpadAdd: string; +- NumpadSubtract: string; +- NumpadDecimal: string; +- NumpadDivide: string; +- F1: string; +- F2: string; +- F3: string; +- F4: string; +- F5: string; +- F6: string; +- F7: string; +- F8: string; +- F9: string; +- F10: string; +- F11: string; +- F12: string; +- NumLock: string; +- ScrollLock: string; +- VolumeMute: string; +- VolumeDown: string; +- VolumeUp: string; +- MediaPlayer: string; +- LaunchApp1: string; +- LaunchApp2: string; +- Semicolon: string; +- Equal: string; +- Comma: string; +- Minus: string; +- Period: string; +- Slash: string; +- Backquote: string; +- LeftBracket: string; +- Backslash: string; +- RightBracket: string; +- Quote: string; +- All: string; +-}; +-export type Key = (typeof Key)[keyof typeof Key]; From d7fb3af04d1054086f573d6b258dfdd3cf57d5f7 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 10 Jan 2025 23:54:31 +1000 Subject: [PATCH 38/97] chore: update dependencies and versions in package.json and Cargo.lock files --- package.json | 14 +-- src-tauri/Cargo.lock | 188 ++++++++++++++++---------------------- src-tauri/Cargo.toml | 20 ++-- src-tauri/tauri.conf.json | 2 +- 4 files changed, 96 insertions(+), 128 deletions(-) diff --git a/package.json b/package.json index de5e05e..bdf0440 100644 --- a/package.json +++ b/package.json @@ -10,22 +10,22 @@ "postinstall": "nuxt prepare" }, "dependencies": { - "@tauri-apps/api": "2.1.1", - "@tauri-apps/cli": "2.1.0", + "@tauri-apps/api": "2.2.0", + "@tauri-apps/cli": "2.2.2", "@tauri-apps/plugin-autostart": "2.2.0", "@tauri-apps/plugin-os": "2.2.0", - "nuxt": "3.15.0", + "nuxt": "3.15.1", "overlayscrollbars": "2.10.1", "overlayscrollbars-vue": "0.5.9", - "sass-embedded": "1.83.0", - "uuid": "11.0.3", + "sass-embedded": "1.83.1", + "uuid": "11.0.5", "vue": "3.5.13", - "wrdu-keyboard": "3.0.0" + "wrdu-keyboard": "^3.1.0" }, "overrides": { "chokidar": "^3.6.0" }, "patchedDependencies": { - "wrdu-keyboard@3.0.0": "patches/wrdu-keyboard@3.0.0.patch" + "wrdu-keyboard@3.1.0": "patches/wrdu-keyboard@3.1.0.patch" } } diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index a5d7869..96fdceb 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4,9 +4,9 @@ version = 3 [[package]] name = "active-win-pos-rs" -version = "0.8.4" +version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e227f8493de9f5e493f8e762ac7516d2ae42464df2e8122fcafd604f0b16c634" +checksum = "3b37fd8a23464e913e3fcc1ff2d347c06d77f9101ee680c42b32d020508dc834" dependencies = [ "appkit-nsworkspace-bindings", "core-foundation 0.9.4", @@ -31,18 +31,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" -[[package]] -name = "ahash" -version = "0.8.11" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e89da841a80418a9b391ebaea17f5c112ffaaa96f621d2c285b5174da76b9011" -dependencies = [ - "cfg-if", - "once_cell", - "version_check", - "zerocopy", -] - [[package]] name = "aho-corasick" version = "1.1.3" @@ -631,16 +619,16 @@ dependencies = [ [[package]] name = "cargo_metadata" -version = "0.18.1" +version = "0.19.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2d886547e41f740c616ae73108f6eb70afe6d940c7bc697cb30f13daec073037" +checksum = "8769706aad5d996120af43197bf46ef6ad0fda35216b4505f926a365a232d924" dependencies = [ "camino", "cargo-platform", "semver", "serde", "serde_json", - "thiserror 1.0.63", + "thiserror 2.0.3", ] [[package]] @@ -1582,6 +1570,12 @@ version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a0d2fde1f7b3d48b8395d5f2de76c18a528bd6a9cdde438df747bfcba3e05d6f" + [[package]] name = "foreign-types" version = "0.3.2" @@ -2137,18 +2131,25 @@ name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" + +[[package]] +name = "hashbrown" +version = "0.15.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf151400ff0baff5465007dd2f3e717f3fe502074ca563069ce3a6629d07b289" dependencies = [ - "ahash", "allocator-api2", + "equivalent", + "foldhash", ] [[package]] name = "hashlink" -version = "0.9.1" +version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba4ff7128dee98c7dc9794b6a411377e1404dba1c97deb8d1a55297bd25d8af" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ - "hashbrown 0.14.5", + "hashbrown 0.15.2", ] [[package]] @@ -2611,15 +2612,6 @@ dependencies = [ "configparser", ] -[[package]] -name = "instant" -version = "0.1.13" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e0242819d153cba4b4b05a5a8f2a7e9bbf97b6055b2a002b395c96b5ff3c0222" -dependencies = [ - "cfg-if", -] - [[package]] name = "interpolate_name" version = "0.2.4" @@ -2829,9 +2821,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.155" +version = "0.2.169" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "b5aba8db14291edd000dfcc4d620c7ebfb122c613afb886ca8803fa4e128a20a" [[package]] name = "libfuzzer-sys" @@ -4054,7 +4046,7 @@ dependencies = [ [[package]] name = "qopy" -version = "0.3.4" +version = "0.4.0" dependencies = [ "active-win-pos-rs", "applications", @@ -4421,9 +4413,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.11" +version = "0.12.12" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7fe060fe50f524be480214aba758c71f99f90ee8c83c5a36b5e9e1d568eb4eb3" +checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" dependencies = [ "base64 0.22.1", "bytes", @@ -4793,9 +4785,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.134" +version = "1.0.135" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d00f4175c42ee48b15416f6193a959ba3a0d67fc699a0db9ad12df9f83991c7d" +checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" dependencies = [ "itoa 1.0.11", "memchr", @@ -5061,21 +5053,11 @@ dependencies = [ "der", ] -[[package]] -name = "sqlformat" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f895e3734318cc55f1fe66258926c9b910c124d47520339efecbb6c59cec7c1f" -dependencies = [ - "nom", - "unicode_categories", -] - [[package]] name = "sqlx" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "93334716a037193fac19df402f8571269c84a00852f6a7066b5d2616dcd64d3e" +checksum = "4410e73b3c0d8442c5f99b425d7a435b5ee0ae4167b3196771dd3f7a01be745f" dependencies = [ "sqlx-core", "sqlx-macros", @@ -5086,39 +5068,33 @@ dependencies = [ [[package]] name = "sqlx-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d4d8060b456358185f7d50c55d9b5066ad956956fddec42ee2e8567134a8936e" +checksum = "6a007b6936676aa9ab40207cde35daab0a04b823be8ae004368c0793b96a61e0" dependencies = [ - "atoi", - "byteorder", "bytes", "chrono", "crc", "crossbeam-queue", "either", "event-listener", - "futures-channel", "futures-core", "futures-intrusive", "futures-io", "futures-util", - "hashbrown 0.14.5", + "hashbrown 0.15.2", "hashlink", - "hex", "indexmap 2.3.0", "log", "memchr", "native-tls", "once_cell", - "paste", "percent-encoding", "serde", "serde_json", "sha2", "smallvec", - "sqlformat", - "thiserror 1.0.63", + "thiserror 2.0.3", "time", "tokio", "tokio-stream", @@ -5128,9 +5104,9 @@ dependencies = [ [[package]] name = "sqlx-macros" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cac0692bcc9de3b073e8d747391827297e075c7710ff6276d9f7a1f3d58c6657" +checksum = "3112e2ad78643fef903618d78cf0aec1cb3134b019730edb039b69eaf531f310" dependencies = [ "proc-macro2", "quote", @@ -5141,9 +5117,9 @@ dependencies = [ [[package]] name = "sqlx-macros-core" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1804e8a7c7865599c9c79be146dc8a9fd8cc86935fa641d3ea58e5f0688abaa5" +checksum = "4e9f90acc5ab146a99bf5061a7eb4976b573f560bc898ef3bf8435448dd5e7ad" dependencies = [ "dotenvy", "either", @@ -5167,9 +5143,9 @@ dependencies = [ [[package]] name = "sqlx-mysql" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "64bb4714269afa44aef2755150a0fc19d756fb580a67db8885608cf02f47d06a" +checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ "atoi", "base64 0.22.1", @@ -5203,7 +5179,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 1.0.63", + "thiserror 2.0.3", "time", "tracing", "whoami", @@ -5211,9 +5187,9 @@ dependencies = [ [[package]] name = "sqlx-postgres" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6fa91a732d854c5d7726349bb4bb879bb9478993ceb764247660aee25f67c2f8" +checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ "atoi", "base64 0.22.1", @@ -5225,7 +5201,6 @@ dependencies = [ "etcetera", "futures-channel", "futures-core", - "futures-io", "futures-util", "hex", "hkdf", @@ -5243,7 +5218,7 @@ dependencies = [ "smallvec", "sqlx-core", "stringprep", - "thiserror 1.0.63", + "thiserror 2.0.3", "time", "tracing", "whoami", @@ -5251,9 +5226,9 @@ dependencies = [ [[package]] name = "sqlx-sqlite" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d5b2cf34a45953bfd3daaf3db0f7a7878ab9b7a6b91b422d24a7a9e4c857b680" +checksum = "f85ca71d3a5b24e64e1d08dd8fe36c6c95c339a896cc33068148906784620540" dependencies = [ "atoi", "chrono", @@ -5455,9 +5430,9 @@ dependencies = [ [[package]] name = "tao" -version = "0.30.8" +version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6682a07cf5bab0b8a2bd20d0a542917ab928b5edb75ebd4eda6b05cbaab872da" +checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9" dependencies = [ "bitflags 2.6.0", "cocoa 0.26.0", @@ -5470,7 +5445,6 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "instant", "jni", "lazy_static", "libc", @@ -5522,9 +5496,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e545de0a2dfe296fa67db208266cd397c5a55ae782da77973ef4c4fac90e9f2c" +checksum = "2e2e3349fbb2be7af9fad1b43d61ac83ba55ab48d47fbe1b2732f0c8211610a9" dependencies = [ "anyhow", "bytes", @@ -5573,9 +5547,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7bd2a4bcfaf5fb9f4be72520eefcb61ae565038f8ccba2a497d8c28f463b8c01" +checksum = "b274ec7239ada504deb615f1c8abd7ba99631e879709e6f10e5d17217058d976" dependencies = [ "anyhow", "cargo_toml", @@ -5595,9 +5569,9 @@ dependencies = [ [[package]] name = "tauri-codegen" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bf79faeecf301d3e969b1fae977039edb77a4c1f25cc0a961be298b54bff97cf" +checksum = "f77894f9ddb5cb6c04fcfe8c8869ebe0aded4dabf19917118d48be4a95599ab5" dependencies = [ "base64 0.22.1", "brotli", @@ -5632,9 +5606,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.3" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c52027c8c5afb83166dacddc092ee8fff50772f9646d461d8c33ee887e447a03" +checksum = "3240a5caed760a532e8f687be6f05b2c7d11a1d791fb53ccc08cfeb3e5308736" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5819,9 +5793,9 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.3.0" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b7351014c140906bcfff59d96e04b1170c8f602557f40eb37f7de356d4e7067b" +checksum = "ce2d39224390c41ba544f02b4f1721f42256320b3fb8c371e9425cbddeb4a68c" dependencies = [ "base64 0.22.1", "dirs 5.0.1", @@ -5849,9 +5823,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cce18d43f80d4aba3aa8a0c953bbe835f3d0f2370aca75e8dbb14bd4bab27958" +checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7" dependencies = [ "dpi", "gtk", @@ -5868,9 +5842,9 @@ dependencies = [ [[package]] name = "tauri-runtime-wry" -version = "2.2.0" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9f442a38863e10129ffe2cec7bd09c2dcf8a098a3a27801a476a304d5bb991d2" +checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0" dependencies = [ "gtk", "http", @@ -5894,9 +5868,9 @@ dependencies = [ [[package]] name = "tauri-utils" -version = "2.1.0" +version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9271a88f99b4adea0dc71d0baca4505475a0bbd139fb135f62958721aaa8fe54" +checksum = "96fb10e7cc97456b2d5b9c03e335b5de5da982039a303a20d10006885e4523a0" dependencies = [ "brotli", "cargo_metadata", @@ -6094,9 +6068,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.42.0" +version = "1.43.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5cec9b21b0450273377fc97bd4c33a8acffc8c996c987a7c5b319a0083707551" +checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" dependencies = [ "backtrace", "bytes", @@ -6113,9 +6087,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "693d596312e88961bc67d7f1f97af8a70227d9f90c31bba5806eec004978d752" +checksum = "6e06d43f1345a3bcd39f6a56dbb7dcab2ba47e68e8ac134855e7e2bdbaf8cab8" dependencies = [ "proc-macro2", "quote", @@ -6409,12 +6383,6 @@ version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4c87d22b6e3f4a18d4d40ef354e97c90fcb14dd91d7dc0aa9d8a1172ebf7202" -[[package]] -name = "unicode_categories" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e" - [[package]] name = "untrusted" version = "0.9.0" @@ -6481,9 +6449,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.0" +version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8c5f0a0af699448548ad1a2fbf920fb4bee257eae39953ba95cb84891a0446a" +checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" dependencies = [ "getrandom 0.2.15", "serde", @@ -6779,9 +6747,9 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f61ff3d9d0ee4efcb461b14eb3acfda2702d10dc329f339303fc3e57215ae2c" +checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921" dependencies = [ "webview2-com-macros", "webview2-com-sys", @@ -6804,9 +6772,9 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.33.0" +version = "0.34.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a3a3e2eeb58f82361c93f9777014668eb3d07e7d174ee4c819575a9208011886" +checksum = "7a82bce72db6e5ee83c68b5de1e2cd6ea195b9fbff91cb37df5884cbe3222df4" dependencies = [ "thiserror 1.0.63", "windows 0.58.0", @@ -7259,9 +7227,9 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.47.0" +version = "0.48.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "553ca1ce149982123962fac2506aa75b8b76288779a77e72b12fa2fc34938647" +checksum = "1e644bf458e27b11b0ecafc9e5633d1304fdae82baca1d42185669752fe6ca4f" dependencies = [ "base64 0.22.1", "block2", @@ -7289,7 +7257,7 @@ dependencies = [ "sha2", "soup3", "tao-macros", - "thiserror 1.0.63", + "thiserror 2.0.3", "url", "webkit2gtk", "webkit2gtk-sys", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 45e9dd7..d792d17 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,16 +1,16 @@ [package] name = "qopy" -version = "0.3.4" +version = "0.4.0" description = "Qopy" authors = ["pandadev"] edition = "2021" rust-version = "1.70" [build-dependencies] -tauri-build = { version = "2.0.3", features = [] } +tauri-build = { version = "2.0.4", features = [] } [dependencies] -tauri = { version = "2.1.1", features = [ +tauri = { version = "2.2.0", features = [ "macos-private-api", "tray-icon", "image-png" @@ -18,22 +18,22 @@ tauri = { version = "2.1.1", features = [ tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] } tauri-plugin-autostart = "2.2.0" tauri-plugin-os = "2.2.0" -tauri-plugin-updater = "2.3.0" +tauri-plugin-updater = "2.3.1" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" tauri-plugin-prevent-default = "1.0.1" tauri-plugin-global-shortcut = "2.2.0" tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } -sqlx = { version = "0.8.2", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } +sqlx = { version = "0.8.3", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } serde = { version = "1.0.217", features = ["derive"] } -tokio = { version = "1.42.0", features = ["full"] } -serde_json = "1.0.134" +tokio = { version = "1.43.0", features = ["full"] } +serde_json = "1.0.135" rdev = "0.5.3" rand = "0.8.5" base64 = "0.22.1" image = "0.25.5" -reqwest = { version = "0.12.11", features = ["json", "blocking"] } +reqwest = { version = "0.12.12", features = ["json", "blocking"] } url = "2.5.4" regex = "1.11.1" sha2 = "0.10.8" @@ -42,8 +42,8 @@ time = "0.3.37" global-hotkey = "0.6.3" chrono = { version = "0.4.39", features = ["serde"] } log = { version = "0.4.22", features = ["std"] } -uuid = "1.11.0" -active-win-pos-rs = "0.8.4" +uuid = "1.11.1" +active-win-pos-rs = "0.9.0" include_dir = "0.7.4" # hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" } applications = { git = "https://github.com/HuakunShen/applications-rs", branch = "dev" } diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index 9ce7712..c6acd3e 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -1,6 +1,6 @@ { "productName": "Qopy", - "version": "0.3.4", + "version": "0.4.0", "identifier": "net.pandadev.qopy", "build": { "frontendDist": "../dist", From 617c1737b757c9edbf2d782e4049dd843330fd5a Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:05:47 +1000 Subject: [PATCH 39/97] refactor(logger): remove commented-out lines in logger implementation --- src-tauri/src/utils/logger.rs | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src-tauri/src/utils/logger.rs b/src-tauri/src/utils/logger.rs index bd39108..d0e95c6 100644 --- a/src-tauri/src/utils/logger.rs +++ b/src-tauri/src/utils/logger.rs @@ -17,7 +17,6 @@ impl log::Log for FileLogger { if self.enabled(record.metadata()) { let mut file = self.file.try_clone().expect("Failed to clone file handle"); - // Format: timestamp [LEVEL] target: message (file:line) writeln!( file, "{} [{:<5}] {}: {} ({}:{})", @@ -40,15 +39,13 @@ pub fn init_logger(app_data_dir: &std::path::Path) -> Result<(), SetLoggerError> let logs_dir = app_data_dir.join("logs"); std::fs::create_dir_all(&logs_dir).expect("Failed to create logs directory"); - // Use .log extension for standard log files let log_path = logs_dir.join("app.log"); let file = OpenOptions::new() .create(true) - .append(true) // Use append mode instead of write + .append(true) .open(&log_path) .expect("Failed to open log file"); - // Set up panic hook let panic_file = file.try_clone().expect("Failed to clone file handle"); panic::set_hook( Box::new(move |panic_info| { From 416e2f020da3c7a772a6230faa6ab95dfeb4d250 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:27:06 +1000 Subject: [PATCH 40/97] fix: reregister of hotkey not working --- plugins/settings.ts | 1 - src-tauri/src/api/hotkeys.rs | 5 +++-- src-tauri/src/db/settings.rs | 32 +++++--------------------------- src-tauri/src/main.rs | 2 -- 4 files changed, 8 insertions(+), 32 deletions(-) diff --git a/plugins/settings.ts b/plugins/settings.ts index 4ca1b52..8e2fcf2 100644 --- a/plugins/settings.ts +++ b/plugins/settings.ts @@ -1,5 +1,4 @@ import { invoke } from "@tauri-apps/api/core"; -import type { Settings } from "~/types/types"; export default defineNuxtPlugin(() => { return { diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index a0084eb..994894b 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -43,7 +43,8 @@ pub fn setup(app_handle: tauri::AppHandle) { } app_handle.listen("update-shortcut", move |event| { - let payload_str = event.payload(); + let payload_str = event.payload().replace("\\\"", "\""); + let trimmed_str = payload_str.trim_matches('"'); if let Some(old_hotkey) = REGISTERED_HOTKEY.lock().unwrap().take() { let manager_guard = HOTKEY_MANAGER.lock().unwrap(); @@ -52,7 +53,7 @@ pub fn setup(app_handle: tauri::AppHandle) { } } - let payload: Vec = serde_json::from_str(payload_str).unwrap_or_default(); + let payload: Vec = serde_json::from_str(trimmed_str).unwrap_or_default(); if let Err(e) = register_shortcut(&payload) { eprintln!("Error re-registering shortcut: {:?}", e); diff --git a/src-tauri/src/db/settings.rs b/src-tauri/src/db/settings.rs index caee425..2539ab7 100644 --- a/src-tauri/src/db/settings.rs +++ b/src-tauri/src/db/settings.rs @@ -24,32 +24,6 @@ pub async fn initialize_settings(pool: &SqlitePool) -> Result<(), Box, - keybind: Vec -) -> Result<(), String> { - app_handle.emit("update-shortcut", &keybind).map_err(|e| e.to_string())?; - - let json = serde_json::to_string(&keybind).map_err(|e| e.to_string())?; - - sqlx - ::query("INSERT OR REPLACE INTO settings (key, value) VALUES ('keybind', ?)") - .bind(json) - .execute(&*pool).await - .map_err(|e| e.to_string())?; - - let _ = app_handle.track_event( - "keybind_saved", - Some(serde_json::json!({ - "keybind": keybind - })) - ); - - Ok(()) -} - #[tauri::command] pub async fn get_setting( pool: tauri::State<'_, SqlitePool>, @@ -74,7 +48,7 @@ pub async fn save_setting( sqlx ::query("INSERT OR REPLACE INTO settings (key, value) VALUES (?, ?)") .bind(key.clone()) - .bind(value) + .bind(value.clone()) .execute(&*pool).await .map_err(|e| e.to_string())?; @@ -85,6 +59,10 @@ pub async fn save_setting( })) ); + if key == "keybind" { + let _ = app_handle.emit("update-shortcut", &value).map_err(|e| e.to_string())?; + } + Ok(()) } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 0291cbc..179500c 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -124,8 +124,6 @@ fn main() { db::history::read_image, db::settings::get_setting, db::settings::save_setting, - db::settings::save_keybind, - db::settings::get_keybind, utils::commands::fetch_page_meta ] ) From 42aa72f3b812a0c2f97ed83efe477c1981c997a7 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:32:58 +1000 Subject: [PATCH 41/97] refactor: replace lazy_static with OnceCell for hotkey manager and registered hotkey --- src-tauri/src/api/hotkeys.rs | 107 +++++++++++++++++------------------ 1 file changed, 53 insertions(+), 54 deletions(-) diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 994894b..a530b27 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -6,20 +6,20 @@ use global_hotkey::{ GlobalHotKeyManager, HotKeyState, }; -use lazy_static::lazy_static; use std::str::FromStr; use std::sync::Mutex; -use tauri::{ AppHandle, Listener, Manager }; +use tauri::{ AppHandle, Manager, Listener }; use tauri_plugin_aptabase::EventTracker; +use tokio::sync::OnceCell; -lazy_static! { - static ref HOTKEY_MANAGER: Mutex> = Mutex::new(None); - static ref REGISTERED_HOTKEY: Mutex> = Mutex::new(None); -} +static HOTKEY_MANAGER: OnceCell>> = OnceCell::const_new(); +static REGISTERED_HOTKEY: OnceCell>> = OnceCell::const_new(); pub fn setup(app_handle: tauri::AppHandle) { - let app_handle_clone = app_handle.clone(); + let _ = HOTKEY_MANAGER.set(Mutex::new(None)); + let _ = REGISTERED_HOTKEY.set(Mutex::new(None)); + let app_handle_clone = app_handle.clone(); let manager = match GlobalHotKeyManager::new() { Ok(manager) => manager, Err(err) => { @@ -28,8 +28,8 @@ pub fn setup(app_handle: tauri::AppHandle) { } }; - { - let mut manager_guard = HOTKEY_MANAGER.lock().unwrap(); + if let Some(hotkey_manager) = HOTKEY_MANAGER.get() { + let mut manager_guard = hotkey_manager.lock().unwrap(); *manager_guard = Some(manager); } @@ -42,19 +42,17 @@ pub fn setup(app_handle: tauri::AppHandle) { eprintln!("Error registering initial shortcut: {:?}", e); } + setup_event_listeners(&app_handle); + setup_hotkey_receiver(app_handle); +} + +fn setup_event_listeners(app_handle: &AppHandle) { app_handle.listen("update-shortcut", move |event| { let payload_str = event.payload().replace("\\\"", "\""); let trimmed_str = payload_str.trim_matches('"'); - - if let Some(old_hotkey) = REGISTERED_HOTKEY.lock().unwrap().take() { - let manager_guard = HOTKEY_MANAGER.lock().unwrap(); - if let Some(manager) = manager_guard.as_ref() { - let _ = manager.unregister(old_hotkey); - } - } - + unregister_current_hotkey(); + let payload: Vec = serde_json::from_str(trimmed_str).unwrap_or_default(); - if let Err(e) = register_shortcut(&payload) { eprintln!("Error re-registering shortcut: {:?}", e); } @@ -62,21 +60,16 @@ pub fn setup(app_handle: tauri::AppHandle) { app_handle.listen("save_keybind", move |event| { let payload_str = event.payload().to_string(); - - if let Some(old_hotkey) = REGISTERED_HOTKEY.lock().unwrap().take() { - let manager_guard = HOTKEY_MANAGER.lock().unwrap(); - if let Some(manager) = manager_guard.as_ref() { - let _ = manager.unregister(old_hotkey); - } - } - + unregister_current_hotkey(); + let payload: Vec = serde_json::from_str(&payload_str).unwrap_or_default(); if let Err(e) = register_shortcut(&payload) { eprintln!("Error registering saved shortcut: {:?}", e); } }); +} - let app_handle_for_hotkey = app_handle.clone(); +fn setup_hotkey_receiver(app_handle: AppHandle) { tauri::async_runtime::spawn(async move { loop { match GlobalHotKeyEvent::receiver().recv() { @@ -84,24 +77,40 @@ pub fn setup(app_handle: tauri::AppHandle) { if event.state == HotKeyState::Released { continue; } - handle_hotkey_event(&app_handle_for_hotkey); - } - Err(e) => { - eprintln!("Error receiving hotkey event: {:?}", e); + handle_hotkey_event(&app_handle); } + Err(e) => eprintln!("Error receiving hotkey event: {:?}", e), } } }); } +fn unregister_current_hotkey() { + if let Some(registered) = REGISTERED_HOTKEY.get() { + if let Some(old_hotkey) = registered.lock().unwrap().take() { + if let Some(manager) = HOTKEY_MANAGER.get() { + if let Some(manager) = manager.lock().unwrap().as_ref() { + let _ = manager.unregister(old_hotkey); + } + } + } + } +} + fn register_shortcut(shortcut: &[String]) -> Result<(), Box> { let hotkey = parse_hotkey(shortcut)?; - let manager_guard = HOTKEY_MANAGER.lock().unwrap(); - if let Some(manager) = manager_guard.as_ref() { - manager.register(hotkey.clone())?; - *REGISTERED_HOTKEY.lock().unwrap() = Some(hotkey); - Ok(()) + if let Some(manager) = HOTKEY_MANAGER.get() { + let manager_guard = manager.lock().unwrap(); + if let Some(manager) = manager_guard.as_ref() { + manager.register(hotkey.clone())?; + if let Some(registered) = REGISTERED_HOTKEY.get() { + *registered.lock().unwrap() = Some(hotkey); + } + Ok(()) + } else { + Err("Hotkey manager not initialized".into()) + } } else { Err("Hotkey manager not initialized".into()) } @@ -113,21 +122,11 @@ fn parse_hotkey(shortcut: &[String]) -> Result { - modifiers |= Modifiers::CONTROL; - } - "AltLeft" => { - modifiers |= Modifiers::ALT; - } - "ShiftLeft" => { - modifiers |= Modifiers::SHIFT; - } - "MetaLeft" => { - modifiers |= Modifiers::META; - } - key => { - code = Some(Code::from(KeyCode::from_str(key)?)); - } + "ControlLeft" => modifiers |= Modifiers::CONTROL, + "AltLeft" => modifiers |= Modifiers::ALT, + "ShiftLeft" => modifiers |= Modifiers::SHIFT, + "MetaLeft" => modifiers |= Modifiers::META, + key => code = Some(Code::from(KeyCode::from_str(key)?)), } } @@ -157,8 +156,8 @@ fn handle_hotkey_event(app_handle: &AppHandle) { "hotkey_triggered", Some( serde_json::json!({ - "action": if window.is_visible().unwrap() { "hide" } else { "show" } - }) + "action": if window.is_visible().unwrap() { "hide" } else { "show" } + }) ) ); -} +} \ No newline at end of file From c2ea79c29d48b2d3722563d1c4ad9922e5a4de3c Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 00:41:25 +1000 Subject: [PATCH 42/97] chore(release): change release to access token instead of actions bot --- .github/workflows/release.yml | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 6a11091..479bc4a 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -233,6 +233,7 @@ jobs: - uses: actions/checkout@v4 with: fetch-depth: 0 + token: ${{ secrets.PAT }} - name: Check if release already exists id: check_release @@ -240,14 +241,12 @@ jobs: VERSION="${{ needs.prepare.outputs.version }}" RELEASE_EXISTS=$(gh release view v$VERSION --json id --jq '.id' 2>/dev/null || echo "") if [ -n "$RELEASE_EXISTS" ]; then - echo "Release v$VERSION already exists. Skipping release creation." echo "SKIP_RELEASE=true" >> $GITHUB_ENV else - echo "Release v$VERSION does not exist. Proceeding with release creation." echo "SKIP_RELEASE=false" >> $GITHUB_ENV fi env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PAT }} - name: Download all artifacts if: env.SKIP_RELEASE == 'false' @@ -312,7 +311,7 @@ jobs: if: env.SKIP_RELEASE == 'false' uses: softprops/action-gh-release@v2 env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + GITHUB_TOKEN: ${{ secrets.PAT }} with: draft: true tag_name: v${{ needs.prepare.outputs.version }} From 105213a631df6be71cae8349e18546fabd189922 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 01:00:32 +1000 Subject: [PATCH 43/97] feat: add macOS build script for Tauri application --- .github/scripts/macOS.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100755 .github/scripts/macOS.sh diff --git a/.github/scripts/macOS.sh b/.github/scripts/macOS.sh new file mode 100755 index 0000000..0800a69 --- /dev/null +++ b/.github/scripts/macOS.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +if [ -f .env ]; then + export $(cat .env | grep -v '^#' | xargs) +fi + +set -e + +required_vars=("APPLE_CERTIFICATE" "APPLE_CERTIFICATE_PASSWORD" "APPLE_ID" "APPLE_ID_PASSWORD" "KEYCHAIN_PASSWORD" "APP_BUNDLE_ID") +for var in "${required_vars[@]}"; do + if [ -z "${!var}" ]; then + exit 1 + fi +done + +bun run tauri build + +rm -f certificate.p12 +echo "$APPLE_CERTIFICATE" | base64 --decode > certificate.p12 2>/dev/null +security import certificate.p12 -P "$APPLE_CERTIFICATE_PASSWORD" -A 2>/dev/null + +SIGNING_IDENTITY=$(security find-identity -v -p codesigning | grep "Apple Development" | head -1 | awk -F '"' '{print $2}') + +if [ -z "$SIGNING_IDENTITY" ]; then + exit 1 +fi + +codesign --force --options runtime --sign "$SIGNING_IDENTITY" src-tauri/target/release/bundle/macos/*.app 2>/dev/null + +rm -f certificate.p12 + +hdiutil create -volname "Qopy" -srcfolder src-tauri/target/release/bundle/dmg -ov -format UDZO Qopy.dmg + +codesign --force --sign "$APPLE_CERTIFICATE" Qopy.dmg 2>/dev/null + +xcrun notarytool submit Qopy.dmg --apple-id "$APPLE_ID" --password "$APPLE_ID_PASSWORD" --team-id "$APPLE_CERTIFICATE" --wait + +xcrun stapler staple Qopy.dmg + +exit 0 From 0980b95b72debdd73179079cc1255e8af1e9779e Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 01:17:39 +1000 Subject: [PATCH 44/97] refactor: refactor hotkey management to use a single state structure with Arc and Mutex --- src-tauri/src/api/hotkeys.rs | 77 ++++++++++++++++-------------------- 1 file changed, 34 insertions(+), 43 deletions(-) diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index a530b27..87cb11a 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -6,20 +6,21 @@ use global_hotkey::{ GlobalHotKeyManager, HotKeyState, }; +use parking_lot::Mutex; use std::str::FromStr; -use std::sync::Mutex; +use std::sync::Arc; use tauri::{ AppHandle, Manager, Listener }; use tauri_plugin_aptabase::EventTracker; -use tokio::sync::OnceCell; -static HOTKEY_MANAGER: OnceCell>> = OnceCell::const_new(); -static REGISTERED_HOTKEY: OnceCell>> = OnceCell::const_new(); +#[derive(Default)] +struct HotkeyState { + manager: Option, + registered_hotkey: Option, +} pub fn setup(app_handle: tauri::AppHandle) { - let _ = HOTKEY_MANAGER.set(Mutex::new(None)); - let _ = REGISTERED_HOTKEY.set(Mutex::new(None)); - - let app_handle_clone = app_handle.clone(); + let state = Arc::new(Mutex::new(HotkeyState::default())); + let manager = match GlobalHotKeyManager::new() { Ok(manager) => manager, Err(err) => { @@ -28,49 +29,48 @@ pub fn setup(app_handle: tauri::AppHandle) { } }; - if let Some(hotkey_manager) = HOTKEY_MANAGER.get() { - let mut manager_guard = hotkey_manager.lock().unwrap(); - *manager_guard = Some(manager); + { + let mut hotkey_state = state.lock(); + hotkey_state.manager = Some(manager); } let rt = app_handle.state::(); let initial_keybind = rt - .block_on(crate::db::settings::get_keybind(app_handle_clone.clone())) + .block_on(crate::db::settings::get_keybind(app_handle.clone())) .expect("Failed to get initial keybind"); - if let Err(e) = register_shortcut(&initial_keybind) { + if let Err(e) = register_shortcut(&state, &initial_keybind) { eprintln!("Error registering initial shortcut: {:?}", e); } - setup_event_listeners(&app_handle); - setup_hotkey_receiver(app_handle); -} - -fn setup_event_listeners(app_handle: &AppHandle) { + let state_clone = state.clone(); app_handle.listen("update-shortcut", move |event| { let payload_str = event.payload().replace("\\\"", "\""); let trimmed_str = payload_str.trim_matches('"'); - unregister_current_hotkey(); + unregister_current_hotkey(&state_clone); let payload: Vec = serde_json::from_str(trimmed_str).unwrap_or_default(); - if let Err(e) = register_shortcut(&payload) { + if let Err(e) = register_shortcut(&state_clone, &payload) { eprintln!("Error re-registering shortcut: {:?}", e); } }); + let state_clone = state.clone(); app_handle.listen("save_keybind", move |event| { let payload_str = event.payload().to_string(); - unregister_current_hotkey(); + unregister_current_hotkey(&state_clone); let payload: Vec = serde_json::from_str(&payload_str).unwrap_or_default(); - if let Err(e) = register_shortcut(&payload) { + if let Err(e) = register_shortcut(&state_clone, &payload) { eprintln!("Error registering saved shortcut: {:?}", e); } }); + + setup_hotkey_receiver(app_handle); } fn setup_hotkey_receiver(app_handle: AppHandle) { - tauri::async_runtime::spawn(async move { + std::thread::spawn(move || { loop { match GlobalHotKeyEvent::receiver().recv() { Ok(event) => { @@ -85,32 +85,23 @@ fn setup_hotkey_receiver(app_handle: AppHandle) { }); } -fn unregister_current_hotkey() { - if let Some(registered) = REGISTERED_HOTKEY.get() { - if let Some(old_hotkey) = registered.lock().unwrap().take() { - if let Some(manager) = HOTKEY_MANAGER.get() { - if let Some(manager) = manager.lock().unwrap().as_ref() { - let _ = manager.unregister(old_hotkey); - } - } +fn unregister_current_hotkey(state: &Arc>) { + let mut hotkey_state = state.lock(); + if let Some(old_hotkey) = hotkey_state.registered_hotkey.take() { + if let Some(manager) = &hotkey_state.manager { + let _ = manager.unregister(old_hotkey); } } } -fn register_shortcut(shortcut: &[String]) -> Result<(), Box> { +fn register_shortcut(state: &Arc>, shortcut: &[String]) -> Result<(), Box> { let hotkey = parse_hotkey(shortcut)?; + let mut hotkey_state = state.lock(); - if let Some(manager) = HOTKEY_MANAGER.get() { - let manager_guard = manager.lock().unwrap(); - if let Some(manager) = manager_guard.as_ref() { - manager.register(hotkey.clone())?; - if let Some(registered) = REGISTERED_HOTKEY.get() { - *registered.lock().unwrap() = Some(hotkey); - } - Ok(()) - } else { - Err("Hotkey manager not initialized".into()) - } + if let Some(manager) = &hotkey_state.manager { + manager.register(hotkey.clone())?; + hotkey_state.registered_hotkey = Some(hotkey); + Ok(()) } else { Err("Hotkey manager not initialized".into()) } From 52bf66984b4d359f80a05a6832b60d5c66a62900 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 11 Jan 2025 01:17:45 +1000 Subject: [PATCH 45/97] fix(deps): update wrdu-keyboard to version 3.2.0 and add parking_lot dependency --- package.json | 5 +- patches/wrdu-keyboard@3.1.0.patch | 190 ------------------------------ src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 1 + 4 files changed, 3 insertions(+), 194 deletions(-) delete mode 100644 patches/wrdu-keyboard@3.1.0.patch diff --git a/package.json b/package.json index bdf0440..ba35d74 100644 --- a/package.json +++ b/package.json @@ -20,12 +20,9 @@ "sass-embedded": "1.83.1", "uuid": "11.0.5", "vue": "3.5.13", - "wrdu-keyboard": "^3.1.0" + "wrdu-keyboard": "3.2.0" }, "overrides": { "chokidar": "^3.6.0" - }, - "patchedDependencies": { - "wrdu-keyboard@3.1.0": "patches/wrdu-keyboard@3.1.0.patch" } } diff --git a/patches/wrdu-keyboard@3.1.0.patch b/patches/wrdu-keyboard@3.1.0.patch deleted file mode 100644 index ffc2318..0000000 --- a/patches/wrdu-keyboard@3.1.0.patch +++ /dev/null @@ -1,190 +0,0 @@ -diff --git a/node_modules/wrdu-keyboard/.DS_Store b/.DS_Store -new file mode 100644 -index 0000000000000000000000000000000000000000..5008ddfcf53c02e82d7eee2e57c38e5672ef89f6 -Binary files /dev/null and b/.DS_Store differ -diff --git a/dist/runtime/composables.d.ts b/dist/runtime/composables.d.ts -index fb2e51205317d4bf2530528da750cd18f6486022..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 ---- a/dist/runtime/composables.d.ts -+++ b/dist/runtime/composables.d.ts -@@ -1,3 +0,0 @@ --import type { Keyboard } from './keyboard.js'; --export * from './keyboard.js'; --export declare const useKeyboard: () => Keyboard; -diff --git a/dist/runtime/keyboard.d.ts b/dist/runtime/keyboard.d.ts -index 1cd64674456f1598acb93ed77d4f84ff4823ba40..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 ---- a/dist/runtime/keyboard.d.ts -+++ b/dist/runtime/keyboard.d.ts -@@ -1,25 +0,0 @@ --import { Key } from './types/keys.js'; --import { type Plugin } from '#app'; --type Handler = (event: KeyboardEvent) => void; --type Config = { -- once?: boolean; -- prevent?: boolean; --}; --type PublicConfig = Omit; --type New = (keys: Key[], handler: Handler, config?: PublicConfig) => void; --export interface Keyboard { -- init: () => void; -- stop: () => void; -- clear: () => void; -- down: New; -- up: New; -- prevent: { -- down: New; -- up: New; -- }; --} --type KeyboardPlugin = Plugin<{ -- keyboard: Keyboard; --}>; --declare const keyboard: KeyboardPlugin; --export default keyboard; -diff --git a/dist/runtime/keyboard.js b/dist/runtime/keyboard.js -index e49c3d115e570a91ad814e4fba013d61a6399637..a2e390e66b59c1af3506d7394ce6a6600f6d205e 100644 ---- a/dist/runtime/keyboard.js -+++ b/dist/runtime/keyboard.js -@@ -12,15 +12,22 @@ const onKeydown = (event) => { - const keyString = getKeyString(pressedArray); - if (handlers.down[keyString]) { - handlers.down[keyString].forEach((eventHandler) => { -- if (eventHandler.prevent) { -- event.preventDefault(); -- } -+ if (eventHandler.prevent) event.preventDefault(); - eventHandler.handler(event); - if (eventHandler.once) { - handlers.down[keyString] = handlers.down[keyString].filter((h) => h !== eventHandler); - } - }); - } -+ if (handlers.down["All"]) { -+ handlers.down["All"].forEach((eventHandler) => { -+ if (eventHandler.prevent) event.preventDefault(); -+ eventHandler.handler(event); -+ if (eventHandler.once) { -+ handlers.down["All"] = handlers.down["All"].filter((h) => h !== eventHandler); -+ } -+ }); -+ } - }; - const onKeyup = (event) => { - const releasedArray = Array.from(pressedKeys); -diff --git a/dist/runtime/types/keys.d.ts b/dist/runtime/types/keys.d.ts -index f48fa8415a91f17626f9aaa0a03f14426785744b..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 100644 ---- a/dist/runtime/types/keys.d.ts -+++ b/dist/runtime/types/keys.d.ts -@@ -1,113 +0,0 @@ --export declare const Key: { -- Backspace: string; -- Tab: string; -- Enter: string; -- LeftShift: string; -- RightShift: string; -- LeftControl: string; -- RightControl: string; -- LeftAlt: string; -- RightAlt: string; -- Pause: string; -- CapsLock: string; -- Escape: string; -- Space: string; -- PageUp: string; -- PageDown: string; -- End: string; -- Home: string; -- LeftArrow: string; -- UpArrow: string; -- RightArrow: string; -- DownArrow: string; -- PrintScreen: string; -- Insert: string; -- Delete: string; -- Zero: string; -- One: string; -- Two: string; -- Three: string; -- Four: string; -- Five: string; -- Six: string; -- Seven: string; -- Eight: string; -- Nine: string; -- A: string; -- B: string; -- C: string; -- D: string; -- E: string; -- F: string; -- G: string; -- H: string; -- I: string; -- J: string; -- K: string; -- L: string; -- M: string; -- N: string; -- O: string; -- P: string; -- Q: string; -- R: string; -- S: string; -- T: string; -- U: string; -- V: string; -- W: string; -- X: string; -- Y: string; -- Z: string; -- LeftMeta: string; -- RightMeta: string; -- ContextMenu: string; -- NumpadZero: string; -- NumpadOne: string; -- NumpadTwo: string; -- NumpadThree: string; -- NumpadFour: string; -- NumpadFive: string; -- NumpadSix: string; -- NumpadSeven: string; -- NumpadEight: string; -- NumpadNine: string; -- NumpadMultiply: string; -- NumpadAdd: string; -- NumpadSubtract: string; -- NumpadDecimal: string; -- NumpadDivide: string; -- F1: string; -- F2: string; -- F3: string; -- F4: string; -- F5: string; -- F6: string; -- F7: string; -- F8: string; -- F9: string; -- F10: string; -- F11: string; -- F12: string; -- NumLock: string; -- ScrollLock: string; -- VolumeMute: string; -- VolumeDown: string; -- VolumeUp: string; -- MediaPlayer: string; -- LaunchApp1: string; -- LaunchApp2: string; -- Semicolon: string; -- Equal: string; -- Comma: string; -- Minus: string; -- Period: string; -- Slash: string; -- Backquote: string; -- LeftBracket: string; -- Backslash: string; -- RightBracket: string; -- Quote: string; -- All: string; --}; --export type Key = (typeof Key)[keyof typeof Key]; diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 96fdceb..3fc6af5 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -4058,6 +4058,7 @@ dependencies = [ "lazy_static", "log", "meta_fetcher", + "parking_lot", "rand 0.8.5", "rdev", "regex", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d792d17..dc3b86c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -48,6 +48,7 @@ include_dir = "0.7.4" # hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" } applications = { git = "https://github.com/HuakunShen/applications-rs", branch = "dev" } meta_fetcher = "0.1.1" +parking_lot = "0.12.3" [features] custom-protocol = ["tauri/custom-protocol"] From 4458eae1d5e53f1301f3a4868545e0f6e5b613fa Mon Sep 17 00:00:00 2001 From: Frumkin13 <79981355+Frumkin13@users.noreply.github.com> Date: Tue, 21 Jan 2025 20:35:02 +0300 Subject: [PATCH 46/97] Russian Translation Russian Readme file --- README_ru.md | 130 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 README_ru.md diff --git a/README_ru.md b/README_ru.md new file mode 100644 index 0000000..36a2000 --- /dev/null +++ b/README_ru.md @@ -0,0 +1,130 @@ + + + +Qopy + +Простой и исправленный менеджер буфера обмена как для Windows, так и для Linux. + + + Windows (x64) + +• + + Windows (arm64) + + + + Linux (deb) + +• + + Linux (rpm) + +• + + Linux (AppImage) + + + + macOS (Silicon) + +• + + macOS (Intel) + + + +Тестовые версии можно найти тут + + + +[discord »](https://discord.gg/invite/Y7SbYphVw9) + +> \[!IMPORTANT] +> +> **Нажав на звезду**, Вы будете получать все уведомления от Github о новых версиях без задержек \~ ⭐️ + + + Star History + + + + + + + + +[](https://wakatime.com/badge/user/018ce503-097f-4057-9599-db20b190920c/project/fe76359d-56c2-4a13-8413-55207b6ad298) + +## 📋 Что такое Qopy + +Qopy представляет собой исправленный менеджер буфера обмена, разработанный как простая альтернатива стандартному буферу обмена в Windows. Его цель - обеспечить более быструю и надежную работу, предоставляя при этом обширный набор функций по сравнению со своим аналогом в Windows.. + +## 🚧 Дорожная карта +- [ ] [Руководство по установке](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md) +- [ ] Синхронизация между устройствами https://github.com/0PandaDEV/Qopy/issues/8 +- [ ] Настройки https://github.com/0PandaDEV/Qopy/issues/2 +- [x] Метаданные для скопированных элементов https://github.com/0PandaDEV/Qopy/issues/5 +- [ ] Выделение кода https://github.com/0PandaDEV/Qopy/issues/7 +- [ ] Интеграция Streamshare https://github.com/0PandaDEV/Qopy/issues/4 +- [ ] Фильтр типов контента https://github.com/0PandaDEV/Qopy/issues/16 +- [ ] Превью скопированных файлов https://github.com/0PandaDEV/Qopy/issues/15 +- [ ] Конвертация файлов в другие форматы https://github.com/0PandaDEV/Qopy/issues/17 +- [x] Свои горячие клавиши https://github.com/0PandaDEV/Qopy/issues/3 +- [x] Поддержка macOS https://github.com/0PandaDEV/Qopy/issues/13 + +Если у вас есть идеи для функций, которые можно добавить в будущем, пожалуйста, напишите об этом [здесь](https://github.com/0pandadev/Qopy/issues). + +## 📦 Концепты + +Здесь вы можете увидеть несколько концепцов, которые могут быть не реализованы: + + + + + +## ❤️ Пожертвования и Поддержка + +Qopy имеет открытый исходный код и бесплатен для использования. Я ценю пожертвования в поддержку постоянной разработки и улучшений. Ваши взносы являются добровольными и помогают мне улучшить приложение для всех. + + + +## ⌨️ Локальная разработка + +Вы можете использовать GitHub Codespaces для онлайн-разработки: + +[![][codespaces-shield]][codespaces-link] + +Или, чтобы настроить Qopy на вашем компьютере, вам необходимо установить Rust и bun. Затем выполните следующие действия: + +```zsh +git clone https://github.com/0pandadev/Qopy.git +cd Qopy +bun i +bun dev +``` + +> \[!Tip] +> +> Если вы заинтересованы во внесении кода, не стесняйтесь смотреть здесь [Issues](https://github.com/0pandadev/Qopy/issues). + +## 🔨 Сборка для продакшена + +Чтобы собрать для продакшена,просто выполните: + +```zsh +bun build +``` + +> \[!NOTE] +> +> Не волнуйтесь, в конце произойдет сбой, потому что он не сможет обнаружить Приватный ключ, но установочные файлы будут сгенерированы независимо от этого. +> +> Вы можете найти его в `src-tauri/target/release/bundle`. + +## 📝 Лицензия + +Qopy лицнзирован под GPL-3. Смотрите [LICENSE file](./LICENCE) для дополнительной информации. + +[codespaces-link]: https://codespaces.new/0pandadev/Qopy +[codespaces-shield]: https://github.com/codespaces/badge.svg From 12b8f9a49e51da61059eadf4578c87f3479ead4b Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 24 Jan 2025 00:07:33 +1000 Subject: [PATCH 47/97] fix: update dependencies versions in package.json and Cargo.lock files --- package.json | 6 +- src-tauri/Cargo.lock | 157 ++++++++++++++++++++++++------------------- src-tauri/Cargo.toml | 12 ++-- 3 files changed, 97 insertions(+), 78 deletions(-) diff --git a/package.json b/package.json index ba35d74..af86563 100644 --- a/package.json +++ b/package.json @@ -11,13 +11,13 @@ }, "dependencies": { "@tauri-apps/api": "2.2.0", - "@tauri-apps/cli": "2.2.2", + "@tauri-apps/cli": "2.2.5", "@tauri-apps/plugin-autostart": "2.2.0", "@tauri-apps/plugin-os": "2.2.0", - "nuxt": "3.15.1", + "nuxt": "3.15.2", "overlayscrollbars": "2.10.1", "overlayscrollbars-vue": "0.5.9", - "sass-embedded": "1.83.1", + "sass-embedded": "1.83.4", "uuid": "11.0.5", "vue": "3.5.13", "wrdu-keyboard": "3.2.0" diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3fc6af5..f4215d8 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -407,7 +407,7 @@ version = "0.68.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "726e4313eb6ec35d2730258ad4e15b547ee75d6afaa1361a922e78e59b7d8078" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cexpr", "clang-sys", "lazy_static", @@ -453,9 +453,9 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.6.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b048fb63fd8b5923fc5aa7b340d8e156aec7ec02f0c78fa8a6ddc2613f6f71de" +checksum = "8f68f53c83ab957f72c32642f3868eec03eb974d1fb82e453128456482613d36" dependencies = [ "serde", ] @@ -580,7 +580,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8ca26ef0159422fb77631dc9d17b102f253b876fe1586b03b803e63a309b4ee2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cairo-sys-rs", "glib", "libc", @@ -633,12 +633,12 @@ dependencies = [ [[package]] name = "cargo_toml" -version = "0.17.2" +version = "0.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a969e13a7589e9e3e4207e153bae624ade2b5622fb4684a4923b23ec3d57719" +checksum = "5fbd1fe9db3ebf71b89060adaf7b0504c2d6a425cf061313099547e382c2e472" dependencies = [ "serde", - "toml 0.8.2", + "toml 0.8.19", ] [[package]] @@ -786,7 +786,7 @@ version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "cocoa-foundation 0.2.0", "core-foundation 0.10.0", @@ -816,7 +816,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block", "core-foundation 0.10.0", "core-graphics-types 0.2.0", @@ -962,7 +962,7 @@ version = "0.24.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa95a34622365fa5bbf40b20b75dba8dfa8c94c734aea8ac9a5ca38af14316f1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.10.0", "core-graphics-types 0.2.0", "foreign-types 0.5.0", @@ -986,7 +986,7 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3d44a101f213f6c4cdc1853d4b78aef6db6bdfa3468798cc1d9912f4735013eb" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.10.0", "libc", ] @@ -1379,7 +1379,7 @@ dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.2", + "toml 0.8.19", "vswhom", "winreg 0.52.0", ] @@ -1964,7 +1964,7 @@ version = "0.18.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "233daaf6e83ae6a12a52055f568f9d7cf4671dabb78ff9560ab6da230ce00ee5" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "futures-channel", "futures-core", "futures-executor", @@ -1988,7 +1988,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bb0228f477c0900c880fd78c8759b95c7636dbd7842707f49e132378aa2acdc" dependencies = [ "heck 0.4.1", - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro-error", "proc-macro2", "quote", @@ -2640,9 +2640,9 @@ dependencies = [ [[package]] name = "itertools" -version = "0.13.0" +version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "413ee7dfc52ee1a4949ceeb7dbc8a33f2d6c088194d9f922fb8318faf1f01186" +checksum = "2b192c782037fadd9cfa75548310488aabdbf3d2da73885b31bd0abd03351285" dependencies = [ "either", ] @@ -2756,7 +2756,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b750dcadc39a09dbadd74e118f6dd6598df77fa01df0cfcdc52c28dece74528a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "serde", "unicode-segmentation", ] @@ -2868,7 +2868,7 @@ version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c0ff37bd590ca25063e35af745c343cb7a0271906fb7b37e4813e8f79f00268d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "libc", "redox_syscall 0.5.3", ] @@ -2922,9 +2922,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.22" +version = "0.4.25" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7a70ba024b9dc04c27ea2f0c0548feb474ec5c54bba33a7f72f873a39d07b24" +checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" [[package]] name = "loop9" @@ -3112,7 +3112,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "jni-sys", "log", "ndk-sys", @@ -3148,7 +3148,7 @@ version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2eb04e9c688eff1c89d72b407f168cf79bb9e867a9d3323ed6c01519eb9cc053" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "libc", "memoffset", @@ -3287,7 +3287,7 @@ version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "af1844ef2428cc3e1cb900be36181049ef3d3193c63e43026cfe202983b27a56" dependencies = [ - "proc-macro-crate 2.0.2", + "proc-macro-crate 2.0.0", "proc-macro2", "quote", "syn 2.0.87", @@ -3327,7 +3327,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "libc", "objc2", @@ -3343,7 +3343,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -3367,7 +3367,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3409,7 +3409,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "dispatch", "libc", @@ -3434,7 +3434,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3446,7 +3446,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-foundation", @@ -3469,7 +3469,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-cloud-kit", @@ -3501,7 +3501,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-core-location", @@ -3514,7 +3514,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "block2", "objc2", "objc2-app-kit", @@ -3542,7 +3542,7 @@ version = "0.10.66" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9529f4786b70a3e8c61e11179af17ab6188ad8d0ded78c5529441ed39d4bd9c1" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cfg-if", "foreign-types 0.3.2", "libc", @@ -3969,11 +3969,10 @@ dependencies = [ [[package]] name = "proc-macro-crate" -version = "2.0.2" +version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00f26d3400549137f92511a46ac1cd8ce37cb5598a96d382381458b992a5d24" +checksum = "7e8366a6159044a37876a2b9817124296703c586a5c92e2c53751fa06d8d43e8" dependencies = [ - "toml_datetime", "toml_edit 0.20.2", ] @@ -4363,7 +4362,7 @@ version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a908a6e00f1fdd0dfd9c0eb08ce85126f6d8bbda50017e74bc4a4b7d4a926a4" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", ] [[package]] @@ -4561,7 +4560,7 @@ version = "0.38.34" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "70dc5ec042f7a43c4a73241207cecc9873a06d45debb38b329f8541d85c2730f" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "errno", "libc", "linux-raw-sys", @@ -4685,7 +4684,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "core-foundation-sys 0.8.7", "libc", @@ -4786,9 +4785,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.135" +version = "1.0.137" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b0d7ba2887406110130a978386c4e1befb98c674b4fba677954e4db976630d9" +checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" dependencies = [ "itoa 1.0.11", "memchr", @@ -5150,7 +5149,7 @@ checksum = "4560278f0e00ce64938540546f59f590d60beee33fffbd3b9cd47851e5fff233" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "bytes", "chrono", @@ -5194,7 +5193,7 @@ checksum = "c5b98a57f363ed6764d5b3a12bfedf62f07aa16e1856a7ddc2a0bb190a959613" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.6.0", + "bitflags 2.8.0", "byteorder", "chrono", "crc", @@ -5401,7 +5400,7 @@ version = "0.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c879d448e9d986b661742763247d3693ed13609438cf3d006f51f5368a5ba6b" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -5425,7 +5424,7 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.2", + "toml 0.8.19", "version-compare", ] @@ -5435,7 +5434,7 @@ version = "0.31.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "cocoa 0.26.0", "core-foundation 0.10.0", "core-graphics 0.24.0", @@ -5497,9 +5496,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.2.0" +version = "2.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2e2e3349fbb2be7af9fad1b43d61ac83ba55ab48d47fbe1b2732f0c8211610a9" +checksum = "78f6efc261c7905839b4914889a5b25df07f0ff89c63fb4afd6ff8c96af15e4d" dependencies = [ "anyhow", "bytes", @@ -5548,9 +5547,9 @@ dependencies = [ [[package]] name = "tauri-build" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b274ec7239ada504deb615f1c8abd7ba99631e879709e6f10e5d17217058d976" +checksum = "8e950124f6779c6cf98e3260c7a6c8488a74aa6350dd54c6950fdaa349bca2df" dependencies = [ "anyhow", "cargo_toml", @@ -5564,7 +5563,7 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -5632,7 +5631,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.2", + "toml 0.8.19", "walkdir", ] @@ -5720,7 +5719,7 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.3", - "toml 0.8.2", + "toml 0.8.19", "url", "uuid", ] @@ -5760,12 +5759,12 @@ dependencies = [ [[package]] name = "tauri-plugin-prevent-default" -version = "1.0.1" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce34a821424cdb5c74b390ddc8f08774d836030c07ab8dd35bd180690ef1331e" +checksum = "e67a858c8ffe9dcaec78962ed5c33716e0b52542062dfdbb03dff970b8dc2690" dependencies = [ - "bitflags 2.6.0", - "itertools 0.13.0", + "bitflags 2.8.0", + "itertools 0.14.0", "serde", "strum", "tauri", @@ -5897,7 +5896,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.3", - "toml 0.8.2", + "toml 0.8.19", "url", "urlpattern", "uuid", @@ -6156,21 +6155,21 @@ dependencies = [ [[package]] name = "toml" -version = "0.8.2" +version = "0.8.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "185d8ab0dfbb35cf1399a6344d8484209c088f75f8f68230da55d48d95d43e3d" +checksum = "a1ed1f98e3fdc28d6d910e6737ae6ab1a93bf1985935a1193e68f93eeb68d24e" dependencies = [ "serde", "serde_spanned", "toml_datetime", - "toml_edit 0.20.2", + "toml_edit 0.22.22", ] [[package]] name = "toml_datetime" -version = "0.6.3" +version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7cda73e2f1397b1262d6dfdcef8aafae14d1de7748d66822d3bfeeb6d03e5e4b" +checksum = "0dd7358ecb8fc2f8d014bf86f6f638ce72ba252a2c3a2572f2a795f1d23efb41" dependencies = [ "serde", ] @@ -6185,7 +6184,7 @@ dependencies = [ "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.5.40", ] [[package]] @@ -6193,12 +6192,23 @@ name = "toml_edit" version = "0.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "396e4d48bbb2b7554c944bde63101b5ae446cff6ec4a24227428f15eb72ef338" +dependencies = [ + "indexmap 2.3.0", + "toml_datetime", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.22.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae48d6208a266e853d946088ed816055e556cc6028c5e8e2b84d9fa5dd7c7f5" dependencies = [ "indexmap 2.3.0", "serde", "serde_spanned", "toml_datetime", - "winnow", + "winnow 0.6.24", ] [[package]] @@ -6450,9 +6460,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.11.1" +version = "1.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b913a3b5fe84142e269d63cc62b64319ccaf89b748fc31fe025177f767a756c4" +checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" dependencies = [ "getrandom 0.2.15", "serde", @@ -6643,7 +6653,7 @@ version = "0.31.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3f45d1222915ef1fd2057220c1d9d9624b7654443ea35c3877f7a52bd0a5a2d" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "rustix", "wayland-backend", "wayland-scanner", @@ -6655,7 +6665,7 @@ version = "0.32.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b5755d77ae9040bb872a25026555ce4cb0ae75fd923e90d25fba07d81057de0" dependencies = [ - "bitflags 2.6.0", + "bitflags 2.8.0", "wayland-backend", "wayland-client", "wayland-scanner", @@ -7195,6 +7205,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.6.24" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8d71a593cc5c42ad7876e2c1fda56f314f3754c084128833e64f1345ff8a03a" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.10.1" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index dc3b86c..3d71bff 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -7,10 +7,10 @@ edition = "2021" rust-version = "1.70" [build-dependencies] -tauri-build = { version = "2.0.4", features = [] } +tauri-build = { version = "2.0.5", features = [] } [dependencies] -tauri = { version = "2.2.0", features = [ +tauri = { version = "2.2.3", features = [ "macos-private-api", "tray-icon", "image-png" @@ -22,13 +22,13 @@ tauri-plugin-updater = "2.3.1" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" -tauri-plugin-prevent-default = "1.0.1" +tauri-plugin-prevent-default = "1.0.2" tauri-plugin-global-shortcut = "2.2.0" tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.3", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } serde = { version = "1.0.217", features = ["derive"] } tokio = { version = "1.43.0", features = ["full"] } -serde_json = "1.0.135" +serde_json = "1.0.137" rdev = "0.5.3" rand = "0.8.5" base64 = "0.22.1" @@ -41,8 +41,8 @@ lazy_static = "1.5.0" time = "0.3.37" global-hotkey = "0.6.3" chrono = { version = "0.4.39", features = ["serde"] } -log = { version = "0.4.22", features = ["std"] } -uuid = "1.11.1" +log = { version = "0.4.25", features = ["std"] } +uuid = "1.12.1" active-win-pos-rs = "0.9.0" include_dir = "0.7.4" # hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" } From d53d64a732b71b0d018310609a6f3dce14b0f23f Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 16:14:29 +0100 Subject: [PATCH 48/97] chore: update project dependencies and lock files --- .gitignore | 1 + package.json | 4 +- src-tauri/Cargo.lock | 137 +++++++++++++++++++++++++++++++++++++++---- src-tauri/Cargo.toml | 8 +-- 4 files changed, 132 insertions(+), 18 deletions(-) diff --git a/.gitignore b/.gitignore index c000981..1e5c576 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ logs bun.lockb .gitignore .vscode +bun.lock \ No newline at end of file diff --git a/package.json b/package.json index af86563..73d8ae7 100644 --- a/package.json +++ b/package.json @@ -11,10 +11,10 @@ }, "dependencies": { "@tauri-apps/api": "2.2.0", - "@tauri-apps/cli": "2.2.5", + "@tauri-apps/cli": "2.2.7", "@tauri-apps/plugin-autostart": "2.2.0", "@tauri-apps/plugin-os": "2.2.0", - "nuxt": "3.15.2", + "nuxt": "3.15.4", "overlayscrollbars": "2.10.1", "overlayscrollbars-vue": "0.5.9", "sass-embedded": "1.83.4", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index f4215d8..5941134 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1241,6 +1241,15 @@ dependencies = [ "dirs-sys 0.4.1", ] +[[package]] +name = "dirs" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" +dependencies = [ + "dirs-sys 0.5.0", +] + [[package]] name = "dirs-sys" version = "0.3.7" @@ -1248,7 +1257,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.5", "winapi", ] @@ -1260,10 +1269,22 @@ checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" dependencies = [ "libc", "option-ext", - "redox_users", + "redox_users 0.4.5", "windows-sys 0.48.0", ] +[[package]] +name = "dirs-sys" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" +dependencies = [ + "libc", + "option-ext", + "redox_users 0.5.0", + "windows-sys 0.59.0", +] + [[package]] name = "dispatch" version = "0.2.0" @@ -1910,6 +1931,18 @@ dependencies = [ "wasi 0.11.0+wasi-snapshot-preview1", ] +[[package]] +name = "getrandom" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43a49c392881ce6d5c3b8cb70f98717b7c07aabbdff06687b9030dbfbe2725f8" +dependencies = [ + "cfg-if", + "libc", + "wasi 0.13.3+wasi-0.2.2", + "windows-targets 0.52.6", +] + [[package]] name = "gif" version = "0.13.1" @@ -3938,7 +3971,7 @@ version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77957b295656769bb8ad2b6a6b09d897d94f05c41b069aede1fcdaa675eaea04" dependencies = [ - "zerocopy", + "zerocopy 0.7.35", ] [[package]] @@ -4058,7 +4091,7 @@ dependencies = [ "log", "meta_fetcher", "parking_lot", - "rand 0.8.5", + "rand 0.9.0", "rdev", "regex", "reqwest", @@ -4199,6 +4232,17 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3779b94aeb87e8bd4e834cee3650289ee9e0d5677f976ecdb6d219e5f4f6cd94" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.0", + "zerocopy 0.8.14", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -4219,6 +4263,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.0", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -4237,6 +4291,16 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b08f3c9802962f7e1b25113931d94f43ed9725bebc59db9d0c3e9a23b67e15ff" +dependencies = [ + "getrandom 0.3.1", + "zerocopy 0.8.14", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -4376,6 +4440,17 @@ dependencies = [ "thiserror 1.0.63", ] +[[package]] +name = "redox_users" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd6f9d3d47bdd2ad6945c5015a226ec6155d0bcdfd8f7cd29f86b71f8de99d2b" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.3", +] + [[package]] name = "regex" version = "1.11.1" @@ -4785,9 +4860,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.137" +version = "1.0.138" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "930cfb6e6abf99298aaad7d29abbef7a9999a9a8806a40088f55f0dcec03146b" +checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" dependencies = [ "itoa 1.0.11", "memchr", @@ -5496,13 +5571,13 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.2.3" +version = "2.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "78f6efc261c7905839b4914889a5b25df07f0ff89c63fb4afd6ff8c96af15e4d" +checksum = "58a998b6be84104ca05c7e9a21f2180ddec020c8b84ea59a8fc8530a2a19588d" dependencies = [ "anyhow", "bytes", - "dirs 5.0.1", + "dirs 6.0.0", "dunce", "embed_plist", "futures-util", @@ -5793,9 +5868,9 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.3.1" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce2d39224390c41ba544f02b4f1721f42256320b3fb8c371e9425cbddeb4a68c" +checksum = "ad3de2b9203bb00b9765e637a9878aaace34df40ae484878b8cea7a5bd5f9188" dependencies = [ "base64 0.22.1", "dirs 5.0.1", @@ -6548,6 +6623,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasi" +version = "0.13.3+wasi-0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26816d2e1a4a36a2940b96c5296ce403917633dff8f3440e9b236ed6f6bacad2" +dependencies = [ + "wit-bindgen-rt", +] + [[package]] name = "wasite" version = "0.1.0" @@ -7233,6 +7317,15 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "wit-bindgen-rt" +version = "0.33.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3268f3d866458b787f390cf61f4bbb563b922d091359f9608842999eaee3943c" +dependencies = [ + "bitflags 2.8.0", +] + [[package]] name = "write16" version = "1.0.0" @@ -7459,7 +7552,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b9b4fd18abc82b8136838da5d50bae7bdea537c574d8dc1a34ed098d6c166f0" dependencies = [ "byteorder", - "zerocopy-derive", + "zerocopy-derive 0.7.35", +] + +[[package]] +name = "zerocopy" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a367f292d93d4eab890745e75a778da40909cab4d6ff8173693812f79c4a2468" +dependencies = [ + "zerocopy-derive 0.8.14", ] [[package]] @@ -7473,6 +7575,17 @@ dependencies = [ "syn 2.0.87", ] +[[package]] +name = "zerocopy-derive" +version = "0.8.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3931cb58c62c13adec22e38686b559c86a30565e16ad6e8510a337cedc611e1" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "zerofrom" version = "0.1.4" diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 3d71bff..4f2379d 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -10,7 +10,7 @@ rust-version = "1.70" tauri-build = { version = "2.0.5", features = [] } [dependencies] -tauri = { version = "2.2.3", features = [ +tauri = { version = "2.2.5", features = [ "macos-private-api", "tray-icon", "image-png" @@ -18,7 +18,7 @@ tauri = { version = "2.2.3", features = [ tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] } tauri-plugin-autostart = "2.2.0" tauri-plugin-os = "2.2.0" -tauri-plugin-updater = "2.3.1" +tauri-plugin-updater = "2.4.0" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" @@ -28,9 +28,9 @@ tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptaba sqlx = { version = "0.8.3", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } serde = { version = "1.0.217", features = ["derive"] } tokio = { version = "1.43.0", features = ["full"] } -serde_json = "1.0.137" +serde_json = "1.0.138" rdev = "0.5.3" -rand = "0.8.5" +rand = "0.9.0" base64 = "0.22.1" image = "0.25.5" reqwest = { version = "0.12.12", features = ["json", "blocking"] } From 6016fbb4943d1d6f02c2064879d8d8f959476738 Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:38:43 +0100 Subject: [PATCH 49/97] chore: update Cargo dependencies and modify Aptabase plugin configuration --- src-tauri/Cargo.lock | 51 ++++++++++++++++++++++---------------------- src-tauri/Cargo.toml | 11 +++++++--- 2 files changed, 33 insertions(+), 29 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 5941134..ebfbe74 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -1660,9 +1660,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -1675,9 +1675,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -1685,15 +1685,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -1713,9 +1713,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -1732,9 +1732,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", @@ -1743,21 +1743,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -3631,9 +3631,9 @@ dependencies = [ [[package]] name = "os_info" -version = "3.8.2" +version = "3.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae99c7fa6dd38c7cafe1ec085e804f8f555a2f8659b0dbe03f1f9963a9b51092" +checksum = "6e6520c8cc998c5741ee68ec1dc369fc47e5f0ea5320018ecf2a1ccd6328f48b" dependencies = [ "log", "serde", @@ -5462,9 +5462,9 @@ dependencies = [ [[package]] name = "sys-locale" -version = "0.3.1" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e801cf239ecd6ccd71f03d270d67dd53d13e90aab208bf4b8fe4ad957ea949b0" +checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" dependencies = [ "libc", ] @@ -5695,9 +5695,9 @@ dependencies = [ [[package]] name = "tauri-plugin" -version = "2.0.1" +version = "2.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2e6660a409963e4d57b9bfab4addd141eeff41bd3a7fb14e13004a832cf7ef6" +checksum = "5841b9a0200e954ef7457f8d327091424328891e267a97b641dc246cc54d0dec" dependencies = [ "anyhow", "glob", @@ -5713,12 +5713,11 @@ dependencies = [ [[package]] name = "tauri-plugin-aptabase" version = "0.5.1" -source = "git+https://github.com/aptabase/tauri-plugin-aptabase?branch=v2#373abe1954bf20152082e506d36be07727259bb5" dependencies = [ "futures", "log", "os_info", - "rand 0.8.5", + "rand 0.9.0", "reqwest", "serde", "serde_json", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 4f2379d..43259a1 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -13,7 +13,7 @@ tauri-build = { version = "2.0.5", features = [] } tauri = { version = "2.2.5", features = [ "macos-private-api", "tray-icon", - "image-png" + "image-png", ] } tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] } tauri-plugin-autostart = "2.2.0" @@ -24,8 +24,13 @@ tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" tauri-plugin-prevent-default = "1.0.2" tauri-plugin-global-shortcut = "2.2.0" -tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } -sqlx = { version = "0.8.3", features = ["runtime-tokio-native-tls", "sqlite", "chrono"] } +# tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } +tauri-plugin-aptabase = { path = "C:\\Users\\pandadev\\Documents\\Developer\\Rust\\tauri-plugin-aptabase" } +sqlx = { version = "0.8.3", features = [ + "runtime-tokio-native-tls", + "sqlite", + "chrono", +] } serde = { version = "1.0.217", features = ["derive"] } tokio = { version = "1.43.0", features = ["full"] } serde_json = "1.0.138" From 66df55017f5fa255c773b391c86e718ec56e7910 Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:39:04 +0100 Subject: [PATCH 50/97] refactor: simplify random ID generation in history initialization --- src-tauri/src/db/history.rs | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/src-tauri/src/db/history.rs b/src-tauri/src/db/history.rs index 5ee32e6..3b0a4b2 100644 --- a/src-tauri/src/db/history.rs +++ b/src-tauri/src/db/history.rs @@ -1,23 +1,26 @@ use crate::utils::types::{ ContentType, HistoryItem }; use base64::{ engine::general_purpose::STANDARD, Engine }; -use rand::distributions::Alphanumeric; -use rand::{ thread_rng, Rng }; +use rand::{ rng, Rng }; +use rand::distr::Alphanumeric; use sqlx::{ Row, SqlitePool }; use std::fs; use tauri_plugin_aptabase::EventTracker; pub async fn initialize_history(pool: &SqlitePool) -> Result<(), Box> { - let id: String = thread_rng().sample_iter(&Alphanumeric).take(16).map(char::from).collect(); + let id: String = rng() + .sample_iter(&Alphanumeric) + .take(16) + .map(char::from) + .collect(); - sqlx - ::query( - "INSERT INTO history (id, source, content_type, content, timestamp) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)" - ) - .bind(id) - .bind("System") - .bind("text") - .bind("Welcome to your clipboard history!") - .execute(pool).await?; + sqlx::query( + "INSERT INTO history (id, source, content_type, content, timestamp) VALUES (?, ?, ?, ?, CURRENT_TIMESTAMP)" + ) + .bind(id) + .bind("System") + .bind("text") + .bind("Welcome to your clipboard history!") + .execute(pool).await?; Ok(()) } From 2d3732b2f2557d6d3b4d5896596957299b04c95c Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:39:10 +0100 Subject: [PATCH 51/97] fix: improve thread safety in hotkey management --- src-tauri/src/api/hotkeys.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src-tauri/src/api/hotkeys.rs b/src-tauri/src/api/hotkeys.rs index 87cb11a..6152ea0 100644 --- a/src-tauri/src/api/hotkeys.rs +++ b/src-tauri/src/api/hotkeys.rs @@ -18,9 +18,10 @@ struct HotkeyState { registered_hotkey: Option, } +unsafe impl Send for HotkeyState {} + pub fn setup(app_handle: tauri::AppHandle) { let state = Arc::new(Mutex::new(HotkeyState::default())); - let manager = match GlobalHotKeyManager::new() { Ok(manager) => manager, Err(err) => { @@ -43,7 +44,7 @@ pub fn setup(app_handle: tauri::AppHandle) { eprintln!("Error registering initial shortcut: {:?}", e); } - let state_clone = state.clone(); + let state_clone = Arc::clone(&state); app_handle.listen("update-shortcut", move |event| { let payload_str = event.payload().replace("\\\"", "\""); let trimmed_str = payload_str.trim_matches('"'); @@ -55,7 +56,7 @@ pub fn setup(app_handle: tauri::AppHandle) { } }); - let state_clone = state.clone(); + let state_clone = Arc::clone(&state); app_handle.listen("save_keybind", move |event| { let payload_str = event.payload().to_string(); unregister_current_hotkey(&state_clone); From 81db3e5f7930844d99c7a4f3c90a2125a27cd359 Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:45:49 +0100 Subject: [PATCH 52/97] fix: aptabase plugin dependency not found --- src-tauri/Cargo.lock | 1 + src-tauri/Cargo.toml | 3 +-- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index ebfbe74..2c96021 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -5713,6 +5713,7 @@ dependencies = [ [[package]] name = "tauri-plugin-aptabase" version = "0.5.1" +source = "git+https://github.com/0PandaDEV/tauri-plugin-aptabase?branch=v2#c843c6b5b814fcc9daeb3f7fe4c67276c38bc6dd" dependencies = [ "futures", "log", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 43259a1..ec6bda7 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -24,8 +24,7 @@ tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" tauri-plugin-prevent-default = "1.0.2" tauri-plugin-global-shortcut = "2.2.0" -# tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } -tauri-plugin-aptabase = { path = "C:\\Users\\pandadev\\Documents\\Developer\\Rust\\tauri-plugin-aptabase" } +tauri-plugin-aptabase = { git = "https://github.com/0PandaDEV/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.3", features = [ "runtime-tokio-native-tls", "sqlite", From 761914637cd99f28b1c67a92b043ffd550a66096 Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Wed, 29 Jan 2025 17:46:02 +0100 Subject: [PATCH 53/97] fix: link and color types cannot be pasted --- src-tauri/src/api/clipboard.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src-tauri/src/api/clipboard.rs b/src-tauri/src/api/clipboard.rs index 381a7d9..f06881c 100644 --- a/src-tauri/src/api/clipboard.rs +++ b/src-tauri/src/api/clipboard.rs @@ -33,6 +33,8 @@ pub async fn write_and_paste( match content_type.as_str() { "text" => clipboard.write_text(content).map_err(|e| e.to_string())?, + "link" => clipboard.write_text(content).map_err(|e| e.to_string())?, + "color" => clipboard.write_text(content).map_err(|e| e.to_string())?, "image" => { clipboard.write_image_base64(content).map_err(|e| e.to_string())?; } From a77f69567bfd5243a51677bbc59f903f5f225561 Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Fri, 31 Jan 2025 17:46:27 +0100 Subject: [PATCH 54/97] chore: fix aptabase package source --- src-tauri/Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index ec6bda7..0e0a0bf 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -24,7 +24,7 @@ tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" tauri-plugin-prevent-default = "1.0.2" tauri-plugin-global-shortcut = "2.2.0" -tauri-plugin-aptabase = { git = "https://github.com/0PandaDEV/tauri-plugin-aptabase", branch = "v2" } +tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.3", features = [ "runtime-tokio-native-tls", "sqlite", From 540c9f44fa9971b6a79323b132656dead5d5aad9 Mon Sep 17 00:00:00 2001 From: Denisskas <110352433+Denisskas@users.noreply.github.com> Date: Mon, 10 Feb 2025 00:33:01 +0400 Subject: [PATCH 55/97] Update README_ru.md --- README_ru.md | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/README_ru.md b/README_ru.md index 36a2000..e9f63ad 100644 --- a/README_ru.md +++ b/README_ru.md @@ -58,20 +58,20 @@ ## 📋 Что такое Qopy -Qopy представляет собой исправленный менеджер буфера обмена, разработанный как простая альтернатива стандартному буферу обмена в Windows. Его цель - обеспечить более быструю и надежную работу, предоставляя при этом обширный набор функций по сравнению со своим аналогом в Windows.. +Qopy представляет собой исправленный менеджер буфера обмена, разработанный как простая альтернатива стандартному буферу обмена в Windows. Его цель - обеспечить более быструю и надежную работу, предоставляя при этом обширный набор функций по сравнению со своим аналогом в Windows. ## 🚧 Дорожная карта - [ ] [Руководство по установке](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md) -- [ ] Синхронизация между устройствами https://github.com/0PandaDEV/Qopy/issues/8 +- [ ] Синхронизация буфера обмена между устройствами https://github.com/0PandaDEV/Qopy/issues/8 - [ ] Настройки https://github.com/0PandaDEV/Qopy/issues/2 - [x] Метаданные для скопированных элементов https://github.com/0PandaDEV/Qopy/issues/5 - [ ] Выделение кода https://github.com/0PandaDEV/Qopy/issues/7 - [ ] Интеграция Streamshare https://github.com/0PandaDEV/Qopy/issues/4 - [ ] Фильтр типов контента https://github.com/0PandaDEV/Qopy/issues/16 -- [ ] Превью скопированных файлов https://github.com/0PandaDEV/Qopy/issues/15 +- [ ] Превью для скопированных файлов https://github.com/0PandaDEV/Qopy/issues/15 - [ ] Конвертация файлов в другие форматы https://github.com/0PandaDEV/Qopy/issues/17 -- [x] Свои горячие клавиши https://github.com/0PandaDEV/Qopy/issues/3 -- [x] Поддержка macOS https://github.com/0PandaDEV/Qopy/issues/13 +- [x] Опция для пользовательской привязки клавиш https://github.com/0PandaDEV/Qopy/issues/3 +- [x] Поддержка macOS https://github.com/0PandaDEV/Qopy/issues/13 Если у вас есть идеи для функций, которые можно добавить в будущем, пожалуйста, напишите об этом [здесь](https://github.com/0pandadev/Qopy/issues). @@ -124,7 +124,7 @@ bun build ## 📝 Лицензия -Qopy лицнзирован под GPL-3. Смотрите [LICENSE file](./LICENCE) для дополнительной информации. +Qopy лицензирован под GPL-3. Смотрите [LICENSE file](./LICENCE) для дополнительной информации. [codespaces-link]: https://codespaces.new/0pandadev/Qopy [codespaces-shield]: https://github.com/codespaces/badge.svg From 1cb993616c43e1c1747956e22b6e9b3741674552 Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Thu, 13 Feb 2025 23:39:43 +0100 Subject: [PATCH 56/97] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 3d76477..cb7d09a 100644 --- a/README.md +++ b/README.md @@ -63,7 +63,7 @@ Qopy is a fixed clipboard manager designed as a simple alternative to the standa ## 🚧 Roadmap - [ ] [Setup guide](https://github.com/0PandaDEV/Qopy/blob/main/GET_STARTED.md) - [ ] Sync Clipboard across devices https://github.com/0PandaDEV/Qopy/issues/8 -- [ ] Settings https://github.com/0PandaDEV/Qopy/issues/2 +- [x] Settings https://github.com/0PandaDEV/Qopy/issues/2 - [x] Metadata for copied items https://github.com/0PandaDEV/Qopy/issues/5 - [ ] Code highlighting https://github.com/0PandaDEV/Qopy/issues/7 - [ ] Streamshare integration https://github.com/0PandaDEV/Qopy/issues/4 From a3145c2572f67f80b0280fc0a9ec35c975cc62d3 Mon Sep 17 00:00:00 2001 From: pandadev <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 14 Feb 2025 17:54:02 +0100 Subject: [PATCH 57/97] chore: update dependencies and Rust crate versions --- package.json | 4 +- src-tauri/Cargo.lock | 106 +++++++++++++++++++++++++++++++++---------- src-tauri/Cargo.toml | 6 +-- 3 files changed, 86 insertions(+), 30 deletions(-) diff --git a/package.json b/package.json index 73d8ae7..52610ce 100644 --- a/package.json +++ b/package.json @@ -15,9 +15,9 @@ "@tauri-apps/plugin-autostart": "2.2.0", "@tauri-apps/plugin-os": "2.2.0", "nuxt": "3.15.4", - "overlayscrollbars": "2.10.1", + "overlayscrollbars": "2.11.0", "overlayscrollbars-vue": "0.5.9", - "sass-embedded": "1.83.4", + "sass-embedded": "1.85.0", "uuid": "11.0.5", "vue": "3.5.13", "wrdu-keyboard": "3.2.0" diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2c96021..2b5d4bd 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -481,6 +481,25 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-sys" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae85a0696e7ea3b835a453750bf002770776609115e6d25c6d2ff28a8200f7e7" +dependencies = [ + "objc-sys", +] + +[[package]] +name = "block2" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f" +dependencies = [ + "block-sys", + "objc2", +] + [[package]] name = "block2" version = "0.5.1" @@ -2402,6 +2421,16 @@ dependencies = [ "png", ] +[[package]] +name = "icrate" +version = "0.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" +dependencies = [ + "block2 0.4.0", + "objc2", +] + [[package]] name = "icu_collections" version = "1.5.0" @@ -3361,7 +3390,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e4e89ad9e3d7d297152b17d39ed92cd50ca8063a89a9fa569046d41568891eff" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "libc", "objc2", "objc2-core-data", @@ -3377,7 +3406,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -3389,7 +3418,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -3401,7 +3430,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -3412,7 +3441,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -3424,7 +3453,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-contacts", "objc2-foundation", @@ -3443,7 +3472,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ee638a5da3799329310ad4cfa62fbf045d5f56e3ef5ba4149e7452dcf89d5a8" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "dispatch", "libc", "objc2", @@ -3455,7 +3484,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -3468,11 +3497,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] +[[package]] +name = "objc2-osa-kit" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6788b04a18ea31e3dc3ab256b8546639e5bbae07c1a0dc4ea8615252bc6aee9a" +dependencies = [ + "bitflags 2.8.0", + "objc2", + "objc2-app-kit", + "objc2-foundation", +] + [[package]] name = "objc2-quartz-core" version = "0.2.2" @@ -3480,7 +3521,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", "objc2-metal", @@ -3503,7 +3544,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-cloud-kit", "objc2-core-data", @@ -3523,7 +3564,7 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" dependencies = [ - "block2", + "block2 0.5.1", "objc2", "objc2-foundation", ] @@ -3535,7 +3576,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-core-location", "objc2-foundation", @@ -3548,7 +3589,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" dependencies = [ "bitflags 2.8.0", - "block2", + "block2 0.5.1", "objc2", "objc2-app-kit", "objc2-foundation", @@ -3640,6 +3681,20 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "osakit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35366a452fce3f8947eb2f33226a133aaf0cacedef2af67ade348d58be7f85d0" +dependencies = [ + "icrate", + "objc2-foundation", + "objc2-osa-kit", + "serde", + "serde_json", + "thiserror 1.0.63", +] + [[package]] name = "pango" version = "0.18.3" @@ -4545,7 +4600,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8af382a047821a08aa6bfc09ab0d80ff48d45d8726f7cd8e44891f7cb4a4278e" dependencies = [ "ashpd", - "block2", + "block2 0.5.1", "glib-sys", "gobject-sys", "gtk-sys", @@ -5713,7 +5768,7 @@ dependencies = [ [[package]] name = "tauri-plugin-aptabase" version = "0.5.1" -source = "git+https://github.com/0PandaDEV/tauri-plugin-aptabase?branch=v2#c843c6b5b814fcc9daeb3f7fe4c67276c38bc6dd" +source = "git+https://github.com/aptabase/tauri-plugin-aptabase?branch=v2#8085ab1e03707236bae8865fedc5114631bdbb2f" dependencies = [ "futures", "log", @@ -5834,9 +5889,9 @@ dependencies = [ [[package]] name = "tauri-plugin-prevent-default" -version = "1.0.2" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e67a858c8ffe9dcaec78962ed5c33716e0b52542062dfdbb03dff970b8dc2690" +checksum = "f193196a5c3cd34c719b67ecc002763047717cf3d74f8f1961b5fdfdd4212664" dependencies = [ "bitflags 2.8.0", "itertools 0.14.0", @@ -5868,17 +5923,18 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ad3de2b9203bb00b9765e637a9878aaace34df40ae484878b8cea7a5bd5f9188" +checksum = "ebf3da08c36fb03c98c76e5563d4e74d9a590df0f40978cbe07f39cb52833f7c" dependencies = [ "base64 0.22.1", - "dirs 5.0.1", + "dirs 6.0.0", "flate2", "futures-util", "http", "infer", "minisign-verify", + "osakit", "percent-encoding", "reqwest", "semver", @@ -6535,11 +6591,11 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.12.1" +version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3758f5e68192bb96cc8f9b7e2c2cfdabb435499a28499a42f8f984092adad4b" +checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.1", "serde", ] @@ -7345,7 +7401,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e644bf458e27b11b0ecafc9e5633d1304fdae82baca1d42185669752fe6ca4f" dependencies = [ "base64 0.22.1", - "block2", + "block2 0.5.1", "cookie", "crossbeam-channel", "dpi", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 0e0a0bf..6159dcd 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -18,11 +18,11 @@ tauri = { version = "2.2.5", features = [ tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] } tauri-plugin-autostart = "2.2.0" tauri-plugin-os = "2.2.0" -tauri-plugin-updater = "2.4.0" +tauri-plugin-updater = "2.5.0" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" -tauri-plugin-prevent-default = "1.0.2" +tauri-plugin-prevent-default = "1.1.0" tauri-plugin-global-shortcut = "2.2.0" tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.3", features = [ @@ -46,7 +46,7 @@ time = "0.3.37" global-hotkey = "0.6.3" chrono = { version = "0.4.39", features = ["serde"] } log = { version = "0.4.25", features = ["std"] } -uuid = "1.12.1" +uuid = "1.13.1" active-win-pos-rs = "0.9.0" include_dir = "0.7.4" # hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" } From d47bd34a045e3561fd0f0a8284448cbde7044520 Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Fri, 14 Feb 2025 22:36:47 +0100 Subject: [PATCH 58/97] Update README.md --- README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index cb7d09a..4653349 100644 --- a/README.md +++ b/README.md @@ -46,10 +46,9 @@ The fixed and simple clipboard manager for both Windows and Linux. Star History - + - - + From 57b8158c04156edf7d87ba8b33ec411d888730c5 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 15:25:38 +0100 Subject: [PATCH 59/97] fix: wrong secret signing key name for macos --- .github/workflows/build.yml | 2 +- .github/workflows/release.yml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 4c23943..2b6f9b3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -85,7 +85,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} APPLE_SIGNING_IDENTITY: ${{ env.CERT_ID }} with: diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 479bc4a..b3c8847 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -79,7 +79,7 @@ jobs: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }} APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} - TAURI_PRIVATE_KEY: ${{ secrets.TAURI_PRIVATE_KEY }} + TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_KEY_PASSWORD: ${{ secrets.TAURI_KEY_PASSWORD }} with: args: ${{ matrix.args }} From 237d3d099686369688198a340f840faddce6d4fd Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 17:30:47 +0100 Subject: [PATCH 60/97] chore: optimize macOS build workflow configuration --- .github/workflows/build.yml | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b6f9b3..68a0afd 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,18 +18,12 @@ jobs: - uses: actions/checkout@v4 - name: Get version id: get_version - run: echo "VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")" >> $GITHUB_OUTPUT + run: echo "VERSION=$(node -p \"require('./src-tauri/tauri.conf.json').version\")" >> $GITHUB_OUTPUT build-macos: needs: prepare - strategy: - matrix: - include: - - args: "--target aarch64-apple-darwin" - arch: "silicon" - - args: "--target x86_64-apple-darwin" - arch: "intel" runs-on: macos-latest + timeout-minutes: 30 env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} @@ -39,7 +33,7 @@ jobs: - name: Redact Sensitive Information run: | function redact_output { - sed -e "s/${{ secrets.REDACT_PATTERN }}/REDACTED/g" + sed -e "s/${{ secrets.APPLE_ID }}/REDACTED/g;s/${{ secrets.APPLE_ID_PASSWORD }}/REDACTED/g;s/${{ secrets.APPLE_CERTIFICATE }}/REDACTED/g;s/${{ secrets.APPLE_CERTIFICATE_PASSWORD }}/REDACTED/g;s/${{ secrets.KEYCHAIN_PASSWORD }}/REDACTED/g;s/${{ secrets.PAT }}/REDACTED/g;s/${{ secrets.TAURI_SIGNING_PRIVATE_KEY }}/REDACTED/g" } exec > >(redact_output) 2>&1 - uses: actions/setup-node@v4 @@ -94,7 +88,7 @@ jobs: if: failure() run: | echo "Attempting manual signing:" - codesign --force --options runtime --sign "$CERT_ID" --entitlements src-tauri/entitlements.plist src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Qopy.app + timeout 300 codesign --force --options runtime --sign "$CERT_ID" --entitlements src-tauri/entitlements.plist src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Qopy.app echo "Verifying signature:" codesign -dv --verbose=4 src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Qopy.app | sed 's/.*Authority=.*/Authority=REDACTED/' - name: Rename and Publish macOS Artifacts From 31d894ea41c772f54a3800ceaf6e9c0540e5cfcf Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 17:31:55 +0100 Subject: [PATCH 61/97] fix: remove unnecessary quotes in version extraction script --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 68a0afd..9869be6 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - name: Get version id: get_version - run: echo "VERSION=$(node -p \"require('./src-tauri/tauri.conf.json').version\")" >> $GITHUB_OUTPUT + run: echo "VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")" >> $GITHUB_OUTPUT build-macos: needs: prepare From 48507096d522e0e17a994b19dbf89ec78da69245 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:05:12 +0100 Subject: [PATCH 62/97] feat: add multi-architecture support for macOS builds --- .github/workflows/build.yml | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 9869be6..0cea7b1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,12 +18,19 @@ jobs: - uses: actions/checkout@v4 - name: Get version id: get_version - run: echo "VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")" >> $GITHUB_OUTPUT + run: echo "VERSION=$(node -p \"require('./src-tauri/tauri.conf.json').version\")" >> $GITHUB_OUTPUT build-macos: needs: prepare runs-on: macos-latest timeout-minutes: 30 + strategy: + matrix: + include: + - args: "--target aarch64-apple-darwin" + arch: "arm64" + - args: "--target x86_64-apple-darwin" + arch: "x64" env: APPLE_ID: ${{ secrets.APPLE_ID }} APPLE_ID_PASSWORD: ${{ secrets.APPLE_ID_PASSWORD }} @@ -88,24 +95,31 @@ jobs: if: failure() run: | echo "Attempting manual signing:" - timeout 300 codesign --force --options runtime --sign "$CERT_ID" --entitlements src-tauri/entitlements.plist src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Qopy.app + timeout 300 codesign --force --options runtime --sign "$CERT_ID" --entitlements src-tauri/entitlements.plist src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/Qopy.app echo "Verifying signature:" - codesign -dv --verbose=4 src-tauri/target/aarch64-apple-darwin/release/bundle/macos/Qopy.app | sed 's/.*Authority=.*/Authority=REDACTED/' + codesign -dv --verbose=4 "src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/Qopy.app" | sed 's/.*Authority=.*/Authority=REDACTED/' + - name: Set architecture label + run: | + if [[ "${{ matrix.args }}" == "--target aarch64-apple-darwin" ]]; then + echo "ARCH_LABEL=aarch64-apple-darwin" >> $GITHUB_ENV + else + echo "ARCH_LABEL=x86_64-apple-darwin" >> $GITHUB_ENV + fi - name: Rename and Publish macOS Artifacts run: | - mv src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/*.dmg src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.dmg - mv src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/*.app.tar.gz src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.app.tar.gz - mv src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/*.app.tar.gz.sig src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.app.tar.gz.sig + mv src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/dmg/*.dmg src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/dmg/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.dmg + mv src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/*.app.tar.gz src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.app.tar.gz + mv src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/*.app.tar.gz.sig src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/Qopy-${{ needs.prepare.outputs.version }}_${{ matrix.arch }}.app.tar.gz.sig - uses: actions/upload-artifact@v4 with: name: macos-dmg-${{ matrix.arch }} - path: src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/dmg/*.dmg + path: "src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/dmg/*.dmg" - uses: actions/upload-artifact@v4 with: name: updater-macos-${{ matrix.arch }} path: | - src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/*.app.tar.gz - src-tauri/target/${{ matrix.args == '--target aarch64-apple-darwin' && 'aarch64-apple-darwin' || 'x86_64-apple-darwin' }}/release/bundle/macos/*.app.tar.gz.sig + src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/*.app.tar.gz + src-tauri/target/${{ env.ARCH_LABEL }}/release/bundle/macos/*.app.tar.gz.sig build-windows: needs: prepare From 0b0c613319769c61037a5954e08181cd6ab11eff Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:06:02 +0100 Subject: [PATCH 63/97] fix: correct version extraction script syntax --- .github/workflows/build.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0cea7b1..0182aa8 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -18,7 +18,7 @@ jobs: - uses: actions/checkout@v4 - name: Get version id: get_version - run: echo "VERSION=$(node -p \"require('./src-tauri/tauri.conf.json').version\")" >> $GITHUB_OUTPUT + run: echo "VERSION=$(node -p "require('./src-tauri/tauri.conf.json').version")" >> $GITHUB_OUTPUT build-macos: needs: prepare From ece4d32b4be393d69a7e68a7ec737ed1bd2f8112 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Sat, 15 Feb 2025 18:20:08 +0100 Subject: [PATCH 64/97] fix: extend macOS keychain session timeout to 2 hours --- .github/workflows/build.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 0182aa8..7e4cc80 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -72,6 +72,7 @@ jobs: security create-keychain -p "$KEYCHAIN_PASSWORD" build.keychain security default-keychain -s build.keychain security unlock-keychain -p "$KEYCHAIN_PASSWORD" build.keychain + security set-keychain-settings -lut 7200 build.keychain security import certificate.p12 -k build.keychain -P "$APPLE_CERTIFICATE_PASSWORD" -T /usr/bin/codesign security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" build.keychain security find-identity -v -p codesigning build.keychain From 67584b42852f5332dc30b2001643508511482b2f Mon Sep 17 00:00:00 2001 From: PandaDEV Date: Thu, 6 Mar 2025 21:53:45 +0100 Subject: [PATCH 65/97] Update LICENSE --- LICENSE | 143 ++++++++++++++++++++++++++------------------------------ 1 file changed, 65 insertions(+), 78 deletions(-) diff --git a/LICENSE b/LICENSE index f288702..0ad25db 100644 --- a/LICENSE +++ b/LICENSE @@ -1,5 +1,5 @@ - GNU GENERAL PUBLIC LICENSE - Version 3, 29 June 2007 + GNU AFFERO GENERAL PUBLIC LICENSE + Version 3, 19 November 2007 Copyright (C) 2007 Free Software Foundation, Inc. Everyone is permitted to copy and distribute verbatim copies @@ -7,17 +7,15 @@ Preamble - The GNU General Public License is a free, copyleft license for -software and other kinds of works. + The GNU Affero General Public License is a free, copyleft license for +software and other kinds of works, specifically designed to ensure +cooperation with the community in the case of network server software. The licenses for most software and other practical works are designed to take away your freedom to share and change the works. By contrast, -the GNU General Public License is intended to guarantee your freedom to +our General Public Licenses are intended to guarantee your freedom to share and change all versions of a program--to make sure it remains free -software for all its users. We, the Free Software Foundation, use the -GNU General Public License for most of our software; it applies also to -any other work released this way by its authors. You can apply it to -your programs, too. +software for all its users. When we speak of free software, we are referring to freedom, not price. Our General Public Licenses are designed to make sure that you @@ -26,44 +24,34 @@ them if you wish), that you receive source code or can get it if you want it, that you can change the software or use pieces of it in new free programs, and that you know you can do these things. - To protect your rights, we need to prevent others from denying you -these rights or asking you to surrender the rights. Therefore, you have -certain responsibilities if you distribute copies of the software, or if -you modify it: responsibilities to respect the freedom of others. + Developers that use our General Public Licenses protect your rights +with two steps: (1) assert copyright on the software, and (2) offer +you this License which gives you legal permission to copy, distribute +and/or modify the software. - For example, if you distribute copies of such a program, whether -gratis or for a fee, you must pass on to the recipients the same -freedoms that you received. You must make sure that they, too, receive -or can get the source code. And you must show them these terms so they -know their rights. + A secondary benefit of defending all users' freedom is that +improvements made in alternate versions of the program, if they +receive widespread use, become available for other developers to +incorporate. Many developers of free software are heartened and +encouraged by the resulting cooperation. However, in the case of +software used on network servers, this result may fail to come about. +The GNU General Public License permits making a modified version and +letting the public access it on a server without ever releasing its +source code to the public. - Developers that use the GNU GPL protect your rights with two steps: -(1) assert copyright on the software, and (2) offer you this License -giving you legal permission to copy, distribute and/or modify it. + The GNU Affero General Public License is designed specifically to +ensure that, in such cases, the modified source code becomes available +to the community. It requires the operator of a network server to +provide the source code of the modified version running there to the +users of that server. Therefore, public use of a modified version, on +a publicly accessible server, gives the public access to the source +code of the modified version. - For the developers' and authors' protection, the GPL clearly explains -that there is no warranty for this free software. For both users' and -authors' sake, the GPL requires that modified versions be marked as -changed, so that their problems will not be attributed erroneously to -authors of previous versions. - - Some devices are designed to deny users access to install or run -modified versions of the software inside them, although the manufacturer -can do so. This is fundamentally incompatible with the aim of -protecting users' freedom to change the software. The systematic -pattern of such abuse occurs in the area of products for individuals to -use, which is precisely where it is most unacceptable. Therefore, we -have designed this version of the GPL to prohibit the practice for those -products. If such problems arise substantially in other domains, we -stand ready to extend this provision to those domains in future versions -of the GPL, as needed to protect the freedom of users. - - Finally, every program is threatened constantly by software patents. -States should not allow patents to restrict development and use of -software on general-purpose computers, but in those that do, we wish to -avoid the special danger that patents applied to a free program could -make it effectively proprietary. To prevent this, the GPL assures that -patents cannot be used to render the program non-free. + An older license, called the Affero General Public License and +published by Affero, was designed to accomplish similar goals. This is +a different license, not a version of the Affero GPL, but Affero has +released a new version of the Affero GPL which permits relicensing under +this license. The precise terms and conditions for copying, distribution and modification follow. @@ -72,7 +60,7 @@ modification follow. 0. Definitions. - "This License" refers to version 3 of the GNU General Public License. + "This License" refers to version 3 of the GNU Affero General Public License. "Copyright" also means copyright-like laws that apply to other kinds of works, such as semiconductor masks. @@ -549,35 +537,45 @@ to collect a royalty for further conveying from those to whom you convey the Program, the only way you could satisfy both those terms and this License would be to refrain entirely from conveying the Program. - 13. Use with the GNU Affero General Public License. + 13. Remote Network Interaction; Use with the GNU General Public License. + + Notwithstanding any other provision of this License, if you modify the +Program, your modified version must prominently offer all users +interacting with it remotely through a computer network (if your version +supports such interaction) an opportunity to receive the Corresponding +Source of your version by providing access to the Corresponding Source +from a network server at no charge, through some standard or customary +means of facilitating copying of software. This Corresponding Source +shall include the Corresponding Source for any work covered by version 3 +of the GNU General Public License that is incorporated pursuant to the +following paragraph. Notwithstanding any other provision of this License, you have permission to link or combine any covered work with a work licensed -under version 3 of the GNU Affero General Public License into a single +under version 3 of the GNU General Public License into a single combined work, and to convey the resulting work. The terms of this License will continue to apply to the part which is the covered work, -but the special requirements of the GNU Affero General Public License, -section 13, concerning interaction through a network will apply to the -combination as such. +but the work with which it is combined will remain governed by version +3 of the GNU General Public License. 14. Revised Versions of this License. The Free Software Foundation may publish revised and/or new versions of -the GNU General Public License from time to time. Such new versions will -be similar in spirit to the present version, but may differ in detail to +the GNU Affero General Public License from time to time. Such new versions +will be similar in spirit to the present version, but may differ in detail to address new problems or concerns. Each version is given a distinguishing version number. If the -Program specifies that a certain numbered version of the GNU General +Program specifies that a certain numbered version of the GNU Affero General Public License "or any later version" applies to it, you have the option of following the terms and conditions either of that numbered version or of any later version published by the Free Software Foundation. If the Program does not specify a version number of the -GNU General Public License, you may choose any version ever published +GNU Affero General Public License, you may choose any version ever published by the Free Software Foundation. If the Program specifies that a proxy can decide which future -versions of the GNU General Public License can be used, that proxy's +versions of the GNU Affero General Public License can be used, that proxy's public statement of acceptance of a version permanently authorizes you to choose that version for the Program. @@ -635,40 +633,29 @@ the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or + it under the terms of the GNU Affero General Public License as published + by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. + GNU Affero General Public License for more details. - You should have received a copy of the GNU General Public License + You should have received a copy of the GNU Affero General Public License along with this program. If not, see . Also add information on how to contact you by electronic and paper mail. - If the program does terminal interaction, make it output a short -notice like this when it starts in an interactive mode: - - Copyright (C) - This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'. - This is free software, and you are welcome to redistribute it - under certain conditions; type `show c' for details. - -The hypothetical commands `show w' and `show c' should show the appropriate -parts of the General Public License. Of course, your program's commands -might be different; for a GUI interface, you would use an "about box". + If your software can interact with users remotely through a computer +network, you should also make sure that it provides a way for users to +get its source. For example, if your program is a web application, its +interface could display a "Source" link that leads users to an archive +of the code. There are many ways you could offer source, and different +solutions will be better for different programs; see section 13 for the +specific requirements. You should also get your employer (if you work as a programmer) or school, if any, to sign a "copyright disclaimer" for the program, if necessary. -For more information on this, and how to apply and follow the GNU GPL, see +For more information on this, and how to apply and follow the GNU AGPL, see . - - The GNU General Public License does not permit incorporating your program -into proprietary programs. If your program is a subroutine library, you -may consider it more useful to permit linking proprietary applications with -the library. If this is what you want to do, use the GNU Lesser General -Public License instead of this License. But first, please read -. From 65a5cba69b277ecab03126375f0197dbd4c7858f Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:28:29 +0100 Subject: [PATCH 66/97] chore: update dependencies and Rust crate versions --- src-tauri/Cargo.lock | 777 ++++++++++++++++++++++++------------------- src-tauri/Cargo.toml | 28 +- 2 files changed, 448 insertions(+), 357 deletions(-) diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 2b5d4bd..0df7e49 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -497,7 +497,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e58aa60e59d8dbfcc36138f5f18be5f24394d33b38b24f7fd0b1caa33095f22f" dependencies = [ "block-sys", - "objc2", + "objc2 0.5.2", ] [[package]] @@ -506,7 +506,16 @@ version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2c132eebf10f5cad5289222520a4a058514204aed6d791f1cf4fe8088b82d15f" dependencies = [ - "objc2", + "objc2 0.5.2", +] + +[[package]] +name = "block2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d59b4c170e16f0405a2e95aff44432a0d41aa97675f3d52623effe95792a037" +dependencies = [ + "objc2 0.6.0", ] [[package]] @@ -657,7 +666,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5fbd1fe9db3ebf71b89060adaf7b0504c2d6a425cf061313099547e382c2e472" dependencies = [ "serde", - "toml 0.8.19", + "toml", ] [[package]] @@ -720,9 +729,9 @@ checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.39" +version = "0.4.40" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7e36cc9d416881d2e24f9a963be5fb1cd90966419ac844274161d10488b3e825" +checksum = "1a7964611d71df112cb1730f2ee67324fcf4d0fc6606acbbe9bfe06df124637c" dependencies = [ "android-tzdata", "iana-time-zone", @@ -730,7 +739,7 @@ dependencies = [ "num-traits", "serde", "wasm-bindgen", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -752,9 +761,9 @@ checksum = "bd61885ce9f906c60f40d384b132fed04685f51d3da3576d27b8e4f274b6100d" dependencies = [ "clipboard-win", "image", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "x11rb", ] @@ -791,7 +800,7 @@ checksum = "f6140449f97a6e97f9511815c5632d84c8aacf8ac271ad77c559218161a1373c" dependencies = [ "bitflags 1.3.2", "block", - "cocoa-foundation 0.1.2", + "cocoa-foundation", "core-foundation 0.9.4", "core-graphics 0.23.2", "foreign-types 0.5.0", @@ -799,22 +808,6 @@ dependencies = [ "objc", ] -[[package]] -name = "cocoa" -version = "0.26.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f79398230a6e2c08f5c9760610eb6924b52aa9e7950a619602baba59dcbbdbb2" -dependencies = [ - "bitflags 2.8.0", - "block", - "cocoa-foundation 0.2.0", - "core-foundation 0.10.0", - "core-graphics 0.24.0", - "foreign-types 0.5.0", - "libc", - "objc", -] - [[package]] name = "cocoa-foundation" version = "0.1.2" @@ -829,20 +822,6 @@ dependencies = [ "objc", ] -[[package]] -name = "cocoa-foundation" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e14045fb83be07b5acf1c0884b2180461635b433455fa35d1cd6f17f1450679d" -dependencies = [ - "bitflags 2.8.0", - "block", - "core-foundation 0.10.0", - "core-graphics-types 0.2.0", - "libc", - "objc", -] - [[package]] name = "color_quant" version = "1.1.0" @@ -1251,15 +1230,6 @@ dependencies = [ "dirs-sys 0.3.7", ] -[[package]] -name = "dirs" -version = "5.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" -dependencies = [ - "dirs-sys 0.4.1", -] - [[package]] name = "dirs" version = "6.0.0" @@ -1280,18 +1250,6 @@ dependencies = [ "winapi", ] -[[package]] -name = "dirs-sys" -version = "0.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" -dependencies = [ - "libc", - "option-ext", - "redox_users 0.4.5", - "windows-sys 0.48.0", -] - [[package]] name = "dirs-sys" version = "0.5.0" @@ -1412,14 +1370,14 @@ dependencies = [ [[package]] name = "embed-resource" -version = "2.4.3" +version = "3.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4edcacde9351c33139a41e3c97eb2334351a81a2791bebb0b243df837128f602" +checksum = "7fbc6e0d8e0c03a655b53ca813f0463d2c956bc4db8138dbc89f120b066551e3" dependencies = [ "cc", "memchr", "rustc_version", - "toml 0.8.19", + "toml", "vswhom", "winreg 0.52.0", ] @@ -1920,9 +1878,9 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.5.0" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc3655aa6818d65bc620d6911f05aa7b6aeb596291e1e9f79e52df85583d1e30" +checksum = "4fd4b8790c0792e3b11895efdf5f289ebe8b59107a6624f1cce68f24ff8c7035" dependencies = [ "rustix", "windows-targets 0.52.6", @@ -2065,17 +2023,17 @@ checksum = "d2fabcfbdc87f4758337ca535fb41a6d701b65693ce38287d856d1674551ec9b" [[package]] name = "global-hotkey" -version = "0.6.3" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b00d88f1be7bf4cd2e61623ce08e84be2dfa4eab458e5d632d3dab95f16c1f64" +checksum = "41fbb3a4e56c901ee66c190fdb3fa08344e6d09593cc6c61f8eb9add7144b271" dependencies = [ "crossbeam-channel", "keyboard-types", - "objc2", - "objc2-app-kit", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", "once_cell", "serde", - "thiserror 1.0.63", + "thiserror 2.0.3", "windows-sys 0.59.0", "x11-dl", ] @@ -2413,9 +2371,9 @@ dependencies = [ [[package]] name = "ico" -version = "0.3.0" +version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3804960be0bb5e4edb1e1ad67afd321a9ecfd875c3e65c099468fd2717d7cae" +checksum = "cc50b891e4acf8fe0e71ef88ec43ad82ee07b3810ad09de10f1d01f072ed4b98" dependencies = [ "byteorder", "png", @@ -2428,7 +2386,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3fb69199826926eb864697bddd27f73d9fddcffc004f5733131e15b465e30642" dependencies = [ "block2 0.4.0", - "objc2", + "objc2 0.5.2", ] [[package]] @@ -2658,9 +2616,9 @@ dependencies = [ [[package]] name = "infer" -version = "0.16.0" +version = "0.19.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc150e5ce2330295b8616ce0e3f53250e53af31759a9dbedad1621ba29151847" +checksum = "a588916bfdfd92e71cacef98a63d9b1f0d74d6599980d11894290e7ddefffcf7" dependencies = [ "cfb", ] @@ -2783,10 +2741,11 @@ checksum = "f5d4a7da358eff58addd2877a45865158f0d78c911d43a5784ceb7bbf52833b0" [[package]] name = "js-sys" -version = "0.3.69" +version = "0.3.77" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "29c15563dc2726973df627357ce0c9ddddbea194836909d655df6a75d2cf296d" +checksum = "1cfaf33c695fc6e08064efbc1f72ec937429614f25eef83af942d0e227c3a28f" dependencies = [ + "once_cell", "wasm-bindgen", ] @@ -2984,9 +2943,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.25" +version = "0.4.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "04cbf5b083de1c7e0222a7a51dbfdba1cbe1c6ab0b15e29fff3f6c077fd9cd9f" +checksum = "30bde2b3dc3671ae49d8e2e9f044c7c005836e7a023ee57cffa25ab82764bb9e" [[package]] name = "loop9" @@ -3133,21 +3092,22 @@ dependencies = [ [[package]] name = "muda" -version = "0.15.1" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8123dfd4996055ac9b15a60ad263b44b01e539007523ad7a4a533a3d93b0591" +checksum = "4de14a9b5d569ca68d7c891d613b390cf5ab4f851c77aaa2f9e435555d3d9492" dependencies = [ "crossbeam-channel", "dpi", "gtk", "keyboard-types", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", "once_cell", "png", "serde", - "thiserror 1.0.63", + "thiserror 2.0.3", "windows-sys 0.59.0", ] @@ -3369,9 +3329,6 @@ name = "objc-sys" version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cdb91bdd390c7ce1a8607f35f3ca7151b65afc0ff5ff3b34fa350f7d7c7e4310" -dependencies = [ - "cc", -] [[package]] name = "objc2" @@ -3383,6 +3340,16 @@ dependencies = [ "objc2-encode", ] +[[package]] +name = "objc2" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3531f65190d9cff863b77a99857e74c314dd16bf56c538c4b57c7cbc3f3a6e59" +dependencies = [ + "objc2-encode", + "objc2-exception-helper", +] + [[package]] name = "objc2-app-kit" version = "0.2.2" @@ -3392,35 +3359,41 @@ dependencies = [ "bitflags 2.8.0", "block2 0.5.1", "libc", - "objc2", - "objc2-core-data", - "objc2-core-image", - "objc2-foundation", - "objc2-quartz-core", + "objc2 0.5.2", + "objc2-core-data 0.2.2", + "objc2-core-image 0.2.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", +] + +[[package]] +name = "objc2-app-kit" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5906f93257178e2f7ae069efb89fbd6ee94f0592740b5f8a1512ca498814d0fb" +dependencies = [ + "bitflags 2.8.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-cloud-kit", + "objc2-core-data 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-core-image 0.3.0", + "objc2-foundation 0.3.0", + "objc2-quartz-core 0.3.0", ] [[package]] name = "objc2-cloud-kit" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "74dd3b56391c7a0596a295029734d3c1c5e7e510a4cb30245f8221ccea96b009" +checksum = "6c1948a9be5f469deadbd6bcb86ad7ff9e47b4f632380139722f7d9840c0d42c" dependencies = [ "bitflags 2.8.0", - "block2 0.5.1", - "objc2", - "objc2-core-location", - "objc2-foundation", -] - -[[package]] -name = "objc2-contacts" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a5ff520e9c33812fd374d8deecef01d4a840e7b41862d849513de77e44aa4889" -dependencies = [ - "block2 0.5.1", - "objc2", - "objc2-foundation", + "objc2 0.6.0", + "objc2-foundation 0.3.0", ] [[package]] @@ -3431,8 +3404,41 @@ checksum = "617fbf49e071c178c0b24c080767db52958f716d9eabdf0890523aeae54773ef" dependencies = [ "bitflags 2.8.0", "block2 0.5.1", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", +] + +[[package]] +name = "objc2-core-data" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f860f8e841f6d32f754836f51e6bc7777cd7e7053cf18528233f6811d3eceb4" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", +] + +[[package]] +name = "objc2-core-foundation" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daeaf60f25471d26948a1c2f840e3f7d86f4109e3af4e8e4b5cd70c39690d925" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", +] + +[[package]] +name = "objc2-core-graphics" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dca602628b65356b6513290a21a6405b4d4027b8b250f0b98dddbb28b7de02" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-io-surface", ] [[package]] @@ -3442,28 +3448,35 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "55260963a527c99f1819c4f8e3b47fe04f9650694ef348ffd2227e8196d34c80" dependencies = [ "block2 0.5.1", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] [[package]] -name = "objc2-core-location" -version = "0.2.2" +name = "objc2-core-image" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "000cfee34e683244f284252ee206a27953279d370e309649dc3ee317b37e5781" +checksum = "6ffa6bea72bf42c78b0b34e89c0bafac877d5f80bf91e159a5d96ea7f693ca56" dependencies = [ - "block2 0.5.1", - "objc2", - "objc2-contacts", - "objc2-foundation", + "objc2 0.6.0", + "objc2-foundation 0.3.0", ] [[package]] name = "objc2-encode" -version = "4.0.3" +version = "4.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7891e71393cd1f227313c9379a26a584ff3d7e6e7159e988851f0934c993f0f8" +checksum = "ef25abbcd74fb2609453eb695bd2f860d389e457f67dc17cafc8b8cbc89d0c33" + +[[package]] +name = "objc2-exception-helper" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7a1c5fbb72d7735b076bb47b578523aedc40f3c439bea6dfd595c089d79d98a" +dependencies = [ + "cc", +] [[package]] name = "objc2-foundation" @@ -3475,19 +3488,31 @@ dependencies = [ "block2 0.5.1", "dispatch", "libc", - "objc2", + "objc2 0.5.2", ] [[package]] -name = "objc2-link-presentation" -version = "0.2.2" +name = "objc2-foundation" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1a1ae721c5e35be65f01a03b6d2ac13a54cb4fa70d8a5da293d7b0020261398" +checksum = "3a21c6c9014b82c39515db5b396f91645182611c97d24637cf56ac01e5f8d998" dependencies = [ - "block2 0.5.1", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "bitflags 2.8.0", + "block2 0.6.0", + "libc", + "objc2 0.6.0", + "objc2-core-foundation", +] + +[[package]] +name = "objc2-io-surface" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "161a8b87e32610086e1a7a9e9ec39f84459db7b3a0881c1f16ca5a2605581c19" +dependencies = [ + "bitflags 2.8.0", + "objc2 0.6.0", + "objc2-core-foundation", ] [[package]] @@ -3498,8 +3523,8 @@ checksum = "dd0cba1276f6023976a406a14ffa85e1fdd19df6b0f737b063b95f6c8c7aadd6" dependencies = [ "bitflags 2.8.0", "block2 0.5.1", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3509,9 +3534,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6788b04a18ea31e3dc3ab256b8546639e5bbae07c1a0dc4ea8615252bc6aee9a" dependencies = [ "bitflags 2.8.0", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", ] [[package]] @@ -3522,77 +3547,46 @@ checksum = "e42bee7bff906b14b167da2bac5efe6b6a07e6f7c0a21a7308d40c960242dc7a" dependencies = [ "bitflags 2.8.0", "block2 0.5.1", - "objc2", - "objc2-foundation", + "objc2 0.5.2", + "objc2-foundation 0.2.2", "objc2-metal", ] [[package]] -name = "objc2-symbols" -version = "0.2.2" +name = "objc2-quartz-core" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0a684efe3dec1b305badae1a28f6555f6ddd3bb2c2267896782858d5a78404dc" +checksum = "6fb3794501bb1bee12f08dcad8c61f2a5875791ad1c6f47faa71a0f033f20071" dependencies = [ - "objc2", - "objc2-foundation", + "bitflags 2.8.0", + "objc2 0.6.0", + "objc2-foundation 0.3.0", ] [[package]] name = "objc2-ui-kit" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8bb46798b20cd6b91cbd113524c490f1686f4c4e8f49502431415f3512e2b6f" +checksum = "777a571be14a42a3990d4ebedaeb8b54cd17377ec21b92e8200ac03797b3bee1" dependencies = [ "bitflags 2.8.0", - "block2 0.5.1", - "objc2", - "objc2-cloud-kit", - "objc2-core-data", - "objc2-core-image", - "objc2-core-location", - "objc2-foundation", - "objc2-link-presentation", - "objc2-quartz-core", - "objc2-symbols", - "objc2-uniform-type-identifiers", - "objc2-user-notifications", -] - -[[package]] -name = "objc2-uniform-type-identifiers" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44fa5f9748dbfe1ca6c0b79ad20725a11eca7c2218bceb4b005cb1be26273bfe" -dependencies = [ - "block2 0.5.1", - "objc2", - "objc2-foundation", -] - -[[package]] -name = "objc2-user-notifications" -version = "0.2.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "76cfcbf642358e8689af64cee815d139339f3ed8ad05103ed5eaf73db8d84cb3" -dependencies = [ - "bitflags 2.8.0", - "block2 0.5.1", - "objc2", - "objc2-core-location", - "objc2-foundation", + "objc2 0.6.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", ] [[package]] name = "objc2-web-kit" -version = "0.2.2" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68bc69301064cebefc6c4c90ce9cba69225239e4b8ff99d445a2b5563797da65" +checksum = "b717127e4014b0f9f3e8bba3d3f2acec81f1bde01f656823036e823ed2c94dce" dependencies = [ "bitflags 2.8.0", - "block2 0.5.1", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "block2 0.6.0", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", ] [[package]] @@ -3606,9 +3600,9 @@ dependencies = [ [[package]] name = "once_cell" -version = "1.19.0" +version = "1.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" +checksum = "cde51589ab56b20a6f686b2c68f7a0bd6add753d697abf720d63f8db3ab7b1ad" [[package]] name = "openssl" @@ -3688,7 +3682,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "35366a452fce3f8947eb2f33226a133aaf0cacedef2af67ade348d58be7f85d0" dependencies = [ "icrate", - "objc2-foundation", + "objc2-foundation 0.2.2", "objc2-osa-kit", "serde", "serde_json", @@ -4543,9 +4537,9 @@ checksum = "2b15c43186be67a4fd63bee50d0303afffcef381492ebe2c5d87f324e1b8815c" [[package]] name = "reqwest" -version = "0.12.12" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43e734407157c3c2034e0258f5e4473ddb361b1e85f95a66690d67264d7cd1da" +checksum = "989e327e510263980e231de548a33e63d34962d29ae61b467389a1a09627a254" dependencies = [ "base64 0.22.1", "bytes", @@ -4606,9 +4600,9 @@ dependencies = [ "gtk-sys", "js-sys", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", "raw-window-handle", "wasm-bindgen", "wasm-bindgen-futures", @@ -4873,9 +4867,9 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "02fc4265df13d6fa1d00ecff087228cc0a2b5f3c0e87e258d8b94a156e984c70" +checksum = "5f0e2c6ed6606019b4e29e69dbaba95b11854410e5347d525002456dbbb786b6" dependencies = [ "serde_derive", ] @@ -4893,9 +4887,9 @@ dependencies = [ [[package]] name = "serde_derive" -version = "1.0.217" +version = "1.0.219" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a9bf7cf98d04a2b28aead066b7496853d4779c9cc183c440dbac457641e19a0" +checksum = "5b0276cf7f2c73365f7157c8123c21cd9a50fbbd844757af28ca1f5925fc2a00" dependencies = [ "proc-macro2", "quote", @@ -4915,9 +4909,9 @@ dependencies = [ [[package]] name = "serde_json" -version = "1.0.138" +version = "1.0.140" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d434192e7da787e94a6ea7e9670b26a036d0ca41e0b7efb2676dd32bae872949" +checksum = "20068b6e96dc6c9bd23e01df8827e6c7e1f2fddd43c21810382803c136b99373" dependencies = [ "itoa 1.0.11", "memchr", @@ -5127,10 +5121,10 @@ dependencies = [ "foreign-types 0.5.0", "js-sys", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", - "objc2-quartz-core", + "objc2 0.5.2", + "objc2-app-kit 0.2.2", + "objc2-foundation 0.2.2", + "objc2-quartz-core 0.2.2", "raw-window-handle", "redox_syscall 0.5.3", "wasm-bindgen", @@ -5436,18 +5430,18 @@ checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "strum" -version = "0.26.3" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8fec0f0aef304996cf250b31b5a10dee7980c85da9d759361292b8bca5a18f06" +checksum = "f64def088c51c9510a8579e3c5d67c65349dcf755e5479ad3d010aa6454e2c32" dependencies = [ "strum_macros", ] [[package]] name = "strum_macros" -version = "0.26.4" +version = "0.27.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4c6bee85a5a24955dc440386795aa378cd9cf82acd5f764469152d2270e581be" +checksum = "c77a8c5abcaf0f9ce05d62342b7d298c346515365c36b673df4ebe3ced01fde8" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5554,18 +5548,17 @@ dependencies = [ "cfg-expr", "heck 0.5.0", "pkg-config", - "toml 0.8.19", + "toml", "version-compare", ] [[package]] name = "tao" -version = "0.31.1" +version = "0.32.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3731d04d4ac210cd5f344087733943b9bfb1a32654387dad4d1c70de21aee2c9" +checksum = "63c8b1020610b9138dd7b1e06cf259ae91aa05c30f3bd0d6b42a03997b92dec1" dependencies = [ "bitflags 2.8.0", - "cocoa 0.26.0", "core-foundation 0.10.0", "core-graphics 0.24.0", "crossbeam-channel", @@ -5582,7 +5575,9 @@ dependencies = [ "ndk", "ndk-context", "ndk-sys", - "objc", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "once_cell", "parking_lot", "raw-window-handle", @@ -5590,8 +5585,8 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows 0.58.0", - "windows-core 0.58.0", + "windows 0.60.0", + "windows-core 0.60.1", "windows-version", "x11-dl", ] @@ -5626,9 +5621,9 @@ checksum = "61c41af27dd6d1e27b1b16b489db798443478cef1f06a660c96db617ba5de3b1" [[package]] name = "tauri" -version = "2.2.5" +version = "2.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "58a998b6be84104ca05c7e9a21f2180ddec020c8b84ea59a8fc8530a2a19588d" +checksum = "3be747b26bf28674977fac47bdf6963fd9c7578271c3fbeb25d8686de6596f35" dependencies = [ "anyhow", "bytes", @@ -5647,9 +5642,9 @@ dependencies = [ "log", "mime", "muda", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", "percent-encoding", "plist", "raw-window-handle", @@ -5672,18 +5667,18 @@ dependencies = [ "webkit2gtk", "webview2-com", "window-vibrancy", - "windows 0.58.0", + "windows 0.60.0", ] [[package]] name = "tauri-build" -version = "2.0.5" +version = "2.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e950124f6779c6cf98e3260c7a6c8488a74aa6350dd54c6950fdaa349bca2df" +checksum = "51a2e96f3c0baa0581656bb58e6fdd0f7c9c31eaf6721a0c08689d938fe85f2d" dependencies = [ "anyhow", "cargo_toml", - "dirs 5.0.1", + "dirs 6.0.0", "glob", "heck 0.5.0", "json-patch", @@ -5693,15 +5688,15 @@ dependencies = [ "serde_json", "tauri-utils", "tauri-winres", - "toml 0.8.19", + "toml", "walkdir", ] [[package]] name = "tauri-codegen" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f77894f9ddb5cb6c04fcfe8c8869ebe0aded4dabf19917118d48be4a95599ab5" +checksum = "e357ec3daf8faad1029bc7109e7f5b308ceb63b6073d110d7388923a4cce5e55" dependencies = [ "base64 0.22.1", "brotli", @@ -5736,9 +5731,9 @@ dependencies = [ [[package]] name = "tauri-macros" -version = "2.0.4" +version = "2.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3240a5caed760a532e8f687be6f05b2c7d11a1d791fb53ccc08cfeb3e5308736" +checksum = "447ee4dd94690d77f1422f2b57e783c654ba75c535ad6f6e727887330804fff2" dependencies = [ "heck 0.5.0", "proc-macro2", @@ -5761,7 +5756,7 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "toml 0.8.19", + "toml", "walkdir", ] @@ -5849,7 +5844,7 @@ dependencies = [ "tauri-plugin", "tauri-utils", "thiserror 2.0.3", - "toml 0.8.19", + "toml", "url", "uuid", ] @@ -5871,11 +5866,11 @@ dependencies = [ [[package]] name = "tauri-plugin-os" -version = "2.2.0" +version = "2.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dda2d571a9baf0664c1f2088db227e3072f9028602fafa885deade7547c3b738" +checksum = "424f19432397850c2ddd42aa58078630c15287bbce3866eb1d90e7dbee680637" dependencies = [ - "gethostname 0.5.0", + "gethostname 1.0.0", "log", "os_info", "serde", @@ -5889,9 +5884,9 @@ dependencies = [ [[package]] name = "tauri-plugin-prevent-default" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f193196a5c3cd34c719b67ecc002763047717cf3d74f8f1961b5fdfdd4212664" +checksum = "68c872b8d017f6f10ff5d44ce315e28c8848a302a2eff0ed6768aa80214ea5d6" dependencies = [ "bitflags 2.8.0", "itertools 0.14.0", @@ -5923,9 +5918,9 @@ dependencies = [ [[package]] name = "tauri-plugin-updater" -version = "2.5.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ebf3da08c36fb03c98c76e5563d4e74d9a590df0f40978cbe07f39cb52833f7c" +checksum = "67cd78a6cbd1255e989e96eedec004e9e8949e6c6359b41f861279aba64ea306" dependencies = [ "base64 0.22.1", "dirs 6.0.0", @@ -5933,6 +5928,7 @@ dependencies = [ "futures-util", "http", "infer", + "log", "minisign-verify", "osakit", "percent-encoding", @@ -5954,9 +5950,9 @@ dependencies = [ [[package]] name = "tauri-runtime" -version = "2.3.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2274ef891ccc0a8d318deffa9d70053f947664d12d58b9c0d1ae5e89237e01f7" +checksum = "e758a405ab39e25f4d1235c5f06fe563f44b01ee18bbe38ddec5356d4f581908" dependencies = [ "dpi", "gtk", @@ -5968,22 +5964,23 @@ dependencies = [ "tauri-utils", "thiserror 2.0.3", "url", - "windows 0.58.0", + "windows 0.60.0", ] [[package]] name = "tauri-runtime-wry" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3707b40711d3b9f6519150869e358ffbde7c57567fb9b5a8b51150606939b2a0" +checksum = "8b2beb90decade4c71e8b09c9e4a9245837a8a97693f945b77e32baf13f51fec" dependencies = [ "gtk", "http", "jni", "log", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-foundation 0.3.0", + "once_cell", "percent-encoding", "raw-window-handle", "softbuffer", @@ -5993,15 +5990,15 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows 0.58.0", + "windows 0.60.0", "wry", ] [[package]] name = "tauri-utils" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96fb10e7cc97456b2d5b9c03e335b5de5da982039a303a20d10006885e4523a0" +checksum = "107a959dbd5ff53d89a98f6f2e3e987c611334141a43630caae1d80e79446dd6" dependencies = [ "brotli", "cargo_metadata", @@ -6027,7 +6024,7 @@ dependencies = [ "serde_with", "swift-rs", "thiserror 2.0.3", - "toml 0.8.19", + "toml", "url", "urlpattern", "uuid", @@ -6036,12 +6033,12 @@ dependencies = [ [[package]] name = "tauri-winres" -version = "0.1.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5993dc129e544393574288923d1ec447c857f3f644187f4fbf7d9a875fbfc4fb" +checksum = "56eaa45f707bedf34d19312c26d350bc0f3c59a47e58e8adbeecdc850d2c13a0" dependencies = [ "embed-resource", - "toml 0.7.8", + "toml", ] [[package]] @@ -6143,9 +6140,9 @@ dependencies = [ [[package]] name = "time" -version = "0.3.37" +version = "0.3.39" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35e7868883861bd0e56d9ac6efcaaca0d6d5d82a2a7ec8209ff492c07cf37b21" +checksum = "dad298b01a40a23aac4580b67e3dbedb7cc8402f3592d7f49469de2ea4aecdd8" dependencies = [ "deranged", "itoa 1.0.11", @@ -6158,15 +6155,15 @@ dependencies = [ [[package]] name = "time-core" -version = "0.1.2" +version = "0.1.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ef927ca75afb808a4d64dd374f00a2adf8d0fcff8e7b184af886c3c87ec4a3f3" +checksum = "765c97a5b985b7c11d7bc27fa927dc4fe6af3a6dfb021d28deb60d3bf51e76ef" [[package]] name = "time-macros" -version = "0.2.19" +version = "0.2.20" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2834e6017e3e5e4b9834939793b282bc03b37a3336245fa820e35e233e2a85de" +checksum = "e8093bc3e81c3bc5f7879de09619d06c9a5a5e45ca44dfeeb7225bae38005c5c" dependencies = [ "num-conv", "time-core", @@ -6199,9 +6196,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.43.0" +version = "1.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d61fa4ffa3de412bfea335c6ecff681de2b609ba3c77ef3e00e521813a9ed9e" +checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a" dependencies = [ "backtrace", "bytes", @@ -6272,18 +6269,6 @@ dependencies = [ "tokio", ] -[[package]] -name = "toml" -version = "0.7.8" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd79e69d3b627db300ff956027cc6c3798cef26d22526befdfcd12feeb6d2257" -dependencies = [ - "serde", - "serde_spanned", - "toml_datetime", - "toml_edit 0.19.15", -] - [[package]] name = "toml" version = "0.8.19" @@ -6312,8 +6297,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ "indexmap 2.3.0", - "serde", - "serde_spanned", "toml_datetime", "winnow 0.5.40", ] @@ -6403,22 +6386,23 @@ dependencies = [ [[package]] name = "tray-icon" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "533fc2d4105e0e3d96ce1c71f2d308c9fbbe2ef9c587cab63dd627ab5bde218f" +checksum = "d433764348e7084bad2c5ea22c96c71b61b17afe3a11645710f533bd72b6a2b5" dependencies = [ - "core-graphics 0.24.0", "crossbeam-channel", - "dirs 5.0.1", + "dirs 6.0.0", "libappindicator", "muda", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.0", "once_cell", "png", "serde", - "thiserror 1.0.63", + "thiserror 2.0.3", "windows-sys 0.59.0", ] @@ -6591,9 +6575,9 @@ checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "uuid" -version = "1.13.1" +version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ced87ca4be083373936a67f8de945faa23b6b42384bd5b64434850802c6dccd0" +checksum = "e0f540e3240398cce6128b64ba83fdbdd86129c16a3aa1a3a252efd66eb3d587" dependencies = [ "getrandom 0.3.1", "serde", @@ -6696,23 +6680,24 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4be2531df63900aeb2bca0daaaddec08491ee64ceecbee5076636a3b026795a8" +checksum = "1edc8929d7499fc4e8f0be2262a241556cfc54a0bea223790e71446f2aab1ef5" dependencies = [ "cfg-if", + "once_cell", + "rustversion", "wasm-bindgen-macro", ] [[package]] name = "wasm-bindgen-backend" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "614d787b966d3989fa7bb98a654e369c762374fd3213d212cfc0251257e747da" +checksum = "2f0a0651a5c2bc21487bde11ee802ccaf4c51935d0d3d42a6101f98161700bc6" dependencies = [ "bumpalo", "log", - "once_cell", "proc-macro2", "quote", "syn 2.0.87", @@ -6733,9 +6718,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a1f8823de937b71b9460c0c34e25f3da88250760bec0ebac694b49997550d726" +checksum = "7fe63fc6d09ed3792bd0897b314f53de8e16568c2b3f7982f468c0bf9bd0b407" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -6743,9 +6728,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" +checksum = "8ae87ea40c9f689fc23f209965b6fb8a99ad69aeeb0231408be24920604395de" dependencies = [ "proc-macro2", "quote", @@ -6756,9 +6741,12 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.92" +version = "0.2.100" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af190c94f2773fdb3729c55b007a722abb5384da03bc0986df4c289bf5567e96" +checksum = "1a05d73b933a847d6cccdda8f838a22ff101ad9bf93e33684f39c1f5f0eece3d" +dependencies = [ + "unicode-ident", +] [[package]] name = "wasm-streams" @@ -6898,14 +6886,14 @@ dependencies = [ [[package]] name = "webview2-com" -version = "0.34.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "823e7ebcfaea51e78f72c87fc3b65a1e602c321f407a0b36dbb327d7bb7cd921" +checksum = "b0d606f600e5272b514dbb66539dd068211cc20155be8d3958201b4b5bd79ed3" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows 0.58.0", - "windows-core 0.58.0", + "windows 0.60.0", + "windows-core 0.60.1", "windows-implement", "windows-interface", ] @@ -6923,13 +6911,13 @@ dependencies = [ [[package]] name = "webview2-com-sys" -version = "0.34.0" +version = "0.36.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a82bce72db6e5ee83c68b5de1e2cd6ea195b9fbff91cb37df5884cbe3222df4" +checksum = "bfb27fccd3c27f68e9a6af1bcf48c2d82534b8675b83608a4d81446d095a17ac" dependencies = [ - "thiserror 1.0.63", - "windows 0.58.0", - "windows-core 0.58.0", + "thiserror 2.0.3", + "windows 0.60.0", + "windows-core 0.60.1", ] [[package]] @@ -6999,13 +6987,14 @@ checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" [[package]] name = "window-vibrancy" -version = "0.5.2" +version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ea403deff7b51fff19e261330f71608ff2cdef5721d72b64180bb95be7c4150" +checksum = "d9bec5a31f3f9362f2258fd0e9c9dd61a9ca432e7306cc78c444258f0dce9a9c" dependencies = [ - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", "raw-window-handle", "windows-sys 0.59.0", "windows-version", @@ -7022,12 +7011,24 @@ dependencies = [ [[package]] name = "windows" -version = "0.58.0" +version = "0.60.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd04d41d93c4992d421894c18c8b43496aa748dd4c081bac0dc93eb0489272b6" +checksum = "ddf874e74c7a99773e62b1c671427abf01a425e77c3d3fb9fb1e4883ea934529" dependencies = [ - "windows-core 0.58.0", - "windows-targets 0.52.6", + "windows-collections", + "windows-core 0.60.1", + "windows-future", + "windows-link", + "windows-numerics", +] + +[[package]] +name = "windows-collections" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5467f79cc1ba3f52ebb2ed41dbb459b8e7db636cc3429458d9a852e15bc24dec" +dependencies = [ + "windows-core 0.60.1", ] [[package]] @@ -7041,22 +7042,32 @@ dependencies = [ [[package]] name = "windows-core" -version = "0.58.0" +version = "0.60.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ba6d44ec8c2591c134257ce647b7ea6b20335bf6379a27dac5f1641fcf59f99" +checksum = "ca21a92a9cae9bf4ccae5cf8368dce0837100ddf6e6d57936749e85f152f6247" dependencies = [ "windows-implement", "windows-interface", + "windows-link", "windows-result", "windows-strings", - "windows-targets 0.52.6", +] + +[[package]] +name = "windows-future" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a787db4595e7eb80239b74ce8babfb1363d8e343ab072f2ffe901400c03349f0" +dependencies = [ + "windows-core 0.60.1", + "windows-link", ] [[package]] name = "windows-implement" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2bbd5b46c938e506ecbce286b6628a02171d56153ba733b6c741fc627ec9579b" +checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1" dependencies = [ "proc-macro2", "quote", @@ -7065,9 +7076,9 @@ dependencies = [ [[package]] name = "windows-interface" -version = "0.58.0" +version = "0.59.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "053c4c462dc91d3b1504c6fe5a726dd15e216ba718e84a0e46a88fbe5ded3515" +checksum = "cb26fd936d991781ea39e87c3a27285081e3c0da5ca0fcbc02d368cc6f52ff01" dependencies = [ "proc-macro2", "quote", @@ -7075,33 +7086,48 @@ dependencies = [ ] [[package]] -name = "windows-registry" -version = "0.2.0" +name = "windows-link" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +checksum = "6dccfd733ce2b1753b03b6d3c65edf020262ea35e20ccdf3e288043e6dd620e3" + +[[package]] +name = "windows-numerics" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "005dea54e2f6499f2cee279b8f703b3cf3b5734a2d8d21867c8f44003182eeed" +dependencies = [ + "windows-core 0.60.1", + "windows-link", +] + +[[package]] +name = "windows-registry" +version = "0.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4286ad90ddb45071efd1a66dfa43eb02dd0dfbae1545ad6cc3c51cf34d7e8ba3" dependencies = [ "windows-result", "windows-strings", - "windows-targets 0.52.6", + "windows-targets 0.53.0", ] [[package]] name = "windows-result" -version = "0.2.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +checksum = "06374efe858fab7e4f881500e6e86ec8bc28f9462c47e5a9941a0142ad86b189" dependencies = [ - "windows-targets 0.52.6", + "windows-link", ] [[package]] name = "windows-strings" -version = "0.1.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319" dependencies = [ - "windows-result", - "windows-targets 0.52.6", + "windows-link", ] [[package]] @@ -7179,13 +7205,29 @@ dependencies = [ "windows_aarch64_gnullvm 0.52.6", "windows_aarch64_msvc 0.52.6", "windows_i686_gnu 0.52.6", - "windows_i686_gnullvm", + "windows_i686_gnullvm 0.52.6", "windows_i686_msvc 0.52.6", "windows_x86_64_gnu 0.52.6", "windows_x86_64_gnullvm 0.52.6", "windows_x86_64_msvc 0.52.6", ] +[[package]] +name = "windows-targets" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b1e4c7e8ceaaf9cb7d7507c974735728ab453b67ef8f18febdd7c11fe59dca8b" +dependencies = [ + "windows_aarch64_gnullvm 0.53.0", + "windows_aarch64_msvc 0.53.0", + "windows_i686_gnu 0.53.0", + "windows_i686_gnullvm 0.53.0", + "windows_i686_msvc 0.53.0", + "windows_x86_64_gnu 0.53.0", + "windows_x86_64_gnullvm 0.53.0", + "windows_x86_64_msvc 0.53.0", +] + [[package]] name = "windows-version" version = "0.1.1" @@ -7222,6 +7264,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "86b8d5f90ddd19cb4a147a5fa63ca848db3df085e25fee3cc10b39b6eebae764" + [[package]] name = "windows_aarch64_msvc" version = "0.42.2" @@ -7240,6 +7288,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7651a1f62a11b8cbd5e0d42526e55f2c99886c77e007179efff86c2b137e66c" + [[package]] name = "windows_i686_gnu" version = "0.42.2" @@ -7258,12 +7312,24 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" +[[package]] +name = "windows_i686_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c1dc67659d35f387f5f6c479dc4e28f1d4bb90ddd1a5d3da2e5d97b42d6272c3" + [[package]] name = "windows_i686_gnullvm" version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9ce6ccbdedbf6d6354471319e781c0dfef054c81fbc7cf83f338a4296c0cae11" + [[package]] name = "windows_i686_msvc" version = "0.42.2" @@ -7282,6 +7348,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" +[[package]] +name = "windows_i686_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "581fee95406bb13382d2f65cd4a908ca7b1e4c2f1917f143ba16efe98a589b5d" + [[package]] name = "windows_x86_64_gnu" version = "0.42.2" @@ -7300,6 +7372,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e55b5ac9ea33f2fc1716d1742db15574fd6fc8dadc51caab1c16a3d3b4190ba" + [[package]] name = "windows_x86_64_gnullvm" version = "0.42.2" @@ -7318,6 +7396,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0a6e035dd0599267ce1ee132e51c27dd29437f63325753051e71dd9e42406c57" + [[package]] name = "windows_x86_64_msvc" version = "0.42.2" @@ -7336,6 +7420,12 @@ version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "271414315aff87387382ec3d271b52d7ae78726f5d44ac98b4f4030c91880486" + [[package]] name = "winnow" version = "0.5.40" @@ -7396,12 +7486,12 @@ checksum = "1e9df38ee2d2c3c5948ea468a8406ff0db0b29ae1ffde1bcf20ef305bcc95c51" [[package]] name = "wry" -version = "0.48.0" +version = "0.50.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e644bf458e27b11b0ecafc9e5633d1304fdae82baca1d42185669752fe6ca4f" +checksum = "804a7d1613bd699beccaa60f3b3c679acee21cebba1945a693f5eab95c08d1fa" dependencies = [ "base64 0.22.1", - "block2 0.5.1", + "block2 0.6.0", "cookie", "crossbeam-channel", "dpi", @@ -7415,9 +7505,10 @@ dependencies = [ "kuchikiki", "libc", "ndk", - "objc2", - "objc2-app-kit", - "objc2-foundation", + "objc2 0.6.0", + "objc2-app-kit 0.3.0", + "objc2-core-foundation", + "objc2-foundation 0.3.0", "objc2-ui-kit", "objc2-web-kit", "once_cell", @@ -7431,8 +7522,8 @@ dependencies = [ "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows 0.58.0", - "windows-core 0.58.0", + "windows 0.60.0", + "windows-core 0.60.1", "windows-version", "x11-dl", ] diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index 6159dcd..6e01f96 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -7,22 +7,22 @@ edition = "2021" rust-version = "1.70" [build-dependencies] -tauri-build = { version = "2.0.5", features = [] } +tauri-build = { version = "2.0.6", features = [] } [dependencies] -tauri = { version = "2.2.5", features = [ +tauri = { version = "2.3.1", features = [ "macos-private-api", "tray-icon", "image-png", ] } tauri-plugin-sql = { version = "2.2.0", features = ["sqlite"] } tauri-plugin-autostart = "2.2.0" -tauri-plugin-os = "2.2.0" -tauri-plugin-updater = "2.5.0" +tauri-plugin-os = "2.2.1" +tauri-plugin-updater = "2.6.0" tauri-plugin-dialog = "2.2.0" tauri-plugin-fs = "2.2.0" tauri-plugin-clipboard = "2.1.11" -tauri-plugin-prevent-default = "1.1.0" +tauri-plugin-prevent-default = "1.2.1" tauri-plugin-global-shortcut = "2.2.0" tauri-plugin-aptabase = { git = "https://github.com/aptabase/tauri-plugin-aptabase", branch = "v2" } sqlx = { version = "0.8.3", features = [ @@ -30,23 +30,23 @@ sqlx = { version = "0.8.3", features = [ "sqlite", "chrono", ] } -serde = { version = "1.0.217", features = ["derive"] } -tokio = { version = "1.43.0", features = ["full"] } -serde_json = "1.0.138" +serde = { version = "1.0.219", features = ["derive"] } +tokio = { version = "1.44.1", features = ["full"] } +serde_json = "1.0.140" rdev = "0.5.3" rand = "0.9.0" base64 = "0.22.1" image = "0.25.5" -reqwest = { version = "0.12.12", features = ["json", "blocking"] } +reqwest = { version = "0.12.14", features = ["json", "blocking"] } url = "2.5.4" regex = "1.11.1" sha2 = "0.10.8" lazy_static = "1.5.0" -time = "0.3.37" -global-hotkey = "0.6.3" -chrono = { version = "0.4.39", features = ["serde"] } -log = { version = "0.4.25", features = ["std"] } -uuid = "1.13.1" +time = "0.3.39" +global-hotkey = "0.6.4" +chrono = { version = "0.4.40", features = ["serde"] } +log = { version = "0.4.26", features = ["std"] } +uuid = "1.15.1" active-win-pos-rs = "0.9.0" include_dir = "0.7.4" # hyperpolyglot = { git = "https://github.com/0pandadev/hyperpolyglot" } From b2395e19a9672b4e6eb331131a910ae703c67fdc Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:28:51 +0100 Subject: [PATCH 67/97] chore: update Tauri and other dependencies to latest versions --- package.json | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/package.json b/package.json index 52610ce..2dbe25d 100644 --- a/package.json +++ b/package.json @@ -10,17 +10,17 @@ "postinstall": "nuxt prepare" }, "dependencies": { - "@tauri-apps/api": "2.2.0", - "@tauri-apps/cli": "2.2.7", + "@tauri-apps/api": "2.3.0", + "@tauri-apps/cli": "2.3.1", "@tauri-apps/plugin-autostart": "2.2.0", - "@tauri-apps/plugin-os": "2.2.0", - "nuxt": "3.15.4", - "overlayscrollbars": "2.11.0", + "@tauri-apps/plugin-os": "2.2.1", + "nuxt": "3.16.0", + "overlayscrollbars": "2.11.1", "overlayscrollbars-vue": "0.5.9", - "sass-embedded": "1.85.0", - "uuid": "11.0.5", + "sass-embedded": "1.85.1", + "uuid": "11.1.0", "vue": "3.5.13", - "wrdu-keyboard": "3.2.0" + "wrdu-keyboard": "github:0PandaDEV/keyboard" }, "overrides": { "chokidar": "^3.6.0" From b6269c60c9cc1fdf333b7a82848bda8e30aa15a8 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Thu, 13 Mar 2025 15:29:02 +0100 Subject: [PATCH 68/97] refactor: integrate keyboard functionality and clean up event handling --- app.vue | 4 +-- nuxt.config.ts | 1 - pages/index.vue | 57 ++++++++++++++++++++++++++++++++----------- pages/settings.vue | 18 +++++++------- src-tauri/src/main.rs | 2 +- 5 files changed, 55 insertions(+), 27 deletions(-) diff --git a/app.vue b/app.vue index 581cff2..325d877 100644 --- a/app.vue +++ b/app.vue @@ -9,13 +9,13 @@ import { listen } from "@tauri-apps/api/event"; import { app, window } from "@tauri-apps/api"; import { disable, enable } from "@tauri-apps/plugin-autostart"; import { onMounted } from "vue"; +import { keyboard } from "wrdu-keyboard"; -const keyboard = useKeyboard(); const { $settings } = useNuxtApp(); +keyboard.init() onMounted(async () => { await listen("settings", async () => { - keyboard.clear(); await navigateTo("/settings"); await app.show(); await window.getCurrentWindow().show(); diff --git a/nuxt.config.ts b/nuxt.config.ts index a401152..071a2b2 100644 --- a/nuxt.config.ts +++ b/nuxt.config.ts @@ -3,7 +3,6 @@ export default defineNuxtConfig({ devtools: { enabled: false }, compatibilityDate: "2024-07-04", ssr: false, - modules: ["wrdu-keyboard"], vite: { css: { preprocessorOptions: { diff --git a/pages/index.vue b/pages/index.vue index 8f1ad50..5cf7816 100644 --- a/pages/index.vue +++ b/pages/index.vue @@ -174,7 +174,7 @@ import type { InfoColor, InfoCode, } from "~/types/types"; -import { Key } from "wrdu-keyboard/key"; +import { Key, keyboard } from "wrdu-keyboard"; interface GroupedHistory { label: string; @@ -207,8 +207,6 @@ const imageLoading = ref(false); const pageTitle = ref(""); const pageOgImage = ref(""); -const keyboard = useKeyboard(); - const isSameDay = (date1: Date, date2: Date): boolean => { return ( date1.getFullYear() === date2.getFullYear() && @@ -593,39 +591,70 @@ const setupEventListeners = async (): Promise => { } } focusSearchInput(); + + // Re-register keyboard shortcuts on focus + keyboard.clear(); + keyboard.prevent.down([Key.DownArrow], () => { + selectNext(); + }); + + keyboard.prevent.down([Key.UpArrow], () => { + selectPrevious(); + }); + + keyboard.prevent.down([Key.Enter], () => { + pasteSelectedItem(); + }); + + keyboard.prevent.down([Key.Escape], () => { + hideApp(); + }); + + switch (os.value) { + case "macos": + keyboard.prevent.down([Key.LeftMeta, Key.K], () => {}); + keyboard.prevent.down([Key.RightMeta, Key.K], () => {}); + break; + + case "linux": + case "windows": + keyboard.prevent.down([Key.LeftControl, Key.K], () => {}); + keyboard.prevent.down([Key.RightControl, Key.K], () => {}); + break; + } }); await listen("tauri://blur", () => { searchInput.value?.blur(); + keyboard.clear(); }); - keyboard.prevent.down([Key.DownArrow], (event) => { + keyboard.prevent.down([Key.DownArrow], () => { selectNext(); }); - keyboard.prevent.down([Key.UpArrow], (event) => { + keyboard.prevent.down([Key.UpArrow], () => { selectPrevious(); }); - keyboard.prevent.down([Key.Enter], (event) => { + keyboard.prevent.down([Key.Enter], () => { pasteSelectedItem(); }); - keyboard.prevent.down([Key.Escape], (event) => { + keyboard.prevent.down([Key.Escape], () => { hideApp(); }); switch (os.value) { case "macos": - keyboard.prevent.down([Key.LeftMeta, Key.K], (event) => {}); - - keyboard.prevent.down([Key.RightMeta, Key.K], (event) => {}); + keyboard.prevent.down([Key.LeftMeta, Key.K], () => {}); + keyboard.prevent.down([Key.RightMeta, Key.K], () => {}); break; - case "linux" || "windows": - keyboard.prevent.down([Key.LeftControl, Key.K], (event) => {}); - - keyboard.prevent.down([Key.RightControl, Key.K], (event) => {}); + case "linux": + case "windows": + keyboard.prevent.down([Key.LeftControl, Key.K], () => {}); + keyboard.prevent.down([Key.RightControl, Key.K], () => {}); break; } }; diff --git a/pages/settings.vue b/pages/settings.vue index f1f3c6c..aa1e16f 100644 --- a/pages/settings.vue +++ b/pages/settings.vue @@ -93,9 +93,9 @@ import { invoke } from "@tauri-apps/api/core"; import { onMounted, onUnmounted, reactive, ref } from "vue"; import { platform } from "@tauri-apps/plugin-os"; import { useRouter } from "vue-router"; -import { Key } from "wrdu-keyboard/key"; import { KeyValues, KeyLabels } from "../types/keys"; import { disable, enable } from "@tauri-apps/plugin-autostart"; +import { Key, keyboard } from "wrdu-keyboard"; const activeModifiers = reactive>(new Set()); const isKeybindInputFocused = ref(false); @@ -104,7 +104,6 @@ const keybindInput = ref(null); const lastBlurTime = ref(0); const os = ref(""); const router = useRouter(); -const keyboard = useKeyboard(); const showEmptyKeybindError = ref(false); const autostart = ref(false); const { $settings } = useNuxtApp(); @@ -189,13 +188,13 @@ const toggleAutostart = async () => { os.value = platform(); onMounted(async () => { - keyboard.down([Key.All], (event) => { + keyboard.prevent.down([Key.All], (event: KeyboardEvent) => { if (isKeybindInputFocused.value) { onKeyDown(event); } }); - keyboard.down([Key.Escape], (event) => { + keyboard.prevent.down([Key.Escape], () => { if (isKeybindInputFocused.value) { keybindInput.value?.blur(); } else { @@ -205,27 +204,28 @@ onMounted(async () => { switch (os.value) { case "macos": - keyboard.down([Key.LeftMeta, Key.Enter], (event) => { + keyboard.prevent.down([Key.LeftMeta, Key.Enter], () => { if (!isKeybindInputFocused.value) { saveKeybind(); } }); - keyboard.down([Key.RightMeta, Key.Enter], (event) => { + keyboard.prevent.down([Key.RightMeta, Key.Enter], () => { if (!isKeybindInputFocused.value) { saveKeybind(); } }); break; - case "linux" || "windows": - keyboard.down([Key.LeftControl, Key.Enter], (event) => { + case "linux": + case "windows": + keyboard.prevent.down([Key.LeftControl, Key.Enter], () => { if (!isKeybindInputFocused.value) { saveKeybind(); } }); - keyboard.down([Key.RightControl, Key.Enter], (event) => { + keyboard.prevent.down([Key.RightControl, Key.Enter], () => { if (!isKeybindInputFocused.value) { saveKeybind(); } diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index 179500c..bc3664f 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -129,4 +129,4 @@ fn main() { ) .run(tauri::generate_context!()) .expect("error while running tauri application"); -} +} \ No newline at end of file From dfd425ba0cdb9184c0bf997cba8907d747cbe1d5 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:59:06 +0100 Subject: [PATCH 69/97] fix: app icon showing up in mac dock --- src-tauri/src/main.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index bc3664f..a3d73b4 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -57,6 +57,7 @@ fn main() { .build() ) .setup(|app| { + app.set_activation_policy(tauri::ActivationPolicy::Accessory); let app_data_dir = app.path().app_data_dir().unwrap(); utils::logger::init_logger(&app_data_dir).expect("Failed to initialize logger"); From 50643c6a61cdd3e992ed0e7d15a95f7d14363c26 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:59:20 +0100 Subject: [PATCH 70/97] feat: add new SVG icon components for Cmd, Code, Ctrl, Enter, File, Image, K, Link, and Text --- components/Icons/Cmd.vue | 39 ++++++++++++++++++++++++++++++++++++ components/Icons/Code.vue | 16 +++++++++++++++ components/Icons/Ctrl.vue | 32 +++++++++++++++++++++++++++++ components/Icons/Enter.vue | 41 ++++++++++++++++++++++++++++++++++++++ components/Icons/File.vue | 16 +++++++++++++++ components/Icons/Image.vue | 15 ++++++++++++++ components/Icons/K.vue | 29 +++++++++++++++++++++++++++ components/Icons/Link.vue | 15 ++++++++++++++ components/Icons/Text.vue | 16 +++++++++++++++ 9 files changed, 219 insertions(+) create mode 100644 components/Icons/Cmd.vue create mode 100644 components/Icons/Code.vue create mode 100644 components/Icons/Ctrl.vue create mode 100644 components/Icons/Enter.vue create mode 100644 components/Icons/File.vue create mode 100644 components/Icons/Image.vue create mode 100644 components/Icons/K.vue create mode 100644 components/Icons/Link.vue create mode 100644 components/Icons/Text.vue diff --git a/components/Icons/Cmd.vue b/components/Icons/Cmd.vue new file mode 100644 index 0000000..7b18f3e --- /dev/null +++ b/components/Icons/Cmd.vue @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + diff --git a/components/Icons/Code.vue b/components/Icons/Code.vue new file mode 100644 index 0000000..975d5c2 --- /dev/null +++ b/components/Icons/Code.vue @@ -0,0 +1,16 @@ + + + + + + + diff --git a/components/Icons/Ctrl.vue b/components/Icons/Ctrl.vue new file mode 100644 index 0000000..60163bd --- /dev/null +++ b/components/Icons/Ctrl.vue @@ -0,0 +1,32 @@ + + + + + + + + + diff --git a/components/Icons/Enter.vue b/components/Icons/Enter.vue new file mode 100644 index 0000000..e6e50a0 --- /dev/null +++ b/components/Icons/Enter.vue @@ -0,0 +1,41 @@ + + + + + + + + + + diff --git a/components/Icons/File.vue b/components/Icons/File.vue new file mode 100644 index 0000000..1f64eba --- /dev/null +++ b/components/Icons/File.vue @@ -0,0 +1,16 @@ + + + + + + + diff --git a/components/Icons/Image.vue b/components/Icons/Image.vue new file mode 100644 index 0000000..ed67dbe --- /dev/null +++ b/components/Icons/Image.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/components/Icons/K.vue b/components/Icons/K.vue new file mode 100644 index 0000000..8bdc675 --- /dev/null +++ b/components/Icons/K.vue @@ -0,0 +1,29 @@ + + + + + + + + + + + + + diff --git a/components/Icons/Link.vue b/components/Icons/Link.vue new file mode 100644 index 0000000..2036f60 --- /dev/null +++ b/components/Icons/Link.vue @@ -0,0 +1,15 @@ + + + + + + + diff --git a/components/Icons/Text.vue b/components/Icons/Text.vue new file mode 100644 index 0000000..3b9eb2d --- /dev/null +++ b/components/Icons/Text.vue @@ -0,0 +1,16 @@ + + + + + + + From 7345483ba19cc3d01dec50431ee0d5f821f113b1 Mon Sep 17 00:00:00 2001 From: PandaDEV <70103896+0PandaDEV@users.noreply.github.com> Date: Fri, 14 Mar 2025 23:59:38 +0100 Subject: [PATCH 71/97] refactor: update Noise component styles and replace noise image with a new format; remove unused SVG icons --- components/Noise.vue | 16 +++++++++------- public/cmd.svg | 18 ------------------ public/ctrl.svg | 8 -------- public/enter.svg | 9 --------- public/icons/Code.svg | 7 ------- public/icons/File.svg | 7 ------- public/icons/Image.svg | 10 ---------- public/icons/Link.svg | 7 ------- public/icons/Text.svg | 7 ------- public/k.svg | 12 ------------ public/noise.png | Bin 3350 -> 0 bytes public/noise.webp | Bin 0 -> 382624 bytes 12 files changed, 9 insertions(+), 92 deletions(-) delete mode 100644 public/cmd.svg delete mode 100644 public/ctrl.svg delete mode 100644 public/enter.svg delete mode 100644 public/icons/Code.svg delete mode 100644 public/icons/File.svg delete mode 100644 public/icons/Image.svg delete mode 100644 public/icons/Link.svg delete mode 100644 public/icons/Text.svg delete mode 100644 public/k.svg delete mode 100644 public/noise.png create mode 100644 public/noise.webp diff --git a/components/Noise.vue b/components/Noise.vue index 67fcbff..9be572c 100644 --- a/components/Noise.vue +++ b/components/Noise.vue @@ -6,16 +6,18 @@ .noise { position: absolute; overflow: hidden; - top: 0; - left: 0; - width: 100vw; - height: 100vh; + top: 1px; + left: 1px; + width: calc(100vw - 2px); + height: calc(100vh - 2px); pointer-events: none; user-select: none; - z-index: -1; - background-image: url('/noise.png'); + background-image: url("/noise.webp"); background-repeat: repeat; image-rendering: pixelated; overflow: hidden; + mix-blend-mode: multiply; + opacity: .5; + border-radius: 12px; } - \ No newline at end of file + diff --git a/public/cmd.svg b/public/cmd.svg deleted file mode 100644 index fc2ea77..0000000 --- a/public/cmd.svg +++ /dev/null @@ -1,18 +0,0 @@ - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/public/ctrl.svg b/public/ctrl.svg deleted file mode 100644 index 91c0d68..0000000 --- a/public/ctrl.svg +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/public/enter.svg b/public/enter.svg deleted file mode 100644 index 7ede06c..0000000 --- a/public/enter.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/public/icons/Code.svg b/public/icons/Code.svg deleted file mode 100644 index 5c34abf..0000000 --- a/public/icons/Code.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/public/icons/File.svg b/public/icons/File.svg deleted file mode 100644 index 8341a3c..0000000 --- a/public/icons/File.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/public/icons/Image.svg b/public/icons/Image.svg deleted file mode 100644 index c177239..0000000 --- a/public/icons/Image.svg +++ /dev/null @@ -1,10 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/public/icons/Link.svg b/public/icons/Link.svg deleted file mode 100644 index 64cc120..0000000 --- a/public/icons/Link.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/public/icons/Text.svg b/public/icons/Text.svg deleted file mode 100644 index 0ca21e8..0000000 --- a/public/icons/Text.svg +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/public/k.svg b/public/k.svg deleted file mode 100644 index 64eb413..0000000 --- a/public/k.svg +++ /dev/null @@ -1,12 +0,0 @@ - - - - - - - - - - - - \ No newline at end of file diff --git a/public/noise.png b/public/noise.png deleted file mode 100644 index 2a0223ef2c8c6c3184206e86eacb248ce66c3fcd..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 3350 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 diff --git a/public/noise.webp b/public/noise.webp new file mode 100644 index 0000000000000000000000000000000000000000..b0e8845a350173061747f45b0718963b1e91f9a5 GIT binary patch literal 382624 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
Back
Qopy
Save
{{ row.label }}
Startup
Qopy Hotkey
Launch Qopy at login
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
O{le{6+2Sc279Ejq^6HhXUV3?y<;8xP>dtYa$|; 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
_<&2ybrM-WO_6r9j-|k;n#GKsX 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
2n1tAZj<{5f z!>N0n>>V9a<^A-eO2>ix1A=74R^uR?B_J<|ySOLaT-=+KDSw1+zL0^+BmA1;V1Y^E zW@_bwe@Q06Bdv+}a*3G;CVQ4~QNIV?gPz;730&WpUbYCtZ2YaeTV&?O!Sk3WdOdRi zmQ7%c-FqV$1<_5?no(S)cS@S^S*288$am$~caXY^?3%mt_T%h&8Y7NNFmm8MA7Xys zy!dF9c~^1OJ}$nZg3Lt`A`L-DuxcZvD_$A_t!u@coE7mPe?7}x5?~!2Y-uTn$8i0= z%y`YsUQ#Uz1MC0{mdpeu03cN%pfVQtb1J>Gdq2ioign)yonGnb~tcDL{vygMwgC(o)IZO%)957k&HK$w?Y^W*U_O|e`z#`ZFN zTl_N-!{yVDixo;j0_VUMYe_=SZr?3-Dq(ntk8EwYcN*jiEnyY~1B)rO+^T>8EW{11&0(TF-k~_6ZWcSg<5q zLPUF4iXqW6^>_PJGRCARxZN~8lED%iQ`A~+N9!eaRqv6dKS+`k%ArX3gdYnY8QWX1 z`}IXX6pMP^{gQ#g1Koa2H6qWNa0Cy6>c>?Xa$&y$Ak*cnLDq?Fr0XmHqX|J=pL1bxPfoA?ZFUya@>9 zhzck3OcDnRX4?gZ8bjYN;F7r@RBtCWgoKljkwF~Lq9hmQEeHC~9XaB$kOUD^{gswM zw<_3O*9#GW)}wM>V&VxZk5eUqO$NPSf3?m#mRwCd%W4jEx;##P`G3yYJuR1p`~p`Z zYxKb&B;$jZmZ2&_(evkLZ^^1>2NKPQVZ5z7A_Nzm!3C@lx+43= zuFJbOPD|@Bei&a2%y5@>M+cWp7jy{vGJsTj5kUov^_60^4e?5I{b8L+1uD!W!?c3R zpvD>5&*wCm;X; zpnIcsiFiU4(GrRiSyF%D=y*F^$|(EH;;x2jw@q)Z1cw}+T{NjpMD*(WwICO=W0ShJSA<