mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-09-19 04:08:45 +03:00
feat(examples): add desktop/web application example (#5537)
This commit is contained in:
parent
2d9c2b4724
commit
6c13840cee
@ -20,7 +20,8 @@ exclude = [
|
|||||||
"examples/api/src-tauri",
|
"examples/api/src-tauri",
|
||||||
"examples/updater/src-tauri",
|
"examples/updater/src-tauri",
|
||||||
"examples/resources/src-tauri",
|
"examples/resources/src-tauri",
|
||||||
"examples/sidecar/src-tauri"
|
"examples/sidecar/src-tauri",
|
||||||
|
"examples/web/core"
|
||||||
]
|
]
|
||||||
|
|
||||||
# default to small, optimized workspace release binaries
|
# default to small, optimized workspace release binaries
|
||||||
|
@ -57,7 +57,7 @@ fn map_core_assets(
|
|||||||
let mut hasher = Sha256::new();
|
let mut hasher = Sha256::new();
|
||||||
hasher.update(&script);
|
hasher.update(&script);
|
||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
scripts.push(format!("'sha256-{}'", base64::encode(&hash)));
|
scripts.push(format!("'sha256-{}'", base64::encode(hash)));
|
||||||
}
|
}
|
||||||
csp_hashes
|
csp_hashes
|
||||||
.inline_scripts
|
.inline_scripts
|
||||||
@ -76,7 +76,7 @@ fn map_core_assets(
|
|||||||
let hash = hasher.finalize();
|
let hash = hasher.finalize();
|
||||||
csp_hashes
|
csp_hashes
|
||||||
.styles
|
.styles
|
||||||
.push(format!("'sha256-{}'", base64::encode(&hash)));
|
.push(format!("'sha256-{}'", base64::encode(hash)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -457,7 +457,7 @@ fn ico_icon<P: AsRef<Path>>(
|
|||||||
path: P,
|
path: P,
|
||||||
) -> Result<TokenStream, EmbeddedAssetsError> {
|
) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = std::fs::read(&path)
|
let bytes = std::fs::read(path)
|
||||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||||
.to_vec();
|
.to_vec();
|
||||||
let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))
|
let icon_dir = ico::IconDir::read(std::io::Cursor::new(bytes))
|
||||||
@ -485,7 +485,7 @@ fn ico_icon<P: AsRef<Path>>(
|
|||||||
|
|
||||||
fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, EmbeddedAssetsError> {
|
fn raw_icon<P: AsRef<Path>>(out_dir: &Path, path: P) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = std::fs::read(&path)
|
let bytes = std::fs::read(path)
|
||||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||||
.to_vec();
|
.to_vec();
|
||||||
|
|
||||||
@ -507,7 +507,7 @@ fn png_icon<P: AsRef<Path>>(
|
|||||||
path: P,
|
path: P,
|
||||||
) -> Result<TokenStream, EmbeddedAssetsError> {
|
) -> Result<TokenStream, EmbeddedAssetsError> {
|
||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
let bytes = std::fs::read(&path)
|
let bytes = std::fs::read(path)
|
||||||
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
.unwrap_or_else(|e| panic!("failed to read icon {}: {}", path.display(), e))
|
||||||
.to_vec();
|
.to_vec();
|
||||||
let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
|
let decoder = png::Decoder::new(std::io::Cursor::new(bytes));
|
||||||
@ -537,13 +537,13 @@ fn write_if_changed(out_path: &Path, data: &[u8]) -> std::io::Result<()> {
|
|||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::io::Write;
|
use std::io::Write;
|
||||||
|
|
||||||
if let Ok(curr) = std::fs::read(&out_path) {
|
if let Ok(curr) = std::fs::read(out_path) {
|
||||||
if curr == data {
|
if curr == data {
|
||||||
return Ok(());
|
return Ok(());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut out_file = File::create(&out_path)?;
|
let mut out_file = File::create(out_path)?;
|
||||||
out_file.write_all(data)
|
out_file.write_all(data)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2981,7 +2981,7 @@ mod build {
|
|||||||
|
|
||||||
tokens.append_all(match self {
|
tokens.append_all(match self {
|
||||||
Self::App(path) => {
|
Self::App(path) => {
|
||||||
let path = path_buf_lit(&path);
|
let path = path_buf_lit(path);
|
||||||
quote! { #prefix::App(#path) }
|
quote! { #prefix::App(#path) }
|
||||||
}
|
}
|
||||||
Self::External(url) => {
|
Self::External(url) => {
|
||||||
@ -3211,7 +3211,7 @@ mod build {
|
|||||||
quote! { #prefix::OfflineInstaller { silent: #silent } }
|
quote! { #prefix::OfflineInstaller { silent: #silent } }
|
||||||
}
|
}
|
||||||
Self::FixedRuntime { path } => {
|
Self::FixedRuntime { path } => {
|
||||||
let path = path_buf_lit(&path);
|
let path = path_buf_lit(path);
|
||||||
quote! { #prefix::FixedRuntime { path: #path } }
|
quote! { #prefix::FixedRuntime { path: #path } }
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
@ -15,7 +15,7 @@ pub enum ArchiveReader<R: Read + Seek> {
|
|||||||
/// A plain reader.
|
/// A plain reader.
|
||||||
Plain(R),
|
Plain(R),
|
||||||
/// A GZ- compressed reader (decoder).
|
/// A GZ- compressed reader (decoder).
|
||||||
GzCompressed(flate2::read::GzDecoder<R>),
|
GzCompressed(Box<flate2::read::GzDecoder<R>>),
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<R: Read + Seek> Read for ArchiveReader<R> {
|
impl<R: Read + Seek> Read for ArchiveReader<R> {
|
||||||
@ -161,7 +161,9 @@ impl<'a, R: Read + Seek> Extract<'a, R> {
|
|||||||
};
|
};
|
||||||
Extract {
|
Extract {
|
||||||
reader: match compression {
|
reader: match compression {
|
||||||
Some(Compression::Gz) => ArchiveReader::GzCompressed(flate2::read::GzDecoder::new(reader)),
|
Some(Compression::Gz) => {
|
||||||
|
ArchiveReader::GzCompressed(Box::new(flate2::read::GzDecoder::new(reader)))
|
||||||
|
}
|
||||||
_ => ArchiveReader::Plain(reader),
|
_ => ArchiveReader::Plain(reader),
|
||||||
},
|
},
|
||||||
archive_format,
|
archive_format,
|
||||||
@ -248,7 +250,7 @@ impl<'a, R: Read + Seek> Extract<'a, R> {
|
|||||||
fs::create_dir_all(&out_path)?;
|
fs::create_dir_all(&out_path)?;
|
||||||
} else {
|
} else {
|
||||||
if let Some(out_path_parent) = out_path.parent() {
|
if let Some(out_path_parent) = out_path.parent() {
|
||||||
fs::create_dir_all(&out_path_parent)?;
|
fs::create_dir_all(out_path_parent)?;
|
||||||
}
|
}
|
||||||
let mut out_file = fs::File::create(&out_path)?;
|
let mut out_file = fs::File::create(&out_path)?;
|
||||||
io::copy(&mut file, &mut out_file)?;
|
io::copy(&mut file, &mut out_file)?;
|
||||||
|
@ -98,7 +98,7 @@ fn walkdir_and_copy(source: &path::Path, dest: &path::Path) -> crate::api::Resul
|
|||||||
|
|
||||||
let element = entry?;
|
let element = entry?;
|
||||||
let metadata = element.metadata()?;
|
let metadata = element.metadata()?;
|
||||||
let destination = dest.join(element.path().strip_prefix(&source)?);
|
let destination = dest.join(element.path().strip_prefix(source)?);
|
||||||
|
|
||||||
// we make sure it's a directory and destination doesnt exist
|
// we make sure it's a directory and destination doesnt exist
|
||||||
if metadata.is_dir() && !&destination.exists() {
|
if metadata.is_dir() && !&destination.exists() {
|
||||||
|
@ -436,7 +436,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_cmd_output() {
|
fn test_cmd_output() {
|
||||||
// create a command to run cat.
|
// create a command to run cat.
|
||||||
let cmd = Command::new("cat").args(&["test/api/test.txt"]);
|
let cmd = Command::new("cat").args(["test/api/test.txt"]);
|
||||||
let (mut rx, _) = cmd.spawn().unwrap();
|
let (mut rx, _) = cmd.spawn().unwrap();
|
||||||
|
|
||||||
crate::async_runtime::block_on(async move {
|
crate::async_runtime::block_on(async move {
|
||||||
@ -458,7 +458,7 @@ mod test {
|
|||||||
#[test]
|
#[test]
|
||||||
// test the failure case
|
// test the failure case
|
||||||
fn test_cmd_fail() {
|
fn test_cmd_fail() {
|
||||||
let cmd = Command::new("cat").args(&["test/api/"]);
|
let cmd = Command::new("cat").args(["test/api/"]);
|
||||||
let (mut rx, _) = cmd.spawn().unwrap();
|
let (mut rx, _) = cmd.spawn().unwrap();
|
||||||
|
|
||||||
crate::async_runtime::block_on(async move {
|
crate::async_runtime::block_on(async move {
|
||||||
|
@ -103,7 +103,7 @@ impl Cmd {
|
|||||||
} = value
|
} = value
|
||||||
{
|
{
|
||||||
if crate::api::file::SafePathBuf::new(path.clone()).is_err()
|
if crate::api::file::SafePathBuf::new(path.clone()).is_err()
|
||||||
|| !scopes.fs.is_allowed(&path)
|
|| !scopes.fs.is_allowed(path)
|
||||||
{
|
{
|
||||||
return Err(crate::Error::PathNotAllowed(path.clone()).into_anyhow());
|
return Err(crate::Error::PathNotAllowed(path.clone()).into_anyhow());
|
||||||
}
|
}
|
||||||
|
@ -1066,7 +1066,7 @@ impl<R: Runtime> WindowManager<R> {
|
|||||||
// ignore "index.html" just to simplify the url
|
// ignore "index.html" just to simplify the url
|
||||||
if path.to_str() != Some("index.html") {
|
if path.to_str() != Some("index.html") {
|
||||||
url
|
url
|
||||||
.join(&*path.to_string_lossy())
|
.join(&path.to_string_lossy())
|
||||||
.map_err(crate::Error::InvalidUrl)
|
.map_err(crate::Error::InvalidUrl)
|
||||||
// this will never fail
|
// this will never fail
|
||||||
.unwrap()
|
.unwrap()
|
||||||
|
@ -148,9 +148,9 @@ impl Scope {
|
|||||||
let mut list = self.allowed_patterns.lock().unwrap();
|
let mut list = self.allowed_patterns.lock().unwrap();
|
||||||
|
|
||||||
// allow the directory to be read
|
// allow the directory to be read
|
||||||
push_pattern(&mut list, &path, escaped_pattern)?;
|
push_pattern(&mut list, path, escaped_pattern)?;
|
||||||
// allow its files and subdirectories to be read
|
// allow its files and subdirectories to be read
|
||||||
push_pattern(&mut list, &path, |p| {
|
push_pattern(&mut list, path, |p| {
|
||||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -165,7 +165,7 @@ impl Scope {
|
|||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
push_pattern(
|
push_pattern(
|
||||||
&mut self.allowed_patterns.lock().unwrap(),
|
&mut self.allowed_patterns.lock().unwrap(),
|
||||||
&path,
|
path,
|
||||||
escaped_pattern,
|
escaped_pattern,
|
||||||
)?;
|
)?;
|
||||||
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
self.trigger(Event::PathAllowed(path.to_path_buf()));
|
||||||
@ -181,9 +181,9 @@ impl Scope {
|
|||||||
let mut list = self.forbidden_patterns.lock().unwrap();
|
let mut list = self.forbidden_patterns.lock().unwrap();
|
||||||
|
|
||||||
// allow the directory to be read
|
// allow the directory to be read
|
||||||
push_pattern(&mut list, &path, escaped_pattern)?;
|
push_pattern(&mut list, path, escaped_pattern)?;
|
||||||
// allow its files and subdirectories to be read
|
// allow its files and subdirectories to be read
|
||||||
push_pattern(&mut list, &path, |p| {
|
push_pattern(&mut list, path, |p| {
|
||||||
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
escaped_pattern_with(p, if recursive { "**" } else { "*" })
|
||||||
})?;
|
})?;
|
||||||
}
|
}
|
||||||
@ -198,7 +198,7 @@ impl Scope {
|
|||||||
let path = path.as_ref();
|
let path = path.as_ref();
|
||||||
push_pattern(
|
push_pattern(
|
||||||
&mut self.forbidden_patterns.lock().unwrap(),
|
&mut self.forbidden_patterns.lock().unwrap(),
|
||||||
&path,
|
path,
|
||||||
escaped_pattern,
|
escaped_pattern,
|
||||||
)?;
|
)?;
|
||||||
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
self.trigger(Event::PathForbidden(path.to_path_buf()));
|
||||||
|
@ -306,8 +306,8 @@ impl Scope {
|
|||||||
// The prevention of argument escaping is handled by the usage of std::process::Command::arg by
|
// The prevention of argument escaping is handled by the usage of std::process::Command::arg by
|
||||||
// the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
|
// the `open` dependency. This behavior should be re-confirmed during upgrades of `open`.
|
||||||
match with.map(Program::name) {
|
match with.map(Program::name) {
|
||||||
Some(program) => ::open::with(&path, program),
|
Some(program) => ::open::with(path, program),
|
||||||
None => ::open::that(&path),
|
None => ::open::that(path),
|
||||||
}
|
}
|
||||||
.map_err(Into::into)
|
.map_err(Into::into)
|
||||||
}
|
}
|
||||||
|
@ -177,7 +177,7 @@ async fn async_stateful_command_with_result(
|
|||||||
state: State<'_, MyState>,
|
state: State<'_, MyState>,
|
||||||
) -> Result<String, MyError> {
|
) -> Result<String, MyError> {
|
||||||
println!("{:?} {:?}", the_argument, state.inner());
|
println!("{:?} {:?}", the_argument, state.inner());
|
||||||
Ok(the_argument.unwrap_or_else(|| "".to_string()))
|
Ok(the_argument.unwrap_or_default())
|
||||||
}
|
}
|
||||||
|
|
||||||
// Non-Ident command function arguments
|
// Non-Ident command function arguments
|
||||||
|
8
examples/web/.gitignore
vendored
Normal file
8
examples/web/.gitignore
vendored
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
.DS_Store
|
||||||
|
node_modules
|
||||||
|
/build
|
||||||
|
/.svelte-kit
|
||||||
|
/package
|
||||||
|
.env
|
||||||
|
.env.*
|
||||||
|
!.env.example
|
1
examples/web/.npmrc
Normal file
1
examples/web/.npmrc
Normal file
@ -0,0 +1 @@
|
|||||||
|
engine-strict=true
|
35
examples/web/README.md
Normal file
35
examples/web/README.md
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
# Desktop / Web Example
|
||||||
|
|
||||||
|
This example showcases an application that has shares code between a desktop and a Web target.
|
||||||
|
|
||||||
|
The Web application uses WASM to communicate with the Rust backend, while the desktop app leverages Tauri commands.
|
||||||
|
|
||||||
|
## Architecture
|
||||||
|
|
||||||
|
The Rust code lives in the `core/` folder and it is a Cargo workspace with three crates:
|
||||||
|
|
||||||
|
- tauri: desktop application. Contains the commands that are used by the frontend to access the Rust APIs;
|
||||||
|
- wasm: library that is compiled to WASM to be used by the Web application;
|
||||||
|
- api: code shared between the Tauri and the WASM crates. Most of the logic should live here, with only the specifics in the tauri and wasm crates.
|
||||||
|
|
||||||
|
The Rust code bridge is defined in the `src/api/` folder, which defines `desktop/index.js` and a `web/index.js` interfaces.
|
||||||
|
To access the proper interface according to the build target, a resolve alias is defined in vite.config.js, so the API can be imported
|
||||||
|
with `import * as api from '$api'`.
|
||||||
|
|
||||||
|
## Running the desktop application
|
||||||
|
|
||||||
|
Use the following commands to run the desktop application:
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn
|
||||||
|
yarn tauri dev
|
||||||
|
```
|
||||||
|
|
||||||
|
## Running the Web application
|
||||||
|
|
||||||
|
Use the following commands to run the Web application:
|
||||||
|
|
||||||
|
```
|
||||||
|
yarn
|
||||||
|
yarn dev:web
|
||||||
|
```
|
3395
examples/web/core/Cargo.lock
generated
Normal file
3395
examples/web/core/Cargo.lock
generated
Normal file
File diff suppressed because it is too large
Load Diff
6
examples/web/core/Cargo.toml
Normal file
6
examples/web/core/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[workspace]
|
||||||
|
members = [
|
||||||
|
"api",
|
||||||
|
"tauri",
|
||||||
|
"wasm"
|
||||||
|
]
|
6
examples/web/core/api/Cargo.toml
Normal file
6
examples/web/core/api/Cargo.toml
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
[package]
|
||||||
|
name = "api"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[dependencies]
|
3
examples/web/core/api/src/lib.rs
Normal file
3
examples/web/core/api/src/lib.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
pub fn greet(name: &str) -> String {
|
||||||
|
format!("Hello, {name}!")
|
||||||
|
}
|
3
examples/web/core/tauri/.gitignore
vendored
Normal file
3
examples/web/core/tauri/.gitignore
vendored
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
# Generated by Cargo
|
||||||
|
# will have compiled files and executables
|
||||||
|
/target/
|
25
examples/web/core/tauri/Cargo.toml
Normal file
25
examples/web/core/tauri/Cargo.toml
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
[package]
|
||||||
|
name = "app"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "A Tauri App"
|
||||||
|
authors = ["you"]
|
||||||
|
license = ""
|
||||||
|
repository = ""
|
||||||
|
default-run = "app"
|
||||||
|
edition = "2021"
|
||||||
|
rust-version = "1.59"
|
||||||
|
|
||||||
|
[build-dependencies]
|
||||||
|
tauri-build = { path = "../../../../core/tauri-build", features = [] }
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
api = { path = "../api" }
|
||||||
|
tauri = { path = "../../../../core/tauri", features = ["dialog"] }
|
||||||
|
|
||||||
|
[features]
|
||||||
|
# by default Tauri runs in production mode
|
||||||
|
# when `tauri dev` runs it is executed with `cargo run --no-default-features` if `devPath` is an URL
|
||||||
|
default = [ "custom-protocol" ]
|
||||||
|
# this feature is used for production builds where `devPath` points to the filesystem
|
||||||
|
# DO NOT remove this
|
||||||
|
custom-protocol = [ "tauri/custom-protocol" ]
|
3
examples/web/core/tauri/build.rs
Normal file
3
examples/web/core/tauri/build.rs
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
fn main() {
|
||||||
|
tauri_build::build()
|
||||||
|
}
|
16
examples/web/core/tauri/src/main.rs
Normal file
16
examples/web/core/tauri/src/main.rs
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
#![cfg_attr(
|
||||||
|
all(not(debug_assertions), target_os = "windows"),
|
||||||
|
windows_subsystem = "windows"
|
||||||
|
)]
|
||||||
|
|
||||||
|
#[tauri::command]
|
||||||
|
fn greet(window: tauri::Window, name: String) {
|
||||||
|
tauri::api::dialog::message(Some(&window), "Tauri Example", api::greet(&name));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
tauri::Builder::default()
|
||||||
|
.invoke_handler(tauri::generate_handler![greet])
|
||||||
|
.run(tauri::generate_context!())
|
||||||
|
.expect("error while running tauri application");
|
||||||
|
}
|
65
examples/web/core/tauri/tauri.conf.json
Normal file
65
examples/web/core/tauri/tauri.conf.json
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
{
|
||||||
|
"build": {
|
||||||
|
"beforeBuildCommand": "yarn build:tauri",
|
||||||
|
"beforeDevCommand": "yarn dev:tauri",
|
||||||
|
"devPath": "http://127.0.0.1:5173",
|
||||||
|
"distDir": "../../build"
|
||||||
|
},
|
||||||
|
"package": {
|
||||||
|
"productName": "app",
|
||||||
|
"version": "0.1.0"
|
||||||
|
},
|
||||||
|
"tauri": {
|
||||||
|
"allowlist": {
|
||||||
|
"all": false
|
||||||
|
},
|
||||||
|
"bundle": {
|
||||||
|
"active": true,
|
||||||
|
"category": "DeveloperTool",
|
||||||
|
"copyright": "",
|
||||||
|
"deb": {
|
||||||
|
"depends": []
|
||||||
|
},
|
||||||
|
"externalBin": [],
|
||||||
|
"icon": [
|
||||||
|
"../../../.icons/32x32.png",
|
||||||
|
"../../../.icons/128x128.png",
|
||||||
|
"../../../.icons/128x128@2x.png",
|
||||||
|
"../../../.icons/icon.icns",
|
||||||
|
"../../../.icons/icon.ico"
|
||||||
|
],
|
||||||
|
"identifier": "com.tauri.app",
|
||||||
|
"longDescription": "",
|
||||||
|
"macOS": {
|
||||||
|
"entitlements": null,
|
||||||
|
"exceptionDomain": "",
|
||||||
|
"frameworks": [],
|
||||||
|
"providerShortName": null,
|
||||||
|
"signingIdentity": null
|
||||||
|
},
|
||||||
|
"resources": [],
|
||||||
|
"shortDescription": "",
|
||||||
|
"targets": "all",
|
||||||
|
"windows": {
|
||||||
|
"certificateThumbprint": null,
|
||||||
|
"digestAlgorithm": "sha256",
|
||||||
|
"timestampUrl": ""
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": {
|
||||||
|
"csp": null
|
||||||
|
},
|
||||||
|
"updater": {
|
||||||
|
"active": false
|
||||||
|
},
|
||||||
|
"windows": [
|
||||||
|
{
|
||||||
|
"fullscreen": false,
|
||||||
|
"height": 600,
|
||||||
|
"resizable": true,
|
||||||
|
"title": "Tauri Example",
|
||||||
|
"width": 800
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
11
examples/web/core/wasm/Cargo.toml
Normal file
11
examples/web/core/wasm/Cargo.toml
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
[package]
|
||||||
|
name = "wasm"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2021"
|
||||||
|
|
||||||
|
[lib]
|
||||||
|
crate-type = ["cdylib"]
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
api = { path = "../api" }
|
||||||
|
wasm-bindgen = "0.2"
|
11
examples/web/core/wasm/src/lib.rs
Normal file
11
examples/web/core/wasm/src/lib.rs
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
use wasm_bindgen::prelude::*;
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
extern "C" {
|
||||||
|
pub fn alert(s: &str);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[wasm_bindgen]
|
||||||
|
pub fn greet(name: &str) {
|
||||||
|
alert(&api::greet(name));
|
||||||
|
}
|
37
examples/web/package.json
Normal file
37
examples/web/package.json
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
"name": "web",
|
||||||
|
"version": "0.0.1",
|
||||||
|
"private": true,
|
||||||
|
"scripts": {
|
||||||
|
"preinstall": "yarn wasm",
|
||||||
|
"dev:web": "yarn wasm && cross-env TARGET=web vite dev",
|
||||||
|
"build:web": "yarn wasm && cross-env TARGET=web vite build",
|
||||||
|
"preview": "vite preview",
|
||||||
|
"dev:tauri": "cross-env TARGET=tauri vite dev",
|
||||||
|
"build:tauri": "cross-env TARGET=tauri vite build",
|
||||||
|
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
|
||||||
|
"check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
|
||||||
|
"tauri": "node ../../tooling/cli/node/tauri.js",
|
||||||
|
"wasm": "wasm-pack build ./core/wasm --target web"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"@tauri-apps/api": "../../tooling/api/dist",
|
||||||
|
"wasm": "core/wasm/pkg"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@sveltejs/adapter-auto": "next",
|
||||||
|
"@sveltejs/adapter-static": "^1.0.0-next.47",
|
||||||
|
"@sveltejs/kit": "next",
|
||||||
|
"cross-env": "^7.0.3",
|
||||||
|
"svelte": "^3.44.0",
|
||||||
|
"svelte-check": "^2.7.1",
|
||||||
|
"svelte-preprocess": "^4.10.6",
|
||||||
|
"tslib": "^2.3.1",
|
||||||
|
"typescript": "^4.7.4",
|
||||||
|
"vite": "^3.1.0",
|
||||||
|
"vite-plugin-static-copy": "^0.11.1",
|
||||||
|
"vite-plugin-top-level-await": "^1.2.1",
|
||||||
|
"vite-plugin-wasm": "^3.1.0"
|
||||||
|
},
|
||||||
|
"type": "module"
|
||||||
|
}
|
12
examples/web/src/api/desktop/index.js
Normal file
12
examples/web/src/api/desktop/index.js
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import { invoke } from '@tauri-apps/api/tauri'
|
||||||
|
|
||||||
|
export const NAME = 'Tauri'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Greets someone.
|
||||||
|
* @param {string} name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function greet(name) {
|
||||||
|
return invoke('greet', { name })
|
||||||
|
}
|
16
examples/web/src/api/web/index.js
Normal file
16
examples/web/src/api/web/index.js
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import init, * as wasm from 'wasm'
|
||||||
|
|
||||||
|
function initialize() {
|
||||||
|
return init('wasm/wasm_bg.wasm')
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NAME = 'WEB'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Greets someone.
|
||||||
|
* @param {string} name
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
export async function greet(name) {
|
||||||
|
return initialize().then(() => wasm.greet(name))
|
||||||
|
}
|
9
examples/web/src/app.d.ts
vendored
Normal file
9
examples/web/src/app.d.ts
vendored
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
// See https://kit.svelte.dev/docs/types#app
|
||||||
|
// for information about these interfaces
|
||||||
|
// and what to do when importing types
|
||||||
|
declare namespace App {
|
||||||
|
// interface Locals {}
|
||||||
|
// interface PageData {}
|
||||||
|
// interface Error {}
|
||||||
|
// interface Platform {}
|
||||||
|
}
|
12
examples/web/src/app.html
Normal file
12
examples/web/src/app.html
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="utf-8" />
|
||||||
|
<link rel="icon" href="%sveltekit.assets%/favicon.png" />
|
||||||
|
<meta name="viewport" content="width=device-width" />
|
||||||
|
%sveltekit.head%
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div>%sveltekit.body%</div>
|
||||||
|
</body>
|
||||||
|
</html>
|
2
examples/web/src/routes/+page.js
Normal file
2
examples/web/src/routes/+page.js
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
// TODO: how can we disable SSR on Tauri but keep it on the Web target?
|
||||||
|
export const ssr = false
|
14
examples/web/src/routes/+page.svelte
Normal file
14
examples/web/src/routes/+page.svelte
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
<script>
|
||||||
|
import * as api from '$api'
|
||||||
|
|
||||||
|
let name = api.NAME
|
||||||
|
|
||||||
|
function greet() {
|
||||||
|
api.greet(name)
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<input bind:value={name} />
|
||||||
|
<button on:click={greet}>Greet</button>
|
||||||
|
</div>
|
BIN
examples/web/static/favicon.png
Normal file
BIN
examples/web/static/favicon.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.5 KiB |
23
examples/web/svelte.config.js
Normal file
23
examples/web/svelte.config.js
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
import autoAdapter from '@sveltejs/adapter-auto'
|
||||||
|
import staticAdapter from '@sveltejs/adapter-static'
|
||||||
|
import preprocess from 'svelte-preprocess'
|
||||||
|
|
||||||
|
const TARGET = process.env.TARGET
|
||||||
|
|
||||||
|
/** @type {import('@sveltejs/kit').Config} */
|
||||||
|
const config = {
|
||||||
|
// Consult https://github.com/sveltejs/svelte-preprocess
|
||||||
|
// for more information about preprocessors
|
||||||
|
preprocess: preprocess(),
|
||||||
|
|
||||||
|
kit: {
|
||||||
|
adapter:
|
||||||
|
TARGET === 'tauri'
|
||||||
|
? staticAdapter({
|
||||||
|
fallback: 'index.html'
|
||||||
|
})
|
||||||
|
: autoAdapter()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
17
examples/web/tsconfig.json
Normal file
17
examples/web/tsconfig.json
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"extends": "./.svelte-kit/tsconfig.json",
|
||||||
|
"compilerOptions": {
|
||||||
|
"allowJs": true,
|
||||||
|
"checkJs": true,
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"skipLibCheck": true,
|
||||||
|
"sourceMap": true,
|
||||||
|
"strict": true
|
||||||
|
}
|
||||||
|
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias
|
||||||
|
//
|
||||||
|
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
|
||||||
|
// from the referenced tsconfig.json - TypeScript does not merge them in
|
||||||
|
}
|
39
examples/web/vite.config.ts
Normal file
39
examples/web/vite.config.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import { resolve } from 'path'
|
||||||
|
import { sveltekit } from '@sveltejs/kit/vite'
|
||||||
|
import wasm from 'vite-plugin-wasm'
|
||||||
|
import topLevelAwait from 'vite-plugin-top-level-await'
|
||||||
|
import { viteStaticCopy } from 'vite-plugin-static-copy'
|
||||||
|
import type { UserConfig } from 'vite'
|
||||||
|
|
||||||
|
const TARGET = process.env.TARGET
|
||||||
|
|
||||||
|
const plugins = [sveltekit()]
|
||||||
|
|
||||||
|
if (TARGET === 'web') {
|
||||||
|
plugins.push(wasm())
|
||||||
|
plugins.push(topLevelAwait())
|
||||||
|
plugins.push(
|
||||||
|
viteStaticCopy({
|
||||||
|
targets: [
|
||||||
|
{
|
||||||
|
src: 'core/wasm/pkg/wasm_bg.wasm',
|
||||||
|
dest: 'wasm'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const config: UserConfig = {
|
||||||
|
plugins,
|
||||||
|
resolve: {
|
||||||
|
alias: {
|
||||||
|
$api:
|
||||||
|
TARGET === 'tauri'
|
||||||
|
? resolve('./src/api/desktop')
|
||||||
|
: resolve('./src/api/web')
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default config
|
1394
examples/web/yarn.lock
Normal file
1394
examples/web/yarn.lock
Normal file
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user