feat: show toast in client

I honestly have no idea what's happening here, some goofy magic with Rust I guess
This commit is contained in:
ByteAtATime 2025-10-29 20:35:26 -07:00
parent 11ddac30dd
commit 9f52ff9193
3 changed files with 63 additions and 13 deletions

View file

@ -33,11 +33,11 @@ const raycastApi = {
public primaryAction: RaycastApiType.Toast.ActionOptions | undefined;
constructor(private options: ToastOptions) {
rustyscript.functions.showToast(options);
console.log(`new toast ${JSON.stringify(options)}`);
}
public show = () => {
public show = async () => {
await rustyscript.async_functions.showToast(this.options);
console.log(`show toast ${JSON.stringify(this.options)}`);
};
},

View file

@ -1,5 +1,5 @@
declare namespace rustyscript {
declare namespace functions {
function showToast(toast: import("./index").ToastOptions): void;
declare namespace async_functions {
function showToast(toast: import("./index").ToastOptions): Promise<void>;
}
}

View file

@ -1,23 +1,60 @@
use iced::Element;
use iced::futures::channel::mpsc;
use iced::futures::{SinkExt, StreamExt};
use iced::widget::{button, text};
use iced::{Subscription, futures};
use rustyscript::{Module, Runtime, RuntimeOptions, serde_json::Value};
use std::sync::Mutex;
static SENDER: Mutex<Option<mpsc::UnboundedSender<Message>>> = Mutex::new(None);
static RECEIVER: Mutex<Option<mpsc::UnboundedReceiver<Message>>> = Mutex::new(None);
#[derive(serde::Deserialize)]
struct ToastOptions {
title: String,
message: Option<String>,
style: Option<String>,
}
#[derive(Default)]
struct State {
toast_message: String,
}
#[derive(Debug, Clone)]
enum Message {
Increment,
UpdateToast(String),
}
fn view(counter: &u64) -> Element<Message> {
button(text(counter)).on_press(Message::Increment).into()
fn view(state: &State) -> Element<'_, Message> {
button(text(&state.toast_message)).into()
}
fn update(counter: &mut u64, message: Message) {
fn update(state: &mut State, message: Message) {
match message {
Message::Increment => *counter += 1,
Message::UpdateToast(new_message) => state.toast_message = new_message,
}
}
fn subscription(_state: &State) -> Subscription<Message> {
struct ToastListener;
if let Some(receiver) = RECEIVER.lock().unwrap().take() {
let stream = futures::stream::unfold(receiver, |mut receiver| async {
receiver.next().await.map(|message| (message, receiver))
});
Subscription::run_with_id(std::any::TypeId::of::<ToastListener>(), stream)
} else {
Subscription::none()
}
}
fn main() -> Result<(), rustyscript::Error> {
let (sender, receiver) = mpsc::unbounded();
*SENDER.lock().unwrap() = Some(sender);
*RECEIVER.lock().unwrap() = Some(receiver);
let mut runtime = Runtime::new(RuntimeOptions {
..Default::default()
})?;
@ -53,9 +90,20 @@ fn main() -> Result<(), rustyscript::Error> {
"#,
);
runtime.register_function("showToast", |args| {
println!("ooh new toast from js: {:?}", args[0]);
Ok(Value::Null)
runtime.register_async_function("showToast", |args| {
Box::pin(async move {
if let Ok(value) = serde_json::from_value::<ToastOptions>(args[0].clone()) {
if let Some(mut sender) = SENDER.lock().unwrap().clone() {
sender
.send(Message::UpdateToast(
value.message.unwrap_or("".to_string()).clone(),
))
.await
.unwrap();
}
}
Ok(Value::Null)
})
})?;
runtime.load_module(&module)?;
@ -65,6 +113,8 @@ fn main() -> Result<(), rustyscript::Error> {
tokio_runtime.block_on(async { runtime.load_module_async(&command_runner).await })?;
iced::run("A cool counter", update, view)
iced::application("flare", update, view)
.subscription(subscription)
.run()
.map_err(|e| rustyscript::Error::Runtime(e.to_string()))
}