mirror of
https://github.com/ByteAtATime/raycast-linux.git
synced 2025-12-23 10:11:57 +00:00
feat: add clipboard history tracking
This commit is contained in:
parent
bd703cbf36
commit
87ffb66766
4 changed files with 216 additions and 2 deletions
76
Cargo.lock
generated
76
Cargo.lock
generated
|
|
@ -189,6 +189,7 @@ dependencies = [
|
|||
"parking_lot",
|
||||
"percent-encoding",
|
||||
"windows-sys 0.60.2",
|
||||
"wl-clipboard-rs",
|
||||
"x11rb",
|
||||
]
|
||||
|
||||
|
|
@ -411,7 +412,7 @@ dependencies = [
|
|||
"anyhow",
|
||||
"arrayvec",
|
||||
"log",
|
||||
"nom",
|
||||
"nom 8.0.0",
|
||||
"num-rational",
|
||||
"v_frame",
|
||||
]
|
||||
|
|
@ -1430,6 +1431,12 @@ version = "0.1.4"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
|
||||
|
||||
[[package]]
|
||||
name = "fixedbitset"
|
||||
version = "0.4.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0ce7134b9999ecaf8bcd65542e436736ef32ddca1b3e06094cb6ec5755203b80"
|
||||
|
||||
[[package]]
|
||||
name = "flare"
|
||||
version = "0.1.0"
|
||||
|
|
@ -2954,6 +2961,12 @@ version = "0.3.17"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.8.9"
|
||||
|
|
@ -3109,6 +3122,16 @@ dependencies = [
|
|||
"memoffset",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "8.0.0"
|
||||
|
|
@ -3671,6 +3694,16 @@ dependencies = [
|
|||
"pin-project-lite",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "os_pipe"
|
||||
version = "1.2.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"windows-sys 0.45.0",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "owned_ttf_parser"
|
||||
version = "0.25.1"
|
||||
|
|
@ -3727,6 +3760,16 @@ version = "2.3.2"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
|
||||
|
||||
[[package]]
|
||||
name = "petgraph"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b4c5cc86750666a3ed20bdaf5ca2a0344f9c67674cae0515bec2da16fbaa47db"
|
||||
dependencies = [
|
||||
"fixedbitset",
|
||||
"indexmap",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "phf"
|
||||
version = "0.13.1"
|
||||
|
|
@ -5212,6 +5255,18 @@ dependencies = [
|
|||
"once_cell",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tree_magic_mini"
|
||||
version = "3.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f943391d896cdfe8eec03a04d7110332d445be7df856db382dd96a730667562c"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"nom 7.1.3",
|
||||
"once_cell",
|
||||
"petgraph",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "try-lock"
|
||||
version = "0.2.5"
|
||||
|
|
@ -6510,6 +6565,25 @@ version = "0.46.0"
|
|||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
|
||||
|
||||
[[package]]
|
||||
name = "wl-clipboard-rs"
|
||||
version = "0.9.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb"
|
||||
dependencies = [
|
||||
"libc",
|
||||
"log",
|
||||
"os_pipe",
|
||||
"rustix 0.38.44",
|
||||
"tempfile",
|
||||
"thiserror 2.0.17",
|
||||
"tree_magic_mini",
|
||||
"wayland-backend",
|
||||
"wayland-client",
|
||||
"wayland-protocols",
|
||||
"wayland-protocols-wlr",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "writeable"
|
||||
version = "0.6.2"
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ phf = { version = "0.13.1", features = ["macros"] }
|
|||
rmp-serde = "1.3.0"
|
||||
dirs = "6.0.0"
|
||||
webbrowser = "1.0.6"
|
||||
arboard = "3.6.1"
|
||||
arboard = { version = "3.6.1", features = ["wayland-data-control"] }
|
||||
walkdir = "2.5.0"
|
||||
rayon = "1.11.0"
|
||||
which = "8.0.0"
|
||||
|
|
|
|||
135
src/clipboard_history.rs
Normal file
135
src/clipboard_history.rs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
use std::collections::VecDeque;
|
||||
use std::fs;
|
||||
use std::path::PathBuf;
|
||||
use std::sync::{LazyLock, Mutex, Once};
|
||||
use std::thread;
|
||||
use std::time::Duration;
|
||||
|
||||
const MAX_HISTORY_SIZE: usize = 100;
|
||||
const POLL_INTERVAL_MS: u64 = 500;
|
||||
const HISTORY_FILE: &str = "clipboard_history.json";
|
||||
|
||||
static CLIPBOARD_HISTORY: LazyLock<Mutex<VecDeque<ClipboardEntry>>> =
|
||||
LazyLock::new(|| Mutex::new(VecDeque::new()));
|
||||
|
||||
static LAST_CONTENT: LazyLock<Mutex<String>> = LazyLock::new(|| Mutex::new(String::new()));
|
||||
|
||||
static INIT: Once = Once::new();
|
||||
|
||||
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
|
||||
pub struct ClipboardEntry {
|
||||
pub content: String,
|
||||
pub timestamp: u64,
|
||||
}
|
||||
|
||||
fn get_history_path() -> PathBuf {
|
||||
if let Some(data_dir) = dirs::data_dir() {
|
||||
return data_dir.join("flare").join(HISTORY_FILE);
|
||||
}
|
||||
if let Some(home_dir) = dirs::home_dir() {
|
||||
return home_dir.join(".flare").join(HISTORY_FILE);
|
||||
}
|
||||
PathBuf::from(HISTORY_FILE)
|
||||
}
|
||||
|
||||
fn load_history() -> VecDeque<ClipboardEntry> {
|
||||
let path = get_history_path();
|
||||
if let Ok(data) = fs::read_to_string(&path) {
|
||||
if let Ok(entries) = serde_json::from_str::<Vec<ClipboardEntry>>(&data) {
|
||||
return VecDeque::from(entries);
|
||||
}
|
||||
}
|
||||
VecDeque::new()
|
||||
}
|
||||
|
||||
fn save_history(history: &VecDeque<ClipboardEntry>) {
|
||||
let path = get_history_path();
|
||||
if let Some(parent) = path.parent() {
|
||||
let _ = fs::create_dir_all(parent);
|
||||
}
|
||||
|
||||
let entries: Vec<_> = history.iter().collect();
|
||||
if let Ok(data) = serde_json::to_string(&entries) {
|
||||
let _ = fs::write(&path, data);
|
||||
}
|
||||
}
|
||||
|
||||
fn add_entry(content: String) {
|
||||
let mut history = CLIPBOARD_HISTORY.lock().unwrap();
|
||||
|
||||
if let Some(front) = history.front() {
|
||||
if front.content == content {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
let timestamp = std::time::SystemTime::now()
|
||||
.duration_since(std::time::UNIX_EPOCH)
|
||||
.map(|d| d.as_secs())
|
||||
.unwrap_or(0);
|
||||
|
||||
let entry = ClipboardEntry { content, timestamp };
|
||||
|
||||
history.push_front(entry);
|
||||
|
||||
while history.len() > MAX_HISTORY_SIZE {
|
||||
history.pop_back();
|
||||
}
|
||||
|
||||
save_history(&history);
|
||||
}
|
||||
|
||||
fn poll_clipboard() {
|
||||
loop {
|
||||
let result = arboard::Clipboard::new().and_then(|mut c| c.get_text());
|
||||
|
||||
if let Ok(text) = result {
|
||||
if !text.is_empty() {
|
||||
let mut last = LAST_CONTENT.lock().unwrap();
|
||||
if *last != text {
|
||||
*last = text.clone();
|
||||
drop(last);
|
||||
add_entry(text);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
thread::sleep(Duration::from_millis(POLL_INTERVAL_MS));
|
||||
}
|
||||
}
|
||||
|
||||
pub fn init() {
|
||||
INIT.call_once(|| {
|
||||
let loaded = load_history();
|
||||
{
|
||||
let mut history = CLIPBOARD_HISTORY.lock().unwrap();
|
||||
*history = loaded;
|
||||
}
|
||||
|
||||
if let Some(entry) = CLIPBOARD_HISTORY.lock().unwrap().front() {
|
||||
let mut last = LAST_CONTENT.lock().unwrap();
|
||||
*last = entry.content.clone();
|
||||
}
|
||||
|
||||
thread::spawn(poll_clipboard);
|
||||
});
|
||||
}
|
||||
|
||||
pub fn get_history() -> Vec<ClipboardEntry> {
|
||||
let history = CLIPBOARD_HISTORY.lock().unwrap();
|
||||
history.iter().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn clear_history() {
|
||||
let mut history = CLIPBOARD_HISTORY.lock().unwrap();
|
||||
history.clear();
|
||||
save_history(&history);
|
||||
}
|
||||
|
||||
pub fn remove_entry(index: usize) {
|
||||
let mut history = CLIPBOARD_HISTORY.lock().unwrap();
|
||||
if index < history.len() {
|
||||
history.remove(index);
|
||||
save_history(&history);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
mod apps;
|
||||
mod clipboard;
|
||||
mod clipboard_history;
|
||||
mod components;
|
||||
mod deep_link;
|
||||
mod encryption;
|
||||
|
|
@ -243,6 +244,8 @@ fn run_daemon() -> Result<(), Box<dyn std::error::Error>> {
|
|||
eprintln!("Failed to register deep links: {}", e);
|
||||
}
|
||||
|
||||
clipboard_history::init();
|
||||
|
||||
let flare_settings = preferences::FlareSettings::load();
|
||||
|
||||
let result = if flare_settings.use_layer_shell {
|
||||
|
|
@ -291,6 +294,8 @@ fn run_daemon() -> Result<(), Box<dyn std::error::Error>> {
|
|||
}
|
||||
}
|
||||
|
||||
clipboard_history::init();
|
||||
|
||||
let result = iced::daemon(boot, update, daemon_view)
|
||||
.subscription(subscription)
|
||||
.title("Flare")
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue