From a11bc181431e2abb17578ac8210bdc49cfe10c67 Mon Sep 17 00:00:00 2001
From: pandadev <70103896+0PandaDEV@users.noreply.github.com>
Date: Tue, 16 Jul 2024 17:22:21 +0200
Subject: [PATCH] fixed favicon support
---
app.vue | 43 ++++++--
src-tauri/Cargo.lock | 217 ++++++++++++++++++++++++++++++++++++-
src-tauri/Cargo.toml | 6 +-
src-tauri/src/clipboard.rs | 96 ++++++++++++----
src-tauri/src/database.rs | 3 +-
src-tauri/src/main.rs | 14 +--
6 files changed, 336 insertions(+), 43 deletions(-)
diff --git a/app.vue b/app.vue
index 7d0b524..560c294 100644
--- a/app.vue
+++ b/app.vue
@@ -32,7 +32,7 @@
:class="['result clothoid-corner', { 'selected': isSelected(groupIndex, index) }]"
@click="selectItem(groupIndex, index)"
:ref="el => { if (isSelected(groupIndex, index)) selectedElement = el }">
-
+
{{ truncateContent(item.content) }}
@@ -178,8 +178,12 @@ const truncateContent = (content) => {
};
const isUrl = (str) => {
- const urlPattern = /^(https?:\/\/)?(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)/;
- return urlPattern.test(str);
+ try {
+ new URL(str);
+ return true;
+ } catch {
+ return false;
+ }
};
const isYoutubeWatchUrl = (url) => {
@@ -191,19 +195,32 @@ const getYoutubeThumbnail = (url) => {
return `https://img.youtube.com/vi/${videoId}/0.jpg`;
};
-const getFavicon = (url) => {
- const domain = url.replace(/^(https?:\/\/)?(www\.)?/, '').split('/')[0];
- return `https://www.google.com/s2/favicons?domain=${domain}&sz=32`;
+const getFaviconFromDb = (favicon) => {
+ return `data:image/png;base64,${favicon}`;
};
const refreshHistory = async () => {
- const rawHistory = await db.value.select('SELECT * FROM history ORDER BY timestamp DESC');
- history.value = rawHistory.map(item => {
+ history.value = [];
+ await loadMoreHistory();
+};
+
+const loadMoreHistory = async () => {
+ const lastTimestamp = history.value.length > 0 ? history.value[history.value.length - 1].timestamp : '9999-12-31T23:59:59Z';
+ const batchSize = 100;
+
+ const rawHistory = await db.value.select(
+ 'SELECT * FROM history WHERE timestamp < ? ORDER BY timestamp DESC LIMIT ?',
+ [lastTimestamp, batchSize]
+ );
+
+ const newItems = rawHistory.map(item => {
if (item.type === 'image' && !item.content.startsWith('data:image')) {
return { ...item, content: `data:image/png;base64,${item.content}` };
}
return item;
});
+
+ history.value = [...history.value, ...newItems];
};
onMounted(async () => {
@@ -234,6 +251,13 @@ onMounted(async () => {
await listen('tauri://blur', hideApp);
await listen('tauri://focus', focusSearchInput);
focusSearchInput();
+
+ const resultsElement = resultsContainer.value.$el;
+ resultsElement.addEventListener('scroll', () => {
+ if (resultsElement.scrollTop + resultsElement.clientHeight >= resultsElement.scrollHeight - 100) {
+ loadMoreHistory();
+ }
+ });
});
const hideApp = async () => {
@@ -242,6 +266,7 @@ const hideApp = async () => {
};
const showApp = async () => {
+ history.value = [];
await refreshHistory();
await app.show();
await window.getCurrent().show();
@@ -295,4 +320,4 @@ watch([selectedGroupIndex, selectedItemIndex], scrollToSelectedItem);
+
\ No newline at end of file
diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock
index 6400856..cebe294 100644
--- a/src-tauri/Cargo.lock
+++ b/src-tauri/Cargo.lock
@@ -160,6 +160,12 @@ dependencies = [
"num-traits",
]
+[[package]]
+name = "atomic-waker"
+version = "1.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0"
+
[[package]]
name = "auto-launch"
version = "0.5.0"
@@ -987,6 +993,15 @@ version = "1.2.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4ef6b89e5b37196644d8796de5268852ff179b44e96276cf4290264843743bb7"
+[[package]]
+name = "encoding_rs"
+version = "0.8.34"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b45de904aa0b010bce2ab45264d0631681847fa7b6f2eaa7dab7619943bc4f59"
+dependencies = [
+ "cfg-if",
+]
+
[[package]]
name = "equivalent"
version = "1.0.1"
@@ -1594,6 +1609,25 @@ dependencies = [
"syn 2.0.68",
]
+[[package]]
+name = "h2"
+version = "0.4.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "fa82e28a107a8cc405f0839610bdc9b15f1e25ec7d696aa5cf173edbcb1486ab"
+dependencies = [
+ "atomic-waker",
+ "bytes",
+ "fnv",
+ "futures-core",
+ "futures-sink",
+ "http",
+ "indexmap 2.2.6",
+ "slab",
+ "tokio",
+ "tokio-util",
+ "tracing",
+]
+
[[package]]
name = "half"
version = "2.4.1"
@@ -1746,6 +1780,7 @@ dependencies = [
"bytes",
"futures-channel",
"futures-util",
+ "h2",
"http",
"http-body",
"httparse",
@@ -1756,6 +1791,39 @@ dependencies = [
"want",
]
+[[package]]
+name = "hyper-rustls"
+version = "0.27.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5ee4be2c948921a1a5320b629c4193916ed787a7f7f293fd3f7f5a6c9de74155"
+dependencies = [
+ "futures-util",
+ "http",
+ "hyper",
+ "hyper-util",
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+ "tokio-rustls",
+ "tower-service",
+]
+
+[[package]]
+name = "hyper-tls"
+version = "0.6.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0"
+dependencies = [
+ "bytes",
+ "http-body-util",
+ "hyper",
+ "hyper-util",
+ "native-tls",
+ "tokio",
+ "tokio-native-tls",
+ "tower-service",
+]
+
[[package]]
name = "hyper-util"
version = "0.1.6"
@@ -2537,6 +2605,15 @@ dependencies = [
"syn 1.0.109",
]
+[[package]]
+name = "num_threads"
+version = "0.1.7"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "5c7398b9c8b70908f6371f47ed36737907c87c52af34c268fed0bf0ceb92ead9"
+dependencies = [
+ "libc",
+]
+
[[package]]
name = "objc"
version = "0.2.7"
@@ -3146,12 +3223,15 @@ version = "0.1.0"
dependencies = [
"arboard",
"base64 0.22.1",
- "clipboard-win",
"image 0.25.1",
+ "log",
"rand 0.8.5",
"rdev",
+ "regex",
+ "reqwest",
"serde",
"serde_json",
+ "simplelog",
"sqlx",
"tauri",
"tauri-build",
@@ -3162,6 +3242,7 @@ dependencies = [
"tauri-plugin-sql",
"tauri-plugin-window-state",
"tokio",
+ "url",
]
[[package]]
@@ -3448,25 +3529,34 @@ checksum = "c7d6d2a27d57148378eb5e111173f4276ad26340ecc5c49a4a2152167a2d6a37"
dependencies = [
"base64 0.22.1",
"bytes",
+ "encoding_rs",
+ "futures-channel",
"futures-core",
"futures-util",
+ "h2",
"http",
"http-body",
"http-body-util",
"hyper",
+ "hyper-rustls",
+ "hyper-tls",
"hyper-util",
"ipnet",
"js-sys",
"log",
"mime",
+ "native-tls",
"once_cell",
"percent-encoding",
"pin-project-lite",
+ "rustls-pemfile",
"serde",
"serde_json",
"serde_urlencoded",
"sync_wrapper",
+ "system-configuration",
"tokio",
+ "tokio-native-tls",
"tokio-util",
"tower-service",
"url",
@@ -3486,6 +3576,21 @@ dependencies = [
"bytemuck",
]
+[[package]]
+name = "ring"
+version = "0.17.8"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c17fa4cb658e3583423e915b9f3acc01cceaee1860e33d59ebae66adc3a2dc0d"
+dependencies = [
+ "cc",
+ "cfg-if",
+ "getrandom 0.2.15",
+ "libc",
+ "spin",
+ "untrusted",
+ "windows-sys 0.52.0",
+]
+
[[package]]
name = "rsa"
version = "0.9.6"
@@ -3534,6 +3639,46 @@ dependencies = [
"windows-sys 0.52.0",
]
+[[package]]
+name = "rustls"
+version = "0.23.10"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "05cff451f60db80f490f3c182b77c35260baace73209e9cdbbe526bfe3a4d402"
+dependencies = [
+ "once_cell",
+ "rustls-pki-types",
+ "rustls-webpki",
+ "subtle",
+ "zeroize",
+]
+
+[[package]]
+name = "rustls-pemfile"
+version = "2.1.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "29993a25686778eb88d4189742cd713c9bce943bc54251a33509dc63cbacf73d"
+dependencies = [
+ "base64 0.22.1",
+ "rustls-pki-types",
+]
+
+[[package]]
+name = "rustls-pki-types"
+version = "1.7.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "976295e77ce332211c0d24d92c0e83e50f5c5f046d11082cea19f3df13a3562d"
+
+[[package]]
+name = "rustls-webpki"
+version = "0.102.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f9a6fccd794a42c2c105b513a2f62bc3fd8f3ba57a4593677ceb0bd035164d78"
+dependencies = [
+ "ring",
+ "rustls-pki-types",
+ "untrusted",
+]
+
[[package]]
name = "rustversion"
version = "1.0.17"
@@ -3866,6 +4011,17 @@ dependencies = [
"quote",
]
+[[package]]
+name = "simplelog"
+version = "0.12.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "16257adbfaef1ee58b1363bdc0664c9b8e1e30aed86049635fb5f147d065a9c0"
+dependencies = [
+ "log",
+ "termcolor",
+ "time",
+]
+
[[package]]
name = "siphasher"
version = "0.3.11"
@@ -4286,6 +4442,27 @@ dependencies = [
"libc",
]
+[[package]]
+name = "system-configuration"
+version = "0.5.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7"
+dependencies = [
+ "bitflags 1.3.2",
+ "core-foundation 0.9.4",
+ "system-configuration-sys",
+]
+
+[[package]]
+name = "system-configuration-sys"
+version = "0.5.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a75fb188eb626b924683e3b95e3a48e63551fcfb51949de2f06a9d91dbee93c9"
+dependencies = [
+ "core-foundation-sys 0.8.6",
+ "libc",
+]
+
[[package]]
name = "system-deps"
version = "6.2.2"
@@ -4693,6 +4870,15 @@ dependencies = [
"utf-8",
]
+[[package]]
+name = "termcolor"
+version = "1.4.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "06794f8f6c5c898b3275aebefa6b8a1cb24cd2c6c79397ab15774837a0bc5755"
+dependencies = [
+ "winapi-util",
+]
+
[[package]]
name = "thin-slice"
version = "0.1.1"
@@ -4748,7 +4934,9 @@ checksum = "5dfd88e563464686c916c7e46e623e520ddc6d79fa6641390f2e3fa86e83e885"
dependencies = [
"deranged",
"itoa 1.0.11",
+ "libc",
"num-conv",
+ "num_threads",
"powerfmt",
"serde",
"time-core",
@@ -4816,6 +5004,27 @@ dependencies = [
"syn 2.0.68",
]
+[[package]]
+name = "tokio-native-tls"
+version = "0.3.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bbae76ab933c85776efabc971569dd6119c580d8f5d448769dec1764bf796ef2"
+dependencies = [
+ "native-tls",
+ "tokio",
+]
+
+[[package]]
+name = "tokio-rustls"
+version = "0.26.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c7bc40d0e5a97695bb96e27995cd3a08538541b0a846f65bba7a359f36700d4"
+dependencies = [
+ "rustls",
+ "rustls-pki-types",
+ "tokio",
+]
+
[[package]]
name = "tokio-stream"
version = "0.1.15"
@@ -5106,6 +5315,12 @@ version = "0.1.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39ec24b3121d976906ece63c9daad25b85969647682eee313cb5779fdd69e14e"
+[[package]]
+name = "untrusted"
+version = "0.9.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "8ecb6da28b8a351d773b68d5825ac39017e680750f980f3a1a85cd8dd28a47c1"
+
[[package]]
name = "url"
version = "2.5.2"
diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml
index 7b06e2d..fd6f731 100644
--- a/src-tauri/Cargo.toml
+++ b/src-tauri/Cargo.toml
@@ -26,4 +26,8 @@ rand = "0.8"
base64 = "0.22.1"
arboard = "3.4.0"
image = "0.25.1"
-clipboard-win = "5.3.1"
+reqwest = { version = "0.12.5", features = ["blocking"] }
+url = "2.5.2"
+log = "0.4"
+simplelog = "0.12.2"
+regex = "1"
\ No newline at end of file
diff --git a/src-tauri/src/clipboard.rs b/src-tauri/src/clipboard.rs
index ca57960..a2df6ef 100644
--- a/src-tauri/src/clipboard.rs
+++ b/src-tauri/src/clipboard.rs
@@ -1,6 +1,5 @@
use base64::engine::general_purpose::STANDARD;
use base64::Engine;
-use clipboard_win::{formats, get_clipboard, is_format_avail};
use rand::distributions::Alphanumeric;
use rand::{thread_rng, Rng};
use rdev::{listen, simulate, EventType, Key};
@@ -10,6 +9,10 @@ use std::thread;
use std::time::Duration;
use tauri::Manager;
use tokio::runtime::Runtime;
+use url::Url;
+use reqwest::Client;
+use arboard::Clipboard;
+use regex::Regex;
#[tauri::command]
pub fn simulate_paste() {
@@ -30,6 +33,7 @@ pub fn simulate_paste() {
pub fn setup(app_handle: tauri::AppHandle) {
let (tx, rx) = mpsc::channel();
+ let mut is_processing = false;
std::thread::spawn(move || {
listen(move |event| match event.event_type {
@@ -37,34 +41,31 @@ pub fn setup(app_handle: tauri::AppHandle) {
let _ = tx.send(true);
}
EventType::KeyRelease(Key::KeyC) => {
- if rx.try_recv().is_ok() {
+ if rx.try_recv().is_ok() && !is_processing {
+ is_processing = true;
let pool = app_handle.state::();
let rt = app_handle.state::();
- if let Ok(content) = get_clipboard(formats::Unicode) {
+ let mut clipboard = Clipboard::new().unwrap();
+
+ if let Ok(content) = clipboard.get_text() {
rt.block_on(async {
insert_content_if_not_exists(&pool, "text", content).await;
});
}
- if is_format_avail(formats::Bitmap.into()) {
- match get_clipboard(formats::Bitmap) {
- Ok(image) => {
- rt.block_on(async {
- let base64_image = STANDARD.encode(&image);
- insert_content_if_not_exists(&pool, "image", base64_image)
- .await;
- });
- }
- Err(e) => {
- println!("Error reading image from clipboard: {:?}", e);
- }
- }
- } else {
- println!("No image format available in clipboard");
+ if let Ok(image) = clipboard.get_image() {
+ rt.block_on(async {
+ let base64_image = STANDARD.encode(&image.bytes);
+ insert_content_if_not_exists(&pool, "image", base64_image).await;
+ });
}
+ is_processing = false;
}
}
+ EventType::KeyRelease(Key::ControlLeft | Key::ControlRight) => {
+ is_processing = false;
+ }
_ => {}
})
.unwrap();
@@ -72,27 +73,74 @@ pub fn setup(app_handle: tauri::AppHandle) {
}
async fn insert_content_if_not_exists(pool: &SqlitePool, content_type: &str, content: String) {
- let exists: bool = sqlx::query_scalar(
- "SELECT EXISTS(SELECT 1 FROM history WHERE content_type = ? AND content = ?)",
+ let last_content: Option = sqlx::query_scalar(
+ "SELECT content FROM history WHERE content_type = ? ORDER BY timestamp DESC LIMIT 1",
)
.bind(content_type)
- .bind(&content)
.fetch_one(pool)
.await
- .unwrap_or(false);
+ .unwrap_or(None);
- if !exists {
+ if last_content.as_deref() != Some(&content) {
let id: String = thread_rng()
.sample_iter(&Alphanumeric)
.take(16)
.map(char::from)
.collect();
- let _ = sqlx::query("INSERT INTO history (id, content_type, content) VALUES (?, ?, ?)")
+ let url_regex = Regex::new(r"https?:\/\/(www\.)?[-a-zA-Z0-9@:%._\+~#=]{1,256}\.[a-zA-Z0-9()]{1,6}\b([-a-zA-Z0-9()@:%_\+.~#?&//=]*)").unwrap();
+ let favicon_base64 = if content_type == "text" {
+ if let Some(url_match) = url_regex.find(&content) {
+ let url_str = url_match.as_str();
+ match Url::parse(url_str) {
+ Ok(url) => {
+ match fetch_favicon_as_base64(url).await {
+ Ok(Some(favicon)) => {
+ println!("Favicon fetched successfully.");
+ Some(favicon)
+ },
+ Ok(None) => {
+ println!("No favicon found.");
+ None
+ },
+ Err(e) => {
+ println!("Failed to fetch favicon: {}", e);
+ None
+ }
+ }
+ },
+ Err(e) => {
+ println!("Failed to parse URL: {}", e);
+ None
+ }
+ }
+ } else {
+ None
+ }
+ } else {
+ None
+ };
+
+ let _ = sqlx::query("INSERT INTO history (id, content_type, content, favicon) VALUES (?, ?, ?, ?)")
.bind(id)
.bind(content_type)
.bind(content)
+ .bind(favicon_base64)
.execute(pool)
.await;
}
}
+
+async fn fetch_favicon_as_base64(url: Url) -> Result