Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5bfffa9
clean: run cargo fmt
Jan 20, 2026
0d423ea
refactor: use PathBuf instead of string
Jan 21, 2026
d91f119
doc: general
Jan 21, 2026
260d9a7
feat: add config entries and glob
Jan 21, 2026
79ee28e
fix: unbreak
Jan 21, 2026
64d2185
feat: working exclude pattern system
Jan 21, 2026
2310313
Add include patterns
Jan 21, 2026
c6dfb78
refactor: do the pattern parsing the right way + other stuff
Jan 21, 2026
ba10b99
clean: run cargo fmt
Jan 21, 2026
468e19c
Merge branch 'windows-support' into path_exclusions
Mnem42 Jan 21, 2026
9afbe9a
refactor: better error handling and stuff
Jan 21, 2026
52a3e61
clean: run clippy --fix and fmt
Jan 21, 2026
37e5ae5
clean: remove parse_patterns
Jan 21, 2026
c3f3403
Merge branch 'path_exclusions' of https://github.com/Mnem42/rustcast …
Jan 21, 2026
aac19f9
fix: this match
Jan 21, 2026
ebbb976
fix: general minor unbreaking
Jan 22, 2026
fb0726f
refactor: promote windows.rs into a directory and split
Jan 22, 2026
340fd70
clean: run clippy --fix and fmt
Jan 22, 2026
994d972
clean: comment out capture_frontmost + remove meaningless warn
Jan 22, 2026
ff35cd6
refactor: split unit conversion
Jan 22, 2026
d304761
docs: convert the tsdocs to rust docs
Jan 22, 2026
4156b58
feat: opening urls on all platforms
Jan 22, 2026
21d75d4
feat: general minor logging improvements
Jan 22, 2026
1eab43b
refactor: move debug stuff out of cfg'd stuff
Jan 22, 2026
3607173
refactor: make update handling logs trace
Jan 22, 2026
3eea53e
stuff
Jan 22, 2026
83e2aec
clean: run fmt and clippy
Jan 22, 2026
3d6ff63
clean: clear warning
Jan 22, 2026
b00cbba
feat: general app finding refactors
Jan 23, 2026
7020ef8
clean: run fmt and clippy --fix
Jan 23, 2026
bb7d177
fix: usage of index_exclude_patterns
Jan 23, 2026
527f77a
feat: make it possible to set depths for include files
Jan 23, 2026
173e356
fix: remove trailing slash
Jan 23, 2026
e7f33ca
general fixes for macos
Jan 26, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
298 changes: 297 additions & 1 deletion Cargo.lock

Large diffs are not rendered by default.

14 changes: 13 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,14 @@ edition = "2024"

[target.'cfg(target_os = "windows")'.dependencies]
winreg = "0.52"
windows = { version = "0.58", features = ["Win32_UI_WindowsAndMessaging", "Win32_Foundation", "Win32_Graphics_Gdi", "Win32_System_Com", "Win32_UI_Shell"] }
windows = { version = "0.58", features = [
"Win32_UI_WindowsAndMessaging",
"Win32_Foundation",
"Win32_Graphics_Gdi",
"Win32_System_Com",
"Win32_UI_Shell",
"Win32_Globalization"
]}

[target.'cfg(target_os = "macos")'.dependencies]
objc2 = "0.6.3"
Expand Down Expand Up @@ -36,6 +43,11 @@ tray-icon = "0.21.3"
url = "2.5.8"
tracing = "0.1.44"
tracing-subscriber = "0.3.22"
glob = "0.3.3"
open = "5.3.3"
regex = "1.12.2"
lnk = "0.6.3"
codepage = "0.1.2"

[package.metadata.bundle]
name = "RustCast"
Expand Down
1 change: 1 addition & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
//! Main logic for the app

use crate::commands::Function;
use iced::window::{self, Id, Settings};

Expand Down
16 changes: 8 additions & 8 deletions src/app/apps.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ use crate::{
app::{Message, Page, RUSTCAST_DESC_NAME},
clipboard::ClipBoardContentType,
commands::Function,
cross_platform::get_img_handle,
styles::{result_button_style, result_row_container_style},
utils::handle_from_icns,
};

/// This tells each "App" what to do when it is clicked, whether it is a function, a message, or a display
Expand Down Expand Up @@ -73,7 +73,7 @@ impl App {
App {
open_command: AppCommand::Function(Function::Quit),
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: "Quit RustCast".to_string(),
Expand All @@ -82,7 +82,7 @@ impl App {
App {
open_command: AppCommand::Function(Function::OpenPrefPane),
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: "Open RustCast Preferences".to_string(),
Expand All @@ -91,7 +91,7 @@ impl App {
App {
open_command: AppCommand::Message(Message::SwitchToPage(Page::EmojiSearch)),
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: "Search for an Emoji".to_string(),
Expand All @@ -100,7 +100,7 @@ impl App {
App {
open_command: AppCommand::Message(Message::SwitchToPage(Page::ClipboardHistory)),
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: "Clipboard History".to_string(),
Expand All @@ -109,7 +109,7 @@ impl App {
App {
open_command: AppCommand::Message(Message::ReloadConfig),
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: "Reload RustCast".to_string(),
Expand All @@ -118,7 +118,7 @@ impl App {
App {
open_command: AppCommand::Display,
desc: RUSTCAST_DESC_NAME.to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/Applications/Rustcast.app/Contents/Resources/icon.icns",
)),
name: format!("Current RustCast Version: {app_version}"),
Expand All @@ -129,7 +129,7 @@ impl App {
"/System/Library/CoreServices/Finder.app".to_string(),
)),
desc: "Application".to_string(),
icons: handle_from_icns(Path::new(
icons: get_img_handle(Path::new(
"/System/Library/CoreServices/Finder.app/Contents/Resources/Finder.icns",
)),
name: "Finder".to_string(),
Expand Down
16 changes: 12 additions & 4 deletions src/app/menubar.rs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ use tray_icon::{

use crate::{
app::{Message, tile::ExtSender},
cross_platform::{open_settings, open_url},
cross_platform::open_settings,
};

/// This creates a new menubar icon for the app
Expand Down Expand Up @@ -91,7 +91,9 @@ fn init_event_handler(sender: ExtSender, hotkey_id: u32) {
.spawn(async move { sender.clone().try_send(Message::HideTrayIcon).unwrap() });
}
"open_issue_page" => {
open_url("https://github.com/unsecretised/rustcast/issues/new");
if let Err(e) = open::that("https://github.com/unsecretised/rustcast/issues/new") {
tracing::error!("Error opening url: {}", e)
}
}
"show_rustcast" => {
runtime.spawn(async move {
Expand All @@ -102,13 +104,19 @@ fn init_event_handler(sender: ExtSender, hotkey_id: u32) {
});
}
"open_help_page" => {
open_url("https://github.com/unsecretised/rustcast/discussions/new?category=q-a");
if let Err(e) = open::that(
"https://github.com/unsecretised/rustcast/discussions/new?category=q-a",
) {
tracing::error!("Error opening url: {}", e)
}
}
"open_preferences" => {
open_settings();
}
"open_github_page" => {
open_url("https://github.com/unsecretised/rustcast");
if let Err(e) = open::that("https://github.com/unsecretised/rustcast") {
tracing::error!("Error opening url: {}", e)
}
}
_ => {}
}
Expand Down
33 changes: 22 additions & 11 deletions src/app/tile.rs
Original file line number Diff line number Diff line change
Expand Up @@ -217,19 +217,30 @@ impl Tile {
}

// Unused, keeping it for now
pub fn capture_frontmost(&mut self) {
#[cfg(target_os = "macos")]
{
use objc2_app_kit::NSWorkspace;
// pub fn capture_frontmost(&mut self) {
// #[cfg(target_os = "macos")]
// {
// use objc2_app_kit::NSWorkspace;

let ws = NSWorkspace::sharedWorkspace();
self.frontmost = ws.frontmostApplication();
};
// let ws = NSWorkspace::sharedWorkspace();
// self.frontmost = ws.frontmostApplication();
// };

#[cfg(target_os = "windows")]
{
self.frontmost = Some(unsafe { GetForegroundWindow() });
}
// #[cfg(target_os = "windows")]
// {
// use windows::Win32::UI::WindowsAndMessaging::GetForegroundWindow;

// self.frontmost = Some(unsafe { GetForegroundWindow() });
// }
// }

/// Gets the frontmost application to focus later.
#[cfg(target_os = "macos")]
pub fn capture_frontmost(&mut self) {
use objc2_app_kit::NSWorkspace;

let ws = NSWorkspace::sharedWorkspace();
self.frontmost = ws.frontmostApplication();
}

/// Restores the frontmost application.
Expand Down
15 changes: 12 additions & 3 deletions src/app/tile/elm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@ use iced::{Length::Fill, widget::text_input};

use rayon::slice::ParallelSliceMut;

#[cfg(target_os = "windows")]
use crate::app;
use crate::app::tile::AppIndex;
use crate::styles::{contents_style, rustcast_text_input_style};
use crate::utils::get_installed_apps;
use crate::utils::index_installed_apps;
use crate::{
app::{Message, Page, apps::App, default_settings, tile::Tile},
config::Config,
Expand Down Expand Up @@ -63,7 +65,7 @@ pub fn new(hotkey: HotKey, config: &Config) -> (Tile, Task<Message>) {
let (id, open) = window::open(settings);

#[cfg(target_os = "windows")]
let open: Task<iced::window::Id> = open.discard();
let open: Task<app::Message> = open.discard();

#[cfg(target_os = "macos")]
let open = open.discard().chain(window::run(id, |handle| {
Expand All @@ -72,7 +74,14 @@ pub fn new(hotkey: HotKey, config: &Config) -> (Tile, Task<Message>) {
Message::OpenWindow
}));

let mut options: Vec<App> = get_installed_apps(config);
let options = index_installed_apps(config);

if let Err(ref e) = options {
tracing::error!("Error indexing apps: {e}")
}

// Still try to load the rest
let mut options = options.unwrap_or_default();

options.extend(config.shells.iter().map(|x| x.to_app()));
options.extend(App::basic_apps());
Expand Down
34 changes: 20 additions & 14 deletions src/app/tile/update.rs
Original file line number Diff line number Diff line change
Expand Up @@ -33,17 +33,17 @@ use crate::clipboard::ClipBoardContentType;
use crate::commands::Function;
use crate::config::Config;
use crate::unit_conversion;
use crate::utils::get_installed_apps;

use crate::utils::is_valid_url;
use crate::utils::index_installed_apps;
#[cfg(target_os = "macos")]
use crate::{
cross_platform::macos::focus_this_app,
cross_platform::macos::haptics::{HapticPattern, perform_haptic},
};

pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
tracing::debug!("Handling update (message: {:?})", message);
tracing::trace!("Handling update (message: {:?})", message);

match message {
Message::OpenWindow => {
#[cfg(target_os = "macos")]
Expand Down Expand Up @@ -103,7 +103,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
}

tile.query_lc = input.trim().to_lowercase();
tile.query = input;
tile.query = input.clone();
let prev_size = tile.results.len();
if tile.query_lc.is_empty() && tile.page != Page::ClipboardHistory {
tile.results = vec![];
Expand Down Expand Up @@ -202,7 +202,7 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
}
})
.collect();
} else if tile.results.is_empty() && is_valid_url(&tile.query) {
} else if tile.results.is_empty() && url::Url::parse(&input).is_ok() {
tile.results.push(App {
open_command: AppCommand::Function(Function::OpenWebsite(tile.query.clone())),
desc: "Web Browsing".to_string(),
Expand Down Expand Up @@ -336,16 +336,22 @@ pub fn handle_update(tile: &mut Tile, message: Message) -> Task<Message> {
)
.unwrap();

let mut new_options: Vec<App> = get_installed_apps(&new_config);

new_options.extend(new_config.shells.iter().map(|x| x.to_app()));
new_options.extend(App::basic_apps());
new_options.par_sort_by_key(|x| x.name.len());
match index_installed_apps(&new_config) {
Ok(mut new_options) => {
new_options.extend(new_config.shells.iter().map(|x| x.to_app()));
new_options.extend(App::basic_apps());
new_options.par_sort_by_key(|x| x.name.len());

tile.theme = new_config.theme.to_owned().into();
tile.config = new_config;
tile.options = AppIndex::from_apps(new_options);
Task::none()
tile.theme = new_config.theme.to_owned().into();
tile.config = new_config;
tile.options = AppIndex::from_apps(new_options);
Task::none()
}
Err(e) => {
tracing::error!("Error reindexing apps: {}", e);
Task::none()
}
}
}

Message::KeyPressed(hk_id) => {
Expand Down
18 changes: 7 additions & 11 deletions src/commands.rs
Original file line number Diff line number Diff line change
Expand Up @@ -75,23 +75,15 @@ impl Function {
);
}

#[cfg(target_os = "macos")]
Function::OpenWebsite(url) => {
let open_url = if url.starts_with("http") {
url.to_owned()
} else {
format!("https://{}", url)
};

thread::spawn(move || {
NSWorkspace::new().openURL(
&NSURL::URLWithString_relativeToURL(
&objc2_foundation::NSString::from_str(&open_url),
None,
)
.unwrap(),
);
});
// Should never get here without it being validated first
open::that(open_url).unwrap();
}

Function::Calculate(expr) => {
Expand All @@ -110,8 +102,8 @@ impl Function {
}
},

#[cfg(target_os = "macos")]
Function::OpenPrefPane => {
#[cfg(target_os = "macos")]
thread::spawn(move || {
NSWorkspace::new().openURL(&NSURL::fileURLWithPath(
&objc2_foundation::NSString::from_str(
Expand All @@ -123,6 +115,10 @@ impl Function {
}

Function::Quit => std::process::exit(0),
f => {
// TODO: something in the UI to show this
tracing::error!("The function {:?} is unimplemented for this platform", f);
}
}
}
}
Loading