Merge remote-tracking branch 'upstream/main' into updater

This commit is contained in:
Max Coppen 2022-11-15 19:07:41 +01:00
commit 8b9299ec1c
87 changed files with 15088 additions and 667 deletions

52
.github/workflows/test.yml vendored Normal file
View file

@ -0,0 +1,52 @@
name: Test
on:
push:
branches:
- main
paths:
- '.github/workflows/test.yml'
- 'src/**'
- 'examples/test/**'
pull_request:
branches:
- main
paths:
- '.github/workflows/test.yml'
- 'src/**'
- 'examples/test/**'
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
jobs:
build-and-test:
runs-on: ubuntu-latest
timeout-minutes: 15
steps:
- uses: actions/checkout@v3
- name: Install stable toolchain
uses: actions-rs/toolchain@v1
with:
toolchain: stable
override: true
target: wasm32-unknown-unknown
- uses: Swatinem/rust-cache@v1
- name: Install native deps
run: |
sudo apt-get update
sudo apt-get install -y webkit2gtk-4.0 at-spi2-core
- name: Install Tauri CLI
run: |
cd examples/test
wget -qO- https://github.com/tauri-apps/tauri/releases/download/cli.rs-v1.2.0/cargo-tauri-x86_64-unknown-linux-gnu.tgz | tar -xzf- -C ~/.cargo/bin
- name: Install Trunk
run: |
cd examples/test
wget -qO- https://github.com/thedodd/trunk/releases/download/v0.16.0/trunk-x86_64-unknown-linux-gnu.tar.gz | tar -xzf- -C ~/.cargo/bin
- name: Run test app
run: |
cd examples/test
export CARGO_UNSTABLE_SPARSE_REGISTRY=true
xvfb-run cargo tauri dev --exit-on-panic --config ./src-tauri/ci.tauri.conf.json

3592
Cargo.lock generated

File diff suppressed because it is too large Load diff

View file

@ -11,19 +11,31 @@ js-sys = "0.3.59"
serde = { version = "1.0.140", features = ["derive"] } serde = { version = "1.0.140", features = ["derive"] }
wasm-bindgen = { version = "0.2.82", features = ["serde_json"] } wasm-bindgen = { version = "0.2.82", features = ["serde_json"] }
wasm-bindgen-futures = "0.4.32" wasm-bindgen-futures = "0.4.32"
url = { version = "2.3.1", optional = true } url = { version = "2.3.1", optional = true, features = ["serde"] }
thiserror = "1.0.37" thiserror = "1.0.37"
semver = { version = "1.0.14", optional = true } semver = { version = "1.0.14", optional = true, features = ["serde"] }
log = "0.4.17"
[dev-dependencies] [dev-dependencies]
wasm-bindgen-test = "0.3.33" wasm-bindgen-test = "0.3.33"
tauri-sys = { path = ".", features = ["all"] } tauri-sys = { path = ".", features = ["all"] }
# tauri = "1.1.1"
[package.metadata.docs.rs]
all-features = true
[features] [features]
all = ["app", "clipboard", "event", "mocks", "tauri", "updater"] all = ["app", "clipboard", "event", "mocks", "tauri", "window", "process", "dialog", "os", "notification"]
app = ["dep:semver"] app = ["dep:semver"]
clipboard = [] clipboard = []
dialog = []
event = [] event = []
mocks = [] mocks = []
tauri = ["dep:url"] tauri = ["dep:url"]
updater = [] window = []
process = []
os = ["dep:semver"]
notification = []
[workspace]
members = ["examples/test", "examples/test/src-tauri"]

View file

@ -43,6 +43,7 @@ All modules are gated by accordingly named Cargo features. It is recommended you
- **all**: Enables all modules. - **all**: Enables all modules.
- **app**: Enables the `app` module. - **app**: Enables the `app` module.
- **clipboard**: Enables the `clipboard` module. - **clipboard**: Enables the `clipboard` module.
- **dialog**: Enables the `dialog` module.
- **event**: Enables the `event` module. - **event**: Enables the `event` module.
- **mocks**: Enables the `mocks` module. - **mocks**: Enables the `mocks` module.
- **tauri**: Enables the `tauri` module. - **tauri**: Enables the `tauri` module.
@ -54,20 +55,20 @@ These API bindings are not completely on-par with `@tauri-apps/api` yet, but her
- [x] `app` - [x] `app`
- [ ] `cli` - [ ] `cli`
- [x] `clipboard` - [x] `clipboard`
- [ ] `dialog` - [x] `dialog`
- [x] `event` - [x] `event`
- [ ] `fs` - [ ] `fs`
- [ ] `global_shortcut` - [ ] `global_shortcut`
- [ ] `http` - [ ] `http`
- [x] `mocks` - [x] `mocks`
- [ ] `notification` - [x] `notification`
- [ ] `os` - [x] `os`
- [ ] `path` - [ ] `path`
- [ ] `process` - [x] `process`
- [ ] `shell` - [ ] `shell`
- [x] `tauri` - [x] `tauri`
- [ ] `updater` - [ ] `updater`
- [ ] `window` - [x] `window`
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. 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.

View file

@ -1,32 +0,0 @@
use std::process::Command;
fn main() {
/* Shared arguments */
let sargs: [&str; 9] = [
"--outdir=dist",
"--format=esm",
"--bundle",
"tauri/tooling/api/src/app.ts",
"tauri/tooling/api/src/clipboard.ts",
"tauri/tooling/api/src/tauri.ts",
"tauri/tooling/api/src/event.ts",
"tauri/tooling/api/src/mocks.ts",
"tauri/tooling/api/src/updater.ts",
];
if cfg!(windows) {
/* Use cmd if the target is windows */
Command::new("cmd")
.args(&["/C", "esbuild"])
.args(&sargs)
.output()
.unwrap();
} else if cfg!(unix) {
Command::new("esbuild")
.args(&sargs)
.output()
.unwrap();
} else {
panic!("Unsupported build target");
}
}

View file

@ -1,3 +0,0 @@
/dist/
/target/
/Cargo.lock

View file

@ -1,16 +0,0 @@
[build]
target = "./index.html"
[watch]
ignore = ["./src-tauri"]
[serve]
address = "127.0.0.1"
port = 1420
open = false
# [[hooks]]
# # Runs SSG on production builds
# stage = "post_build"
# command = "bash"
# command_arguments = ["-c", "if [[ $TRUNK_PROFILE == \"release\" ]]; then cargo run --release --features ssg -- $TRUNK_STAGING_DIR; fi"]

View file

@ -1,12 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Tauri + Yew App</title>
<link data-trunk rel="css" href="style.css" />
<link data-trunk rel="copy-dir" href="public" />
</head>
<body>
<!--app-html-->
</body>
</html>

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

View file

@ -1,9 +0,0 @@
[package]
name = "shared"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
serde = { version = "1.0.140", features = ["derive"] }

View file

@ -1,12 +0,0 @@
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Reply<'a> {
pub data: &'a str,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RequestBody<'a> {
pub id: i32,
pub name: &'a str,
}

View file

@ -1,37 +0,0 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use shared::{Reply, RequestBody};
#[tauri::command]
fn log_operation(event: String, payload: Option<String>) {
println!("{} {:?}", event, payload);
}
#[tauri::command]
fn perform_request(endpoint: String, body: RequestBody) -> String {
println!("{} {:?}", endpoint, body);
"message response".into()
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![log_operation, perform_request])
.on_page_load(|window, _| {
let window_ = window.clone();
window.listen("js-event", move |event| {
println!("got js-event with message '{:?}'", event.payload());
let reply = Reply {
data: "something else",
};
window_
.emit("rust-event", Some(reply))
.expect("failed to emit");
});
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View file

@ -1,82 +0,0 @@
mod views;
use sycamore::prelude::*;
#[cfg(not(feature = "ssg"))]
use sycamore_router::{Router, HistoryIntegration};
#[component]
fn Header<G: Html>(cx: Scope) -> View<G> {
view! { cx,
header(style="display: flex; gap: 1em; margin-bottom: 1em;") {
a(href="/") {
"Welcome"
}
a(href="/app") {
"App"
}
a(href="/clipboard") {
"Clipboard"
}
a(href="/updater") {
"Updater"
}
a(href="/communication") {
"Communication"
}
}
}
}
#[cfg(all(not(debug_assertions), not(feature = "ssg")))]
fn main() {
wasm_logger::init(wasm_logger::Config::default());
sycamore::hydrate(|cx| view! { cx,
Header
Router(
integration=HistoryIntegration::new(),
view=views::switch
)
});
}
#[cfg(all(debug_assertions, not(feature = "ssg")))]
fn main() {
use sycamore::view;
wasm_logger::init(wasm_logger::Config::default());
sycamore::render(|cx| view! { cx,
Header
Router(
integration=HistoryIntegration::new(),
view=views::switch
)
});
}
#[cfg(feature = "ssg")]
fn main() {
use sycamore_router::StaticRouter;
let out_dir = std::env::args().nth(1).unwrap();
println!("out_dir {}", out_dir);
let template = std::fs::read_to_string(format!("{}/index.html", out_dir)).unwrap();
let html = sycamore::render_to_string(|cx| view! { cx,
Header
StaticRouter(
route=route.clone(),
view=views::switch
)
});
let html = template.replace("<!--app-html-->\n", &html);
let path = format!("{}/index.html", out_dir);
println!("Writing html to file \"{}\"", path);
std::fs::write(path, html).unwrap();
}

View file

@ -1,78 +0,0 @@
use gloo_timers::callback::Timeout;
use sycamore::prelude::*;
use tauri_sys::app;
#[component]
pub fn App<G: Html>(cx: Scope) -> View<G> {
let show_app = |_| {
sycamore::futures::spawn_local(async move {
let res = app::hide().await;
log::debug!("app hide res {:?}", res);
let timeout = Timeout::new(2_000, move || {
sycamore::futures::spawn_local(async move {
let res = app::show().await;
log::debug!("app show res {:?}", res);
});
});
timeout.forget();
});
};
let hide_app = |_| {
sycamore::futures::spawn_local(async move {
let res = app::hide().await;
log::debug!("app hide res {:?}", res);
});
};
let get_name = |_| {
sycamore::futures::spawn_local(async move {
let res = app::get_name().await;
log::debug!("app name {:?}", res);
});
};
let get_version = |_| {
sycamore::futures::spawn_local(async move {
let res = app::get_version().await;
log::debug!("app version {:?}", res);
});
};
let get_tauri_version = |_| {
sycamore::futures::spawn_local(async move {
let res = app::get_tauri_version().await;
log::debug!("tauri version {:?}", res);
});
};
view! { cx,
div {
button(class="btn",id="get_name",on:click=get_name) {
"Get App Name"
}
button(class="btn",id="get_version",on:click=get_version) {
"Get App Version"
}
button(class="btn",id="get_tauri_version",on:click=get_tauri_version) {
"Get Tauri Version"
}
}
div {
button(class="btn",id="show",title="Hides and shows the app after 2 seconds",on:click=show_app) {
"Show"
}
button(class="btn",id="hide",on:click=hide_app) {
"Hide"
}
}
}
}

View file

@ -1,42 +0,0 @@
use sycamore::prelude::*;
use tauri_sys::clipboard::{read_text, write_text};
#[component]
pub fn Clipboard<G: Html>(cx: Scope) -> View<G> {
let text = create_signal(cx, "clipboard message".to_string());
let write = move |_| {
sycamore::futures::spawn_local_scoped(cx, async move {
write_text(&text.get()).await
// .then(() => {
// onMessage('Wrote to the clipboard')
// })
// .catch(onMessage)
});
};
let read = |_| {
sycamore::futures::spawn_local(async move {
let text = read_text().await;
log::info!("Read text from clipboard {:?}", text);
// readText()
// .then((contents) => {
// onMessage(`Clipboard contents: ${contents}`)
// })
// .catch(onMessage)
});
};
view! { cx,
div(class="flex gap-1") {
input(class="grow input",placeholder="Text to write to the clipboard",bind:value=text)
button(class="btn",type="button",on:click=write) {
"Write"
}
button(class="btn",type="button",on:click=read) {
"Read"
}
}
}
}

View file

@ -1,90 +0,0 @@
use serde::{Deserialize, Serialize};
use sycamore::prelude::*;
use tauri_sys::event::{emit, listen};
use tauri_sys::tauri::invoke;
use shared::RequestBody;
#[component]
pub fn Communication<'a, G: Html>(cx: Scope<'a>) -> View<G> {
let unlisten = create_signal::<Option<Box<&dyn FnOnce()>>>(cx, None);
// on_mount(cx, move || {
// sycamore::futures::spawn_local_scoped(cx, async move {
// let unlisten_raw = listen::<Reply>("rust-event", &|reply| log::debug!("got reply {:?}", reply)).await;
// unlisten.set(Some(Box::new(&unlisten_raw)));
// });
// });
// on_cleanup(cx, || {
// if let Some(unlisten) = unlisten .take().as_deref() {
// (unlisten)()
// }
// });
let log = |_| {
#[derive(Serialize)]
struct Payload<'a> {
event: &'a str,
payload: &'a str,
}
sycamore::futures::spawn_local(async move {
let res = invoke::<_, ()>(
"log_operation",
&Payload {
event: "tauri-click",
payload: "this payload is optional because we used Option in Rust",
},
)
.await;
log::debug!("Emitted event, response {:?}", res);
});
};
let perform_request = |_| {
sycamore::futures::spawn_local(async move {
#[derive(Serialize)]
struct Payload<'a> {
endpoint: &'a str,
body: RequestBody<'a>
}
let res = invoke::<_, String>(
"perform_request",
&Payload {
endpoint: "dummy endpoint arg",
body: RequestBody {
id: 5,
name: "test",
},
},
)
.await;
log::debug!("Got reply {:?}", res);
});
};
let emit_event = |_| {
sycamore::futures::spawn_local(async move {
emit("js-event", &"this is the payload string").await;
});
};
view! { cx,
div {
button(class="btn",id="log",on:click=log) {
"Call Log API"
}
button(class="btn",mid="request",on:click=perform_request) {
"Call Request (async) API"
}
button(class="btn",id="event",on:click=emit_event) {
"Send event to Rust"
}
}
}
}

View file

@ -1,33 +0,0 @@
mod app;
mod clipboard;
mod communication;
mod updater;
mod welcome;
use sycamore::view::View;
use sycamore_router::Route;
use sycamore::prelude::*;
#[derive(Debug, Clone, Route)]
pub enum Page {
#[to("/app")]
App,
#[to("/clipboard")]
Clipboard,
#[to("/communication")]
Communication,
#[to("/updater")]
Updater,
#[not_found]
NotFound
}
pub fn switch<G: Html>(cx: Scope, route: &ReadSignal<Page>) -> View<G> {
match route.get().as_ref() {
Page::App => app::App(cx),
Page::Clipboard => clipboard::Clipboard(cx),
Page::Communication => communication::Communication(cx),
Page::Updater => updater::Updater(cx),
Page::NotFound => welcome::Welcome(cx)
}
}

View file

@ -1,10 +0,0 @@
use sycamore::prelude::*;
#[component]
pub fn Welcome<G: Html>(cx: Scope) -> View<G> {
view! { cx,
h1 {
"Welcome"
}
}
}

View file

@ -1,120 +0,0 @@
.logo.yew:hover {
filter: drop-shadow(0 0 2em #20a88a);
}
.logo.sycamore {
color: #0000;
font-size: 3rem;
line-height: 1;
font-weight: 800;
-webkit-background-clip: text;
background-clip: text;
background-image: linear-gradient(to right,#fdba74, #f87171);
font-family: Inter,system-ui,sans-serif;
}
.logo.sycamore:hover {
filter: drop-shadow(0 0 2em #f87171);
}
:root {
font-family: Inter, Avenir, Helvetica, Arial, sans-serif;
font-size: 16px;
line-height: 24px;
font-weight: 400;
color: #0f0f0f;
background-color: #f6f6f6;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
.container {
margin: 0;
padding-top: 10vh;
display: flex;
flex-direction: column;
justify-content: center;
text-align: center;
}
.logo {
height: 6em;
padding: 1.5em;
will-change: filter;
transition: 0.75s;
}
.logo.tauri:hover {
filter: drop-shadow(0 0 2em #24c8db);
}
.row {
display: flex;
justify-content: center;
align-items: center;
}
a {
font-weight: 500;
color: #646cff;
text-decoration: inherit;
}
a:hover {
color: #535bf2;
}
h1 {
text-align: center;
}
input,
button {
border-radius: 8px;
border: 1px solid transparent;
padding: 0.6em 1.2em;
font-size: 1em;
font-weight: 500;
font-family: inherit;
color: #0f0f0f;
background-color: #ffffff;
transition: border-color 0.25s;
box-shadow: 0 2px 2px rgba(0, 0, 0, 0.2);
}
button {
cursor: pointer;
}
button:hover {
border-color: #396cd8;
}
input,
button {
outline: none;
}
#greet-input {
margin-right: 5px;
}
@media (prefers-color-scheme: dark) {
:root {
color: #f6f6f6;
background-color: #2f2f2f;
}
a:hover {
color: #24c8db;
}
input,
button {
color: #ffffff;
background-color: #0f0f0f98;
}
}

2
examples/test/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
target
dist

3894
examples/test/Cargo.lock generated Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,21 +1,19 @@
[package] [package]
name = "tauri-app-ui" name = "tauri-sys-test-ui"
version = "0.0.0" version = "0.0.0"
edition = "2021" edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies] [dependencies]
tauri-sys = { path = "../../", features = ["all"] } tauri-sys = { path = "../../", features = ["all"] }
serde = { version = "1.0.140", features = ["derive"] } sycamore = { git = "https://github.com/sycamore-rs/sycamore", rev = "abd556cbc02047042dad2ebd04405e455a9b11b2", features = ["suspense"] }
sycamore = { git = "https://github.com/sycamore-rs/sycamore", rev = "abd556cbc02047042dad2ebd04405e455a9b11b2", features = ["suspense", "hydrate"] } anyhow = "1.0.66"
sycamore-router = { git = "https://github.com/sycamore-rs/sycamore", rev = "abd556cbc02047042dad2ebd04405e455a9b11b2" } console_error_panic_hook = "0.1.7"
log = "0.4.17" wasm-bindgen-futures = "0.4.32"
futures-util = "0.3.25"
serde = { version = "1.0.147", features = ["derive"] }
wasm-logger = "0.2.0" wasm-logger = "0.2.0"
gloo-timers = "0.2.4" log = "0.4.17"
shared = { path = "shared" }
[features] [features]
ssg = ["sycamore/ssr"] ci = []
[workspace]
members = ["src-tauri", "shared"]

10
examples/test/Trunk.toml Normal file
View file

@ -0,0 +1,10 @@
[build]
target = "./index.html"
[watch]
ignore = ["./src-tauri"]
[serve]
address = "127.0.0.1"
port = 1420
open = false

7
examples/test/index.html Normal file
View file

@ -0,0 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title>Tauri + Yew App</title>
</head>
</html>

View file

@ -1,5 +1,5 @@
[package] [package]
name = "tauri-app" name = "tauri-sys-test"
version = "0.0.0" version = "0.0.0"
description = "A Tauri App" description = "A Tauri App"
authors = ["you"] authors = ["you"]
@ -11,13 +11,12 @@ rust-version = "1.57"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[build-dependencies] [build-dependencies]
tauri-build = { git = "https://github.com/tauri-apps/tauri", features = [] } tauri-build = { version = "1.2", features = [] }
[dependencies] [dependencies]
serde_json = "1.0" serde_json = "1.0"
serde = { version = "1.0", features = ["derive"] } serde = { version = "1.0", features = ["derive"] }
tauri = { git = "https://github.com/tauri-apps/tauri", features = ["api-all", "updater"] } tauri = { version = "1.2", features = ["api-all"] }
shared = { path = "../shared" }
[features] [features]
# by default Tauri runs in production mode # by default Tauri runs in production mode

View file

@ -0,0 +1,6 @@
{
"build": {
"beforeDevCommand": "trunk serve --features ci",
"beforeBuildCommand": "trunk build --release --features ci"
}
}

View file

Before

Width:  |  Height:  |  Size: 3.4 KiB

After

Width:  |  Height:  |  Size: 3.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 6.8 KiB

After

Width:  |  Height:  |  Size: 6.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 974 B

After

Width:  |  Height:  |  Size: 974 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.8 KiB

After

Width:  |  Height:  |  Size: 2.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.8 KiB

After

Width:  |  Height:  |  Size: 3.8 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 3.9 KiB

After

Width:  |  Height:  |  Size: 3.9 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 7.6 KiB

After

Width:  |  Height:  |  Size: 7.6 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 903 B

After

Width:  |  Height:  |  Size: 903 B

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 8.4 KiB

After

Width:  |  Height:  |  Size: 8.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2 KiB

After

Width:  |  Height:  |  Size: 2 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 1.5 KiB

After

Width:  |  Height:  |  Size: 1.5 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 85 KiB

After

Width:  |  Height:  |  Size: 85 KiB

Before After
Before After

View file

Before

Width:  |  Height:  |  Size: 14 KiB

After

Width:  |  Height:  |  Size: 14 KiB

Before After
Before After

View file

@ -0,0 +1,41 @@
#![cfg_attr(
all(not(debug_assertions), target_os = "windows"),
windows_subsystem = "windows"
)]
use std::sync::atomic::{AtomicBool, Ordering};
use tauri::{Manager, State, api::notification::Notification};
struct Received(AtomicBool);
// Learn more about Tauri commands at https://tauri.app/v1/guides/features/command
#[tauri::command]
fn verify_receive(emitted: State<Received>) -> bool {
emitted.0.load(Ordering::Relaxed)
}
#[tauri::command]
fn exit_with_error(e: &str) -> bool {
eprintln!("{}", e);
std::process::exit(1);
}
fn main() {
tauri::Builder::default()
.invoke_handler(tauri::generate_handler![verify_receive, exit_with_error])
.setup(|app| {
app.manage(Received(AtomicBool::new(false)));
let app_handle = app.handle();
app.listen_global("foo", move |_| {
app_handle
.state::<Received>()
.0
.store(true, Ordering::Relaxed);
});
Ok(())
})
.run(tauri::generate_context!())
.expect("error while running tauri application");
}

View file

@ -7,7 +7,7 @@
"withGlobalTauri": true "withGlobalTauri": true
}, },
"package": { "package": {
"productName": "tauri-app", "productName": "tauri-sys-test",
"version": "0.0.0" "version": "0.0.0"
}, },
"tauri": { "tauri": {
@ -63,7 +63,7 @@
"fullscreen": false, "fullscreen": false,
"height": 600, "height": 600,
"resizable": true, "resizable": true,
"title": "tauri-app", "title": "tauri-sys testing suite",
"width": 800 "width": 800
} }
] ]

34
examples/test/src/app.rs Normal file
View file

@ -0,0 +1,34 @@
use anyhow::ensure;
use tauri_sys::app;
pub async fn get_name() -> anyhow::Result<()> {
let name = app::get_name().await?;
ensure!(name == "tauri-sys-test");
Ok(())
}
pub async fn get_version() -> anyhow::Result<()> {
let version = app::get_version().await?;
ensure!(version.major == 0);
ensure!(version.minor == 0);
ensure!(version.patch == 0);
ensure!(version.build.is_empty());
ensure!(version.pre.is_empty());
Ok(())
}
pub async fn get_tauri_version() -> anyhow::Result<()> {
let version = app::get_tauri_version().await?;
ensure!(version.major == 1);
ensure!(version.minor == 2);
ensure!(version.patch == 0);
ensure!(version.build.is_empty());
ensure!(version.pre.is_empty());
Ok(())
}

View file

@ -0,0 +1,12 @@
use anyhow::ensure;
use tauri_sys::clipboard;
pub async fn test() -> anyhow::Result<()> {
clipboard::write_text("foobar").await?;
let text = clipboard::read_text().await?;
ensure!(text == "foobar".to_string());
Ok(())
}

View file

@ -0,0 +1,97 @@
use anyhow::ensure;
use tauri_sys::dialog::{FileDialogBuilder, MessageDialogBuilder, MessageDialogType};
pub async fn ask() -> anyhow::Result<()> {
let mut builder = MessageDialogBuilder::new();
builder.set_title("Tauri");
builder.set_type(MessageDialogType::Warning);
let works = builder
.ask("Does this work? \n Click Yes to mark this test as passing")
.await?;
ensure!(works);
Ok(())
}
pub async fn confirm() -> anyhow::Result<()> {
let mut builder = MessageDialogBuilder::new();
builder.set_title("Tauri");
builder.set_type(MessageDialogType::Warning);
let works = builder
.confirm("Does this work? \n Click Ok to mark this test as passing")
.await?;
ensure!(works);
Ok(())
}
pub async fn message() -> anyhow::Result<()> {
let mut builder = MessageDialogBuilder::new();
builder.set_title("Tauri");
builder.set_type(MessageDialogType::Warning);
builder.message("This is a message just for you!").await?;
Ok(())
}
pub async fn pick_file() -> anyhow::Result<()> {
let mut builder = FileDialogBuilder::new();
builder.set_title("Select a file to mark this test as passing");
let file = builder.pick_file().await?;
ensure!(file.is_some());
Ok(())
}
pub async fn pick_files() -> anyhow::Result<()> {
let mut builder = FileDialogBuilder::new();
builder.set_title("Select a multiple files to mark this test as passing");
let file = builder.pick_files().await?;
ensure!(file.is_some());
ensure!(file.unwrap().len() > 1);
Ok(())
}
pub async fn pick_folder() -> anyhow::Result<()> {
let mut builder = FileDialogBuilder::new();
builder.set_title("Select a folder to mark this test as passing");
let file = builder.pick_folder().await?;
ensure!(file.is_some());
Ok(())
}
pub async fn pick_folders() -> anyhow::Result<()> {
let mut builder = FileDialogBuilder::new();
builder.set_title("Select a multiple folders to mark this test as passing");
let file = builder.pick_folders().await?;
ensure!(file.is_some());
ensure!(file.unwrap().len() > 1);
Ok(())
}
pub async fn save() -> anyhow::Result<()> {
let mut builder = FileDialogBuilder::new();
builder.set_title("Select a file to mark this test as passing");
let file = builder.save().await?;
ensure!(file.is_some());
Ok(())
}

View file

@ -0,0 +1,10 @@
use anyhow::ensure;
use tauri_sys::{event, tauri};
pub async fn emit() -> anyhow::Result<()> {
event::emit("foo", &"bar").await?;
ensure!(tauri::invoke::<_, bool>("verify_receive", &()).await?);
Ok(())
}

175
examples/test/src/main.rs Normal file
View file

@ -0,0 +1,175 @@
mod app;
mod clipboard;
mod event;
mod window;
mod dialog;
mod notification;
mod os;
extern crate console_error_panic_hook;
use std::future::Future;
use std::panic;
use sycamore::prelude::*;
use sycamore::suspense::Suspense;
#[cfg(feature = "ci")]
async fn exit_with_error(e: String) {
use serde::Serialize;
#[derive(Serialize)]
struct Args {
e: String,
}
tauri_sys::tauri::invoke::<_, ()>("exit_with_error", &Args { e })
.await
.unwrap();
}
#[derive(Props)]
pub struct TestProps<'a, F>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
name: &'a str,
test: F,
}
#[component]
pub async fn Test<'a, G: Html, F>(cx: Scope<'a>, props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
let res = props.test.await;
view! { cx,
tr {
td { code { (props.name.to_string()) } }
td { (if let Err(e) = &res {
#[cfg(feature = "ci")]
{
wasm_bindgen_futures::spawn_local(exit_with_error(e.to_string()));
unreachable!()
}
#[cfg(not(feature = "ci"))]
format!("{:?}", e)
} else {
format!("")
})
}
}
}
}
#[cfg(not(feature = "ci"))]
#[component]
pub async fn InteractiveTest<'a, G: Html, F>(cx: Scope<'a>, props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
let mut test = Some(props.test);
let render_test = create_signal(cx, false);
let run_test = |_| {
render_test.set(true);
};
view! { cx,
(if *render_test.get() {
let test = test.take().unwrap();
let fallback = view! { cx,
tr {
td { code { (props.name.to_string()) } }
td {
"Running Test..."
}
}
};
view! { cx,
Suspense(fallback=fallback) {
Test(name=props.name, test=test)
}
}
} else {
view! { cx,
tr {
td { code { (props.name.to_string()) } }
td {
button(on:click=run_test) { "Run Interactive Test"}
}
}
}
})
}
}
#[cfg(feature = "ci")]
#[component]
pub async fn InteractiveTest<'a, G: Html, F>(cx: Scope<'a>, _props: TestProps<'a, F>) -> View<G>
where
F: Future<Output = anyhow::Result<()>> + 'a,
{
view! { cx, "Interactive tests are not run in CI mode" }
}
#[component]
pub async fn Terminate<'a, G: Html>(cx: Scope<'a>) -> View<G> {
#[cfg(feature = "ci")]
sycamore::suspense::await_suspense(cx, async {
tauri_sys::process::exit(0).await;
})
.await;
view! {
cx,
}
}
fn main() {
wasm_logger::init(wasm_logger::Config::default());
panic::set_hook(Box::new(|info| {
console_error_panic_hook::hook(info);
#[cfg(feature = "ci")]
wasm_bindgen_futures::spawn_local(exit_with_error(format!("{}", info)));
}));
sycamore::render(|cx| {
view! { cx,
table {
tbody {
Suspense(fallback=view!{ cx, "Running Tests..." }) {
Test(name="app::get_name",test=app::get_name())
Test(name="app::get_version",test=app::get_version())
Test(name="app::get_tauri_version",test=app::get_tauri_version())
Test(name="clipboard::read_text | clipboard::write_text",test=clipboard::test())
Test(name="event::emit",test=event::emit())
InteractiveTest(name="dialog::message",test=dialog::message())
InteractiveTest(name="dialog::ask",test=dialog::ask())
InteractiveTest(name="dialog::confirm",test=dialog::confirm())
InteractiveTest(name="dialog::pick_file",test=dialog::pick_file())
InteractiveTest(name="dialog::pick_files",test=dialog::pick_files())
InteractiveTest(name="dialog::pick_folder",test=dialog::pick_folder())
InteractiveTest(name="dialog::pick_folders",test=dialog::pick_folders())
InteractiveTest(name="dialog::save",test=dialog::save())
Test(name="os::arch",test=os::arch())
Test(name="os::platform",test=os::platform())
Test(name="os::tempdir",test=os::tempdir())
Test(name="os::kind",test=os::kind())
Test(name="os::version",test=os::version())
Test(name="notification::is_permission_granted",test=notification::is_permission_granted())
Test(name="notification::request_permission",test=notification::request_permission())
InteractiveTest(name="notification::show_notification",test=notification::show_notification())
// Test(name="window::WebviewWindow::new",test=window::create_window())
Terminate
}
}
}
}
});
}

View file

@ -0,0 +1,28 @@
use anyhow::ensure;
use tauri_sys::notification::{self, Permission};
pub async fn is_permission_granted() -> anyhow::Result<()> {
let granted = notification::is_permission_granted().await?;
ensure!(granted);
Ok(())
}
pub async fn request_permission() -> anyhow::Result<()> {
let permission = notification::request_permission().await?;
ensure!(permission == Permission::Granted);
Ok(())
}
pub async fn show_notification() -> anyhow::Result<()> {
let mut n = notification::Notification::default();
n.set_title("TAURI");
n.set_body("Tauri is awesome!");
n.show()?;
Ok(())
}

41
examples/test/src/os.rs Normal file
View file

@ -0,0 +1,41 @@
use tauri_sys::os;
pub async fn arch() -> anyhow::Result<()> {
let arch = os::arch().await?;
log::debug!("{:?}", arch);
Ok(())
}
pub async fn platform() -> anyhow::Result<()> {
let platform = os::platform().await?;
log::debug!("{:?}", platform);
Ok(())
}
pub async fn tempdir() -> anyhow::Result<()> {
let tempdir = os::tempdir().await?;
log::debug!("{:?}", tempdir);
Ok(())
}
pub async fn kind() -> anyhow::Result<()> {
let kind = os::kind().await?;
log::debug!("{:?}", kind);
Ok(())
}
pub async fn version() -> anyhow::Result<()> {
let version = os::version().await?;
log::debug!("{:?}", version);
Ok(())
}

View file

@ -0,0 +1,13 @@
use anyhow::ensure;
use tauri_sys::window;
pub async fn create_window() -> anyhow::Result<()> {
let win = window::WebviewWindow::new("foo", ());
ensure!(win.is_visible().await?);
// ensure!(win.label() == "foo".to_string());
win.close().await?;
Ok(())
}

15
package.json Normal file
View file

@ -0,0 +1,15 @@
{
"name": "tauri-sys",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "esbuild --outdir=src --format=esm --bundle tauri/tooling/api/src/*.ts"
},
"keywords": [],
"author": "",
"license": "ISC",
"devDependencies": {
"esbuild": "^0.15.13"
}
}

237
pnpm-lock.yaml generated Normal file
View file

@ -0,0 +1,237 @@
lockfileVersion: 5.4
specifiers:
esbuild: ^0.15.13
devDependencies:
esbuild: 0.15.13
packages:
/@esbuild/android-arm/0.15.13:
resolution: {integrity: sha512-RY2fVI8O0iFUNvZirXaQ1vMvK0xhCcl0gqRj74Z6yEiO1zAUa7hbsdwZM1kzqbxHK7LFyMizipfXT3JME+12Hw==}
engines: {node: '>=12'}
cpu: [arm]
os: [android]
requiresBuild: true
dev: true
optional: true
/@esbuild/linux-loong64/0.15.13:
resolution: {integrity: sha512-+BoyIm4I8uJmH/QDIH0fu7MG0AEx9OXEDXnqptXCwKOlOqZiS4iraH1Nr7/ObLMokW3sOCeBNyD68ATcV9b9Ag==}
engines: {node: '>=12'}
cpu: [loong64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-android-64/0.15.13:
resolution: {integrity: sha512-yRorukXBlokwTip+Sy4MYskLhJsO0Kn0/Fj43s1krVblfwP+hMD37a4Wmg139GEsMLl+vh8WXp2mq/cTA9J97g==}
engines: {node: '>=12'}
cpu: [x64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-android-arm64/0.15.13:
resolution: {integrity: sha512-TKzyymLD6PiVeyYa4c5wdPw87BeAiTXNtK6amWUcXZxkV51gOk5u5qzmDaYSwiWeecSNHamFsaFjLoi32QR5/w==}
engines: {node: '>=12'}
cpu: [arm64]
os: [android]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-64/0.15.13:
resolution: {integrity: sha512-WAx7c2DaOS6CrRcoYCgXgkXDliLnFv3pQLV6GeW1YcGEZq2Gnl8s9Pg7ahValZkpOa0iE/ojRVQ87sbUhF1Cbg==}
engines: {node: '>=12'}
cpu: [x64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-darwin-arm64/0.15.13:
resolution: {integrity: sha512-U6jFsPfSSxC3V1CLiQqwvDuj3GGrtQNB3P3nNC3+q99EKf94UGpsG9l4CQ83zBs1NHrk1rtCSYT0+KfK5LsD8A==}
engines: {node: '>=12'}
cpu: [arm64]
os: [darwin]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-64/0.15.13:
resolution: {integrity: sha512-whItJgDiOXaDG/idy75qqevIpZjnReZkMGCgQaBWZuKHoElDJC1rh7MpoUgupMcdfOd+PgdEwNQW9DAE6i8wyA==}
engines: {node: '>=12'}
cpu: [x64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-freebsd-arm64/0.15.13:
resolution: {integrity: sha512-6pCSWt8mLUbPtygv7cufV0sZLeylaMwS5Fznj6Rsx9G2AJJsAjQ9ifA+0rQEIg7DwJmi9it+WjzNTEAzzdoM3Q==}
engines: {node: '>=12'}
cpu: [arm64]
os: [freebsd]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-32/0.15.13:
resolution: {integrity: sha512-VbZdWOEdrJiYApm2kkxoTOgsoCO1krBZ3quHdYk3g3ivWaMwNIVPIfEE0f0XQQ0u5pJtBsnk2/7OPiCFIPOe/w==}
engines: {node: '>=12'}
cpu: [ia32]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-64/0.15.13:
resolution: {integrity: sha512-rXmnArVNio6yANSqDQlIO4WiP+Cv7+9EuAHNnag7rByAqFVuRusLbGi2697A5dFPNXoO//IiogVwi3AdcfPC6A==}
engines: {node: '>=12'}
cpu: [x64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm/0.15.13:
resolution: {integrity: sha512-Ac6LpfmJO8WhCMQmO253xX2IU2B3wPDbl4IvR0hnqcPrdfCaUa2j/lLMGTjmQ4W5JsJIdHEdW12dG8lFS0MbxQ==}
engines: {node: '>=12'}
cpu: [arm]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-arm64/0.15.13:
resolution: {integrity: sha512-alEMGU4Z+d17U7KQQw2IV8tQycO6T+rOrgW8OS22Ua25x6kHxoG6Ngry6Aq6uranC+pNWNMB6aHFPh7aTQdORQ==}
engines: {node: '>=12'}
cpu: [arm64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-mips64le/0.15.13:
resolution: {integrity: sha512-47PgmyYEu+yN5rD/MbwS6DxP2FSGPo4Uxg5LwIdxTiyGC2XKwHhHyW7YYEDlSuXLQXEdTO7mYe8zQ74czP7W8A==}
engines: {node: '>=12'}
cpu: [mips64el]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-ppc64le/0.15.13:
resolution: {integrity: sha512-z6n28h2+PC1Ayle9DjKoBRcx/4cxHoOa2e689e2aDJSaKug3jXcQw7mM+GLg+9ydYoNzj8QxNL8ihOv/OnezhA==}
engines: {node: '>=12'}
cpu: [ppc64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-riscv64/0.15.13:
resolution: {integrity: sha512-+Lu4zuuXuQhgLUGyZloWCqTslcCAjMZH1k3Xc9MSEJEpEFdpsSU0sRDXAnk18FKOfEjhu4YMGaykx9xjtpA6ow==}
engines: {node: '>=12'}
cpu: [riscv64]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-linux-s390x/0.15.13:
resolution: {integrity: sha512-BMeXRljruf7J0TMxD5CIXS65y7puiZkAh+s4XFV9qy16SxOuMhxhVIXYLnbdfLrsYGFzx7U9mcdpFWkkvy/Uag==}
engines: {node: '>=12'}
cpu: [s390x]
os: [linux]
requiresBuild: true
dev: true
optional: true
/esbuild-netbsd-64/0.15.13:
resolution: {integrity: sha512-EHj9QZOTel581JPj7UO3xYbltFTYnHy+SIqJVq6yd3KkCrsHRbapiPb0Lx3EOOtybBEE9EyqbmfW1NlSDsSzvQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [netbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-openbsd-64/0.15.13:
resolution: {integrity: sha512-nkuDlIjF/sfUhfx8SKq0+U+Fgx5K9JcPq1mUodnxI0x4kBdCv46rOGWbuJ6eof2n3wdoCLccOoJAbg9ba/bT2w==}
engines: {node: '>=12'}
cpu: [x64]
os: [openbsd]
requiresBuild: true
dev: true
optional: true
/esbuild-sunos-64/0.15.13:
resolution: {integrity: sha512-jVeu2GfxZQ++6lRdY43CS0Tm/r4WuQQ0Pdsrxbw+aOrHQPHV0+LNOLnvbN28M7BSUGnJnHkHm2HozGgNGyeIRw==}
engines: {node: '>=12'}
cpu: [x64]
os: [sunos]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-32/0.15.13:
resolution: {integrity: sha512-XoF2iBf0wnqo16SDq+aDGi/+QbaLFpkiRarPVssMh9KYbFNCqPLlGAWwDvxEVz+ywX6Si37J2AKm+AXq1kC0JA==}
engines: {node: '>=12'}
cpu: [ia32]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-64/0.15.13:
resolution: {integrity: sha512-Et6htEfGycjDrtqb2ng6nT+baesZPYQIW+HUEHK4D1ncggNrDNk3yoboYQ5KtiVrw/JaDMNttz8rrPubV/fvPQ==}
engines: {node: '>=12'}
cpu: [x64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild-windows-arm64/0.15.13:
resolution: {integrity: sha512-3bv7tqntThQC9SWLRouMDmZnlOukBhOCTlkzNqzGCmrkCJI7io5LLjwJBOVY6kOUlIvdxbooNZwjtBvj+7uuVg==}
engines: {node: '>=12'}
cpu: [arm64]
os: [win32]
requiresBuild: true
dev: true
optional: true
/esbuild/0.15.13:
resolution: {integrity: sha512-Cu3SC84oyzzhrK/YyN4iEVy2jZu5t2fz66HEOShHURcjSkOSAVL8C/gfUT+lDJxkVHpg8GZ10DD0rMHRPqMFaQ==}
engines: {node: '>=12'}
hasBin: true
requiresBuild: true
optionalDependencies:
'@esbuild/android-arm': 0.15.13
'@esbuild/linux-loong64': 0.15.13
esbuild-android-64: 0.15.13
esbuild-android-arm64: 0.15.13
esbuild-darwin-64: 0.15.13
esbuild-darwin-arm64: 0.15.13
esbuild-freebsd-64: 0.15.13
esbuild-freebsd-arm64: 0.15.13
esbuild-linux-32: 0.15.13
esbuild-linux-64: 0.15.13
esbuild-linux-arm: 0.15.13
esbuild-linux-arm64: 0.15.13
esbuild-linux-mips64le: 0.15.13
esbuild-linux-ppc64le: 0.15.13
esbuild-linux-riscv64: 0.15.13
esbuild-linux-s390x: 0.15.13
esbuild-netbsd-64: 0.15.13
esbuild-openbsd-64: 0.15.13
esbuild-sunos-64: 0.15.13
esbuild-windows-32: 0.15.13
esbuild-windows-64: 0.15.13
esbuild-windows-arm64: 0.15.13
dev: true

View file

@ -9,8 +9,10 @@ use semver::Version;
/// const appName = await getName(); /// const appName = await getName();
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn get_name() -> String { pub async fn get_name() -> crate::Result<String> {
inner::getName().await.as_string().unwrap() let js_val = inner::getName().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
} }
/// Gets the application version. /// Gets the application version.
@ -23,8 +25,10 @@ pub async fn get_name() -> String {
/// let version = get_version().await; /// let version = get_version().await;
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn get_version() -> Version { pub async fn get_version() -> crate::Result<Version> {
Version::parse(&inner::getVersion().await.as_string().unwrap()).unwrap() let js_val = inner::getVersion().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
} }
/// Gets the Tauri version. /// Gets the Tauri version.
@ -37,8 +41,10 @@ pub async fn get_version() -> Version {
/// let version = get_tauri_version().await; /// let version = get_tauri_version().await;
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn get_tauri_version() -> Version { pub async fn get_tauri_version() -> crate::Result<Version> {
Version::parse(&inner::getTauriVersion().await.as_string().unwrap()).unwrap() let js_val = inner::getTauriVersion().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
} }
/// Shows the application on macOS. This function does not automatically focuses any app window. /// Shows the application on macOS. This function does not automatically focuses any app window.
@ -51,8 +57,8 @@ pub async fn get_tauri_version() -> Version {
/// show().await; /// show().await;
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn show() { pub async fn show() -> crate::Result<()> {
inner::show().await; Ok(inner::show().await?)
} }
/// Hides the application on macOS. /// Hides the application on macOS.
@ -65,19 +71,24 @@ pub async fn show() {
/// hide().await; /// hide().await;
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn hide() { pub async fn hide() -> crate::Result<()> {
inner::hide().await; Ok(inner::hide().await?)
} }
mod inner { mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/dist/app.js")] #[wasm_bindgen(module = "/src/app.js")]
extern "C" { extern "C" {
pub async fn getName() -> JsValue; #[wasm_bindgen(catch)]
pub async fn getTauriVersion() -> JsValue; pub async fn getName() -> Result<JsValue, JsValue>;
pub async fn getVersion() -> JsValue; #[wasm_bindgen(catch)]
pub async fn hide(); pub async fn getTauriVersion() -> Result<JsValue, JsValue>;
pub async fn show(); #[wasm_bindgen(catch)]
pub async fn getVersion() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn hide() -> Result<(), JsValue>;
#[wasm_bindgen(catch)]
pub async fn show() -> Result<(), JsValue>;
} }
} }

55
src/cli.js Normal file
View file

@ -0,0 +1,55 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/cli.ts
async function getMatches() {
return invokeTauriCommand({
__tauriModule: "Cli",
message: {
cmd: "cliMatches"
}
});
}
export {
getMatches
};

View file

@ -8,8 +8,10 @@
/// let clipboard_text = read_text().await; /// let clipboard_text = read_text().await;
/// ``` /// ```
#[inline(always)] #[inline(always)]
pub async fn read_text() -> Option<String> { pub async fn read_text() -> crate::Result<String> {
inner::readText().await.as_string() let js_val = inner::readText().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
} }
/// Writes plain text to the clipboard. /// Writes plain text to the clipboard.
@ -25,16 +27,18 @@ pub async fn read_text() -> Option<String> {
/// ///
/// @returns A promise indicating the success or failure of the operation. /// @returns A promise indicating the success or failure of the operation.
#[inline(always)] #[inline(always)]
pub async fn write_text(text: &str) { pub async fn write_text(text: &str) -> crate::Result<()> {
inner::writeText(text).await Ok(inner::writeText(text).await?)
} }
mod inner { mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/dist/clipboard.js")] #[wasm_bindgen(module = "/src/clipboard.js")]
extern "C" { extern "C" {
pub async fn readText() -> JsValue; #[wasm_bindgen(catch)]
pub async fn writeText(text: &str); pub async fn readText() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn writeText(text: &str) -> Result<(), JsValue>;
} }
} }

111
src/dialog.js Normal file
View file

@ -0,0 +1,111 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/dialog.ts
async function open(options = {}) {
if (typeof options === "object") {
Object.freeze(options);
}
return invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "openDialog",
options
}
});
}
async function save(options = {}) {
if (typeof options === "object") {
Object.freeze(options);
}
return invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "saveDialog",
options
}
});
}
async function message(message2, options) {
const opts = typeof options === "string" ? { title: options } : options;
return invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "messageDialog",
message: message2.toString(),
title: opts?.title?.toString(),
type: opts?.type
}
});
}
async function ask(message2, options) {
const opts = typeof options === "string" ? { title: options } : options;
return invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "askDialog",
message: message2.toString(),
title: opts?.title?.toString(),
type: opts?.type
}
});
}
async function confirm(message2, options) {
const opts = typeof options === "string" ? { title: options } : options;
return invokeTauriCommand({
__tauriModule: "Dialog",
message: {
cmd: "confirmDialog",
message: message2.toString(),
title: opts?.title?.toString(),
type: opts?.type
}
});
}
export {
ask,
confirm,
message,
open,
save
};

604
src/dialog.rs Normal file
View file

@ -0,0 +1,604 @@
use serde::Serialize;
use std::path::{Path, PathBuf};
#[derive(Debug, Serialize)]
struct DialogFilter<'a> {
extensions: &'a [&'a str],
name: &'a str,
}
#[derive(Debug, Default, Serialize)]
#[serde(rename = "camelCase")]
pub struct FileDialogBuilder<'a> {
default_path: Option<&'a Path>,
filters: Vec<DialogFilter<'a>>,
title: Option<&'a str>,
directory: bool,
multiple: bool,
recursive: bool,
}
impl<'a> FileDialogBuilder<'a> {
/// Gets the default file dialog builder.
pub fn new() -> Self {
Self::default()
}
/// Set starting file name or directory of the dialog.
pub fn set_default_path(&mut self, default_path: &'a Path) {
self.default_path = Some(default_path);
}
/// If directory is true, indicates that it will be read recursively later.
/// Defines whether subdirectories will be allowed on the scope or not.
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = FileDialogBuilder::new().set_recursive(true);
/// # Ok(())
/// # }
/// ```
pub fn set_recursive(&mut self, recursive: bool) {
self.recursive = recursive;
}
/// Set the title of the dialog.
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = FileDialogBuilder::new().set_title("Test Title");
/// # Ok(())
/// # }
/// ```
pub fn set_title(&mut self, title: &'a str) {
self.title = Some(title);
}
/// Add file extension filter. Takes in the name of the filter, and list of extensions
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = FileDialogBuilder::new().add_filter("Image", &["png", "jpeg"]);
/// # Ok(())
/// # }
/// ```
pub fn add_filter(&mut self, name: &'a str, extensions: &'a [&'a str]) {
self.filters.push(DialogFilter { name, extensions });
}
/// Add many file extension filters.
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = FileDialogBuilder::new().add_filters(&[("Image", &["png", "jpeg"]),("Video", &["mp4"])]);
/// # Ok(())
/// # }
/// ```
pub fn add_filters(&mut self, filters: impl IntoIterator<Item = (&'a str, &'a [&'a str])>) {
for (name, extensions) in filters.into_iter() {
self.filters.push(DialogFilter {
name: name.as_ref(),
extensions,
});
}
}
/// Shows the dialog to select a single file.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = FileDialogBuilder::new().pick_file().await?;
/// # Ok(())
/// # }
/// ```
pub async fn pick_file(self) -> crate::Result<Option<PathBuf>> {
let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
/// Shows the dialog to select multiple files.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let files = FileDialogBuilder::new().pick_files().await?;
/// # Ok(())
/// # }
/// ```
pub async fn pick_files(mut self) -> crate::Result<Option<Vec<PathBuf>>> {
self.multiple = true;
let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
/// Shows the dialog to select a single folder.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let files = FileDialogBuilder::new().pick_folder().await?;
/// # Ok(())
/// # }
/// ```
pub async fn pick_folder(mut self) -> crate::Result<Option<PathBuf>> {
self.directory = true;
let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
/// Shows the dialog to select multiple folders.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let files = FileDialogBuilder::new().pick_folders().await?;
/// # Ok(())
/// # }
/// ```
pub async fn pick_folders(mut self) -> crate::Result<Option<Vec<PathBuf>>> {
self.directory = true;
self.multiple = true;
let raw = inner::open(serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
/// Open a file/directory save dialog.
///
/// The selected path is added to the filesystem and asset protocol allowlist scopes.
/// When security is more important than the easy of use of this API, prefer writing a dedicated command instead.
///
/// Note that the allowlist scope change is not persisted, so the values are cleared when the application is restarted.
/// You can save it to the filesystem using tauri-plugin-persisted-scope.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::FileDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = FileDialogBuilder::new().save().await?;
/// # Ok(())
/// # }
/// ```
pub async fn save(self) -> crate::Result<Option<PathBuf>> {
let raw = inner::save(serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
}
#[derive(Debug, Default)]
pub enum MessageDialogType {
#[default]
Info,
Warning,
Error,
}
impl Serialize for MessageDialogType {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match self {
MessageDialogType::Info => serializer.serialize_str("info"),
MessageDialogType::Warning => serializer.serialize_str("warning"),
MessageDialogType::Error => serializer.serialize_str("error"),
}
}
}
#[derive(Debug, Default, Serialize)]
pub struct MessageDialogBuilder<'a> {
title: Option<&'a str>,
r#type: MessageDialogType,
}
impl<'a> MessageDialogBuilder<'a> {
pub fn new() -> Self {
Self::default()
}
/// Set the title of the dialog.
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::MessageDialogBuilder;
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = MessageDialogBuilder::new().set_title("Test Title");
/// # Ok(())
/// # }
/// ```
pub fn set_title(&mut self, title: &'a str) {
self.title = Some(title);
}
/// Set the type of the dialog.
///
/// # Example
///
/// ```rust
/// use tauri_sys::dialog::{MessageDialogBuilder,MessageDialogType};
///
/// # fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let _builder = MessageDialogBuilder::new().set_type(MessageDialogType::Error);
/// # Ok(())
/// # }
/// ```
pub fn set_type(&mut self, r#type: MessageDialogType) {
self.r#type = r#type;
}
/// Shows a message dialog with an `Ok` button.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::MessageDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let file = MessageDialogBuilder::new().message("Tauri is awesome").await?;
/// # Ok(())
/// # }
/// ```
pub async fn message(self, message: &str) -> crate::Result<()> {
Ok(inner::message(message, serde_wasm_bindgen::to_value(&self)?).await?)
}
/// Shows a question dialog with `Yes` and `No` buttons.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::MessageDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let confirmation = MessageDialogBuilder::new().ask("Are you sure?").await?;
/// # Ok(())
/// # }
/// ```
pub async fn ask(self, message: &str) -> crate::Result<bool> {
let raw = inner::ask(message, serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
/// Shows a question dialog with `Ok` and `Cancel` buttons.
///
/// # Example
///
/// ```rust,no_run
/// use tauri_sys::dialog::MessageDialogBuilder;
///
/// # async fn main() -> Result<(), Box<dyn std::error::Error>> {
/// let confirmation = MessageDialogBuilder::new().confirm("Are you sure?").await?;
/// # Ok(())
/// # }
/// ```
pub async fn confirm(self, message: &str) -> crate::Result<bool> {
let raw = inner::confirm(message, serde_wasm_bindgen::to_value(&self)?).await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
}
// //! User interaction with the file system using dialog boxes.
// //!
// //! # Example
// //!
// //! ```rust,no_run
// //! use tauri_api::dialog::open;
// //!
// //! let path = open(None).await;
// //! ```
// use serde::Serialize;
// use std::path::PathBuf;
// /// Extension filter for the file dialog.
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// let filter = DialogFilter {
// /// extension: vec![".jpg", ".jpeg", ".png", ".bmp"],
// /// name: "images",
// /// };
// /// ```
// #[derive(Serialize)]
// pub struct DialogFilter {
// /// Extensions to filter, without a `.` prefix.
// pub extensions: Vec<String>,
// /// Filter name
// pub name: String,
// }
// /// Types of a [`message`] dialog.
// #[derive(Serialize)]
// pub enum MessageDialogType {
// Error,
// Info,
// Warning,
// }
// /// Options for the [`message`] dialog.
// #[derive(Serialize)]
// pub struct MessageDialogOptions {
// /// The title of the dialog. Defaults to the app name.
// pub title: Option<String>,
// /// The type of the dialog. Defaults to MessageDialogType::Info.
// #[serde(rename(serialize = "type"))]
// pub kind: MessageDialogType,
// }
// impl MessageDialogOptions {
// /// Creates a new `MessageDialogOptions` with sensible default values.
// pub fn new() -> Self {
// Self {
// title: None,
// kind: MessageDialogType::Info,
// }
// }
// }
// /// Options for an [`open`] dialog.
// #[derive(Serialize)]
// pub struct OpenDialogOptions {
// /// Initial directory or file path.
// #[serde(rename(serialize = "defaultPath"))]
// pub default_path: Option<PathBuf>,
// /// Whether the dialog is a directory selection or not.
// pub directory: bool,
// /// The filters of the dialog.
// pub filters: Vec<DialogFilter>,
// /// Whether the dialog allows multiple selection or not.
// pub multiple: bool,
// /// If `directory` is `true`, indicatees that it will be read recursivley later.
// /// Defines whether subdirectories will be allowed on the scope or not.
// pub recursive: bool,
// /// The title of the dialog window.
// pub title: Option<String>,
// }
// impl OpenDialogOptions {
// /// Creates a new `OpenDialogOptions` with sensible default values.
// pub fn new() -> Self {
// Self {
// default_path: None,
// directory: false,
// filters: Vec::new(),
// multiple: false,
// recursive: false,
// title: None,
// }
// }
// }
// /// Options for the save dialog.
// #[derive(Serialize)]
// pub struct SaveDialogOptions {
// /// Initial directory of the file path.
// /// If it's not a directory path, the dialog interface will change to that folder.
// /// If it's not an existing directory, the file name will be set to the dialog's
// /// file name input and the dialog will be set to the parent folder.
// #[serde(rename(serialize = "defaultPath"))]
// pub default_path: Option<PathBuf>,
// /// The filters of the dialog.
// pub filters: Vec<DialogFilter>,
// /// The title of the dialog window.
// pub title: Option<String>,
// }
// impl SaveDialogOptions {
// /// Creates a new `SaveDialogOptions` with sensible default values.
// pub fn new() -> Self {
// Self {
// default_path: None,
// filters: Vec::new(),
// title: None,
// }
// }
// }
// /// Show a question dialog with `Yes` and `No` buttons.
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{ask, MessageDialogOptions};
// ///
// /// let yes = ask("Are you sure?", None).await;
// /// ```
// /// @param message Message to display.
// /// @param options Dialog options.
// /// @returns Whether the user selected `Yes` or `No`.
// #[inline(always)]
// pub async fn ask(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
// let js_val = inner::ask(message, serde_wasm_bindgen::to_value(&options)?).await?;
// Ok(serde_wasm_bindgen::from_value(js_val)?)
// }
// /// Shows a question dialog with `Ok` and `Cancel` buttons.
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{confirm, MessageDialogOptions};
// ///
// /// let confirmed = confirm("Are you sure?", None).await;
// /// ```
// /// @returns Whether the user selelced `Ok` or `Cancel`.
// pub async fn confirm(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<bool> {
// let js_val = inner::confirm(message, serde_wasm_bindgen::to_value(&options)?).await?;
// Ok(serde_wasm_bindgen::from_value(js_val)?)
// }
// /// Shows a message dialog with an `Ok` button.
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{message, MessageDialogOptions};
// ///
// /// message("Tauri is awesome", None).await;
// /// ```
// /// @param message Message to display.
// /// @param options Dialog options.
// /// @returns Promise resolved when user closes the dialog.
// pub async fn message(message: &str, options: Option<MessageDialogOptions>) -> crate::Result<()> {
// Ok(inner::message(message, serde_wasm_bindgen::to_value(&options)?).await?)
// }
// /// Opens a file/directory selection dialog for a single file.
// /// `multiple` field of [`options`](OpenDialogOptions) must be `false`, if provided.
// ///
// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
// /// When security is mroe important than the ease of use of this API,
// /// prefer writing a dedicated command instead.
// ///
// /// Note that the allowlist scope change is not persisited,
// /// so the values are cleared when the applicaiton is restarted.
// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{open, OpenDialogOptions};
// ///
// /// let file = open(None).await;
// ///
// /// let mut opts = OpenDialogOptions::new();
// /// opts.directory = true;
// /// let dir = open(Some(opts)).await;
// /// ```
// /// @param options Dialog options.
// /// @returns List of file paths, or `None` if user cancelled the dialog.
// pub async fn open(options: Option<OpenDialogOptions>) -> crate::Result<Option<PathBuf>> {
// let file = inner::open(serde_wasm_bindgen::to_value(&options)?).await?;
// Ok(serde_wasm_bindgen::from_value(file)?)
// }
// /// Opens a file/directory selection dialog for multiple files.
// /// `multiple` field of [`options`](OpenDialogOptions) must be `true`, if provided.
// ///
// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
// /// When security is mroe important than the ease of use of this API,
// /// prefer writing a dedicated command instead.
// ///
// /// Note that the allowlist scope change is not persisited,
// /// so the values are cleared when the applicaiton is restarted.
// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{open, OpenDialogOptions};
// ///
// /// let files = open_multiple(None).await;
// ///
// /// let mut opts = OpenDialogOptions::new();
// /// opts.multiple = true;
// /// opts.directory = true;
// /// let dirs = open(Some(opts)).await;
// /// ```
// /// @param options Dialog options.
// /// @returns List of file paths, or `None` if user cancelled the dialog.
// pub async fn open_multiple(
// options: Option<OpenDialogOptions>,
// ) -> crate::Result<Option<Vec<PathBuf>>> {
// let files = inner::open_multiple(serde_wasm_bindgen::to_value(&options)?).await?;
// Ok(serde_wasm_bindgen::from_value(files)?)
// }
// /// Opens a file/directory save dialog.
// ///
// /// The selected paths are added to the filesystem and asset protocol allowlist scopes.
// /// When security is mroe important than the ease of use of this API,
// /// prefer writing a dedicated command instead.
// ///
// /// Note that the allowlist scope change is not persisited,
// /// so the values are cleared when the applicaiton is restarted.
// /// You can save it to the filessytem using the [tauri-plugin-persisted-scope](https://github.com/tauri-apps/tauri-plugin-persisted-scope).
// ///
// /// # Example
// ///
// /// ```rust,no_run
// /// use tauri_api::dialog::{save, SaveDialogOptions};
// ///
// /// let file = save(None).await;
// /// ```
// /// @param options Dialog options.
// /// @returns File path, or `None` if user cancelled the dialog.
// pub async fn save(options: Option<SaveDialogOptions>) -> crate::Result<Option<PathBuf>> {
// let path = inner::save(serde_wasm_bindgen::to_value(&options)?).await?;
// Ok(serde_wasm_bindgen::from_value(path)?)
// }
mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/src/dialog.js")]
extern "C" {
#[wasm_bindgen(catch)]
pub async fn ask(message: &str, options: JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn confirm(message: &str, options: JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn open(options: JsValue) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn message(message: &str, option: JsValue) -> Result<(), JsValue>;
#[wasm_bindgen(catch)]
pub async fn save(options: JsValue) -> Result<JsValue, JsValue>;
}
}

View file

@ -1,6 +1,5 @@
use std::fmt::Debug;
use serde::{de::DeserializeOwned, Deserialize, Serialize}; use serde::{de::DeserializeOwned, Deserialize, Serialize};
use std::fmt::Debug;
use wasm_bindgen::{prelude::Closure, JsValue}; use wasm_bindgen::{prelude::Closure, JsValue};
#[derive(Deserialize)] #[derive(Deserialize)]
@ -45,8 +44,10 @@ impl<T: Debug> Debug for Event<T> {
/// ///
/// @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`. /// @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
#[inline(always)] #[inline(always)]
pub async fn emit<T: Serialize>(event: &str, payload: &T) { pub async fn emit<T: Serialize>(event: &str, payload: &T) -> crate::Result<()> {
inner::emit(event, serde_wasm_bindgen::to_value(payload).unwrap()).await inner::emit(event, serde_wasm_bindgen::to_value(payload)?).await?;
Ok(())
} }
/// Listen to an event from the backend. /// Listen to an event from the backend.
@ -70,7 +71,7 @@ pub async fn emit<T: Serialize>(event: &str, payload: &T) {
/// ///
/// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. /// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
#[inline(always)] #[inline(always)]
pub async fn listen<T, H>(event: &str, mut handler: H) -> impl FnOnce() pub async fn listen<T, H>(event: &str, mut handler: H) -> crate::Result<impl FnOnce()>
where where
T: DeserializeOwned, T: DeserializeOwned,
H: FnMut(Event<T>) + 'static, H: FnMut(Event<T>) + 'static,
@ -79,14 +80,14 @@ where
(handler)(serde_wasm_bindgen::from_value(raw).unwrap()) (handler)(serde_wasm_bindgen::from_value(raw).unwrap())
}); });
let unlisten = inner::listen(event, &closure).await; let unlisten = inner::listen(event, &closure).await?;
closure.forget(); closure.forget();
let unlisten = js_sys::Function::from(unlisten); let unlisten = js_sys::Function::from(unlisten);
move || { Ok(move || {
unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap(); unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap();
} })
} }
/// Listen to an one-off event from the backend. /// Listen to an one-off event from the backend.
@ -115,7 +116,7 @@ where
/// ///
/// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted. /// Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
#[inline(always)] #[inline(always)]
pub async fn once<T, H>(event: &str, mut handler: H) -> impl FnOnce() pub async fn once<T, H>(event: &str, mut handler: H) -> crate::Result<impl FnOnce()>
where where
T: DeserializeOwned, T: DeserializeOwned,
H: FnMut(Event<T>) + 'static, H: FnMut(Event<T>) + 'static,
@ -124,14 +125,14 @@ where
(handler)(serde_wasm_bindgen::from_value(raw).unwrap()) (handler)(serde_wasm_bindgen::from_value(raw).unwrap())
}); });
let unlisten = inner::once(event, &closure).await; let unlisten = inner::once(event, &closure).await?;
closure.forget(); closure.forget();
let unlisten = js_sys::Function::from(unlisten); let unlisten = js_sys::Function::from(unlisten);
move || { Ok(move || {
unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap(); unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap();
} })
} }
mod inner { mod inner {
@ -140,10 +141,19 @@ mod inner {
JsValue, JsValue,
}; };
#[wasm_bindgen(module = "/dist/event.js")] #[wasm_bindgen(module = "/src/event.js")]
extern "C" { extern "C" {
pub async fn emit(event: &str, payload: JsValue); #[wasm_bindgen(catch)]
pub async fn listen(event: &str, handler: &Closure<dyn FnMut(JsValue)>) -> JsValue; pub async fn emit(event: &str, payload: JsValue) -> Result<(), JsValue>;
pub async fn once(event: &str, handler: &Closure<dyn FnMut(JsValue)>) -> JsValue; #[wasm_bindgen(catch)]
pub async fn listen(
event: &str,
handler: &Closure<dyn FnMut(JsValue)>,
) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn once(
event: &str,
handler: &Closure<dyn FnMut(JsValue)>,
) -> Result<JsValue, JsValue>;
} }
} }

243
src/fs.js Normal file
View file

@ -0,0 +1,243 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/fs.ts
var BaseDirectory = /* @__PURE__ */ ((BaseDirectory2) => {
BaseDirectory2[BaseDirectory2["Audio"] = 1] = "Audio";
BaseDirectory2[BaseDirectory2["Cache"] = 2] = "Cache";
BaseDirectory2[BaseDirectory2["Config"] = 3] = "Config";
BaseDirectory2[BaseDirectory2["Data"] = 4] = "Data";
BaseDirectory2[BaseDirectory2["LocalData"] = 5] = "LocalData";
BaseDirectory2[BaseDirectory2["Desktop"] = 6] = "Desktop";
BaseDirectory2[BaseDirectory2["Document"] = 7] = "Document";
BaseDirectory2[BaseDirectory2["Download"] = 8] = "Download";
BaseDirectory2[BaseDirectory2["Executable"] = 9] = "Executable";
BaseDirectory2[BaseDirectory2["Font"] = 10] = "Font";
BaseDirectory2[BaseDirectory2["Home"] = 11] = "Home";
BaseDirectory2[BaseDirectory2["Picture"] = 12] = "Picture";
BaseDirectory2[BaseDirectory2["Public"] = 13] = "Public";
BaseDirectory2[BaseDirectory2["Runtime"] = 14] = "Runtime";
BaseDirectory2[BaseDirectory2["Template"] = 15] = "Template";
BaseDirectory2[BaseDirectory2["Video"] = 16] = "Video";
BaseDirectory2[BaseDirectory2["Resource"] = 17] = "Resource";
BaseDirectory2[BaseDirectory2["App"] = 18] = "App";
BaseDirectory2[BaseDirectory2["Log"] = 19] = "Log";
BaseDirectory2[BaseDirectory2["Temp"] = 20] = "Temp";
BaseDirectory2[BaseDirectory2["AppConfig"] = 21] = "AppConfig";
BaseDirectory2[BaseDirectory2["AppData"] = 22] = "AppData";
BaseDirectory2[BaseDirectory2["AppLocalData"] = 23] = "AppLocalData";
BaseDirectory2[BaseDirectory2["AppCache"] = 24] = "AppCache";
BaseDirectory2[BaseDirectory2["AppLog"] = 25] = "AppLog";
return BaseDirectory2;
})(BaseDirectory || {});
async function readTextFile(filePath, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "readTextFile",
path: filePath,
options
}
});
}
async function readBinaryFile(filePath, options = {}) {
const arr = await invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "readFile",
path: filePath,
options
}
});
return Uint8Array.from(arr);
}
async function writeTextFile(path, contents, options) {
if (typeof options === "object") {
Object.freeze(options);
}
if (typeof path === "object") {
Object.freeze(path);
}
const file = { path: "", contents: "" };
let fileOptions = options;
if (typeof path === "string") {
file.path = path;
} else {
file.path = path.path;
file.contents = path.contents;
}
if (typeof contents === "string") {
file.contents = contents ?? "";
} else {
fileOptions = contents;
}
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "writeFile",
path: file.path,
contents: Array.from(new TextEncoder().encode(file.contents)),
options: fileOptions
}
});
}
async function writeBinaryFile(path, contents, options) {
if (typeof options === "object") {
Object.freeze(options);
}
if (typeof path === "object") {
Object.freeze(path);
}
const file = { path: "", contents: [] };
let fileOptions = options;
if (typeof path === "string") {
file.path = path;
} else {
file.path = path.path;
file.contents = path.contents;
}
if (contents && "dir" in contents) {
fileOptions = contents;
} else if (typeof path === "string") {
file.contents = contents ?? [];
}
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "writeFile",
path: file.path,
contents: Array.from(
file.contents instanceof ArrayBuffer ? new Uint8Array(file.contents) : file.contents
),
options: fileOptions
}
});
}
async function readDir(dir, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "readDir",
path: dir,
options
}
});
}
async function createDir(dir, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "createDir",
path: dir,
options
}
});
}
async function removeDir(dir, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "removeDir",
path: dir,
options
}
});
}
async function copyFile(source, destination, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "copyFile",
source,
destination,
options
}
});
}
async function removeFile(file, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "removeFile",
path: file,
options
}
});
}
async function renameFile(oldPath, newPath, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "renameFile",
oldPath,
newPath,
options
}
});
}
async function exists(path, options = {}) {
return invokeTauriCommand({
__tauriModule: "Fs",
message: {
cmd: "exists",
path,
options
}
});
}
export {
BaseDirectory,
BaseDirectory as Dir,
copyFile,
createDir,
exists,
readBinaryFile,
readDir,
readTextFile,
removeDir,
removeFile,
renameFile,
writeBinaryFile,
writeTextFile as writeFile,
writeTextFile
};

97
src/globalShortcut.js Normal file
View file

@ -0,0 +1,97 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/globalShortcut.ts
async function register(shortcut, handler) {
return invokeTauriCommand({
__tauriModule: "GlobalShortcut",
message: {
cmd: "register",
shortcut,
handler: transformCallback(handler)
}
});
}
async function registerAll(shortcuts, handler) {
return invokeTauriCommand({
__tauriModule: "GlobalShortcut",
message: {
cmd: "registerAll",
shortcuts,
handler: transformCallback(handler)
}
});
}
async function isRegistered(shortcut) {
return invokeTauriCommand({
__tauriModule: "GlobalShortcut",
message: {
cmd: "isRegistered",
shortcut
}
});
}
async function unregister(shortcut) {
return invokeTauriCommand({
__tauriModule: "GlobalShortcut",
message: {
cmd: "unregister",
shortcut
}
});
}
async function unregisterAll() {
return invokeTauriCommand({
__tauriModule: "GlobalShortcut",
message: {
cmd: "unregisterAll"
}
});
}
export {
isRegistered,
register,
registerAll,
unregister,
unregisterAll
};

219
src/http.js Normal file
View file

@ -0,0 +1,219 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/http.ts
var ResponseType = /* @__PURE__ */ ((ResponseType2) => {
ResponseType2[ResponseType2["JSON"] = 1] = "JSON";
ResponseType2[ResponseType2["Text"] = 2] = "Text";
ResponseType2[ResponseType2["Binary"] = 3] = "Binary";
return ResponseType2;
})(ResponseType || {});
var Body = class {
constructor(type, payload) {
this.type = type;
this.payload = payload;
}
static form(data) {
const form = {};
const append = (key, v) => {
if (v !== null) {
let r;
if (typeof v === "string") {
r = v;
} else if (v instanceof Uint8Array || Array.isArray(v)) {
r = Array.from(v);
} else if (v instanceof File) {
r = { file: v.name, mime: v.type, fileName: v.name };
} else if (typeof v.file === "string") {
r = { file: v.file, mime: v.mime, fileName: v.fileName };
} else {
r = { file: Array.from(v.file), mime: v.mime, fileName: v.fileName };
}
form[String(key)] = r;
}
};
if (data instanceof FormData) {
for (const [key, value] of data) {
append(key, value);
}
} else {
for (const [key, value] of Object.entries(data)) {
append(key, value);
}
}
return new Body("Form", form);
}
static json(data) {
return new Body("Json", data);
}
static text(value) {
return new Body("Text", value);
}
static bytes(bytes) {
return new Body(
"Bytes",
Array.from(bytes instanceof ArrayBuffer ? new Uint8Array(bytes) : bytes)
);
}
};
var Response = class {
constructor(response) {
this.url = response.url;
this.status = response.status;
this.ok = this.status >= 200 && this.status < 300;
this.headers = response.headers;
this.rawHeaders = response.rawHeaders;
this.data = response.data;
}
};
var Client = class {
constructor(id) {
this.id = id;
}
async drop() {
return invokeTauriCommand({
__tauriModule: "Http",
message: {
cmd: "dropClient",
client: this.id
}
});
}
async request(options) {
const jsonResponse = !options.responseType || options.responseType === 1 /* JSON */;
if (jsonResponse) {
options.responseType = 2 /* Text */;
}
return invokeTauriCommand({
__tauriModule: "Http",
message: {
cmd: "httpRequest",
client: this.id,
options
}
}).then((res) => {
const response = new Response(res);
if (jsonResponse) {
try {
response.data = JSON.parse(response.data);
} catch (e) {
if (response.ok && response.data === "") {
response.data = {};
} else if (response.ok) {
throw Error(
`Failed to parse response \`${response.data}\` as JSON: ${e};
try setting the \`responseType\` option to \`ResponseType.Text\` or \`ResponseType.Binary\` if the API does not return a JSON response.`
);
}
}
return response;
}
return response;
});
}
async get(url, options) {
return this.request({
method: "GET",
url,
...options
});
}
async post(url, body, options) {
return this.request({
method: "POST",
url,
body,
...options
});
}
async put(url, body, options) {
return this.request({
method: "PUT",
url,
body,
...options
});
}
async patch(url, options) {
return this.request({
method: "PATCH",
url,
...options
});
}
async delete(url, options) {
return this.request({
method: "DELETE",
url,
...options
});
}
};
async function getClient(options) {
return invokeTauriCommand({
__tauriModule: "Http",
message: {
cmd: "createClient",
options
}
}).then((id) => new Client(id));
}
var defaultClient = null;
async function fetch(url, options) {
if (defaultClient === null) {
defaultClient = await getClient();
}
return defaultClient.request({
url,
method: options?.method ?? "GET",
...options
});
}
export {
Body,
Client,
Response,
ResponseType,
fetch,
getClient
};

2414
src/index.js Normal file

File diff suppressed because it is too large Load diff

View file

@ -4,21 +4,49 @@ use wasm_bindgen::JsValue;
pub mod app; pub mod app;
#[cfg(feature = "clipboard")] #[cfg(feature = "clipboard")]
pub mod clipboard; pub mod clipboard;
#[cfg(feature = "dialog")]
pub mod dialog;
#[cfg(feature = "event")] #[cfg(feature = "event")]
pub mod event; pub mod event;
#[cfg(feature = "mocks")] #[cfg(feature = "mocks")]
pub mod mocks; pub mod mocks;
#[cfg(feature = "process")]
pub mod process;
#[cfg(feature = "tauri")] #[cfg(feature = "tauri")]
pub mod tauri; pub mod tauri;
#[cfg(feature = "updater")] #[cfg(feature = "updater")]
pub mod updater; pub mod updater;
#[cfg(feature = "window")]
pub mod window;
#[cfg(feature = "notification")]
pub mod notification;
#[cfg(feature = "os")]
pub mod os;
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
pub enum Error { pub enum Error {
#[error(transparent)] #[error("{0}")]
Serde(#[from] serde_wasm_bindgen::Error), Serde(String),
#[error("{0:?}")] #[error("Unknown Theme \"{0}\". Expected one of \"light\",\"dark\"")]
Other(JsValue), UnknownTheme(String),
#[error("Invalid Url {0}")]
InvalidUrl(#[from] url::ParseError),
#[error("Invalid Version {0}")]
InvalidVersion(#[from] semver::Error),
#[error("{0}")]
Other(String),
}
impl From<serde_wasm_bindgen::Error> for Error {
fn from(e: serde_wasm_bindgen::Error) -> Self {
Self::Serde(format!("{:?}", e))
}
}
impl From<JsValue> for Error {
fn from(e: JsValue) -> Self {
Self::Serde(format!("{:?}", e))
}
} }
pub(crate) type Result<T> = std::result::Result<T, Error>; pub(crate) type Result<T> = std::result::Result<T, Error>;

View file

@ -55,7 +55,7 @@ mod inner {
JsValue, JsValue,
}; };
#[wasm_bindgen(module = "/dist/mocks.js")] #[wasm_bindgen(module = "/src/mocks.js")]
extern "C" { extern "C" {
#[wasm_bindgen(variadic)] #[wasm_bindgen(variadic)]
pub fn mockWindows(current: &str, rest: JsValue); pub fn mockWindows(current: &str, rest: JsValue);

70
src/notification.js Normal file
View file

@ -0,0 +1,70 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/notification.ts
async function isPermissionGranted() {
if (window.Notification.permission !== "default") {
return Promise.resolve(window.Notification.permission === "granted");
}
return invokeTauriCommand({
__tauriModule: "Notification",
message: {
cmd: "isNotificationPermissionGranted"
}
});
}
async function requestPermission() {
return window.Notification.requestPermission();
}
function sendNotification(options) {
if (typeof options === "string") {
new window.Notification(options);
} else {
new window.Notification(options.title, options);
}
}
export {
isPermissionGranted,
requestPermission,
sendNotification
};

84
src/notification.rs Normal file
View file

@ -0,0 +1,84 @@
use log::debug;
use serde::{Deserialize, Serialize};
use wasm_bindgen::JsValue;
pub async fn is_permission_granted() -> crate::Result<bool> {
let raw = inner::isPermissionGranted().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
pub async fn request_permission() -> crate::Result<Permission> {
let raw = inner::requestPermission().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
#[derive(Debug, Default, Clone, Copy, PartialEq, Eq)]
pub enum Permission {
#[default]
Default,
Granted,
Denied,
}
impl<'de> Deserialize<'de> for Permission {
fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
where
D: serde::Deserializer<'de>,
{
match String::deserialize(deserializer)?.as_str() {
"default" => Ok(Permission::Default),
"granted" => Ok(Permission::Granted),
"denied" => Ok(Permission::Denied),
_ => Err(serde::de::Error::custom(
"expected one of default, granted, denied",
)),
}
}
}
#[derive(Debug, Default, Serialize)]
pub struct Notification<'a> {
body: Option<&'a str>,
title: Option<&'a str>,
icon: Option<&'a str>
}
impl<'a> Notification<'a> {
pub fn new() -> Self {
Self::default()
}
pub fn set_title(&mut self, title: &'a str) {
self.title = Some(title);
}
pub fn set_body(&mut self, body: &'a str) {
self.body = Some(body);
}
pub fn set_icon(&mut self, icon: &'a str) {
self.icon = Some(icon);
}
pub fn show(&self) -> crate::Result<()> {
inner::sendNotification(serde_wasm_bindgen::to_value(&self)?)?;
Ok(())
}
}
mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/src/notification.js")]
extern "C" {
#[wasm_bindgen(catch)]
pub async fn isPermissionGranted() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn requestPermission() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub fn sendNotification(notification: JsValue) -> Result<(), JsValue>;
}
}

98
src/os.js Normal file
View file

@ -0,0 +1,98 @@
// tauri/tooling/api/src/helpers/os-check.ts
function isWindows() {
return navigator.appVersion.includes("Win");
}
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/os.ts
var EOL = isWindows() ? "\r\n" : "\n";
async function platform() {
return invokeTauriCommand({
__tauriModule: "Os",
message: {
cmd: "platform"
}
});
}
async function version() {
return invokeTauriCommand({
__tauriModule: "Os",
message: {
cmd: "version"
}
});
}
async function type() {
return invokeTauriCommand({
__tauriModule: "Os",
message: {
cmd: "osType"
}
});
}
async function arch() {
return invokeTauriCommand({
__tauriModule: "Os",
message: {
cmd: "arch"
}
});
}
async function tempdir() {
return invokeTauriCommand({
__tauriModule: "Os",
message: {
cmd: "tempdir"
}
});
}
export {
EOL,
arch,
platform,
tempdir,
type,
version
};

111
src/os.rs Normal file
View file

@ -0,0 +1,111 @@
use std::path::{PathBuf};
use semver::Version;
use serde::{Serialize, Deserialize};
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum Arch {
#[serde(rename = "x86")]
X86,
#[serde(rename = "x86_64")]
X86_64,
#[serde(rename = "arm")]
Arm,
#[serde(rename = "aarch64")]
Aarch64,
#[serde(rename = "mips")]
Mips,
#[serde(rename = "mips64")]
Mips64,
#[serde(rename = "powerpc")]
Powerpc,
#[serde(rename = "powerpc64")]
Powerpc64,
#[serde(rename = "riscv64")]
Riscv64,
#[serde(rename = "s390x")]
S390x,
#[serde(rename = "sparc64")]
Sparc64
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum Platform {
#[serde(rename = "linux")]
Linux,
#[serde(rename = "darwin")]
Darwin,
#[serde(rename = "ios")]
Ios,
#[serde(rename = "freebsd")]
Freebsd,
#[serde(rename = "dragonfly")]
Dragonfly,
#[serde(rename = "netbsd")]
Netbsd,
#[serde(rename = "openbsd")]
Openbsd,
#[serde(rename = "solaris")]
Solaris,
#[serde(rename = "android")]
Android,
#[serde(rename = "win32")]
Win32,
}
#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq)]
pub enum OsKind {
#[serde(rename = "Linux")]
Linux,
#[serde(rename = "Darwin")]
Darwin,
#[serde(rename = "Windows_NT")]
WindowsNt,
}
pub async fn arch() -> crate::Result<Arch> {
let raw = inner::arch().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
pub async fn platform() -> crate::Result<Platform> {
let raw = inner::platform().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
pub async fn tempdir() -> crate::Result<PathBuf> {
let raw = inner::tempdir().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
pub async fn kind() -> crate::Result<OsKind> {
let raw = inner::kind().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
pub async fn version() -> crate::Result<Version> {
let raw = inner::version().await?;
Ok(serde_wasm_bindgen::from_value(raw)?)
}
mod inner {
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "/src/os.js")]
extern "C" {
#[wasm_bindgen(catch)]
pub async fn arch() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn platform() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn tempdir() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch, js_name = "type")]
pub async fn kind() -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)]
pub async fn version() -> Result<JsValue, JsValue>;
}
}

418
src/path.js Normal file
View file

@ -0,0 +1,418 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve2, reject) => {
const callback = transformCallback((e) => {
resolve2(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/fs.ts
var BaseDirectory = /* @__PURE__ */ ((BaseDirectory2) => {
BaseDirectory2[BaseDirectory2["Audio"] = 1] = "Audio";
BaseDirectory2[BaseDirectory2["Cache"] = 2] = "Cache";
BaseDirectory2[BaseDirectory2["Config"] = 3] = "Config";
BaseDirectory2[BaseDirectory2["Data"] = 4] = "Data";
BaseDirectory2[BaseDirectory2["LocalData"] = 5] = "LocalData";
BaseDirectory2[BaseDirectory2["Desktop"] = 6] = "Desktop";
BaseDirectory2[BaseDirectory2["Document"] = 7] = "Document";
BaseDirectory2[BaseDirectory2["Download"] = 8] = "Download";
BaseDirectory2[BaseDirectory2["Executable"] = 9] = "Executable";
BaseDirectory2[BaseDirectory2["Font"] = 10] = "Font";
BaseDirectory2[BaseDirectory2["Home"] = 11] = "Home";
BaseDirectory2[BaseDirectory2["Picture"] = 12] = "Picture";
BaseDirectory2[BaseDirectory2["Public"] = 13] = "Public";
BaseDirectory2[BaseDirectory2["Runtime"] = 14] = "Runtime";
BaseDirectory2[BaseDirectory2["Template"] = 15] = "Template";
BaseDirectory2[BaseDirectory2["Video"] = 16] = "Video";
BaseDirectory2[BaseDirectory2["Resource"] = 17] = "Resource";
BaseDirectory2[BaseDirectory2["App"] = 18] = "App";
BaseDirectory2[BaseDirectory2["Log"] = 19] = "Log";
BaseDirectory2[BaseDirectory2["Temp"] = 20] = "Temp";
BaseDirectory2[BaseDirectory2["AppConfig"] = 21] = "AppConfig";
BaseDirectory2[BaseDirectory2["AppData"] = 22] = "AppData";
BaseDirectory2[BaseDirectory2["AppLocalData"] = 23] = "AppLocalData";
BaseDirectory2[BaseDirectory2["AppCache"] = 24] = "AppCache";
BaseDirectory2[BaseDirectory2["AppLog"] = 25] = "AppLog";
return BaseDirectory2;
})(BaseDirectory || {});
// tauri/tooling/api/src/helpers/os-check.ts
function isWindows() {
return navigator.appVersion.includes("Win");
}
// tauri/tooling/api/src/path.ts
async function appDir() {
return appConfigDir();
}
async function appConfigDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 21 /* AppConfig */
}
});
}
async function appDataDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 22 /* AppData */
}
});
}
async function appLocalDataDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 23 /* AppLocalData */
}
});
}
async function appCacheDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 24 /* AppCache */
}
});
}
async function audioDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 1 /* Audio */
}
});
}
async function cacheDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 2 /* Cache */
}
});
}
async function configDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 3 /* Config */
}
});
}
async function dataDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 4 /* Data */
}
});
}
async function desktopDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 6 /* Desktop */
}
});
}
async function documentDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 7 /* Document */
}
});
}
async function downloadDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 8 /* Download */
}
});
}
async function executableDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 9 /* Executable */
}
});
}
async function fontDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 10 /* Font */
}
});
}
async function homeDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 11 /* Home */
}
});
}
async function localDataDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 5 /* LocalData */
}
});
}
async function pictureDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 12 /* Picture */
}
});
}
async function publicDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 13 /* Public */
}
});
}
async function resourceDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 17 /* Resource */
}
});
}
async function resolveResource(resourcePath) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: resourcePath,
directory: 17 /* Resource */
}
});
}
async function runtimeDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 14 /* Runtime */
}
});
}
async function templateDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 15 /* Template */
}
});
}
async function videoDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 16 /* Video */
}
});
}
async function logDir() {
return appLogDir();
}
async function appLogDir() {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolvePath",
path: "",
directory: 25 /* AppLog */
}
});
}
var sep = isWindows() ? "\\" : "/";
var delimiter = isWindows() ? ";" : ":";
async function resolve(...paths) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "resolve",
paths
}
});
}
async function normalize(path) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "normalize",
path
}
});
}
async function join(...paths) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "join",
paths
}
});
}
async function dirname(path) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "dirname",
path
}
});
}
async function extname(path) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "extname",
path
}
});
}
async function basename(path, ext) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "basename",
path,
ext
}
});
}
async function isAbsolute(path) {
return invokeTauriCommand({
__tauriModule: "Path",
message: {
cmd: "isAbsolute",
path
}
});
}
export {
BaseDirectory,
appCacheDir,
appConfigDir,
appDataDir,
appDir,
appLocalDataDir,
appLogDir,
audioDir,
basename,
cacheDir,
configDir,
dataDir,
delimiter,
desktopDir,
dirname,
documentDir,
downloadDir,
executableDir,
extname,
fontDir,
homeDir,
isAbsolute,
join,
localDataDir,
logDir,
normalize,
pictureDir,
publicDir,
resolve,
resolveResource,
resourceDir,
runtimeDir,
sep,
templateDir,
videoDir
};

65
src/process.js Normal file
View file

@ -0,0 +1,65 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/process.ts
async function exit(exitCode = 0) {
return invokeTauriCommand({
__tauriModule: "Process",
message: {
cmd: "exit",
exitCode
}
});
}
async function relaunch() {
return invokeTauriCommand({
__tauriModule: "Process",
message: {
cmd: "relaunch"
}
});
}
export {
exit,
relaunch
};

18
src/process.rs Normal file
View file

@ -0,0 +1,18 @@
pub async fn exit(exit_code: u32) -> ! {
inner::exit(exit_code).await;
unreachable!()
}
pub fn relaunch() {
inner::relaunch();
}
mod inner {
use wasm_bindgen::prelude::*;
#[wasm_bindgen(module = "/src/process.js")]
extern "C" {
pub async fn exit(exitCode: u32);
pub fn relaunch();
}
}

230
src/shell.js Normal file
View file

@ -0,0 +1,230 @@
// tauri/tooling/api/src/tauri.ts
function uid() {
return window.crypto.getRandomValues(new Uint32Array(1))[0];
}
function transformCallback(callback, once = false) {
const identifier = uid();
const prop = `_${identifier}`;
Object.defineProperty(window, prop, {
value: (result) => {
if (once) {
Reflect.deleteProperty(window, prop);
}
return callback?.(result);
},
writable: false,
configurable: true
});
return identifier;
}
async function invoke(cmd, args = {}) {
return new Promise((resolve, reject) => {
const callback = transformCallback((e) => {
resolve(e);
Reflect.deleteProperty(window, `_${error}`);
}, true);
const error = transformCallback((e) => {
reject(e);
Reflect.deleteProperty(window, `_${callback}`);
}, true);
window.__TAURI_IPC__({
cmd,
callback,
error,
...args
});
});
}
// tauri/tooling/api/src/helpers/tauri.ts
async function invokeTauriCommand(command) {
return invoke("tauri", command);
}
// tauri/tooling/api/src/shell.ts
async function execute(onEvent, program, args = [], options) {
if (typeof args === "object") {
Object.freeze(args);
}
return invokeTauriCommand({
__tauriModule: "Shell",
message: {
cmd: "execute",
program,
args,
options,
onEventFn: transformCallback(onEvent)
}
});
}
var EventEmitter = class {
constructor() {
this.eventListeners = /* @__PURE__ */ Object.create(null);
}
addListener(eventName, listener) {
return this.on(eventName, listener);
}
removeListener(eventName, listener) {
return this.off(eventName, listener);
}
on(eventName, listener) {
if (eventName in this.eventListeners) {
this.eventListeners[eventName].push(listener);
} else {
this.eventListeners[eventName] = [listener];
}
return this;
}
once(eventName, listener) {
const wrapper = (...args) => {
this.removeListener(eventName, wrapper);
listener(...args);
};
return this.addListener(eventName, wrapper);
}
off(eventName, listener) {
if (eventName in this.eventListeners) {
this.eventListeners[eventName] = this.eventListeners[eventName].filter(
(l) => l !== listener
);
}
return this;
}
removeAllListeners(event) {
if (event) {
delete this.eventListeners[event];
} else {
this.eventListeners = /* @__PURE__ */ Object.create(null);
}
return this;
}
emit(eventName, ...args) {
if (eventName in this.eventListeners) {
const listeners = this.eventListeners[eventName];
for (const listener of listeners)
listener(...args);
return true;
}
return false;
}
listenerCount(eventName) {
if (eventName in this.eventListeners)
return this.eventListeners[eventName].length;
return 0;
}
prependListener(eventName, listener) {
if (eventName in this.eventListeners) {
this.eventListeners[eventName].unshift(listener);
} else {
this.eventListeners[eventName] = [listener];
}
return this;
}
prependOnceListener(eventName, listener) {
const wrapper = (...args) => {
this.removeListener(eventName, wrapper);
listener(...args);
};
return this.prependListener(eventName, wrapper);
}
};
var Child = class {
constructor(pid) {
this.pid = pid;
}
async write(data) {
return invokeTauriCommand({
__tauriModule: "Shell",
message: {
cmd: "stdinWrite",
pid: this.pid,
buffer: typeof data === "string" ? data : Array.from(data)
}
});
}
async kill() {
return invokeTauriCommand({
__tauriModule: "Shell",
message: {
cmd: "killChild",
pid: this.pid
}
});
}
};
var Command = class extends EventEmitter {
constructor(program, args = [], options) {
super();
this.stdout = new EventEmitter();
this.stderr = new EventEmitter();
this.program = program;
this.args = typeof args === "string" ? [args] : args;
this.options = options ?? {};
}
static sidecar(program, args = [], options) {
const instance = new Command(program, args, options);
instance.options.sidecar = true;
return instance;
}
async spawn() {
return execute(
(event) => {
switch (event.event) {
case "Error":
this.emit("error", event.payload);
break;
case "Terminated":
this.emit("close", event.payload);
break;
case "Stdout":
this.stdout.emit("data", event.payload);
break;
case "Stderr":
this.stderr.emit("data", event.payload);
break;
}
},
this.program,
this.args,
this.options
).then((pid) => new Child(pid));
}
async execute() {
return new Promise((resolve, reject) => {
this.on("error", reject);
const stdout = [];
const stderr = [];
this.stdout.on("data", (line) => {
stdout.push(line);
});
this.stderr.on("data", (line) => {
stderr.push(line);
});
this.on("close", (payload) => {
resolve({
code: payload.code,
signal: payload.signal,
stdout: stdout.join("\n"),
stderr: stderr.join("\n")
});
});
this.spawn().catch(reject);
});
}
};
async function open(path, openWith) {
return invokeTauriCommand({
__tauriModule: "Shell",
message: {
cmd: "open",
path,
with: openWith
}
});
}
export {
Child,
Command,
EventEmitter,
open
};

View file

@ -36,14 +36,10 @@ use url::Url;
/// ///
/// @return the URL that can be used as source on the webview. /// @return the URL that can be used as source on the webview.
#[inline(always)] #[inline(always)]
pub async fn convert_file_src(file_path: &str, protocol: Option<&str>) -> Url { pub async fn convert_file_src(file_path: &str, protocol: Option<&str>) -> crate::Result<Url> {
Url::parse( let js_val = inner::convertFileSrc(file_path, protocol).await?;
&inner::convertFileSrc(file_path, protocol)
.await Ok(serde_wasm_bindgen::from_value(js_val)?)
.as_string()
.unwrap(),
)
.unwrap()
} }
/// Sends a message to the backend. /// Sends a message to the backend.
@ -66,9 +62,8 @@ pub async fn convert_file_src(file_path: &str, protocol: Option<&str>) -> Url {
/// @return A promise resolving or rejecting to the backend response. /// @return A promise resolving or rejecting to the backend response.
#[inline(always)] #[inline(always)]
pub async fn invoke<A: Serialize, R: DeserializeOwned>(cmd: &str, args: &A) -> crate::Result<R> { pub async fn invoke<A: Serialize, R: DeserializeOwned>(cmd: &str, args: &A) -> crate::Result<R> {
let res = inner::invoke(cmd, serde_wasm_bindgen::to_value(args).unwrap()).await; let raw = inner::invoke(cmd, serde_wasm_bindgen::to_value(args)?).await?;
let raw = res.map_err(crate::Error::Other)?;
serde_wasm_bindgen::from_value(raw).map_err(Into::into) serde_wasm_bindgen::from_value(raw).map_err(Into::into)
} }
@ -77,24 +72,35 @@ pub async fn invoke<A: Serialize, R: DeserializeOwned>(cmd: &str, args: &A) -> c
/// ///
/// @return A unique identifier associated with the callback function. /// @return A unique identifier associated with the callback function.
#[inline(always)] #[inline(always)]
pub async fn transform_callback<T: DeserializeOwned>(callback: &dyn Fn(T), once: bool) -> f64 { pub async fn transform_callback<T: DeserializeOwned>(
inner::transformCallback( callback: &dyn Fn(T),
once: bool,
) -> crate::Result<f64> {
let js_val = inner::transformCallback(
&|raw| callback(serde_wasm_bindgen::from_value(raw).unwrap()), &|raw| callback(serde_wasm_bindgen::from_value(raw).unwrap()),
once, once,
) )
.await .await?;
.as_f64()
.unwrap() Ok(serde_wasm_bindgen::from_value(js_val)?)
} }
mod inner { mod inner {
use wasm_bindgen::{prelude::wasm_bindgen, JsValue}; use wasm_bindgen::{prelude::wasm_bindgen, JsValue};
#[wasm_bindgen(module = "/dist/tauri.js")] #[wasm_bindgen(module = "/src/tauri.js")]
extern "C" { extern "C" {
pub async fn convertFileSrc(filePath: &str, protocol: Option<&str>) -> JsValue; #[wasm_bindgen(catch)]
pub async fn convertFileSrc(
filePath: &str,
protocol: Option<&str>,
) -> Result<JsValue, JsValue>;
#[wasm_bindgen(catch)] #[wasm_bindgen(catch)]
pub async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>; pub async fn invoke(cmd: &str, args: JsValue) -> Result<JsValue, JsValue>;
pub async fn transformCallback(callback: &dyn Fn(JsValue), once: bool) -> JsValue; #[wasm_bindgen(catch)]
pub async fn transformCallback(
callback: &dyn Fn(JsValue),
once: bool,
) -> Result<JsValue, JsValue>;
} }
} }

1004
src/window.js Normal file

File diff suppressed because it is too large Load diff

825
src/window.rs Normal file
View file

@ -0,0 +1,825 @@
use crate::{event::Event, Error};
use serde::{de::DeserializeOwned, Serialize};
use std::fmt::Display;
use wasm_bindgen::{prelude::Closure, JsCast, JsValue};
#[derive(Debug, Clone, PartialEq)]
pub enum Theme {
Light,
Dark,
}
#[derive(Debug, Clone, PartialEq)]
pub enum TitleBarStyle {
Visible,
Transparent,
Overlay,
}
#[derive(Debug, Clone, PartialEq)]
pub enum UserAttentionType {
Critical = 1,
Informational,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Position {
Physical(PhysicalPosition),
Logical(LogicalPosition),
}
#[derive(Debug, Clone, PartialEq)]
pub enum Size {
Physical(PhysicalSize),
Logical(LogicalSize),
}
#[derive(Debug, Clone, PartialEq)]
pub enum CursorIcon {
Default,
Crosshair,
Hand,
Arrow,
Move,
Text,
Wait,
Help,
Progress,
// something cannot be done
NotAllowed,
ContextMenu,
Cell,
VerticalText,
Alias,
Copy,
NoDrop,
// something can be grabbed
Grab,
/// something is grabbed
Grabbing,
AllScroll,
ZoomIn,
ZoomOut,
// edge is to be moved
EResize,
NResize,
NeResize,
NwResize,
SResize,
SeResize,
SwResize,
WResize,
EwResize,
NsResize,
NeswResize,
NwseResize,
ColResize,
RowResize,
}
impl Display for CursorIcon {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
CursorIcon::Default => write!(f, "default"),
CursorIcon::Crosshair => write!(f, "crosshair"),
CursorIcon::Hand => write!(f, "hand"),
CursorIcon::Arrow => write!(f, "arrow"),
CursorIcon::Move => write!(f, "move"),
CursorIcon::Text => write!(f, "text"),
CursorIcon::Wait => write!(f, "wait"),
CursorIcon::Help => write!(f, "help"),
CursorIcon::Progress => write!(f, "progress"),
CursorIcon::NotAllowed => write!(f, "notAllowed"),
CursorIcon::ContextMenu => write!(f, "contextMenu"),
CursorIcon::Cell => write!(f, "cell"),
CursorIcon::VerticalText => write!(f, "verticalText"),
CursorIcon::Alias => write!(f, "alias"),
CursorIcon::Copy => write!(f, "copy"),
CursorIcon::NoDrop => write!(f, "noDrop"),
CursorIcon::Grab => write!(f, "grab"),
CursorIcon::Grabbing => write!(f, "grabbing"),
CursorIcon::AllScroll => write!(f, "allScroll"),
CursorIcon::ZoomIn => write!(f, "zoomIn"),
CursorIcon::ZoomOut => write!(f, "zoomOut"),
CursorIcon::EResize => write!(f, "eResize"),
CursorIcon::NResize => write!(f, "nResize"),
CursorIcon::NeResize => write!(f, "neResize"),
CursorIcon::NwResize => write!(f, "nwResize"),
CursorIcon::SResize => write!(f, "sResize"),
CursorIcon::SeResize => write!(f, "seResize"),
CursorIcon::SwResize => write!(f, "swResize"),
CursorIcon::WResize => write!(f, "wResize"),
CursorIcon::EwResize => write!(f, "ewResize"),
CursorIcon::NsResize => write!(f, "nsResize"),
CursorIcon::NeswResize => write!(f, "neswResize"),
CursorIcon::NwseResize => write!(f, "nwseResize"),
CursorIcon::ColResize => write!(f, "colResize"),
CursorIcon::RowResize => write!(f, "rowResize"),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct WebviewWindow(inner::WebviewWindow);
impl WebviewWindow {
pub fn new(label: &str, options: ()) -> Self {
Self(inner::WebviewWindow::new(label, options))
}
pub fn get_by_label(label: &str) -> Option<Self> {
inner::WebviewWindow::getByLabel(label).map(Self)
}
pub fn label(&self) -> String {
self.0.label()
}
pub async fn scale_factor(&self) -> crate::Result<f64> {
let js_val = self.0.scaleFactor().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn inner_position(&self) -> crate::Result<PhysicalPosition> {
Ok(PhysicalPosition(
self.0.innerPosition().await?.unchecked_into(),
))
}
pub async fn outer_position(&self) -> crate::Result<PhysicalPosition> {
Ok(PhysicalPosition(
self.0.outerPosition().await?.unchecked_into(),
))
}
pub async fn inner_size(&self) -> crate::Result<PhysicalSize> {
Ok(PhysicalSize(self.0.innerSize().await?.unchecked_into()))
}
pub async fn outer_size(&self) -> crate::Result<PhysicalSize> {
Ok(PhysicalSize(self.0.outerSize().await?.unchecked_into()))
}
pub async fn is_fullscreen(&self) -> crate::Result<bool> {
let js_val = self.0.isFullscreen().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn is_maximized(&self) -> crate::Result<bool> {
let js_val = self.0.isMaximized().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn is_decorated(&self) -> crate::Result<bool> {
let js_val = self.0.isDecorated().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn is_resizable(&self) -> crate::Result<bool> {
let js_val = self.0.isResizable().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn is_visible(&self) -> crate::Result<bool> {
let js_val = self.0.isVisible().await?;
Ok(serde_wasm_bindgen::from_value(js_val)?)
}
pub async fn theme(&self) -> crate::Result<Theme> {
let js_val = self.0.theme().await?;
let str = serde_wasm_bindgen::from_value::<String>(js_val)?;
match str.as_str() {
"light" => Ok(Theme::Light),
"dark" => Ok(Theme::Dark),
_ => Err(Error::UnknownTheme(str)),
}
}
pub async fn center(&self) -> crate::Result<()> {
Ok(self.0.center().await?)
}
pub async fn request_user_attention(
&self,
request_type: UserAttentionType,
) -> crate::Result<()> {
Ok(self.0.requestUserAttention(request_type as u32).await?)
}
pub async fn set_resizable(&self, resizable: bool) -> crate::Result<()> {
Ok(self.0.setResizable(resizable).await?)
}
pub async fn set_title(&self, title: impl AsRef<str>) -> crate::Result<()> {
Ok(self.0.setTitle(title.as_ref()).await?)
}
pub async fn maximize(&self) -> crate::Result<()> {
Ok(self.0.maximize().await?)
}
pub async fn unmaximize(&self) -> crate::Result<()> {
Ok(self.0.unmaximize().await?)
}
pub async fn toggle_maximize(&self) -> crate::Result<()> {
Ok(self.0.toggleMaximize().await?)
}
pub async fn minimize(&self) -> crate::Result<()> {
Ok(self.0.minimize().await?)
}
pub async fn unminimize(&self) -> crate::Result<()> {
Ok(self.0.unminimize().await?)
}
pub async fn show(&self) -> crate::Result<()> {
Ok(self.0.show().await?)
}
pub async fn hide(&self) -> crate::Result<()> {
Ok(self.0.hide().await?)
}
pub async fn close(&self) -> crate::Result<()> {
Ok(self.0.close().await?)
}
pub async fn set_decorations(&self, decorations: bool) -> crate::Result<()> {
Ok(self.0.setDecorations(decorations).await?)
}
pub async fn set_always_on_top(&self, always_on_top: bool) -> crate::Result<()> {
Ok(self.0.setAlwaysOnTop(always_on_top).await?)
}
pub async fn set_size(&self, size: Size) -> crate::Result<()> {
match size {
Size::Physical(size) => self.0.setSizePhysical(size.0).await?,
Size::Logical(size) => self.0.setSizeLogical(size.0).await?,
}
Ok(())
}
pub async fn set_min_size(&self, size: Option<Size>) -> crate::Result<()> {
match size {
None => self.0.setMinSizePhysical(None).await?,
Some(Size::Physical(size)) => self.0.setMinSizePhysical(Some(size.0)).await?,
Some(Size::Logical(size)) => self.0.setMinSizeLogical(Some(size.0)).await?,
}
Ok(())
}
pub async fn set_max_size(&self, size: Option<Size>) -> crate::Result<()> {
match size {
None => self.0.setMaxSizePhysical(None).await?,
Some(Size::Physical(size)) => self.0.setMaxSizePhysical(Some(size.0)).await?,
Some(Size::Logical(size)) => self.0.setMaxSizeLogical(Some(size.0)).await?,
}
Ok(())
}
pub async fn set_position(&self, position: Position) -> crate::Result<()> {
match position {
Position::Physical(pos) => self.0.setPositionPhysical(pos.0).await?,
Position::Logical(pos) => self.0.setPositionLogical(pos.0).await?,
}
Ok(())
}
pub async fn set_fullscreen(&self, fullscreen: bool) -> crate::Result<()> {
Ok(self.0.setFullscreen(fullscreen).await?)
}
pub async fn set_focus(&self) -> crate::Result<()> {
Ok(self.0.setFocus().await?)
}
pub async fn set_icon(&self, icon: &[u8]) -> crate::Result<()> {
Ok(self.0.setIcon(icon).await?)
}
pub async fn set_skip_taskbar(&self, skip: bool) -> crate::Result<()> {
Ok(self.0.setSkipTaskbar(skip).await?)
}
pub async fn set_cursor_grab(&self, grab: bool) -> crate::Result<()> {
Ok(self.0.setCursorGrab(grab).await?)
}
pub async fn set_cursor_visible(&self, visible: bool) -> crate::Result<()> {
Ok(self.0.setCursorVisible(visible).await?)
}
pub async fn set_cursor_icon(&self, icon: CursorIcon) -> crate::Result<()> {
Ok(self.0.setCursorIcon(&icon.to_string()).await?)
}
pub async fn set_cursor_position(&self, position: Position) -> crate::Result<()> {
match position {
Position::Physical(pos) => self.0.setCursorPositionPhysical(pos.0).await?,
Position::Logical(pos) => self.0.setCursorPositionLogical(pos.0).await?,
}
Ok(())
}
pub async fn set_ignore_cursor_events(&self, ignore: bool) -> crate::Result<()> {
Ok(self.0.setIgnoreCursorEvents(ignore).await?)
}
pub async fn start_dragging(&self) -> crate::Result<()> {
Ok(self.0.startDragging().await?)
}
#[inline(always)]
pub async fn emit<T: Serialize>(&self, event: &str, payload: &T) -> crate::Result<()> {
self.0
.emit(event, serde_wasm_bindgen::to_value(payload).unwrap())
.await?;
Ok(())
}
#[inline(always)]
pub async fn listen<T, H>(&self, event: &str, mut handler: H) -> crate::Result<impl FnOnce()>
where
T: DeserializeOwned,
H: FnMut(Event<T>) + 'static,
{
let closure = Closure::<dyn FnMut(JsValue)>::new(move |raw| {
(handler)(serde_wasm_bindgen::from_value(raw).unwrap())
});
let unlisten = self.0.listen(event, &closure).await?;
closure.forget();
let unlisten = js_sys::Function::from(unlisten);
Ok(move || {
unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap();
})
}
#[inline(always)]
pub async fn once<T, H>(&self, event: &str, mut handler: H) -> crate::Result<impl FnOnce()>
where
T: DeserializeOwned,
H: FnMut(Event<T>) + 'static,
{
let closure = Closure::<dyn FnMut(JsValue)>::new(move |raw| {
(handler)(serde_wasm_bindgen::from_value(raw).unwrap())
});
let unlisten = self.0.once(event, &closure).await?;
closure.forget();
let unlisten = js_sys::Function::from(unlisten);
Ok(move || {
unlisten.call0(&wasm_bindgen::JsValue::NULL).unwrap();
})
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LogicalPosition(inner::LogicalPosition);
impl LogicalPosition {
pub fn new(x: u32, y: u32) -> Self {
Self(inner::LogicalPosition::new(x, y))
}
pub fn x(&self) -> u32 {
self.0.x()
}
pub fn set_x(&self, x: u32) {
self.0.set_x(x)
}
pub fn y(&self) -> u32 {
self.0.y()
}
pub fn set_y(&self, y: u32) {
self.0.set_y(y)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PhysicalPosition(inner::PhysicalPosition);
impl PhysicalPosition {
pub fn new(x: u32, y: u32) -> Self {
Self(inner::PhysicalPosition::new(x, y))
}
pub fn to_logical(self, scale_factor: u32) -> LogicalPosition {
LogicalPosition(self.0.toLogical(scale_factor))
}
pub fn x(&self) -> u32 {
self.0.x()
}
pub fn set_x(&self, x: u32) {
self.0.set_x(x)
}
pub fn y(&self) -> u32 {
self.0.y()
}
pub fn set_y(&self, y: u32) {
self.0.set_y(y)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct LogicalSize(inner::LogicalSize);
impl LogicalSize {
pub fn new(x: u32, y: u32) -> Self {
Self(inner::LogicalSize::new(x, y))
}
pub fn width(&self) -> u32 {
self.0.width()
}
pub fn set_width(&self, x: u32) {
self.0.set_width(x)
}
pub fn height(&self) -> u32 {
self.0.height()
}
pub fn set_height(&self, y: u32) {
self.0.set_height(y)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct PhysicalSize(inner::PhysicalSize);
impl PhysicalSize {
pub fn new(x: u32, y: u32) -> Self {
Self(inner::PhysicalSize::new(x, y))
}
pub fn to_logical(self, scale_factor: u32) -> LogicalSize {
LogicalSize(self.0.toLogical(scale_factor))
}
pub fn width(&self) -> u32 {
self.0.width()
}
pub fn set_width(&self, x: u32) {
self.0.set_width(x)
}
pub fn height(&self) -> u32 {
self.0.height()
}
pub fn set_height(&self, y: u32) {
self.0.set_height(y)
}
}
#[derive(Debug, Clone, PartialEq)]
pub struct Monitor(JsValue);
impl Monitor {
pub fn name(&self) -> Option<String> {
let raw = js_sys::Reflect::get(&self.0, &JsValue::from_str("name")).unwrap();
raw.as_string()
}
pub fn size(&self) -> PhysicalSize {
let raw = js_sys::Reflect::get(&self.0, &JsValue::from_str("size")).unwrap();
PhysicalSize(raw.unchecked_into())
}
pub fn position(&self) -> PhysicalPosition {
let raw = js_sys::Reflect::get(&self.0, &JsValue::from_str("position")).unwrap();
PhysicalPosition(raw.unchecked_into())
}
pub fn scale_factor(&self) -> u32 {
let raw = js_sys::Reflect::get(&self.0, &JsValue::from_str("size"))
.unwrap()
.as_f64()
.unwrap();
raw as u32
}
}
pub fn current_window() -> WebviewWindow {
WebviewWindow(inner::getCurrent())
}
pub fn all_windows() -> Vec<WebviewWindow> {
inner::getAll().into_iter().map(WebviewWindow).collect()
}
pub async fn current_monitor() -> Monitor {
Monitor(inner::currentMonitor().await)
}
pub async fn primary_monitor() -> Monitor {
Monitor(inner::primaryMonitor().await)
}
#[derive(Debug, Clone)]
pub struct AvailableMonitors {
idx: u32,
array: js_sys::Array,
}
impl Iterator for AvailableMonitors {
type Item = Monitor;
fn next(&mut self) -> Option<Self::Item> {
let raw = self.array.get(self.idx);
if raw.is_undefined() {
None
} else {
let monitor = Monitor(raw);
self.idx += 1;
Some(monitor)
}
}
}
pub async fn available_monitors() -> AvailableMonitors {
AvailableMonitors {
idx: 0,
array: inner::availableMonitors().await.unchecked_into(),
}
}
mod inner {
use wasm_bindgen::{
prelude::{wasm_bindgen, Closure},
JsValue,
};
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[derive(Debug, Clone, PartialEq)]
pub type LogicalPosition;
#[wasm_bindgen(constructor)]
pub fn new(x: u32, y: u32) -> LogicalPosition;
#[wasm_bindgen(method, getter)]
pub fn x(this: &LogicalPosition) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_x(this: &LogicalPosition, x: u32);
#[wasm_bindgen(method, getter)]
pub fn y(this: &LogicalPosition) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_y(this: &LogicalPosition, y: u32);
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[derive(Debug, Clone, PartialEq)]
pub type PhysicalPosition;
#[wasm_bindgen(constructor)]
pub fn new(x: u32, y: u32) -> PhysicalPosition;
#[wasm_bindgen(method)]
pub fn toLogical(this: &PhysicalPosition, scaleFactor: u32) -> LogicalPosition;
#[wasm_bindgen(method, getter)]
pub fn x(this: &PhysicalPosition) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_x(this: &PhysicalPosition, x: u32);
#[wasm_bindgen(method, getter)]
pub fn y(this: &PhysicalPosition) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_y(this: &PhysicalPosition, y: u32);
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[derive(Debug, Clone, PartialEq)]
pub type LogicalSize;
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> LogicalSize;
#[wasm_bindgen(method, getter)]
pub fn width(this: &LogicalSize) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_width(this: &LogicalSize, width: u32);
#[wasm_bindgen(method, getter)]
pub fn height(this: &LogicalSize) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_height(this: &LogicalSize, height: u32);
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[derive(Debug, Clone, PartialEq)]
pub type PhysicalSize;
#[wasm_bindgen(constructor)]
pub fn new(width: u32, height: u32) -> PhysicalSize;
#[wasm_bindgen(method)]
pub fn toLogical(this: &PhysicalSize, scaleFactor: u32) -> LogicalSize;
#[wasm_bindgen(method, getter)]
pub fn width(this: &PhysicalSize) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_width(this: &PhysicalSize, width: u32);
#[wasm_bindgen(method, getter)]
pub fn height(this: &PhysicalSize) -> u32;
#[wasm_bindgen(method, setter)]
pub fn set_height(this: &PhysicalSize, height: u32);
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[derive(Debug, Clone, PartialEq)]
pub type WebviewWindowHandle;
#[wasm_bindgen(constructor)]
pub fn new(label: &str) -> WebviewWindowHandle;
#[wasm_bindgen(method, catch)]
pub async fn listen(
this: &WebviewWindowHandle,
event: &str,
handler: &Closure<dyn FnMut(JsValue)>,
) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn once(
this: &WebviewWindowHandle,
event: &str,
handler: &Closure<dyn FnMut(JsValue)>,
) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn emit(
this: &WebviewWindowHandle,
event: &str,
payload: JsValue,
) -> Result<(), JsValue>;
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[wasm_bindgen(extends = WebviewWindowHandle)]
#[derive(Debug, Clone, PartialEq)]
pub type WindowManager;
#[wasm_bindgen(constructor)]
pub fn new(label: &str) -> WindowManager;
#[wasm_bindgen(method, getter)]
pub fn label(this: &WindowManager) -> String;
#[wasm_bindgen(method, catch)]
pub async fn scaleFactor(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn innerPosition(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn outerPosition(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn innerSize(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn outerSize(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn isFullscreen(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn isMaximized(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn isDecorated(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn isResizable(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn isVisible(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn theme(this: &WindowManager) -> Result<JsValue, JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn center(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn requestUserAttention(
this: &WindowManager,
requestType: u32,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setResizable(this: &WindowManager, resizable: bool) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setTitle(this: &WindowManager, title: &str) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn maximize(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn unmaximize(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn toggleMaximize(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn minimize(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn unminimize(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn show(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn hide(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn close(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setDecorations(this: &WindowManager, decorations: bool)
-> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setAlwaysOnTop(this: &WindowManager, alwaysOnTop: bool)
-> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setSize, catch)]
pub async fn setSizePhysical(
this: &WindowManager,
size: PhysicalSize,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setSize, catch)]
pub async fn setSizeLogical(this: &WindowManager, size: LogicalSize)
-> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setMinSize, catch)]
pub async fn setMinSizePhysical(
this: &WindowManager,
size: Option<PhysicalSize>,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setMinSize, catch)]
pub async fn setMinSizeLogical(
this: &WindowManager,
size: Option<LogicalSize>,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setMaxSize, catch)]
pub async fn setMaxSizePhysical(
this: &WindowManager,
size: Option<PhysicalSize>,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setMinSize, catch)]
pub async fn setMaxSizeLogical(
this: &WindowManager,
size: Option<LogicalSize>,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setPosition, catch)]
pub async fn setPositionPhysical(
this: &WindowManager,
position: PhysicalPosition,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setPosition, catch)]
pub async fn setPositionLogical(
this: &WindowManager,
position: LogicalPosition,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setFullscreen(this: &WindowManager, fullscreen: bool) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setFocus(this: &WindowManager) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setIcon(this: &WindowManager, icon: &[u8]) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setSkipTaskbar(this: &WindowManager, skip: bool) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setCursorGrab(this: &WindowManager, grab: bool) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setCursorVisible(this: &WindowManager, visible: bool) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setCursorIcon(this: &WindowManager, icon: &str) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setCursorPosition, catch)]
pub async fn setCursorPositionPhysical(
this: &WindowManager,
position: PhysicalPosition,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, js_name = setCursorPosition, catch)]
pub async fn setCursorPositionLogical(
this: &WindowManager,
position: LogicalPosition,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn setIgnoreCursorEvents(
this: &WindowManager,
ignore: bool,
) -> Result<(), JsValue>;
#[wasm_bindgen(method, catch)]
pub async fn startDragging(this: &WindowManager) -> Result<(), JsValue>;
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
#[wasm_bindgen(extends = WindowManager)]
#[derive(Debug, Clone, PartialEq)]
pub type WebviewWindow;
#[wasm_bindgen(constructor)]
pub fn new(label: &str, options: ()) -> WebviewWindow;
#[wasm_bindgen(static_method_of = WebviewWindow)]
pub fn getByLabel(label: &str) -> Option<WebviewWindow>;
}
#[wasm_bindgen(module = "/src/window.js")]
extern "C" {
pub fn getCurrent() -> WebviewWindow;
pub fn getAll() -> Vec<WebviewWindow>;
pub async fn currentMonitor() -> JsValue;
pub async fn primaryMonitor() -> JsValue;
pub async fn availableMonitors() -> JsValue;
}
}

2
tauri

@ -1 +1 @@
Subproject commit 35264b4c1801b381e0b867c1c35540f0fbb43365 Subproject commit 2e1bd04775c0f05f1c0b67605e6abec4465dbf84