diff --git a/README.md b/README.md
index 8a95e02..d0f4964 100644
--- a/README.md
+++ b/README.md
@@ -75,6 +75,9 @@ These API bindings are not completely on-par with `@tauri-apps/api` yet, but her
The current API also very closely mirrors the JS API even though that might not be the most ergonomic choice, ideas for improving the API with quality-of-life features beyond the regular JS API interface are very welcome.
+## Examples
+The [`examples/leptos`] crate provides examples of how to use most of the implemented functionality.
+
[wasm-bindgen]: https://github.com/rustwasm/wasm-bindgen
[tauri allowlist]: https://tauri.app/v1/api/config#allowlistconfig
[`esbuild`]: https://esbuild.github.io/getting-started/#install-esbuild
diff --git a/examples/leptos/.gitignore b/examples/leptos/.gitignore
new file mode 100644
index 0000000..48c3ca4
--- /dev/null
+++ b/examples/leptos/.gitignore
@@ -0,0 +1,3 @@
+/dist/
+/target/
+/Cargo.lock
diff --git a/examples/leptos/.taurignore b/examples/leptos/.taurignore
new file mode 100644
index 0000000..1ebdc6d
--- /dev/null
+++ b/examples/leptos/.taurignore
@@ -0,0 +1,3 @@
+/src
+/public
+/Cargo.toml
\ No newline at end of file
diff --git a/examples/leptos/.vscode/extensions.json b/examples/leptos/.vscode/extensions.json
new file mode 100644
index 0000000..24d7cc6
--- /dev/null
+++ b/examples/leptos/.vscode/extensions.json
@@ -0,0 +1,3 @@
+{
+ "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"]
+}
diff --git a/examples/leptos/.vscode/settings.json b/examples/leptos/.vscode/settings.json
new file mode 100644
index 0000000..e6d9d21
--- /dev/null
+++ b/examples/leptos/.vscode/settings.json
@@ -0,0 +1,5 @@
+{
+ "emmet.includeLanguages": {
+ "rust": "html"
+ }
+}
diff --git a/examples/leptos/Cargo.toml b/examples/leptos/Cargo.toml
new file mode 100644
index 0000000..412b34f
--- /dev/null
+++ b/examples/leptos/Cargo.toml
@@ -0,0 +1,22 @@
+[package]
+name = "test-tauri-events-ui"
+version = "0.0.0"
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+[dependencies]
+leptos = { version = "0.6", features = ["csr", "nightly"] }
+wasm-bindgen = "0.2"
+wasm-bindgen-futures = "0.4"
+js-sys = "0.3"
+serde = { version = "1", features = ["derive"] }
+serde-wasm-bindgen = "0.6"
+console_error_panic_hook = "0.1.7"
+tauri-sys = { path = "../tauri-sys", features = ["all"] }
+futures = "0.3"
+tracing = "0.1.40"
+tracing-subscriber = "0.3.18"
+tracing-web = "0.1.3"
+
+[workspace]
+members = ["src-tauri"]
diff --git a/examples/leptos/README.md b/examples/leptos/README.md
new file mode 100644
index 0000000..77812aa
--- /dev/null
+++ b/examples/leptos/README.md
@@ -0,0 +1,7 @@
+# Tauri + Leptos
+
+This template should help get you started developing with Tauri and Leptos.
+
+## Recommended IDE Setup
+
+[VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer).
diff --git a/examples/leptos/Trunk.toml b/examples/leptos/Trunk.toml
new file mode 100644
index 0000000..27a1f84
--- /dev/null
+++ b/examples/leptos/Trunk.toml
@@ -0,0 +1,10 @@
+[build]
+target = "./index.html"
+
+[watch]
+ignore = ["./src-tauri"]
+
+[serve]
+address = "127.0.0.1"
+port = 1420
+open = false
diff --git a/examples/leptos/index.html b/examples/leptos/index.html
new file mode 100644
index 0000000..ae0bf14
--- /dev/null
+++ b/examples/leptos/index.html
@@ -0,0 +1,11 @@
+
+
+
+
+ Tauri + Leptos App
+
+
+
+
+
+
diff --git a/examples/leptos/public/leptos.svg b/examples/leptos/public/leptos.svg
new file mode 100644
index 0000000..7fc2154
--- /dev/null
+++ b/examples/leptos/public/leptos.svg
@@ -0,0 +1,64 @@
+
+
+
\ No newline at end of file
diff --git a/examples/leptos/public/tauri.svg b/examples/leptos/public/tauri.svg
new file mode 100644
index 0000000..31b62c9
--- /dev/null
+++ b/examples/leptos/public/tauri.svg
@@ -0,0 +1,6 @@
+
diff --git a/examples/leptos/src-tauri/.gitignore b/examples/leptos/src-tauri/.gitignore
new file mode 100644
index 0000000..b21bd68
--- /dev/null
+++ b/examples/leptos/src-tauri/.gitignore
@@ -0,0 +1,7 @@
+# Generated by Cargo
+# will have compiled files and executables
+/target/
+
+# Generated by Tauri
+# will have schema files for capabilities auto-completion
+/gen/schemas
diff --git a/examples/leptos/src-tauri/Cargo.toml b/examples/leptos/src-tauri/Cargo.toml
new file mode 100644
index 0000000..395cc0c
--- /dev/null
+++ b/examples/leptos/src-tauri/Cargo.toml
@@ -0,0 +1,20 @@
+[package]
+name = "test-tauri-events"
+version = "0.0.0"
+description = "A Tauri App"
+authors = ["you"]
+edition = "2021"
+
+# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
+
+[build-dependencies]
+# tauri-build = { version = "2.0.0-beta", features = [] }
+tauri-build = { path = "../../tauri/core/tauri-build", features = [] }
+
+[dependencies]
+tauri = { path = "../../tauri/core/tauri", features = [] }
+tauri-plugin-shell = { path = "../../plugins-workspace/plugins/shell" }
+serde = { version = "1", features = ["derive"] }
+serde_json = "1"
+tracing = "0.1.40"
+tracing-subscriber = "0.3.18"
diff --git a/examples/leptos/src-tauri/build.rs b/examples/leptos/src-tauri/build.rs
new file mode 100644
index 0000000..d860e1e
--- /dev/null
+++ b/examples/leptos/src-tauri/build.rs
@@ -0,0 +1,3 @@
+fn main() {
+ tauri_build::build()
+}
diff --git a/examples/leptos/src-tauri/capabilities/default.json b/examples/leptos/src-tauri/capabilities/default.json
new file mode 100644
index 0000000..1914afc
--- /dev/null
+++ b/examples/leptos/src-tauri/capabilities/default.json
@@ -0,0 +1,17 @@
+{
+ "$schema": "../gen/schemas/desktop-schema.json",
+ "identifier": "default",
+ "description": "Capability for the main window",
+ "windows": ["main"],
+ "permissions": [
+ "core:path:default",
+ "core:event:default",
+ "core:window:default",
+ "core:app:default",
+ "core:image:default",
+ "core:resources:default",
+ "core:menu:default",
+ "core:tray:default",
+ "shell:allow-open"
+ ]
+}
diff --git a/examples/leptos/src-tauri/icons/128x128.png b/examples/leptos/src-tauri/icons/128x128.png
new file mode 100644
index 0000000..6be5e50
Binary files /dev/null and b/examples/leptos/src-tauri/icons/128x128.png differ
diff --git a/examples/leptos/src-tauri/icons/128x128@2x.png b/examples/leptos/src-tauri/icons/128x128@2x.png
new file mode 100644
index 0000000..e81bece
Binary files /dev/null and b/examples/leptos/src-tauri/icons/128x128@2x.png differ
diff --git a/examples/leptos/src-tauri/icons/32x32.png b/examples/leptos/src-tauri/icons/32x32.png
new file mode 100644
index 0000000..a437dd5
Binary files /dev/null and b/examples/leptos/src-tauri/icons/32x32.png differ
diff --git a/examples/leptos/src-tauri/icons/Square107x107Logo.png b/examples/leptos/src-tauri/icons/Square107x107Logo.png
new file mode 100644
index 0000000..0ca4f27
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square107x107Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square142x142Logo.png b/examples/leptos/src-tauri/icons/Square142x142Logo.png
new file mode 100644
index 0000000..b81f820
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square142x142Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square150x150Logo.png b/examples/leptos/src-tauri/icons/Square150x150Logo.png
new file mode 100644
index 0000000..624c7bf
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square150x150Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square284x284Logo.png b/examples/leptos/src-tauri/icons/Square284x284Logo.png
new file mode 100644
index 0000000..c021d2b
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square284x284Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square30x30Logo.png b/examples/leptos/src-tauri/icons/Square30x30Logo.png
new file mode 100644
index 0000000..6219700
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square30x30Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square310x310Logo.png b/examples/leptos/src-tauri/icons/Square310x310Logo.png
new file mode 100644
index 0000000..f9bc048
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square310x310Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square44x44Logo.png b/examples/leptos/src-tauri/icons/Square44x44Logo.png
new file mode 100644
index 0000000..d5fbfb2
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square44x44Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square71x71Logo.png b/examples/leptos/src-tauri/icons/Square71x71Logo.png
new file mode 100644
index 0000000..63440d7
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square71x71Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/Square89x89Logo.png b/examples/leptos/src-tauri/icons/Square89x89Logo.png
new file mode 100644
index 0000000..f3f705a
Binary files /dev/null and b/examples/leptos/src-tauri/icons/Square89x89Logo.png differ
diff --git a/examples/leptos/src-tauri/icons/StoreLogo.png b/examples/leptos/src-tauri/icons/StoreLogo.png
new file mode 100644
index 0000000..4556388
Binary files /dev/null and b/examples/leptos/src-tauri/icons/StoreLogo.png differ
diff --git a/examples/leptos/src-tauri/icons/icon.icns b/examples/leptos/src-tauri/icons/icon.icns
new file mode 100644
index 0000000..12a5bce
Binary files /dev/null and b/examples/leptos/src-tauri/icons/icon.icns differ
diff --git a/examples/leptos/src-tauri/icons/icon.ico b/examples/leptos/src-tauri/icons/icon.ico
new file mode 100644
index 0000000..b3636e4
Binary files /dev/null and b/examples/leptos/src-tauri/icons/icon.ico differ
diff --git a/examples/leptos/src-tauri/icons/icon.png b/examples/leptos/src-tauri/icons/icon.png
new file mode 100644
index 0000000..e1cd261
Binary files /dev/null and b/examples/leptos/src-tauri/icons/icon.png differ
diff --git a/examples/leptos/src-tauri/src/main.rs b/examples/leptos/src-tauri/src/main.rs
new file mode 100644
index 0000000..9791f93
--- /dev/null
+++ b/examples/leptos/src-tauri/src/main.rs
@@ -0,0 +1,41 @@
+// Prevents additional console window on Windows in release, DO NOT REMOVE!!
+#![cfg_attr(not(debug_assertions), windows_subsystem = "windows")]
+
+use tauri::{Emitter, Manager};
+
+fn main() {
+ logging::enable();
+ tauri::Builder::default()
+ .invoke_handler(tauri::generate_handler![trigger_listen_events,])
+ .run(tauri::generate_context!())
+ .expect("error while running tauri application");
+}
+
+#[tauri::command]
+fn trigger_listen_events(app: tauri::AppHandle) {
+ tracing::debug!("trigger_listen_event");
+ std::thread::spawn({
+ move || {
+ for i in 1..=100 {
+ app.emit("event::listen", i).unwrap();
+ std::thread::sleep(std::time::Duration::from_millis(500));
+ }
+ }
+ });
+}
+
+mod logging {
+ use tracing_subscriber::{filter::LevelFilter, fmt, prelude::*, Layer, Registry};
+
+ const MAX_LOG_LEVEL: LevelFilter = LevelFilter::DEBUG;
+
+ pub fn enable() {
+ let console_logger = fmt::layer()
+ .with_writer(std::io::stdout)
+ .pretty()
+ .with_filter(MAX_LOG_LEVEL);
+
+ let subscriber = Registry::default().with(console_logger);
+ tracing::subscriber::set_global_default(subscriber).unwrap();
+ }
+}
diff --git a/examples/leptos/src-tauri/tauri.conf.json b/examples/leptos/src-tauri/tauri.conf.json
new file mode 100644
index 0000000..2a5bdb4
--- /dev/null
+++ b/examples/leptos/src-tauri/tauri.conf.json
@@ -0,0 +1,35 @@
+{
+ "productName": "test-tauri-events",
+ "version": "0.0.0",
+ "identifier": "com.tauri.dev",
+ "build": {
+ "beforeDevCommand": "trunk serve",
+ "devUrl": "http://localhost:1420",
+ "beforeBuildCommand": "trunk build",
+ "frontendDist": "../dist"
+ },
+ "app": {
+ "withGlobalTauri": true,
+ "windows": [
+ {
+ "title": "test-tauri-events",
+ "width": 800,
+ "height": 600
+ }
+ ],
+ "security": {
+ "csp": null
+ }
+ },
+ "bundle": {
+ "active": true,
+ "targets": "all",
+ "icon": [
+ "icons/32x32.png",
+ "icons/128x128.png",
+ "icons/128x128@2x.png",
+ "icons/icon.icns",
+ "icons/icon.ico"
+ ]
+ }
+}
diff --git a/examples/leptos/src/app.rs b/examples/leptos/src/app.rs
new file mode 100644
index 0000000..6b397aa
--- /dev/null
+++ b/examples/leptos/src/app.rs
@@ -0,0 +1,517 @@
+use futures::stream::StreamExt;
+use leptos::{ev::MouseEvent, *};
+use std::rc::Rc;
+
+#[component]
+pub fn App() -> impl IntoView {
+ view! {
+
+
+
"core"
+
+
+
+
+
"events"
+
+
+
+
+
"window"
+
+
+
+
+
"menu"
+
+
+
+ }
+}
+
+#[component]
+fn Core() -> impl IntoView {
+ let (convert_path, set_convert_path) = create_signal("".to_string());
+ let (converted_path, set_converted_path) = create_signal("".to_string());
+
+ let do_convert_path = move |_| {
+ let converted = tauri_sys::core::convert_file_src(convert_path());
+ set_converted_path(converted);
+ };
+
+ view! {
+
+
+
+
+
+
{converted_path}
+
+ }
+}
+
+#[component]
+fn Events() -> impl IntoView {
+ let (listen_event, set_listen_event) = create_signal(None);
+ let (emit_count, set_emit_count) = create_signal(0);
+
+ spawn_local(async move {
+ let mut listener = tauri_sys::event::listen::("event::listen")
+ .await
+ .unwrap();
+
+ while let Some(event) = listener.next().await {
+ tracing::debug!(?event);
+ let tauri_sys::event::Event {
+ event: _,
+ id: _,
+ payload,
+ } = event;
+ set_listen_event.set(Some(payload));
+ }
+ });
+
+ spawn_local(async move {
+ let mut listener = tauri_sys::event::listen::("event::emit")
+ .await
+ .unwrap();
+
+ while let Some(event) = listener.next().await {
+ tracing::debug!(?event);
+ let tauri_sys::event::Event {
+ event: _,
+ id: _,
+ payload,
+ } = event;
+ set_emit_count.set(payload);
+ }
+ });
+
+ let trigger_listen_events = move |_| {
+ spawn_local(async move {
+ tauri_sys::core::invoke::<()>("trigger_listen_events", &()).await;
+ });
+ };
+
+ let trigger_emit_event = move |_| {
+ spawn_local(async move {
+ tauri_sys::event::emit("event::emit", &emit_count.with_untracked(|n| n + 1))
+ .await
+ .unwrap();
+ });
+ };
+
+ view! {
+
+
+
+
+ "Last listen event: "
+ {move || listen_event()}
+
+
+
+
+
+
+ "Events emitted: "
+ {move || emit_count()}
+
+
+
+ }
+}
+
+#[component]
+fn Window() -> impl IntoView {
+ view! {
+
+
+
"Windows"
+
+
+
+
+
"Monitors"
+
+
+
+
+
"Events"
+
+
+
+ }
+}
+
+#[component]
+fn WindowWindows() -> impl IntoView {
+ let current_window = create_action(|_| async move { tauri_sys::window::get_current() });
+ let all_windows = create_action(|_| async move { tauri_sys::window::get_all() });
+
+ let refresh = move |_| {
+ current_window.dispatch(());
+ all_windows.dispatch(());
+ };
+
+ current_window.dispatch(());
+ all_windows.dispatch(());
+
+ view! {
+
+
+
"Current window:"
+ {move || {
+ current_window
+ .value()
+ .with(|window| match window {
+ None => "Loading".to_string(),
+ Some(window) => window.label().clone(),
+ })
+ }}
+
+
+
+
"All windows:"
+ {move || {
+ all_windows
+ .value()
+ .with(|windows| match windows {
+ None => "Loading".to_string(),
+ Some(windows) => {
+ let out = windows
+ .iter()
+ .map(|window| { window.label().clone() })
+ .collect::
>()
+ .join(", ");
+ format!("[{out}]")
+ }
+ })
+ }}
+
+
+
+
+ }
+}
+
+#[component]
+fn WindowMonitors() -> impl IntoView {
+ let current_monitor =
+ create_action(|_| async move { tauri_sys::window::current_monitor().await });
+
+ let primary_monitor =
+ create_action(|_| async move { tauri_sys::window::primary_monitor().await });
+
+ let available_monitors =
+ create_action(|_| async move { tauri_sys::window::available_monitors().await });
+
+ let monitor_from_point = create_action(|(x, y): &(isize, isize)| {
+ let x = x.clone();
+ let y = y.clone();
+ async move { tauri_sys::window::monitor_from_point(x, y).await }
+ });
+
+ // let cursor_position =
+ // create_action(|_| async move { tauri_sys::window::cursor_position().await });
+
+ let refresh = move |_| {
+ current_monitor.dispatch(());
+ primary_monitor.dispatch(());
+ available_monitors.dispatch(());
+ };
+
+ let oninput_monitor_from_point = move |e| {
+ let value = event_target_value(&e);
+ let Some((x, y)) = value.split_once(',') else {
+ return;
+ };
+
+ let Ok(x) = x.parse::() else {
+ return;
+ };
+
+ let Ok(y) = y.parse::() else {
+ return;
+ };
+
+ monitor_from_point.dispatch((x, y));
+ };
+
+ current_monitor.dispatch(());
+ primary_monitor.dispatch(());
+ available_monitors.dispatch(());
+
+ view! {
+
+
+
+
"Current monitor:"
+ {move || {
+ current_monitor
+ .value()
+ .with(|monitor| match monitor {
+ None => "Loading".into_view(),
+ Some(Some(monitor)) => view! {
}.into_view(),
+ Some(None) => "Could not detect monitor.".into_view(),
+ })
+ }}
+
+
+
+
"Primary monitor:"
+ {move || {
+ primary_monitor
+ .value()
+ .with(|monitor| match monitor {
+ None => "Loading".into_view(),
+ Some(Some(monitor)) => view! {
}.into_view(),
+ Some(None) => "Could not detect monitor.".into_view(),
+ })
+ }}
+
+
+
+
"Available monitors:"
+ {move || {
+ available_monitors
+ .value()
+ .with(|monitors| match monitors {
+ None => "Loading".into_view(),
+ Some(monitors) => {
+ view! {
+ {monitors
+ .iter()
+ .map(|monitor| view! {
})
+ .collect::
>()}
+ }
+ .into_view()
+ }
+ })
+ }}
+
+
+
+
+
+
+
+ {move || {
+ monitor_from_point
+ .value()
+ .with(|monitor| match monitor {
+ None => "Enter an `x, y` coordinate.".into_view(),
+ Some(Some(monitor)) => view! { }.into_view(),
+ Some(None) => "Could not detect monitor.".into_view(),
+ })
+ }}
+
+
+
+
+
+ // {move || {
+ // cursor_position
+ // .value()
+ // .with(|position| {
+ // position
+ // .as_ref()
+ // .map(|position| {
+ // view! {
+ // {position.x()}
+ // ", "
+ // {position.y()}
+ // }
+ // })
+ // })
+ // }}
+
"Cursor position: "
+
+ // on:mousemove=move |_| cursor_position.dispatch(())
+ "TODO (See https://github.com/tauri-apps/tauri/issues/10340)"
+
+
+
+ }
+}
+
+#[component]
+fn WindowEvents() -> impl IntoView {
+ use tauri_sys::window::{DragDropEvent, DragDropPayload, DragOverPayload};
+
+ let (count, set_count) = create_signal(0);
+ let increment_count = create_action(|count: &usize| {
+ let count = count.clone();
+ let window = tauri_sys::window::get_current();
+ async move {
+ web_sys::console::debug_1(&"0".into());
+ window.emit("count", count).await.unwrap();
+ }
+ });
+
+ let (drag_drop, set_drag_drop) = create_signal(().into_view());
+
+ spawn_local(async move {
+ let mut window = tauri_sys::window::get_current();
+ let mut listener = window.listen::("count").await.unwrap();
+ while let Some(event) = listener.next().await {
+ set_count(event.payload);
+ }
+ });
+
+ spawn_local(async move {
+ let window = tauri_sys::window::get_current();
+ let mut listener = window.on_drag_drop_event().await.unwrap();
+ while let Some(event) = listener.next().await {
+ match event.payload {
+ DragDropEvent::Enter(payload) => {
+ let out = view! {
+
+
"Enter"
+
+ "Paths: ["
+ {payload
+ .paths()
+ .iter()
+ .map(|path| path.to_string_lossy().to_string())
+ .collect::>()
+ .join(", ")} "]"
+
+
+ "Position: " {payload.position().x()} ", " {payload.position().y()}
+
+
+ };
+
+ set_drag_drop(out.into_view());
+ }
+ DragDropEvent::Over(payload) => {
+ let out = view! {
+
+
"Over"
+
+ "Position: " {payload.position().x()} ", " {payload.position().y()}
+
+
+ };
+
+ set_drag_drop(out.into_view());
+ }
+ DragDropEvent::Drop(payload) => {
+ let out = view! {
+
+
"Drop"
+
+ "Paths: ["
+ {payload
+ .paths()
+ .iter()
+ .map(|path| path.to_string_lossy().to_string())
+ .collect::>()
+ .join(", ")} "]"
+
+
+ "Position: " {payload.position().x()} ", " {payload.position().y()}
+
+
+ };
+
+ set_drag_drop(out.into_view());
+ }
+ DragDropEvent::Leave => {
+ let out = view! { "Leave" };
+ set_drag_drop(out.into_view());
+ }
+ }
+ }
+ });
+
+ view! {
+
+
+ "Count: " {count}
+
+
+
+
+
"Drag drop event"
+
{drag_drop}
+
+
+ }
+}
+
+#[component]
+fn Monitor<'a>(monitor: &'a tauri_sys::window::Monitor) -> impl IntoView {
+ view! {
+
+
"Name: " {monitor.name().clone()}
+
"Size: " {monitor.size().width()} " x " {monitor.size().height()}
+
"Position: " {monitor.position().x()} ", " {monitor.position().y()}
+
"Scale: " {monitor.scale_factor()}
+
+ }
+}
+
+#[component]
+fn Menu() -> impl IntoView {
+ let (event, set_event) = create_signal::