From 24c79565832a1898f1c1cf39634cb40271394a78 Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 30 Jul 2026 09:42:17 +0800 Subject: [PATCH 1/5] fix #311 --- src/{platform/mod.rs => platform.rs} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/{platform/mod.rs => platform.rs} (100%) diff --git a/src/platform/mod.rs b/src/platform.rs similarity index 100% rename from src/platform/mod.rs rename to src/platform.rs From ae976baf4993c8ec557ef5bb5191443ad720f7bf Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 30 Jul 2026 09:42:23 +0800 Subject: [PATCH 2/5] fix unused import --- src/app/tile/elm.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/src/app/tile/elm.rs b/src/app/tile/elm.rs index 34ba6c8..dd8fab1 100644 --- a/src/app/tile/elm.rs +++ b/src/app/tile/elm.rs @@ -21,7 +21,6 @@ use crate::app::pages::settings::settings_page; use crate::app::tile::{AppIndex, Hotkeys}; use crate::app::{DEFAULT_WINDOW_HEIGHT, HotkeyCapture, SettingsTab, ToApp, ToApps}; use crate::config::Theme; -use crate::database::load_clipboard; use crate::debounce::Debouncer; use crate::platform::macos::events::Event; use crate::styles::{ From 0a9a6c8c6ce964800fbd5bb8bdff22e1f627ac9d Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 30 Jul 2026 09:52:07 +0800 Subject: [PATCH 3/5] use nucleo-matcher for fuzzysearch --- Cargo.lock | 11 +++++++++++ Cargo.toml | 1 + src/app/tile.rs | 24 +++++++++++++++++------- 3 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index dafbbe2..7775e59 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2960,6 +2960,16 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "nucleo-matcher" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf33f538733d1a5a3494b836ba913207f14d9d4a1d3cd67030c5061bdd2cac85" +dependencies = [ + "memchr", + "unicode-segmentation", +] + [[package]] name = "num-bigint" version = "0.4.8" @@ -4292,6 +4302,7 @@ dependencies = [ "libc", "log", "minreq", + "nucleo-matcher", "objc2 0.6.4", "objc2-app-kit 0.3.2", "objc2-application-services", diff --git a/Cargo.toml b/Cargo.toml index 7bb7ed1..a1a3eb2 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,6 +21,7 @@ image = { version = "0.25.9", features = ["tiff"] } libc = "0.2.180" log = "0.4.29" minreq = { version = "2.14.1", features = ["https"] } +nucleo-matcher = "0.3.1" objc2 = "0.6.3" objc2-app-kit = { version = "0.3.2", features = ["NSImage", "NSScreen"] } objc2-application-services = { version = "0.3.2", default-features = false, features = ["HIServices", "Processes"] } diff --git a/src/app/tile.rs b/src/app/tile.rs index b81f2e2..e881200 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -25,6 +25,8 @@ use iced::{ use iced::{event, window}; use log::{info, warn}; +use nucleo_matcher::pattern::{CaseMatching, Normalization, Pattern}; +use nucleo_matcher::{Matcher, Utf32Str}; use objc2::rc::Retained; use objc2_app_kit::NSRunningApplication; use rayon::iter::{IntoParallelRefIterator, ParallelIterator}; @@ -34,6 +36,7 @@ use tokio::io::{AsyncBufReadExt, AsyncRead}; use tray_icon::TrayIcon; use url::Url; +use std::cell::RefCell; use std::collections::HashMap; use std::fmt::Debug; use std::time::Duration; @@ -56,15 +59,22 @@ struct AppIndex { impl AppIndex { /// Search for an element in the index that starts with the provided prefix fn search_prefix<'a>(&'a self, prefix: &'a str) -> impl ParallelIterator + 'a { + let pattern = Pattern::parse(prefix, CaseMatching::Ignore, Normalization::Smart); + self.by_name.par_iter().filter_map(move |(name, app)| { - if name.starts_with(prefix) - || name.contains(format!(" {prefix}").as_str()) - || name.contains(format!("-{prefix}").as_str()) - { - Some(app) - } else { - None + thread_local! { + static MATCHER: RefCell = RefCell::new( + Matcher::new(nucleo_matcher::Config::DEFAULT.match_paths()) + ); } + + MATCHER.with(|m| { + let mut buf = Vec::new(); + let haystack = Utf32Str::new(name, &mut buf); + pattern + .score(haystack, &mut m.borrow_mut()) + .map(|_score| app) + }) }) } From 507b05815ca5f8d66bbb9b4d06e0159517f28a8d Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 30 Jul 2026 09:58:13 +0800 Subject: [PATCH 4/5] fix tests --- src/app/tile.rs | 9 ++++++++- src/clipboard.rs | 12 ------------ 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/src/app/tile.rs b/src/app/tile.rs index e881200..ca355be 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -716,7 +716,14 @@ mod tests { .map(|app| app.display_name.clone()) .collect(); - assert_eq!(prefix_results, vec!["Safari".to_string()]); + assert_eq!( + prefix_results, + vec![ + "Visual Studio Code".to_string(), + "Signal Desktop".to_string(), + "Safari".to_string() + ] + ); assert_eq!(spaced_results, vec!["Visual Studio Code".to_string()]); assert_eq!(hyphen_results, vec!["Signal Desktop".to_string()]); } diff --git a/src/clipboard.rs b/src/clipboard.rs index 98eb11e..f215d78 100644 --- a/src/clipboard.rs +++ b/src/clipboard.rs @@ -109,16 +109,4 @@ mod tests { ClipBoardContentType::Text("world".to_string()) ); } - - #[test] - fn clipboard_to_app_truncates_and_uses_first_line_for_display() { - let item = - ClipBoardContentType::Text("abcdefghijklmnopqrstuvwxyz\nsecond line".to_string()); - - // let app = item.to_app(); - - // assert_eq!(app.display_name, "abcdefghijklmnopqrstuvwxy"); - // assert_eq!(app.search_name, "abcdefghijklmnopqrstuvwxy"); - // assert_eq!(app.desc, "Clipboard Item"); - } } From 7df308a4b162e01fb19b927eb48ad24ec8e9c84c Mon Sep 17 00:00:00 2001 From: unsecretised Date: Thu, 30 Jul 2026 12:54:33 +0800 Subject: [PATCH 5/5] fix test --- src/app/tile.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/app/tile.rs b/src/app/tile.rs index ca355be..7e340d7 100644 --- a/src/app/tile.rs +++ b/src/app/tile.rs @@ -716,13 +716,10 @@ mod tests { .map(|app| app.display_name.clone()) .collect(); - assert_eq!( - prefix_results, - vec![ - "Visual Studio Code".to_string(), - "Signal Desktop".to_string(), - "Safari".to_string() - ] + assert!( + prefix_results.contains(&"Visual Studio Code".to_string()) + && prefix_results.contains(&"Signal Desktop".to_string()) + && prefix_results.contains(&"Safari".to_string()), ); assert_eq!(spaced_results, vec!["Visual Studio Code".to_string()]); assert_eq!(hyphen_results, vec!["Signal Desktop".to_string()]);