Skip to content

Commit 28ccd2a

Browse files
authored
Merge branch 'master' into clipboardhistory
2 parents 381e63e + 6f64ba5 commit 28ccd2a

12 files changed

Lines changed: 321 additions & 86 deletions

CONTRIBUTING.md

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,3 +20,28 @@ For bug fixes, and helping people to solve their github issues: see
2020
1. Code must compile...
2121
1. A video recording / screenshot would be an added bonus in getting your pull
2222
request merged faster.
23+
24+
## Codebase:
25+
26+
```
27+
.
28+
├── bundling # Bundling related stuff, ignore for the most bit
29+
│   ├── entitlements.plist
30+
│   ├── icon.icns
31+
│   └── Info.plist
32+
├── docs # Website and documentation related stuff. If something new is added to config, then modify this as well before PR-ing
33+
├── Cargo.lock
34+
├── Cargo.toml
35+
├── CONTRIBUTING.md
36+
├── EXTENSIONS.md
37+
├── LICENSE.md
38+
├── README.md
39+
└── src
40+
├── app.rs # Main app logic
41+
├── calculator.rs # Calculator logic
42+
├── commands.rs # Logic for different commands
43+
├── config.rs # Configuration related stuff
44+
├── macos.rs # Macos specific config
45+
├── main.rs # Start app
46+
└── utils.rs # Common functions that are used across files
47+
```

EXTENSIONS.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
# RUSTCAST EXTENSIONS
2+
3+
## Preamble:
4+
5+
RustCast doesn't support extensions yet.
6+
7+
However, it is in my todo list to support them.
8+
9+
This page is currently about methods that might be used to add extensions to
10+
RustCast.
11+
12+
## Methods:
13+
14+
1. Using an MCP server.
15+
- MCP Servers are used by GenAI to call functions and retrieve data.
16+
- Maybe if we stripped the AI from MCP Servers, they could be used for
17+
extensions, not just in rustcast, but in all projects.
18+
19+
1. Using WASM:
20+
- The way Zed does their extension support. Maybe I could also use that?
21+
- Their article can be found
22+
[here](https://zed.dev/blog/zed-decoded-extensions)

README.md

Lines changed: 14 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
> search bar that people can use to do many things, like opening apps,
55
> calculators, quick-notes, etc.
66
7-
![RustCast Demo PreRelease V1](docs/rustcast-demo-with-v0_1_0.png)
7+
![RustCast Demo PreRelease V1](./docs/rustcast-demo-v0-2-0.png)
88

99
## Installation:
1010

@@ -30,7 +30,7 @@ use spotlight :) Have fun!)
3030

3131
### Build it youself:
3232

33-
1. Clone the repo with `git clone https://gitub.com/unsecretised/rustcast.git`
33+
1. Clone the repo with `git clone https://github.com/unsecretised/rustcast.git`
3434
1. Install `cargo bundle` with `cargo install cargo-bundle` (Requires cargo to
3535
be installed)
3636
1. Run `cargo bundle --release` to build RustCast for your system (The App Dir
@@ -54,16 +54,15 @@ bit wonky, and will be fixed in the upcoming releases
5454

5555
### Planned:
5656

57-
- [ ] Select the options using arrow keys 13/12/2025
58-
- [ ] Calculator 15/12/2025
59-
- [ ] Popup note-taking 18/12/2025
60-
- [ ] Clipboard History 20/12/2025
61-
- [ ] Plugin Support 31/12/2025 (Partially implemented on 15/12/2025)
57+
- [ ] Select the options using arrow keys
58+
- [ ] Popup note-taking
59+
- [ ] Clipboard History
60+
- [ ] Plugin Support (Partially implemented on 15/12/2025)
6261
- [ ] Blur / transparent background (Partially implemented on 13/12/2025)
6362
- [ ] Hyperkey - Map CMD + OPT + CTRL + SHIFT to a physical key
6463
- [ ] Ability to pick between tabs in firefox / chromium browsers - using
6564
[Puppeteer](https://pptr.dev/)
66-
- [ ] Cross platform support - (1/2/2026)
65+
- [ ] Cross platform support
6766

6867
### Finished:
6968

@@ -76,14 +75,20 @@ bit wonky, and will be fixed in the upcoming releases
7675
- [x] Customisable themes (13/12/2025)
7776
- [x] Configurable colours
7877
- [x] Spotify control - Ability to control spotify via the app
78+
- [x] Allow variables to be passed into custom shell scripts.
7979
- [x] Google your query. Simply type your query, and then put a `?` at the end,
8080
and press enter
81+
- [x] Calculator (27/12/2025)
8182

82-
### Not Planned:
83+
### Not Possible by me:
8384

8485
- [ ] Tray Icon for quitting the app. One may ask why? Well, because I CAN'T GET
8586
IT TO WORK.. I've SPENT TOO LONG ON THIS
8687

88+
## If you like rustcast, consider starring it on github :)
89+
90+
[![Star History Chart](https://api.star-history.com/svg?repos=unsecretised/rustcast&type=date&legend=top-left)](https://www.star-history.com/#unsecretised/rustcast&type=date&legend=top-left)
91+
8792
## Motivations:
8893

8994
I didn't want to pay for raycast + wanted to get better at rust. Raycast in

docs/config.toml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,3 +28,11 @@ background_opacity = 1.0
2828
blur = false
2929
show_icons = true
3030
show_scroll_bar = true
31+
32+
# searching for `echo abcd > file.txt` will run `echo abcd > file.txt` as the shell command
33+
[[shells]]
34+
command = "echo "
35+
# icon_path is optional
36+
alias = "Variables 1" # the name that will be displayed in the results
37+
alias_lc = "var test" # the name used to search for it
38+

docs/index.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ <h1>
8686
<div class="preview-glow"></div>
8787

8888
<div class="preview-inner">
89-
<img src="rustcast-demo-with-v0_1_0.png" />
89+
<img src="rustcast-demo-v0-2-0.png" />
9090
</div>
9191

9292
<div class="ferris-tag">

docs/rustcast-demo-v0-2-0.png

381 KB
Loading

src/app.rs

Lines changed: 61 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
use crate::clipboard::ClipBoardContentType;
2+
use crate::calculator::Expression;
23
use crate::commands::Function;
34
use crate::config::Config;
45
use crate::macos::{focus_this_app, transform_process_to_ui_element};
@@ -26,15 +27,18 @@ use rayon::iter::{IntoParallelRefIterator, ParallelIterator};
2627
use rayon::slice::ParallelSliceMut;
2728

2829
use std::cmp::min;
29-
use std::fs;
3030
use std::time::Duration;
31+
use std::{fs, thread};
3132

3233
pub const WINDOW_WIDTH: f32 = 500.;
3334
pub const DEFAULT_WINDOW_HEIGHT: f32 = 65.;
3435

36+
pub const RUSTCAST_DESC_NAME: &str = "RustCast";
37+
3538
#[derive(Debug, Clone)]
3639
pub struct App {
3740
pub open_command: Function,
41+
pub desc: String,
3842
pub icons: Option<iced::widget::image::Handle>,
3943
pub name: String,
4044
pub name_lc: String,
@@ -45,23 +49,25 @@ impl App {
4549
vec![
4650
App {
4751
open_command: Function::Quit,
52+
desc: RUSTCAST_DESC_NAME.to_string(),
4853
icons: None,
4954
name: "Quit RustCast".to_string(),
5055
name_lc: "quit".to_string(),
5156
},
5257
App {
5358
open_command: Function::OpenPrefPane,
59+
desc: RUSTCAST_DESC_NAME.to_string(),
5460
icons: None,
5561
name: "Open RustCast Preferences".to_string(),
5662
name_lc: "settings".to_string(),
5763
},
5864
]
5965
}
6066

61-
pub fn render(&self, show_icons: bool) -> impl Into<iced::Element<'_, Message>> {
67+
pub fn render(&self, theme: &crate::config::Theme) -> impl Into<iced::Element<'_, Message>> {
6268
let mut tile = Row::new().width(Fill).height(55);
6369

64-
if show_icons {
70+
if theme.show_icons {
6571
if let Some(icon) = &self.icons {
6672
tile = tile
6773
.push(Viewer::new(icon).height(35).width(35))
@@ -75,26 +81,30 @@ impl App {
7581
}
7682
}
7783

78-
tile = tile
79-
.push(
80-
Button::new(
81-
Text::new(self.name.clone())
82-
.height(Fill)
83-
.width(Fill)
84-
.align_y(Vertical::Center),
85-
)
86-
.on_press(Message::RunFunction(self.open_command.clone()))
87-
.style(|_, _| iced::widget::button::Style {
88-
background: Some(iced::Background::Color(
89-
Theme::KanagawaDragon.palette().background,
90-
)),
91-
text_color: Theme::KanagawaDragon.palette().text,
92-
..Default::default()
93-
})
94-
.width(Fill)
95-
.height(55),
84+
tile = tile.push(
85+
Button::new(
86+
Text::new(&self.name)
87+
.height(Fill)
88+
.width(Fill)
89+
.color(theme.text_color(1.))
90+
.align_y(Vertical::Center),
9691
)
92+
.on_press(Message::RunFunction(self.open_command.clone()))
93+
.style(|_, _| iced::widget::button::Style {
94+
background: Some(iced::Background::Color(
95+
Theme::KanagawaDragon.palette().background,
96+
)),
97+
text_color: Theme::KanagawaDragon.palette().text,
98+
..Default::default()
99+
})
100+
.width(Fill)
101+
.height(55),
102+
);
103+
104+
tile = tile
105+
.push(container(Text::new(&self.desc).color(theme.text_color(0.4))).padding(15))
97106
.width(Fill);
107+
98108
container(tile)
99109
.style(|_| iced::widget::container::Style {
100110
text_color: Some(Theme::KanagawaDragon.palette().text),
@@ -207,7 +217,7 @@ impl Tile {
207217
frontmost: None,
208218
focused: false,
209219
config: config.clone(),
210-
theme: config.theme.to_owned().to_iced_theme(),
220+
theme: config.theme.to_owned().into(),
211221
open_hotkey_id: keybind_id,
212222
clipboard_content: vec![],
213223
page: Page::Main,
@@ -242,6 +252,7 @@ impl Tile {
242252
let rand_num = rand::random_range(0..100);
243253
self.results = vec![App {
244254
open_command: Function::RandomVar(rand_num),
255+
desc: "Easter egg".to_string(),
245256
icons: None,
246257
name: rand_num.to_string(),
247258
name_lc: String::new(),
@@ -257,6 +268,7 @@ impl Tile {
257268
self.results = vec![App {
258269
open_command: Function::GoogleSearch(self.query.clone()),
259270
icons: None,
271+
desc: "Search".to_string(),
260272
name: format!("Search for: {}", self.query),
261273
name_lc: String::new(),
262274
}];
@@ -271,10 +283,25 @@ impl Tile {
271283
}
272284

273285
self.handle_search_query_changed();
286+
287+
if self.results.is_empty()
288+
&& let Some(res) = Expression::from_str(&self.query)
289+
{
290+
self.results.push(App {
291+
open_command: Function::Calculate(res),
292+
desc: RUSTCAST_DESC_NAME.to_string(),
293+
icons: None,
294+
name: res.eval().to_string(),
295+
name_lc: "".to_string(),
296+
});
297+
}
274298
let new_length = self.results.len();
275299

276300
let max_elem = min(5, new_length);
301+
277302
if prev_size != new_length && self.page == Page::Main {
303+
thread::sleep(Duration::from_millis(30));
304+
278305
window::resize(
279306
id,
280307
iced::Size {
@@ -342,7 +369,7 @@ impl Tile {
342369
}
343370

344371
Message::RunFunction(command) => {
345-
command.execute(&self.config);
372+
command.execute(&self.config, &self.query);
346373

347374
if self.config.buffer_rules.clear_on_enter {
348375
window::latest()
@@ -404,9 +431,8 @@ impl Tile {
404431
let mut search_results = Column::new();
405432
for result in &self.results {
406433
search_results =
407-
search_results.push(result.render(self.config.theme.show_icons));
434+
search_results.push(result.render(self.config.theme));
408435
}
409-
410436
Column::new()
411437
.push(title_input)
412438
.push(scrollable(search_results))
@@ -476,13 +502,21 @@ impl Tile {
476502

477503
let mut exact: Vec<App> = filter_vec
478504
.par_iter()
479-
.filter(|x| x.name_lc == query)
505+
.filter(|x| match &x.open_command {
506+
Function::RunShellCommand(_, _) => x
507+
.name_lc
508+
.starts_with(query.split_once(" ").unwrap_or((&query, "")).0),
509+
_ => x.name_lc == query,
510+
})
480511
.cloned()
481512
.collect();
482513

483514
let mut prefix: Vec<App> = filter_vec
484515
.par_iter()
485-
.filter(|x| x.name_lc != query && x.name_lc.starts_with(&query))
516+
.filter(|x| match x.open_command {
517+
Function::RunShellCommand(_, _) => false,
518+
_ => x.name_lc != query && x.name_lc.starts_with(&query),
519+
})
486520
.cloned()
487521
.collect();
488522

0 commit comments

Comments
 (0)