mirror of
https://github.com/tauri-apps/tauri.git
synced 2024-11-28 03:47:37 +03:00
Merge branch 'next' into feat/audit-patches
This commit is contained in:
commit
0d1e3219b4
6
.changes/api-file-dialog-title.md
Normal file
6
.changes/api-file-dialog-title.md
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
"api": minor
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
Add `title` option to file open/save dialogs.
|
@ -2,4 +2,4 @@
|
||||
"cli.rs": patch
|
||||
---
|
||||
|
||||
Define `PLATFORM`, `ARCH`, `FAMILY`, `PLATFORM_TYPE` and `TAURI_DEBUG` environment variables for the `beforeDevCommand` and `beforeBuildCommand` scripts.
|
||||
Define `TAURI_PLATFORM`, `TAURI_ARCH`, `TAURI_FAMILY`, `TAURI_PLATFORM_TYPE`, `TAURI_PLATFORM_VERSION` and `TAURI_DEBUG` environment variables for the `beforeDevCommand` and `beforeBuildCommand` scripts.
|
||||
|
5
.changes/bundler-add-provider-short-name.md
Normal file
5
.changes/bundler-add-provider-short-name.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-bundler": patch
|
||||
---
|
||||
|
||||
Provide a provider short name for macOS app notarization if your Apple developer account is connected to more than one team.
|
5
.changes/bundler-appimage-fuse.md
Normal file
5
.changes/bundler-appimage-fuse.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-bundler": patch
|
||||
---
|
||||
|
||||
Allow building AppImages on systems without FUSE setup.
|
5
.changes/consistent-event-name-usage.md
Normal file
5
.changes/consistent-event-name-usage.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"api": minor
|
||||
---
|
||||
|
||||
Change the `event` field of the `Event` interface to type `EventName` instead of `string`.
|
5
.changes/create-window-return-window.md
Normal file
5
.changes/create-window-return-window.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri": minor
|
||||
---
|
||||
|
||||
**Breaking change:** Return `Window` on `App` and `AppHandle`'s `create_window` function.
|
5
.changes/wix-signing.md
Normal file
5
.changes/wix-signing.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
"tauri-bundler": patch
|
||||
---
|
||||
|
||||
Sign WiX installer in addition to the executable file.
|
2
.github/CONTRIBUTING.md
vendored
2
.github/CONTRIBUTING.md
vendored
@ -46,7 +46,7 @@ Hi! We, the maintainers, are really excited that you are interested in contribut
|
||||
|
||||
First, [join our Discord server](https://discord.gg/SpmNs4S) and let us know that you want to contribute. This way we can point you in the right direction and help ensure your contribution will be as helpful as possible.
|
||||
|
||||
To set up your machine for development, follow the [Tauri setup guide](https://tauri.studio/en/docs/getting-started/intro#setting-up-your-environment) to get all the tools you need to develop Tauri apps. The only additional tool you may need is [Yarn](https://yarnpkg.com/), it is only required if you are developing the Node CLI/API (`tooling/cli.js` and `api`). Next, fork and clone this repo. It is structured as a monorepo, which means that all the various Tauri packages are under the same repository. The development process varies depending on what part of Tauri you are contributing to, see the guides below for per-package instructions.
|
||||
To set up your machine for development, follow the [Tauri setup guide](https://tauri.studio/en/docs/get-started/intro#setting-up-your-environment) to get all the tools you need to develop Tauri apps. The only additional tool you may need is [Yarn](https://yarnpkg.com/), it is only required if you are developing the Node CLI/API (`tooling/cli.js` and `api`). Next, fork and clone this repo. It is structured as a monorepo, which means that all the various Tauri packages are under the same repository. The development process varies depending on what part of Tauri you are contributing to, see the guides below for per-package instructions.
|
||||
|
||||
Some Tauri packages will be automatically built when running one of the examples. Others, however, will need to be built beforehand. To build these automatically, run the `.scripts/setup.sh` (Linux and macOS) or `.scripts/setup.ps1` (Windows) script. This will install the Rust and Node.js CLI and build the JS API. After that, you should be able to run all the examples. Note that the setup script should be executed from the root folder of the respository in order to run correctly.
|
||||
|
||||
|
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@ -23,7 +23,7 @@ Please make sure to read the Pull Request Guidelines: https://github.com/tauri-a
|
||||
- [ ] No
|
||||
|
||||
### Checklist
|
||||
- [ ] When resolving issues, they are referenced in the PR's title (e.g `fix #___, #___`)
|
||||
- [ ] When resolving issues, they are referenced in the PR's title (e.g `fix: remove a typo, closes #___, #___`)
|
||||
- [ ] A change file is added if any packages will require a version bump due to this PR per [the instructions in the readme](https://github.com/tauri-apps/tauri/blob/dev/.changes/readme.md).
|
||||
- [ ] I have added a convincing reason for adding this feature, if necessary
|
||||
|
||||
|
0
docs/splash.png → .github/splash.png
vendored
0
docs/splash.png → .github/splash.png
vendored
Before Width: | Height: | Size: 59 KiB After Width: | Height: | Size: 59 KiB |
59
.github/workflows/update-docs.yml
vendored
59
.github/workflows/update-docs.yml
vendored
@ -40,46 +40,37 @@ jobs:
|
||||
with:
|
||||
repository: tauri-apps/tauri-search-bot
|
||||
path: tauri-search-bot
|
||||
- name: install webkit2gtk
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
# Rust
|
||||
- name: generate rust docs
|
||||
working-directory: ./tauri/core/tauri
|
||||
run: cargo doc --no-deps
|
||||
- name: run rustdocusaurus
|
||||
uses: tauri-apps/rustdocusaurus/github-action@v1
|
||||
with:
|
||||
originPath: ./tauri/target/doc/
|
||||
targetPath: ./tauri-docs/docs/en/api/rust/
|
||||
sidebarPath: "${{ github.workspace }}/tauri-docs/sidebars/rustdoc.json"
|
||||
linksRoot: ""
|
||||
cratesToProcess: "tauri"
|
||||
# Any Rust documentation is currently disabled while we're falling back to docs.rs
|
||||
|
||||
# TypeScript
|
||||
- name: install API deps
|
||||
# - name: install webkit2gtk
|
||||
# run: |
|
||||
# sudo apt-get update
|
||||
# sudo apt-get install -y libgtk-3-dev webkit2gtk-4.0 libappindicator3-dev librsvg2-dev patchelf
|
||||
|
||||
# # Rust
|
||||
# - name: generate rust docs
|
||||
# working-directory: ./tauri/core/tauri
|
||||
# run: cargo doc --no-deps
|
||||
# - name: run rustdocusaurus
|
||||
# uses: tauri-apps/rustdocusaurus/github-action@v1
|
||||
# with:
|
||||
# originPath: ./tauri/target/doc/
|
||||
# targetPath: ./tauri-docs/docs/en/api/rust/
|
||||
# sidebarPath: "${{ github.workspace }}/tauri-docs/sidebars/rustdoc.json"
|
||||
# linksRoot: ""
|
||||
# cratesToProcess: "tauri"
|
||||
|
||||
- name: Generate JS docs
|
||||
working-directory: ./tauri/tooling/api
|
||||
run: yarn && yarn add typescript@4.2
|
||||
- name: run typedocusaurus
|
||||
uses: tauri-apps/typedocusaurus@v1
|
||||
with:
|
||||
originPath: ./tauri/tooling/api/
|
||||
sidebarFile: "${{ github.workspace }}/tauri-docs/sidebars/typedoc.json"
|
||||
targetPath: ${{ github.workspace }}/tauri-docs/docs/en/api/js
|
||||
docusaurusPath: ./tauri-docs/
|
||||
|
||||
# Moving docs for Indexation
|
||||
- name: copy docs
|
||||
working-directory: ./tauri
|
||||
run: |
|
||||
mv docs/sidebar.json ${{ github.workspace }}/tauri-docs/sidebars/core.json
|
||||
cp -r docs/!(.templates) ${{ github.workspace }}/tauri-docs/docs/en
|
||||
mv ARCHITECTURE.md ${{ github.workspace }}/tauri-docs/docs/en/about/architecture.md
|
||||
run: yarn && yarn generate-docs
|
||||
|
||||
- name: Copy JS docs
|
||||
run: cp -r tauri/tooling/api/docs/* tauri-docs/docs/api/js/
|
||||
|
||||
# Indexing
|
||||
- name: meilisearch indexation
|
||||
|
||||
uses: tauri-apps/docusaurus-meilisearch-indexer@v1
|
||||
with:
|
||||
version: ${{ github.event.inputs.version || github.event.release.tag_name }}
|
||||
|
@ -1,4 +1,4 @@
|
||||
<img src="docs/splash.png" alt="Tauri" />
|
||||
<img src=".github/splash.png" alt="Tauri" />
|
||||
|
||||
[![status](https://img.shields.io/badge/Status-Beta-green.svg)](https://github.com/tauri-apps/tauri/tree/dev)
|
||||
[![License](https://img.shields.io/badge/License-MIT%20or%20Apache%202-green.svg)](https://opencollective.com/tauri)
|
||||
@ -7,7 +7,7 @@
|
||||
|
||||
[![Chat Server](https://img.shields.io/badge/chat-on%20discord-7289da.svg)](https://discord.gg/SpmNs4S)
|
||||
[![devto](https://img.shields.io/badge/blog-dev.to-black.svg)](https://dev.to/tauri)
|
||||
[![devto](https://img.shields.io/badge/documentation-tauri.studio-purple.svg)](https://tauri.studio/docs/getting-started/intro)
|
||||
[![devto](https://img.shields.io/badge/documentation-tauri.studio-purple.svg)](https://tauri.studio/docs/get-started/intro)
|
||||
[![https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg](https://good-labs.github.io/greater-good-affirmation/assets/images/badge.svg)](https://good-labs.github.io/greater-good-affirmation)
|
||||
[![support](https://img.shields.io/badge/sponsor-open%20collective-blue.svg)](https://opencollective.com/tauri)
|
||||
|
||||
|
@ -15,7 +15,7 @@ readme = "README.md"
|
||||
[dependencies]
|
||||
sha2 = "0.9"
|
||||
base64 = "0.13"
|
||||
blake3 = { version = "1.2", features = [ "rayon" ] }
|
||||
blake3 = { version = "1.3", features = [ "rayon" ] }
|
||||
proc-macro2 = "1"
|
||||
quote = "1"
|
||||
serde = { version = "1", features = [ "derive" ] }
|
||||
|
@ -14,7 +14,7 @@ readme = "README.md"
|
||||
|
||||
[dependencies]
|
||||
#wry = { version = "0.12", default-features = false, features = [ "file-drop", "protocol" ] }
|
||||
wry = { git = "ssh://git@github.com/tauri-sec/wry", branch = "next", default-features = false, features = [ "file-drop", "protocol" ] }
|
||||
wry = { git = "https://github.com/tauri-apps/wry", rev = "a3829035a3e49e76db77b0db6924e147831124c7", default-features = false, features = [ "file-drop", "protocol", "transparent", "fullscreen" ] }
|
||||
tauri-runtime = { version = "0.2.1", path = "../tauri-runtime" }
|
||||
tauri-utils = { version = "1.0.0-beta.3", path = "../tauri-utils" }
|
||||
uuid = { version = "0.8.2", features = [ "v4" ] }
|
||||
@ -29,10 +29,10 @@ once_cell = { version = "1.8", optional = true}
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
ico = "0.1"
|
||||
webview2-com = "0.9.0"
|
||||
webview2-com = "0.10.0"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
]
|
||||
|
526
core/tauri-runtime-wry/src/egui.rs
Normal file
526
core/tauri-runtime-wry/src/egui.rs
Normal file
@ -0,0 +1,526 @@
|
||||
// Copyright 2019-2021 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{
|
||||
EventLoopIterationContext, MenuEventListeners, Message, WebContextStore, WindowEventListeners,
|
||||
WindowEventListenersMap, WindowHandle, WindowMenuEventListeners, WindowMessage, WindowWrapper,
|
||||
};
|
||||
use wry::application::{
|
||||
event::Event,
|
||||
event_loop::{ControlFlow, EventLoopProxy, EventLoopWindowTarget},
|
||||
window::WindowId,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use glutin::platform::ContextTraitExt;
|
||||
#[cfg(target_os = "linux")]
|
||||
use gtk::prelude::*;
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::sync::atomic::{AtomicU8, Ordering};
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
ops::Deref,
|
||||
rc::Rc,
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
static EGUI_ID: once_cell::sync::Lazy<Mutex<Option<WindowId>>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(None));
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum MaybeRc<T> {
|
||||
Actual(T),
|
||||
Rc(Rc<T>),
|
||||
}
|
||||
|
||||
impl<T> MaybeRc<T> {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(t: T) -> Self {
|
||||
Self::Actual(t)
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> AsRef<T> for MaybeRc<T> {
|
||||
fn as_ref(&self) -> &T {
|
||||
match self {
|
||||
Self::Actual(t) => t,
|
||||
Self::Rc(t) => t,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for MaybeRc<T> {
|
||||
type Target = T;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Actual(v) => v,
|
||||
Self::Rc(r) => r.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> std::borrow::Borrow<T> for MaybeRc<T> {
|
||||
fn borrow(&self) -> &T {
|
||||
match self {
|
||||
Self::Actual(v) => &v,
|
||||
Self::Rc(r) => r.borrow(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
pub enum MaybeRcCell<T> {
|
||||
Actual(RefCell<T>),
|
||||
RcCell(Rc<RefCell<T>>),
|
||||
}
|
||||
|
||||
impl<T> MaybeRcCell<T> {
|
||||
#[allow(dead_code)]
|
||||
pub fn new(t: T) -> Self {
|
||||
Self::Actual(RefCell::new(t))
|
||||
}
|
||||
}
|
||||
|
||||
impl<T> Deref for MaybeRcCell<T> {
|
||||
type Target = RefCell<T>;
|
||||
|
||||
fn deref(&self) -> &Self::Target {
|
||||
match self {
|
||||
Self::Actual(v) => v,
|
||||
Self::RcCell(r) => r.deref(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct GlutinWindowContext {
|
||||
pub context: MaybeRc<glutin::ContextWrapper<glutin::PossiblyCurrent, glutin::window::Window>>,
|
||||
glow_context: MaybeRc<glow::Context>,
|
||||
painter: MaybeRcCell<egui_glow::Painter>,
|
||||
integration: MaybeRcCell<egui_tao::epi::EpiIntegration>,
|
||||
#[cfg(target_os = "linux")]
|
||||
render_flow: Rc<AtomicU8>,
|
||||
}
|
||||
|
||||
impl GlutinWindowContext {
|
||||
pub fn window(&self) -> &glutin::window::Window {
|
||||
self.context.window()
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_gl_window(
|
||||
event_loop: &EventLoopWindowTarget<Message>,
|
||||
windows: &Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
|
||||
window_event_listeners: &WindowEventListeners,
|
||||
menu_event_listeners: &MenuEventListeners,
|
||||
label: String,
|
||||
app: Box<dyn epi::App + Send>,
|
||||
native_options: epi::NativeOptions,
|
||||
proxy: EventLoopProxy<Message>,
|
||||
) {
|
||||
let mut egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
if let WindowHandle::GLWindow(glutin_window_context) =
|
||||
&mut windows.lock().unwrap().get_mut(&id).unwrap().inner
|
||||
{
|
||||
let mut integration = glutin_window_context.integration.borrow_mut();
|
||||
let mut painter = glutin_window_context.painter.borrow_mut();
|
||||
integration.on_exit(glutin_window_context.context.window());
|
||||
painter.destroy(&glutin_window_context.glow_context);
|
||||
}
|
||||
*egui_id = None;
|
||||
let _ = proxy.send_event(Message::Window(id, WindowMessage::Close));
|
||||
}
|
||||
|
||||
let persistence = egui_tao::epi::Persistence::from_app_name(app.name());
|
||||
let window_settings = persistence.load_window_settings();
|
||||
let window_builder =
|
||||
egui_tao::epi::window_builder(&native_options, &window_settings).with_title(app.name());
|
||||
let gl_window = unsafe {
|
||||
glutin::ContextBuilder::new()
|
||||
.with_depth_buffer(0)
|
||||
.with_srgb(true)
|
||||
.with_stencil_buffer(0)
|
||||
.with_vsync(true)
|
||||
.build_windowed(window_builder, event_loop)
|
||||
.unwrap()
|
||||
.make_current()
|
||||
.unwrap()
|
||||
};
|
||||
let window_id = gl_window.window().id();
|
||||
*egui_id = Some(window_id);
|
||||
|
||||
let gl = unsafe { glow::Context::from_loader_function(|s| gl_window.get_proc_address(s)) };
|
||||
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl.enable(glow::FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
struct GlowRepaintSignal(EventLoopProxy<Message>, WindowId);
|
||||
|
||||
impl epi::backend::RepaintSignal for GlowRepaintSignal {
|
||||
fn request_repaint(&self) {
|
||||
let _ = self
|
||||
.0
|
||||
.send_event(Message::Window(self.1, WindowMessage::RequestRedraw));
|
||||
}
|
||||
}
|
||||
|
||||
let repaint_signal = std::sync::Arc::new(GlowRepaintSignal(proxy, window_id));
|
||||
|
||||
let painter = egui_glow::Painter::new(&gl, None, "")
|
||||
.map_err(|error| eprintln!("some OpenGL error occurred {}\n", error))
|
||||
.unwrap();
|
||||
|
||||
let integration = egui_tao::epi::EpiIntegration::new(
|
||||
"egui_glow",
|
||||
gl_window.window(),
|
||||
repaint_signal,
|
||||
persistence,
|
||||
app,
|
||||
);
|
||||
|
||||
window_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(window_id, WindowEventListenersMap::default());
|
||||
|
||||
menu_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(window_id, WindowMenuEventListeners::default());
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
windows.lock().expect("poisoned webview collection").insert(
|
||||
window_id,
|
||||
WindowWrapper {
|
||||
label,
|
||||
inner: WindowHandle::GLWindow(GlutinWindowContext {
|
||||
context: MaybeRc::new(gl_window),
|
||||
glow_context: MaybeRc::new(gl),
|
||||
painter: MaybeRcCell::new(painter),
|
||||
integration: MaybeRcCell::new(integration),
|
||||
}),
|
||||
menu_items: Default::default(),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let area = unsafe { gl_window.raw_handle() };
|
||||
let integration = Rc::new(RefCell::new(integration));
|
||||
let painter = Rc::new(RefCell::new(painter));
|
||||
let render_flow = Rc::new(AtomicU8::new(1));
|
||||
let gl_window = Rc::new(gl_window);
|
||||
let gl = Rc::new(gl);
|
||||
|
||||
let i = integration.clone();
|
||||
let p = painter.clone();
|
||||
let r = render_flow.clone();
|
||||
let gl_window_ = Rc::downgrade(&gl_window);
|
||||
let gl_ = gl.clone();
|
||||
area.connect_render(move |_, _| {
|
||||
if let Some(gl_window) = gl_window_.upgrade() {
|
||||
let mut integration = i.borrow_mut();
|
||||
let mut painter = p.borrow_mut();
|
||||
let (needs_repaint, mut tex_allocation_data, shapes) =
|
||||
integration.update(gl_window.window());
|
||||
let clipped_meshes = integration.egui_ctx.tessellate(shapes);
|
||||
|
||||
for (id, image) in tex_allocation_data.creations {
|
||||
painter.set_texture(&gl_, id, &image);
|
||||
}
|
||||
|
||||
{
|
||||
let color = integration.app.clear_color();
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl_.disable(glow::SCISSOR_TEST);
|
||||
gl_.clear_color(color[0], color[1], color[2], color[3]);
|
||||
gl_.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
painter.upload_egui_texture(&gl_, &integration.egui_ctx.font_image());
|
||||
painter.paint_meshes(
|
||||
&gl_,
|
||||
gl_window.window().inner_size().into(),
|
||||
integration.egui_ctx.pixels_per_point(),
|
||||
clipped_meshes,
|
||||
);
|
||||
}
|
||||
|
||||
for id in tex_allocation_data.destructions.drain(..) {
|
||||
painter.free_texture(id);
|
||||
}
|
||||
|
||||
{
|
||||
let control_flow = if integration.should_quit() {
|
||||
1
|
||||
} else if needs_repaint {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
r.store(control_flow, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
integration.maybe_autosave(gl_window.window());
|
||||
}
|
||||
gtk::Inhibit(false)
|
||||
});
|
||||
|
||||
windows.lock().expect("poisoned webview collection").insert(
|
||||
window_id,
|
||||
WindowWrapper {
|
||||
label,
|
||||
inner: WindowHandle::GLWindow(GlutinWindowContext {
|
||||
context: MaybeRc::Rc(gl_window),
|
||||
glow_context: MaybeRc::Rc(gl),
|
||||
painter: MaybeRcCell::RcCell(painter),
|
||||
integration: MaybeRcCell::RcCell(integration),
|
||||
render_flow,
|
||||
}),
|
||||
menu_items: Default::default(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn win_mac_gl_loop(
|
||||
control_flow: &mut ControlFlow,
|
||||
glutin_window_context: &mut GlutinWindowContext,
|
||||
event: &Event<Message>,
|
||||
is_focused: bool,
|
||||
should_quit: &mut bool,
|
||||
) {
|
||||
let mut redraw = || {
|
||||
let gl_window = &glutin_window_context.context;
|
||||
let gl = &glutin_window_context.glow_context;
|
||||
let mut integration = glutin_window_context.integration.borrow_mut();
|
||||
let mut painter = glutin_window_context.painter.borrow_mut();
|
||||
|
||||
if !is_focused {
|
||||
// On Mac, a minimized Window uses up all CPU: https://github.com/emilk/egui/issues/325
|
||||
// We can't know if we are minimized: https://github.com/rust-windowing/winit/issues/208
|
||||
// But we know if we are focused (in foreground). When minimized, we are not focused.
|
||||
// However, a user may want an egui with an animation in the background,
|
||||
// so we still need to repaint quite fast.
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
||||
let (needs_repaint, mut tex_allocation_data, shapes) = integration.update(gl_window.window());
|
||||
let clipped_meshes = integration.egui_ctx.tessellate(shapes);
|
||||
|
||||
{
|
||||
*control_flow = if integration.should_quit() {
|
||||
*should_quit = true;
|
||||
glutin::event_loop::ControlFlow::Wait
|
||||
} else if needs_repaint {
|
||||
gl_window.window().request_redraw();
|
||||
glutin::event_loop::ControlFlow::Poll
|
||||
} else {
|
||||
glutin::event_loop::ControlFlow::Wait
|
||||
};
|
||||
}
|
||||
|
||||
for (id, image) in tex_allocation_data.creations {
|
||||
painter.set_texture(&gl, id, &image);
|
||||
}
|
||||
|
||||
{
|
||||
let color = integration.app.clear_color();
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
gl.clear_color(color[0], color[1], color[2], color[3]);
|
||||
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
painter.upload_egui_texture(&gl, &integration.egui_ctx.font_image());
|
||||
painter.paint_meshes(
|
||||
&gl,
|
||||
gl_window.window().inner_size().into(),
|
||||
integration.egui_ctx.pixels_per_point(),
|
||||
clipped_meshes,
|
||||
);
|
||||
|
||||
gl_window.swap_buffers().unwrap();
|
||||
}
|
||||
|
||||
for id in tex_allocation_data.destructions.drain(..) {
|
||||
painter.free_texture(id);
|
||||
}
|
||||
|
||||
integration.maybe_autosave(gl_window.window());
|
||||
};
|
||||
|
||||
match event {
|
||||
glutin::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
|
||||
glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
fn linux_gl_loop(
|
||||
control_flow: &mut ControlFlow,
|
||||
glutin_window_context: &mut GlutinWindowContext,
|
||||
event: &Event<Message>,
|
||||
) {
|
||||
let area = unsafe { glutin_window_context.context.raw_handle() };
|
||||
if let glutin::event::Event::MainEventsCleared = event {
|
||||
area.queue_render();
|
||||
match glutin_window_context.render_flow.load(Ordering::Relaxed) {
|
||||
0 => *control_flow = glutin::event_loop::ControlFlow::Poll,
|
||||
1 => *control_flow = glutin::event_loop::ControlFlow::Wait,
|
||||
2 => *control_flow = glutin::event_loop::ControlFlow::Exit,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "egui")]
|
||||
pub fn handle_gl_loop(
|
||||
event: &Event<'_, Message>,
|
||||
_event_loop: &EventLoopWindowTarget<Message>,
|
||||
control_flow: &mut ControlFlow,
|
||||
context: EventLoopIterationContext<'_>,
|
||||
_web_context: &WebContextStore,
|
||||
is_focused: &mut bool,
|
||||
) -> bool {
|
||||
let mut prevent_default = false;
|
||||
let EventLoopIterationContext {
|
||||
callback,
|
||||
windows,
|
||||
window_event_listeners,
|
||||
menu_event_listeners,
|
||||
..
|
||||
} = context;
|
||||
let egui_id = EGUI_ID.lock().unwrap().clone();
|
||||
if let Some(id) = egui_id {
|
||||
let mut windows_lock = windows.lock().unwrap();
|
||||
let mut should_quit = false;
|
||||
if let Some(win) = windows_lock.get_mut(&id) {
|
||||
if let WindowHandle::GLWindow(glutin_window_context) = &mut win.inner {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
win_mac_gl_loop(
|
||||
control_flow,
|
||||
glutin_window_context,
|
||||
&event,
|
||||
*is_focused,
|
||||
&mut should_quit,
|
||||
);
|
||||
#[cfg(target_os = "linux")]
|
||||
linux_gl_loop(control_flow, glutin_window_context, &event);
|
||||
|
||||
if should_quit {
|
||||
drop(windows_lock);
|
||||
} else if let glutin::event::Event::WindowEvent {
|
||||
event, window_id, ..
|
||||
} = event
|
||||
{
|
||||
if window_id == &id {
|
||||
match event {
|
||||
glutin::event::WindowEvent::Focused(new_focused) => {
|
||||
*is_focused = *new_focused;
|
||||
}
|
||||
glutin::event::WindowEvent::Resized(physical_size) => {
|
||||
glutin_window_context.context.resize(*physical_size);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
|
||||
if let glutin::event::WindowEvent::CloseRequested = event {
|
||||
drop(windows_lock);
|
||||
let w = super::on_close_requested(
|
||||
callback,
|
||||
*window_id,
|
||||
windows.clone(),
|
||||
control_flow,
|
||||
window_event_listeners,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
if let Some(mut win) = w {
|
||||
if let WindowHandle::GLWindow(glutin_window_context) = &mut win.inner {
|
||||
// marker
|
||||
let gl_window = &glutin_window_context.context;
|
||||
let mut integration = glutin_window_context.integration.borrow_mut();
|
||||
integration.on_event(&event);
|
||||
if integration.should_quit() {
|
||||
should_quit = true;
|
||||
*control_flow = glutin::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
gl_window.window().request_redraw();
|
||||
}
|
||||
}
|
||||
prevent_default = true;
|
||||
} else {
|
||||
// same as the `marker` above
|
||||
let gl_window = &glutin_window_context.context;
|
||||
let mut integration = glutin_window_context.integration.borrow_mut();
|
||||
integration.on_event(&event);
|
||||
if integration.should_quit() {
|
||||
should_quit = true;
|
||||
*control_flow = glutin::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
gl_window.window().request_redraw();
|
||||
// prevent deadlock on the `if should quit` below
|
||||
drop(integration);
|
||||
drop(windows_lock);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_quit {
|
||||
drop(egui_id);
|
||||
super::on_window_close(
|
||||
callback,
|
||||
id,
|
||||
windows.lock().unwrap(),
|
||||
control_flow,
|
||||
#[cfg(target_os = "linux")]
|
||||
window_event_listeners,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
prevent_default
|
||||
}
|
||||
|
||||
pub fn on_window_close(window_id: &WindowId, webview: &mut WindowWrapper) {
|
||||
// Destrooy GL context if its a GLWindow
|
||||
let mut egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
if &id == window_id {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
if let WindowHandle::GLWindow(glutin_window_context) = &webview.inner {
|
||||
glutin_window_context
|
||||
.integration
|
||||
.borrow_mut()
|
||||
.on_exit(glutin_window_context.window());
|
||||
glutin_window_context
|
||||
.painter
|
||||
.borrow_mut()
|
||||
.destroy(&glutin_window_context.glow_context);
|
||||
*egui_id = None;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
if let WindowHandle::GLWindow(glutin_window_context) = &mut webview.inner {
|
||||
let mut integration = glutin_window_context.integration.borrow_mut();
|
||||
integration.on_exit(glutin_window_context.context.window());
|
||||
glutin_window_context
|
||||
.painter
|
||||
.borrow_mut()
|
||||
.destroy(&glutin_window_context.glow_context);
|
||||
*egui_id = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -37,10 +37,6 @@ use wry::application::platform::windows::{WindowBuilderExtWindows, WindowExtWind
|
||||
#[cfg(feature = "system-tray")]
|
||||
use wry::application::system_tray::{SystemTray as WrySystemTray, SystemTrayBuilder};
|
||||
|
||||
#[cfg(feature = "egui")]
|
||||
static EGUI_ID: once_cell::sync::Lazy<Mutex<Option<WindowId>>> =
|
||||
once_cell::sync::Lazy::new(|| Mutex::new(None));
|
||||
|
||||
use tauri_utils::config::WindowConfig;
|
||||
use uuid::Uuid;
|
||||
use wry::{
|
||||
@ -98,24 +94,12 @@ use std::{
|
||||
thread::{current as current_thread, ThreadId},
|
||||
};
|
||||
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(target_os = "linux")]
|
||||
use glutin::platform::ContextTraitExt;
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(target_os = "linux")]
|
||||
use gtk::prelude::*;
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(target_os = "linux")]
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
rc::Rc,
|
||||
sync::atomic::{AtomicU8, Ordering},
|
||||
};
|
||||
|
||||
#[cfg(feature = "system-tray")]
|
||||
mod system_tray;
|
||||
#[cfg(feature = "system-tray")]
|
||||
use system_tray::*;
|
||||
#[cfg(feature = "egui")]
|
||||
mod egui;
|
||||
|
||||
type WebContextStore = Arc<Mutex<HashMap<Option<PathBuf>, WebContext>>>;
|
||||
// window
|
||||
@ -132,7 +116,7 @@ pub type WindowMenuEventListeners = Arc<Mutex<HashMap<Uuid, MenuEventHandler>>>;
|
||||
macro_rules! getter {
|
||||
($self: ident, $rx: expr, $message: expr) => {{
|
||||
send_user_message(&$self.context, $message)?;
|
||||
$rx.recv().unwrap()
|
||||
$rx.recv().map_err(|_| Error::FailedToReceiveMessage)
|
||||
}};
|
||||
}
|
||||
|
||||
@ -417,14 +401,14 @@ impl fmt::Debug for GlobalShortcutManagerHandle {
|
||||
impl GlobalShortcutManager for GlobalShortcutManagerHandle {
|
||||
fn is_registered(&self, accelerator: &str) -> Result<bool> {
|
||||
let (tx, rx) = channel();
|
||||
Ok(getter!(
|
||||
getter!(
|
||||
self,
|
||||
rx,
|
||||
Message::GlobalShortcut(GlobalShortcutMessage::IsRegistered(
|
||||
accelerator.parse().expect("invalid accelerator"),
|
||||
tx
|
||||
))
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fn register<F: Fn() + Send + 'static>(&mut self, accelerator: &str, handler: F) -> Result<()> {
|
||||
@ -435,7 +419,7 @@ impl GlobalShortcutManager for GlobalShortcutManagerHandle {
|
||||
self,
|
||||
rx,
|
||||
Message::GlobalShortcut(GlobalShortcutMessage::Register(wry_accelerator, tx))
|
||||
)?;
|
||||
)??;
|
||||
|
||||
self.listeners.lock().unwrap().insert(id, Box::new(handler));
|
||||
self
|
||||
@ -453,7 +437,7 @@ impl GlobalShortcutManager for GlobalShortcutManagerHandle {
|
||||
self,
|
||||
rx,
|
||||
Message::GlobalShortcut(GlobalShortcutMessage::UnregisterAll(tx))
|
||||
)?;
|
||||
)??;
|
||||
self.listeners.lock().unwrap().clear();
|
||||
self.shortcuts.lock().unwrap().clear();
|
||||
Ok(())
|
||||
@ -466,7 +450,7 @@ impl GlobalShortcutManager for GlobalShortcutManagerHandle {
|
||||
self,
|
||||
rx,
|
||||
Message::GlobalShortcut(GlobalShortcutMessage::Unregister(shortcut, tx))
|
||||
)?;
|
||||
)??;
|
||||
self.listeners.lock().unwrap().remove(&accelerator_id);
|
||||
}
|
||||
Ok(())
|
||||
@ -485,11 +469,7 @@ unsafe impl Sync for ClipboardManagerWrapper {}
|
||||
impl ClipboardManager for ClipboardManagerWrapper {
|
||||
fn read_text(&self) -> Result<Option<String>> {
|
||||
let (tx, rx) = channel();
|
||||
Ok(getter!(
|
||||
self,
|
||||
rx,
|
||||
Message::Clipboard(ClipboardMessage::ReadText(tx))
|
||||
))
|
||||
getter!(self, rx, Message::Clipboard(ClipboardMessage::ReadText(tx)))
|
||||
}
|
||||
|
||||
fn write_text<T: Into<String>>(&mut self, text: T) -> Result<()> {
|
||||
@ -498,7 +478,7 @@ impl ClipboardManager for ClipboardManagerWrapper {
|
||||
self,
|
||||
rx,
|
||||
Message::Clipboard(ClipboardMessage::WriteText(text.into(), tx))
|
||||
);
|
||||
)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
@ -1122,62 +1102,62 @@ impl Dispatch for WryDispatcher {
|
||||
// Getters
|
||||
|
||||
fn scale_factor(&self) -> Result<f64> {
|
||||
Ok(window_getter!(self, WindowMessage::ScaleFactor))
|
||||
window_getter!(self, WindowMessage::ScaleFactor)
|
||||
}
|
||||
|
||||
fn inner_position(&self) -> Result<PhysicalPosition<i32>> {
|
||||
window_getter!(self, WindowMessage::InnerPosition)
|
||||
window_getter!(self, WindowMessage::InnerPosition)?
|
||||
}
|
||||
|
||||
fn outer_position(&self) -> Result<PhysicalPosition<i32>> {
|
||||
window_getter!(self, WindowMessage::OuterPosition)
|
||||
window_getter!(self, WindowMessage::OuterPosition)?
|
||||
}
|
||||
|
||||
fn inner_size(&self) -> Result<PhysicalSize<u32>> {
|
||||
Ok(window_getter!(self, WindowMessage::InnerSize))
|
||||
window_getter!(self, WindowMessage::InnerSize)
|
||||
}
|
||||
|
||||
fn outer_size(&self) -> Result<PhysicalSize<u32>> {
|
||||
Ok(window_getter!(self, WindowMessage::OuterSize))
|
||||
window_getter!(self, WindowMessage::OuterSize)
|
||||
}
|
||||
|
||||
fn is_fullscreen(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsFullscreen))
|
||||
window_getter!(self, WindowMessage::IsFullscreen)
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsMaximized))
|
||||
window_getter!(self, WindowMessage::IsMaximized)
|
||||
}
|
||||
|
||||
/// Gets the window’s current decoration state.
|
||||
fn is_decorated(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsDecorated))
|
||||
window_getter!(self, WindowMessage::IsDecorated)
|
||||
}
|
||||
|
||||
/// Gets the window’s current resizable state.
|
||||
fn is_resizable(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsResizable))
|
||||
window_getter!(self, WindowMessage::IsResizable)
|
||||
}
|
||||
|
||||
fn is_visible(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsVisible))
|
||||
window_getter!(self, WindowMessage::IsVisible)
|
||||
}
|
||||
|
||||
fn is_menu_visible(&self) -> Result<bool> {
|
||||
Ok(window_getter!(self, WindowMessage::IsMenuVisible))
|
||||
window_getter!(self, WindowMessage::IsMenuVisible)
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Result<Option<Monitor>> {
|
||||
Ok(window_getter!(self, WindowMessage::CurrentMonitor).map(|m| MonitorHandleWrapper(m).into()))
|
||||
Ok(window_getter!(self, WindowMessage::CurrentMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Result<Option<Monitor>> {
|
||||
Ok(window_getter!(self, WindowMessage::PrimaryMonitor).map(|m| MonitorHandleWrapper(m).into()))
|
||||
Ok(window_getter!(self, WindowMessage::PrimaryMonitor)?.map(|m| MonitorHandleWrapper(m).into()))
|
||||
}
|
||||
|
||||
fn available_monitors(&self) -> Result<Vec<Monitor>> {
|
||||
Ok(
|
||||
window_getter!(self, WindowMessage::AvailableMonitors)
|
||||
window_getter!(self, WindowMessage::AvailableMonitors)?
|
||||
.into_iter()
|
||||
.map(|m| MonitorHandleWrapper(m).into())
|
||||
.collect(),
|
||||
@ -1186,12 +1166,12 @@ impl Dispatch for WryDispatcher {
|
||||
|
||||
#[cfg(target_os = "macos")]
|
||||
fn ns_window(&self) -> Result<*mut std::ffi::c_void> {
|
||||
Ok(window_getter!(self, WindowMessage::NSWindow).0)
|
||||
window_getter!(self, WindowMessage::NSWindow).map(|w| w.0)
|
||||
}
|
||||
|
||||
#[cfg(windows)]
|
||||
fn hwnd(&self) -> Result<HWND> {
|
||||
Ok(window_getter!(self, WindowMessage::Hwnd).0)
|
||||
window_getter!(self, WindowMessage::Hwnd).map(|w| w.0)
|
||||
}
|
||||
|
||||
/// Returns the `ApplicatonWindow` from gtk crate that is used by this window.
|
||||
@ -1203,13 +1183,13 @@ impl Dispatch for WryDispatcher {
|
||||
target_os = "openbsd"
|
||||
))]
|
||||
fn gtk_window(&self) -> Result<gtk::ApplicationWindow> {
|
||||
Ok(window_getter!(self, WindowMessage::GtkWindow).0)
|
||||
window_getter!(self, WindowMessage::GtkWindow).map(|w| w.0)
|
||||
}
|
||||
|
||||
// Setters
|
||||
|
||||
fn center(&self) -> Result<()> {
|
||||
window_getter!(self, WindowMessage::Center)
|
||||
window_getter!(self, WindowMessage::Center)?
|
||||
}
|
||||
|
||||
fn print(&self) -> Result<()> {
|
||||
@ -1462,22 +1442,7 @@ enum WindowHandle {
|
||||
Webview(WebView),
|
||||
Window(Arc<Window>),
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
GLWindow(
|
||||
glutin::ContextWrapper<glutin::PossiblyCurrent, glutin::window::Window>,
|
||||
glow::Context,
|
||||
egui_glow::Painter,
|
||||
egui_tao::epi::EpiIntegration,
|
||||
),
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(target_os = "linux")]
|
||||
GLWindow(
|
||||
Rc<glutin::ContextWrapper<glutin::PossiblyCurrent, glutin::window::Window>>,
|
||||
Rc<glow::Context>,
|
||||
Rc<RefCell<egui_glow::Painter>>,
|
||||
Rc<RefCell<egui_tao::epi::EpiIntegration>>,
|
||||
Rc<AtomicU8>,
|
||||
),
|
||||
GLWindow(egui::GlutinWindowContext),
|
||||
}
|
||||
|
||||
impl fmt::Debug for WindowHandle {
|
||||
@ -1492,7 +1457,7 @@ impl WindowHandle {
|
||||
Self::Webview(w) => w.window(),
|
||||
Self::Window(w) => w,
|
||||
#[cfg(feature = "egui")]
|
||||
Self::GLWindow(w, ..) => w.window(),
|
||||
Self::GLWindow(w) => w.window(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -1501,7 +1466,7 @@ impl WindowHandle {
|
||||
WindowHandle::Window(w) => w.inner_size(),
|
||||
WindowHandle::Webview(w) => w.inner_size(),
|
||||
#[cfg(feature = "egui")]
|
||||
WindowHandle::GLWindow(w, ..) => w.window().inner_size(),
|
||||
WindowHandle::GLWindow(w) => w.window().inner_size(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1901,24 +1866,29 @@ impl Runtime for Wry {
|
||||
}
|
||||
|
||||
#[cfg(feature = "egui")]
|
||||
handle_gl_loop(
|
||||
&event,
|
||||
event_loop,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
windows: windows.clone(),
|
||||
window_event_listeners: &window_event_listeners,
|
||||
global_shortcut_manager: global_shortcut_manager.clone(),
|
||||
global_shortcut_manager_handle: &global_shortcut_manager_handle,
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
menu_event_listeners: &menu_event_listeners,
|
||||
#[cfg(feature = "system-tray")]
|
||||
tray_context: &tray_context,
|
||||
},
|
||||
&web_context,
|
||||
&mut is_focused,
|
||||
);
|
||||
{
|
||||
let prevent_default = egui::handle_gl_loop(
|
||||
&event,
|
||||
event_loop,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
windows: windows.clone(),
|
||||
window_event_listeners: &window_event_listeners,
|
||||
global_shortcut_manager: global_shortcut_manager.clone(),
|
||||
global_shortcut_manager_handle: &global_shortcut_manager_handle,
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
menu_event_listeners: &menu_event_listeners,
|
||||
#[cfg(feature = "system-tray")]
|
||||
tray_context: &tray_context,
|
||||
},
|
||||
&web_context,
|
||||
&mut is_focused,
|
||||
);
|
||||
if prevent_default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
iteration = handle_event_loop(
|
||||
event,
|
||||
event_loop,
|
||||
@ -1957,24 +1927,29 @@ impl Runtime for Wry {
|
||||
|
||||
self.event_loop.run(move |event, event_loop, control_flow| {
|
||||
#[cfg(feature = "egui")]
|
||||
handle_gl_loop(
|
||||
&event,
|
||||
event_loop,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
windows: windows.clone(),
|
||||
window_event_listeners: &window_event_listeners,
|
||||
global_shortcut_manager: global_shortcut_manager.clone(),
|
||||
global_shortcut_manager_handle: &global_shortcut_manager_handle,
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
menu_event_listeners: &menu_event_listeners,
|
||||
#[cfg(feature = "system-tray")]
|
||||
tray_context: &tray_context,
|
||||
},
|
||||
&web_context,
|
||||
&mut is_focused,
|
||||
);
|
||||
{
|
||||
let prevent_default = egui::handle_gl_loop(
|
||||
&event,
|
||||
event_loop,
|
||||
control_flow,
|
||||
EventLoopIterationContext {
|
||||
callback: &mut callback,
|
||||
windows: windows.clone(),
|
||||
window_event_listeners: &window_event_listeners,
|
||||
global_shortcut_manager: global_shortcut_manager.clone(),
|
||||
global_shortcut_manager_handle: &global_shortcut_manager_handle,
|
||||
clipboard_manager: clipboard_manager.clone(),
|
||||
menu_event_listeners: &menu_event_listeners,
|
||||
#[cfg(feature = "system-tray")]
|
||||
tray_context: &tray_context,
|
||||
},
|
||||
&web_context,
|
||||
&mut is_focused,
|
||||
);
|
||||
if prevent_default {
|
||||
return;
|
||||
}
|
||||
}
|
||||
handle_event_loop(
|
||||
event,
|
||||
event_loop,
|
||||
@ -1996,7 +1971,7 @@ impl Runtime for Wry {
|
||||
}
|
||||
}
|
||||
|
||||
struct EventLoopIterationContext<'a> {
|
||||
pub struct EventLoopIterationContext<'a> {
|
||||
callback: &'a mut (dyn FnMut(RunEvent) + 'static),
|
||||
windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
|
||||
window_event_listeners: &'a WindowEventListeners,
|
||||
@ -2081,7 +2056,7 @@ fn handle_user_message(
|
||||
#[cfg(target_os = "macos")]
|
||||
WindowMessage::NSWindow(tx) => tx.send(NSWindow(window.ns_window())).unwrap(),
|
||||
#[cfg(windows)]
|
||||
WindowMessage::Hwnd(tx) => tx.send(Hwnd(window.hwnd() as _)).unwrap(),
|
||||
WindowMessage::Hwnd(tx) => tx.send(Hwnd(HWND(window.hwnd() as _))).unwrap(),
|
||||
#[cfg(any(
|
||||
target_os = "linux",
|
||||
target_os = "dragonfly",
|
||||
@ -2247,167 +2222,18 @@ fn handle_user_message(
|
||||
sender.send(Err(Error::CreateWindow)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "egui")]
|
||||
Message::CreateGLWindow(label, app, native_options, proxy) => {
|
||||
let mut egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
if let WindowHandle::GLWindow(gl_window, gl, painter, integration, ..) =
|
||||
&mut windows.lock().unwrap().get_mut(&id).unwrap().inner
|
||||
{
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut integration = integration.borrow_mut();
|
||||
#[cfg(target_os = "linux")]
|
||||
let mut painter = painter.borrow_mut();
|
||||
integration.on_exit(gl_window.window());
|
||||
painter.destroy(gl);
|
||||
}
|
||||
*egui_id = None;
|
||||
let _ = proxy.send_event(Message::Window(id, WindowMessage::Close));
|
||||
}
|
||||
|
||||
let persistence = egui_tao::epi::Persistence::from_app_name(app.name());
|
||||
let window_settings = persistence.load_window_settings();
|
||||
let window_builder =
|
||||
egui_tao::epi::window_builder(&native_options, &window_settings).with_title(app.name());
|
||||
let gl_window = unsafe {
|
||||
glutin::ContextBuilder::new()
|
||||
.with_depth_buffer(0)
|
||||
.with_srgb(true)
|
||||
.with_stencil_buffer(0)
|
||||
.with_vsync(true)
|
||||
.build_windowed(window_builder, event_loop)
|
||||
.unwrap()
|
||||
.make_current()
|
||||
.unwrap()
|
||||
};
|
||||
let window_id = gl_window.window().id();
|
||||
*egui_id = Some(window_id);
|
||||
|
||||
let gl = unsafe { glow::Context::from_loader_function(|s| gl_window.get_proc_address(s)) };
|
||||
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl.enable(glow::FRAMEBUFFER_SRGB);
|
||||
}
|
||||
|
||||
struct GlowRepaintSignal(EventLoopProxy<Message>, WindowId);
|
||||
|
||||
impl epi::backend::RepaintSignal for GlowRepaintSignal {
|
||||
fn request_repaint(&self) {
|
||||
let _ = self
|
||||
.0
|
||||
.send_event(Message::Window(self.1, WindowMessage::RequestRedraw));
|
||||
}
|
||||
}
|
||||
|
||||
let repaint_signal = std::sync::Arc::new(GlowRepaintSignal(proxy, window_id));
|
||||
|
||||
let painter = egui_glow::Painter::new(&gl, None, "")
|
||||
.map_err(|error| eprintln!("some OpenGL error occurred {}\n", error))
|
||||
.unwrap();
|
||||
|
||||
let integration = egui_tao::epi::EpiIntegration::new(
|
||||
"egui_glow",
|
||||
gl_window.window(),
|
||||
repaint_signal,
|
||||
persistence,
|
||||
app,
|
||||
);
|
||||
|
||||
window_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(window_id, WindowEventListenersMap::default());
|
||||
|
||||
menu_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(window_id, WindowMenuEventListeners::default());
|
||||
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
{
|
||||
windows.lock().expect("poisoned webview collection").insert(
|
||||
window_id,
|
||||
WindowWrapper {
|
||||
label,
|
||||
inner: WindowHandle::GLWindow(gl_window, gl, painter, integration),
|
||||
menu_items: Default::default(),
|
||||
},
|
||||
);
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
let area = unsafe { gl_window.raw_handle() };
|
||||
let integration = Rc::new(RefCell::new(integration));
|
||||
let painter = Rc::new(RefCell::new(painter));
|
||||
let render_flow = Rc::new(AtomicU8::new(1));
|
||||
let gl_window = Rc::new(gl_window);
|
||||
let gl = Rc::new(gl);
|
||||
|
||||
let i = integration.clone();
|
||||
let p = painter.clone();
|
||||
let r = render_flow.clone();
|
||||
let gl_window_ = Rc::downgrade(&gl_window);
|
||||
let gl_ = gl.clone();
|
||||
area.connect_render(move |_, _| {
|
||||
if let Some(gl_window) = gl_window_.upgrade() {
|
||||
let mut integration = i.borrow_mut();
|
||||
let mut painter = p.borrow_mut();
|
||||
let (needs_repaint, mut tex_allocation_data, shapes) =
|
||||
integration.update(gl_window.window());
|
||||
let clipped_meshes = integration.egui_ctx.tessellate(shapes);
|
||||
|
||||
for (id, image) in tex_allocation_data.creations {
|
||||
painter.set_texture(&gl_, id, &image);
|
||||
}
|
||||
|
||||
{
|
||||
let color = integration.app.clear_color();
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl_.disable(glow::SCISSOR_TEST);
|
||||
gl_.clear_color(color[0], color[1], color[2], color[3]);
|
||||
gl_.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
painter.upload_egui_texture(&gl_, &integration.egui_ctx.font_image());
|
||||
painter.paint_meshes(
|
||||
&gl_,
|
||||
gl_window.window().inner_size().into(),
|
||||
integration.egui_ctx.pixels_per_point(),
|
||||
clipped_meshes,
|
||||
);
|
||||
}
|
||||
|
||||
for id in tex_allocation_data.destructions.drain(..) {
|
||||
painter.free_texture(id);
|
||||
}
|
||||
|
||||
{
|
||||
let control_flow = if integration.should_quit() {
|
||||
1
|
||||
} else if needs_repaint {
|
||||
0
|
||||
} else {
|
||||
1
|
||||
};
|
||||
r.store(control_flow, Ordering::Relaxed);
|
||||
}
|
||||
|
||||
integration.maybe_autosave(gl_window.window());
|
||||
}
|
||||
gtk::Inhibit(false)
|
||||
});
|
||||
|
||||
windows.lock().expect("poisoned webview collection").insert(
|
||||
window_id,
|
||||
WindowWrapper {
|
||||
label,
|
||||
inner: WindowHandle::GLWindow(gl_window, gl, painter, integration, render_flow),
|
||||
menu_items: Default::default(),
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
Message::CreateGLWindow(label, app, native_options, proxy) => egui::create_gl_window(
|
||||
event_loop,
|
||||
&windows,
|
||||
&window_event_listeners,
|
||||
&menu_event_listeners,
|
||||
label,
|
||||
app,
|
||||
native_options,
|
||||
proxy,
|
||||
),
|
||||
|
||||
#[cfg(feature = "system-tray")]
|
||||
Message::Tray(tray_message) => match tray_message {
|
||||
@ -2636,42 +2462,14 @@ fn handle_event_loop(
|
||||
|
||||
match event {
|
||||
WryWindowEvent::CloseRequested => {
|
||||
let (tx, rx) = channel();
|
||||
let windows_guard = windows.lock().expect("poisoned webview collection");
|
||||
if let Some(w) = windows_guard.get(&window_id) {
|
||||
let label = w.label.clone();
|
||||
drop(windows_guard);
|
||||
for handler in window_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&window_id)
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.values()
|
||||
{
|
||||
handler(&WindowEvent::CloseRequested {
|
||||
label: label.clone(),
|
||||
signal_tx: tx.clone(),
|
||||
});
|
||||
}
|
||||
callback(RunEvent::CloseRequested {
|
||||
label,
|
||||
signal_tx: tx,
|
||||
});
|
||||
if let Ok(true) = rx.try_recv() {
|
||||
} else {
|
||||
on_window_close(
|
||||
callback,
|
||||
window_id,
|
||||
windows.lock().expect("poisoned webview collection"),
|
||||
control_flow,
|
||||
#[cfg(target_os = "linux")]
|
||||
window_event_listeners,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
on_close_requested(
|
||||
callback,
|
||||
window_id,
|
||||
windows.clone(),
|
||||
control_flow,
|
||||
window_event_listeners,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
}
|
||||
WryWindowEvent::Resized(_) => {
|
||||
if let Some(WindowHandle::Webview(webview)) = windows
|
||||
@ -2726,206 +2524,52 @@ fn handle_event_loop(
|
||||
it
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
fn handle_gl_loop(
|
||||
event: &Event<'_, Message>,
|
||||
_event_loop: &EventLoopWindowTarget<Message>,
|
||||
fn on_close_requested<'a>(
|
||||
callback: &'a mut (dyn FnMut(RunEvent) + 'static),
|
||||
window_id: WindowId,
|
||||
windows: Arc<Mutex<HashMap<WindowId, WindowWrapper>>>,
|
||||
control_flow: &mut ControlFlow,
|
||||
context: EventLoopIterationContext<'_>,
|
||||
_web_context: &WebContextStore,
|
||||
is_focused: &mut bool,
|
||||
) {
|
||||
let EventLoopIterationContext {
|
||||
callback,
|
||||
windows,
|
||||
menu_event_listeners,
|
||||
#[cfg(feature = "system-tray")]
|
||||
tray_context,
|
||||
..
|
||||
} = context;
|
||||
let egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
let mut windows = windows.lock().unwrap();
|
||||
let mut should_quit = false;
|
||||
if let Some(win) = windows.get_mut(&id) {
|
||||
if let WindowHandle::GLWindow(gl_window, gl, painter, integration) = &mut win.inner {
|
||||
let mut redraw = || {
|
||||
if !*is_focused {
|
||||
// On Mac, a minimized Window uses up all CPU: https://github.com/emilk/egui/issues/325
|
||||
// We can't know if we are minimized: https://github.com/rust-windowing/winit/issues/208
|
||||
// But we know if we are focused (in foreground). When minimized, we are not focused.
|
||||
// However, a user may want an egui with an animation in the background,
|
||||
// so we still need to repaint quite fast.
|
||||
std::thread::sleep(std::time::Duration::from_millis(10));
|
||||
}
|
||||
|
||||
let (needs_repaint, mut tex_allocation_data, shapes) =
|
||||
integration.update(gl_window.window());
|
||||
let clipped_meshes = integration.egui_ctx.tessellate(shapes);
|
||||
|
||||
for (id, image) in tex_allocation_data.creations {
|
||||
painter.set_texture(&gl, id, &image);
|
||||
}
|
||||
|
||||
{
|
||||
let color = integration.app.clear_color();
|
||||
unsafe {
|
||||
use glow::HasContext as _;
|
||||
gl.disable(glow::SCISSOR_TEST);
|
||||
gl.clear_color(color[0], color[1], color[2], color[3]);
|
||||
gl.clear(glow::COLOR_BUFFER_BIT);
|
||||
}
|
||||
painter.upload_egui_texture(&gl, &integration.egui_ctx.font_image());
|
||||
painter.paint_meshes(
|
||||
&gl,
|
||||
gl_window.window().inner_size().into(),
|
||||
integration.egui_ctx.pixels_per_point(),
|
||||
clipped_meshes,
|
||||
);
|
||||
|
||||
gl_window.swap_buffers().unwrap();
|
||||
}
|
||||
|
||||
for id in tex_allocation_data.destructions.drain(..) {
|
||||
painter.free_texture(id);
|
||||
}
|
||||
|
||||
{
|
||||
*control_flow = if integration.should_quit() {
|
||||
should_quit = true;
|
||||
glutin::event_loop::ControlFlow::Wait
|
||||
} else if needs_repaint {
|
||||
gl_window.window().request_redraw();
|
||||
glutin::event_loop::ControlFlow::Poll
|
||||
} else {
|
||||
glutin::event_loop::ControlFlow::Wait
|
||||
};
|
||||
}
|
||||
|
||||
integration.maybe_autosave(gl_window.window());
|
||||
};
|
||||
match event {
|
||||
// Platform-dependent event handlers to workaround a winit bug
|
||||
// See: https://github.com/rust-windowing/winit/issues/987
|
||||
// See: https://github.com/rust-windowing/winit/issues/1619
|
||||
glutin::event::Event::RedrawEventsCleared if cfg!(windows) => redraw(),
|
||||
glutin::event::Event::RedrawRequested(_) if !cfg!(windows) => redraw(),
|
||||
glutin::event::Event::WindowEvent {
|
||||
event, window_id, ..
|
||||
} => {
|
||||
if window_id == &id {
|
||||
if let glutin::event::WindowEvent::Focused(new_focused) = event {
|
||||
*is_focused = *new_focused;
|
||||
}
|
||||
|
||||
if let glutin::event::WindowEvent::Resized(physical_size) = event {
|
||||
gl_window.resize(*physical_size);
|
||||
}
|
||||
|
||||
integration.on_event(&event);
|
||||
if integration.should_quit() {
|
||||
should_quit = true;
|
||||
*control_flow = glutin::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
|
||||
gl_window.window().request_redraw();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
window_event_listeners: &WindowEventListeners,
|
||||
menu_event_listeners: MenuEventListeners,
|
||||
) -> Option<WindowWrapper> {
|
||||
let (tx, rx) = channel();
|
||||
let windows_guard = windows.lock().expect("poisoned webview collection");
|
||||
if let Some(w) = windows_guard.get(&window_id) {
|
||||
let label = w.label.clone();
|
||||
drop(windows_guard);
|
||||
for handler in window_event_listeners
|
||||
.lock()
|
||||
.unwrap()
|
||||
.get(&window_id)
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap()
|
||||
.values()
|
||||
{
|
||||
handler(&WindowEvent::CloseRequested {
|
||||
label: label.clone(),
|
||||
signal_tx: tx.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
if should_quit {
|
||||
drop(egui_id);
|
||||
callback(RunEvent::CloseRequested {
|
||||
label,
|
||||
signal_tx: tx,
|
||||
});
|
||||
if let Ok(true) = rx.try_recv() {
|
||||
None
|
||||
} else {
|
||||
on_window_close(
|
||||
callback,
|
||||
id,
|
||||
windows,
|
||||
control_flow,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(dead_code)]
|
||||
#[cfg(feature = "egui")]
|
||||
#[cfg(target_os = "linux")]
|
||||
fn handle_gl_loop(
|
||||
event: &Event<'_, Message>,
|
||||
_event_loop: &EventLoopWindowTarget<Message>,
|
||||
control_flow: &mut ControlFlow,
|
||||
context: EventLoopIterationContext<'_>,
|
||||
_web_context: &WebContextStore,
|
||||
is_focused: &mut bool,
|
||||
) {
|
||||
let EventLoopIterationContext {
|
||||
callback,
|
||||
windows,
|
||||
window_event_listeners,
|
||||
menu_event_listeners,
|
||||
..
|
||||
} = context;
|
||||
let egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
let mut windows = windows.lock().unwrap();
|
||||
let mut should_quit = false;
|
||||
if let Some(win) = windows.get_mut(&id) {
|
||||
if let WindowHandle::GLWindow(gl_window, _gl, _painter, integration, render_flow) =
|
||||
&mut win.inner
|
||||
{
|
||||
let mut integration = integration.borrow_mut();
|
||||
let area = unsafe { gl_window.raw_handle() };
|
||||
match event {
|
||||
glutin::event::Event::MainEventsCleared => {
|
||||
area.queue_render();
|
||||
match render_flow.load(Ordering::Relaxed) {
|
||||
0 => *control_flow = glutin::event_loop::ControlFlow::Poll,
|
||||
1 => *control_flow = glutin::event_loop::ControlFlow::Wait,
|
||||
2 => *control_flow = glutin::event_loop::ControlFlow::Exit,
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
glutin::event::Event::WindowEvent {
|
||||
event, window_id, ..
|
||||
} => {
|
||||
if window_id == &id {
|
||||
if let glutin::event::WindowEvent::Focused(new_focused) = event {
|
||||
*is_focused = *new_focused;
|
||||
}
|
||||
|
||||
if let glutin::event::WindowEvent::Resized(physical_size) = event {
|
||||
gl_window.resize(*physical_size);
|
||||
}
|
||||
|
||||
integration.on_event(event);
|
||||
if integration.should_quit() {
|
||||
should_quit = true;
|
||||
*control_flow = glutin::event_loop::ControlFlow::Wait;
|
||||
}
|
||||
|
||||
gl_window.window().request_redraw();
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if should_quit {
|
||||
drop(egui_id);
|
||||
on_window_close(
|
||||
callback,
|
||||
id,
|
||||
windows,
|
||||
window_id,
|
||||
windows.lock().expect("poisoned webview collection"),
|
||||
control_flow,
|
||||
#[cfg(target_os = "linux")]
|
||||
window_event_listeners,
|
||||
menu_event_listeners.clone(),
|
||||
);
|
||||
menu_event_listeners,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
@ -2936,33 +2580,11 @@ fn on_window_close<'a>(
|
||||
control_flow: &mut ControlFlow,
|
||||
#[cfg(target_os = "linux")] window_event_listeners: &WindowEventListeners,
|
||||
menu_event_listeners: MenuEventListeners,
|
||||
) {
|
||||
if let Some(webview) = windows.remove(&window_id) {
|
||||
) -> Option<WindowWrapper> {
|
||||
#[allow(unused_mut)]
|
||||
let w = if let Some(mut webview) = windows.remove(&window_id) {
|
||||
#[cfg(feature = "egui")]
|
||||
{
|
||||
// Destrooy GL context if its a GLWindow
|
||||
let mut egui_id = EGUI_ID.lock().unwrap();
|
||||
if let Some(id) = *egui_id {
|
||||
if id == window_id {
|
||||
#[cfg(not(target_os = "linux"))]
|
||||
if let WindowHandle::GLWindow(gl_window, gl, mut painter, mut integration, ..) =
|
||||
webview.inner
|
||||
{
|
||||
integration.on_exit(gl_window.window());
|
||||
painter.destroy(&gl);
|
||||
*egui_id = None;
|
||||
}
|
||||
#[cfg(target_os = "linux")]
|
||||
if let WindowHandle::GLWindow(gl_window, gl, painter, integration, ..) = webview.inner {
|
||||
let mut integration = integration.borrow_mut();
|
||||
let mut painter = painter.borrow_mut();
|
||||
integration.on_exit(gl_window.window());
|
||||
painter.destroy(&gl);
|
||||
*egui_id = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
egui::on_window_close(&window_id, &mut webview);
|
||||
|
||||
let is_empty = windows.is_empty();
|
||||
drop(windows);
|
||||
@ -2972,7 +2594,7 @@ fn on_window_close<'a>(
|
||||
if is_empty {
|
||||
let (tx, rx) = channel();
|
||||
callback(RunEvent::ExitRequested {
|
||||
window_label: webview.label,
|
||||
window_label: webview.label.clone(),
|
||||
tx,
|
||||
});
|
||||
|
||||
@ -2984,7 +2606,10 @@ fn on_window_close<'a>(
|
||||
callback(RunEvent::Exit);
|
||||
}
|
||||
}
|
||||
}
|
||||
Some(webview)
|
||||
} else {
|
||||
None
|
||||
};
|
||||
// TODO: tao does not fire the destroyed event properly
|
||||
#[cfg(target_os = "linux")]
|
||||
{
|
||||
@ -3000,6 +2625,7 @@ fn on_window_close<'a>(
|
||||
handler(&WindowEvent::Destroyed);
|
||||
}
|
||||
}
|
||||
w
|
||||
}
|
||||
|
||||
fn center_window(window: &Window, window_size: WryPhysicalSize<u32>) -> Result<()> {
|
||||
|
@ -33,10 +33,10 @@ http-range = "0.1.4"
|
||||
infer = "0.4"
|
||||
|
||||
[target."cfg(windows)".dependencies]
|
||||
webview2-com = "0.9.0"
|
||||
webview2-com = "0.10.0"
|
||||
|
||||
[target."cfg(windows)".dependencies.windows]
|
||||
version = "0.29.0"
|
||||
version = "0.30.0"
|
||||
features = [
|
||||
"Win32_Foundation",
|
||||
]
|
||||
|
@ -105,6 +105,9 @@ pub enum Error {
|
||||
/// Failed to send message to webview.
|
||||
#[error("failed to send message to the webview")]
|
||||
FailedToSendMessage,
|
||||
/// Failed to receive message from webview.
|
||||
#[error("failed to receive message from webview")]
|
||||
FailedToReceiveMessage,
|
||||
/// Failed to serialize/deserialize.
|
||||
#[error("JSON error: {0}")]
|
||||
Json(#[from] serde_json::Error),
|
||||
|
@ -111,6 +111,8 @@ pub struct MacConfig {
|
||||
pub use_bootstrapper: bool,
|
||||
/// Identity to use for code signing.
|
||||
pub signing_identity: Option<String>,
|
||||
/// Provider short name for notarization.
|
||||
pub provider_short_name: Option<String>,
|
||||
/// Path to the entitlements file.
|
||||
pub entitlements: Option<String>,
|
||||
}
|
||||
|
@ -74,6 +74,12 @@ impl FileDialogBuilder {
|
||||
self
|
||||
}
|
||||
|
||||
/// Set the title of the dialog.
|
||||
pub fn set_title(mut self, title: &str) -> Self {
|
||||
self.0 = self.0.set_title(title);
|
||||
self
|
||||
}
|
||||
|
||||
/// Pick one file.
|
||||
pub fn pick_file<F: FnOnce(Option<PathBuf>) + Send + 'static>(self, f: F) {
|
||||
run_dialog!(self.0.pick_file(), f)
|
||||
|
@ -377,7 +377,7 @@ macro_rules! shared_app_impl {
|
||||
label: impl Into<String>,
|
||||
url: WindowUrl,
|
||||
setup: F,
|
||||
) -> crate::Result<()>
|
||||
) -> crate::Result<Window<R>>
|
||||
where
|
||||
F: FnOnce(
|
||||
<R::Dispatcher as Dispatch>::WindowBuilder,
|
||||
@ -395,8 +395,7 @@ macro_rules! shared_app_impl {
|
||||
window_builder,
|
||||
webview_attributes,
|
||||
label,
|
||||
))?;
|
||||
Ok(())
|
||||
))
|
||||
}
|
||||
|
||||
#[cfg(feature = "system-tray")]
|
||||
|
@ -25,6 +25,8 @@ pub struct DialogFilter {
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct OpenDialogOptions {
|
||||
/// The title of the dialog window.
|
||||
pub title: Option<String>,
|
||||
/// The filters of the dialog.
|
||||
#[serde(default)]
|
||||
pub filters: Vec<DialogFilter>,
|
||||
@ -42,6 +44,8 @@ pub struct OpenDialogOptions {
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct SaveDialogOptions {
|
||||
/// The title of the dialog window.
|
||||
pub title: Option<String>,
|
||||
/// The filters of the dialog.
|
||||
#[serde(default)]
|
||||
pub filters: Vec<DialogFilter>,
|
||||
|
@ -451,10 +451,6 @@ impl<R: Runtime> Window<R> {
|
||||
/// Returns the monitor on which the window currently resides.
|
||||
///
|
||||
/// Returns None if current monitor can't be detected.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Unsupported
|
||||
pub fn current_monitor(&self) -> crate::Result<Option<Monitor>> {
|
||||
self
|
||||
.window
|
||||
@ -467,10 +463,6 @@ impl<R: Runtime> Window<R> {
|
||||
/// Returns the primary monitor of the system.
|
||||
///
|
||||
/// Returns None if it can't identify any monitor as a primary one.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Unsupported
|
||||
pub fn primary_monitor(&self) -> crate::Result<Option<Monitor>> {
|
||||
self
|
||||
.window
|
||||
@ -481,10 +473,6 @@ impl<R: Runtime> Window<R> {
|
||||
}
|
||||
|
||||
/// Returns the list of all the monitors available on the system.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Linux:** Unsupported
|
||||
pub fn available_monitors(&self) -> crate::Result<Vec<Monitor>> {
|
||||
self
|
||||
.window
|
||||
@ -506,7 +494,7 @@ impl<R: Runtime> Window<R> {
|
||||
.window
|
||||
.dispatcher
|
||||
.hwnd()
|
||||
.map(|hwnd| hwnd as *mut _)
|
||||
.map(|hwnd| hwnd.0 as *mut _)
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -541,6 +529,7 @@ impl<R: Runtime> Window<R> {
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **macOS:** `None` has no effect.
|
||||
/// - **Linux:** Urgency levels have the same effect.
|
||||
pub fn request_user_attention(
|
||||
&self,
|
||||
request_type: Option<UserAttentionType>,
|
||||
|
191
docs/api/cli.md
191
docs/api/cli.md
@ -1,191 +0,0 @@
|
||||
---
|
||||
id: cli
|
||||
title: CLI
|
||||
---
|
||||
|
||||
import Command from '@theme/Command'
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
|
||||
The tauri.js cli is composed in TypeScript and published as JavaScript.
|
||||
|
||||
## `info`
|
||||
|
||||
<Command name="info" />
|
||||
|
||||
```
|
||||
Description
|
||||
Returns the known state of tauri dependencies and configuration
|
||||
```
|
||||
|
||||
It shows a concise list of information about the environment, Rust, Node.js and their versions as well as some relevant configurations.
|
||||
|
||||
<Alert title="Note" icon="info-alt">
|
||||
This command is pretty helpful when you need to have a quick overview of your application. When requesting some help, it can be useful that you share this report with us.
|
||||
</Alert>
|
||||
|
||||
## `init`
|
||||
|
||||
<Command name="init" />
|
||||
|
||||
```
|
||||
Initializes a Tauri project
|
||||
|
||||
USAGE:
|
||||
cargo tauri init [FLAGS] [OPTIONS] [SUBCOMMAND]
|
||||
|
||||
FLAGS:
|
||||
--ci Skip prompting for values
|
||||
-f, --force Force init to overwrite the src-tauri folder
|
||||
-h, --help Print help information
|
||||
-l, --log Enables logging
|
||||
-V, --version Print version information
|
||||
|
||||
OPTIONS:
|
||||
-A, --app-name <app-name> Name of your Tauri application
|
||||
-d, --directory <directory> Set target directory for init
|
||||
-D, --dist-dir <dist-dir> Web assets location, relative to <project-dir>/src-tauri
|
||||
-P, --dev-path <dev-path> Url of your dev server
|
||||
-t, --tauri-path <tauri-path> Path of the Tauri project to use (relative to the cwd)
|
||||
-W, --window-title <window-title> Window title of your Tauri application
|
||||
|
||||
SUBCOMMANDS:
|
||||
help Print this message or the help of the given subcommand(s)
|
||||
plugin Initialize a Tauri plugin.
|
||||
```
|
||||
|
||||
### `init plugin`
|
||||
|
||||
<Command name="init plugin" />
|
||||
|
||||
```
|
||||
Initializes a Tauri plugin project.
|
||||
|
||||
USAGE:
|
||||
cargo tauri init plugin [FLAGS] [OPTIONS] --name <name>
|
||||
|
||||
FLAGS:
|
||||
-a, --api Initializes a Tauri plugin with TypeScript API.
|
||||
-h, --help Print help information
|
||||
-V, --version Print version information
|
||||
|
||||
OPTIONS:
|
||||
-d, --directory <directory> Set target directory for init
|
||||
-n, --name <name> Name of your Tauri plugin
|
||||
-t, --tauri-path <tauri-path> Path of the Tauri project to use (relative to the cwd)
|
||||
```
|
||||
|
||||
## `dev`
|
||||
|
||||
<Command name="dev" />
|
||||
|
||||
```
|
||||
Tauri dev.
|
||||
|
||||
USAGE:
|
||||
cargo tauri dev [FLAGS] [OPTIONS] [--] [args]...
|
||||
|
||||
ARGS:
|
||||
<args>... Args passed to the binary
|
||||
|
||||
FLAGS:
|
||||
-e, --exit-on-panic Exit on panic
|
||||
-h, --help Print help information
|
||||
--release Run the code in release mode
|
||||
-V, --version Print version information
|
||||
|
||||
OPTIONS:
|
||||
-c, --config <config> config JSON to merge with tauri.conf.json
|
||||
-f, --features <features>... list of cargo features to activate
|
||||
-r, --runner <runner> binary to use to run the application
|
||||
-t, --target <target>... target triple to build against
|
||||
```
|
||||
|
||||
This command will open the WebView in development mode. It makes use of the `build.devPath` property from your `src-tauri/tauri.conf.json` file.
|
||||
|
||||
If you have entered a command to the `build.beforeDevCommand` property, this one will be executed before the `dev` command.
|
||||
|
||||
<a href="/docs/api/config#build">See more about the configuration.</a><br/><br/>
|
||||
|
||||
<Alert title="Troubleshooting" type="warning" icon="alert">
|
||||
|
||||
If you're not using `build.beforeDevCommand`, make sure your `build.devPath` is correct and, if using a development server, that it's started before using this command.
|
||||
</Alert>
|
||||
|
||||
## `deps`
|
||||
|
||||
<Command name="deps update" />
|
||||
|
||||
```sh
|
||||
Description
|
||||
Tauri dependency management script
|
||||
Usage
|
||||
$ tauri deps [install|update]
|
||||
```
|
||||
|
||||
|
||||
## `build`
|
||||
|
||||
<Command name="build" />
|
||||
|
||||
```
|
||||
Tauri build.
|
||||
|
||||
USAGE:
|
||||
cargo tauri build [FLAGS] [OPTIONS]
|
||||
|
||||
FLAGS:
|
||||
-d, --debug Builds with the debug flag
|
||||
-h, --help Print help information
|
||||
-v, --verbose Enables verbose logging
|
||||
-V, --version Print version information
|
||||
|
||||
OPTIONS:
|
||||
-b, --bundle <bundle>... list of bundles to package
|
||||
-c, --config <config> config JSON to merge with tauri.conf.json
|
||||
-f, --features <features>... list of cargo features to activate
|
||||
-r, --runner <runner> binary to use to build the application
|
||||
-t, --target <target>... target triple to build against
|
||||
```
|
||||
|
||||
This command will bundle your application, either in production mode or debug mode if you used the `--debug` flag. It makes use of the `build.distDir` property from your `src-tauri/tauri.conf.json` file.
|
||||
|
||||
If you have entered a command to the `build.beforeBuildCommand` property, this one will be executed before the `build` command.
|
||||
|
||||
<a href="/docs/api/config#build">See more about the configuration.</a>
|
||||
|
||||
## `icon`
|
||||
|
||||
<Command name="icon" />
|
||||
|
||||
```
|
||||
Description
|
||||
Create all the icons you need for your Tauri app.
|
||||
|
||||
Usage
|
||||
$ tauri icon /path/to/icon.png
|
||||
|
||||
Options
|
||||
--help, -h Displays this message
|
||||
--log, -l Logging [boolean]
|
||||
--target, -t Target folder (default: 'src-tauri/icons')
|
||||
--compression, -c Compression type [optipng|zopfli]
|
||||
--ci Runs the script in CI mode
|
||||
```
|
||||
|
||||
This command will generate a set of icons, based on the source icon you've entered. Note that the source icon must be 1240x1240 with transparency.
|
||||
|
||||
## `version`
|
||||
|
||||
<Command name="--version" />
|
||||
|
||||
```
|
||||
Description
|
||||
Returns the current version of tauri
|
||||
```
|
||||
|
||||
This command will show the current version of Tauri.
|
||||
|
||||
## CLI usage
|
||||
|
||||
See more about the usage through this [complete guide](/docs/development/integration).
|
@ -1,435 +0,0 @@
|
||||
---
|
||||
title: Configuration
|
||||
---
|
||||
|
||||
import Properties from '@theme/Properties'
|
||||
import Array from '@theme/Array'
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
The `tauri.conf.json` is a file generated by the `tauri init` command (see <a href="/docs/api/cli#tauri-init">here</a>) that lives in your Tauri application source directory (src-tauri).
|
||||
|
||||
Once generated, you may modify it at will to customize your Tauri application.
|
||||
|
||||
# Platform-specific configuration
|
||||
|
||||
In addition to the JSON defined on the `tauri.conf.json` file, Tauri reads a platform-specific configuration on `tauri.linux.conf.json`, `tauri.windows.conf.json` and `tauri.macos.conf.json` and merges it with the main `tauri.conf.json` configuration.
|
||||
|
||||
# Configuration structure
|
||||
|
||||
`tauri.conf.json` is composed of the following properties:
|
||||
|
||||
## `build`
|
||||
|
||||
<Properties anchorRoot="build" rows={[
|
||||
{property: "distDir", type: "string", description: `The path to the production-ready webpage/webapp directory (either absolute or relative to tauri.conf.json) that will be bundled by Tauri.
|
||||
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
The target directory <em>must</em> contain an index.html file.
|
||||
</div>`},
|
||||
{property: "devPath", type: "string", description: `Can be a path to a folder (either absolute or relative to tauri.conf.json) or a URL (like a live reload server).`},
|
||||
{property: "beforeDevCommand", optional: true, type: "string", description: `A command to run before starting Tauri in dev mode. The PLATFORM, ARCH, FAMILY and PLATFORM_TYPE environment variables are set if you perform conditional compilation.`},
|
||||
{property: "beforeBuildCommand", optional: true, type: "string", description: `A command to run before starting Tauri's build pipeline. The PLATFORM, ARCH, FAMILY and PLATFORM_TYPE environment variables are set if you perform conditional compilation.`},
|
||||
{property: "withGlobalTauri", optional: true, type: "boolean", description: "Enables the API injection to the window.__TAURI__ object. Useful if you're using Vanilla JS instead of importing the API using Rollup or Webpack. Reduces the command security since any external code can access it, so be careful with XSS attacks."}
|
||||
]}/>
|
||||
|
||||
```js title=Example
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
"devPath": "http://localhost:4000",
|
||||
"beforeDevCommand": "npm run dev",
|
||||
"beforeBuildCommand": "npm run build",
|
||||
"withGlobalTauri": false
|
||||
}
|
||||
```
|
||||
|
||||
## `package`
|
||||
|
||||
<Properties anchorRoot="package" rows={[
|
||||
{ property: "productName", optional: true, type: "string", description: `Application name. Defaults to the package name specified in Cargo.toml. The binary name is converted to snake-case on Linux.` },
|
||||
{ property: "version", optional: true, type: "string", description: `Application version. Defaults to the version specified in Cargo.toml.` }
|
||||
]}/>
|
||||
|
||||
## `tauri`
|
||||
|
||||
<Properties anchorRoot="tauri" rows={[
|
||||
{
|
||||
property: "cli", optional: true, type: "CliConfig",
|
||||
child: <Properties anchorRoot="tauri.cli" rows={[
|
||||
{ property: "args", optional: true, type: "CliArg[]", description: `List of args for the command.`, child: <Array type="CliArg" name="arg"><Properties anchorRoot="tauri.cli.args" rows={[
|
||||
{ property: "short", optional: true, type: "string", description: `the short version of the argument, without the preceding hyphen (the "-" character).
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
Any leading hyphen will be stripped, and only the first non hyphen character will be used as the short version.
|
||||
</div>` },
|
||||
{ property: "name", type: "string", description: `The unique argument name.` },
|
||||
{ property: "description", optional: true, type: "string", description: `The argument description which will be shown on the help information. Typically, this is a short (one line) description of the arg.` },
|
||||
{ property: "longDescription", optional: true, type: "string", description: `The argument long description which will be shown on the help information.
|
||||
Typically, this a more detailed (multi-line) message that describes the argument` },
|
||||
{ property: "takesValue", optional: true, type: "boolean", description: `Specifies that the argument takes a value at runtime.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
Values for arguments may be specified in any of the following methods:
|
||||
<ul>
|
||||
<li>Using a space such as <code>-o value</code> or <code>--option value</code></li>
|
||||
<li>Using an equals and no space such as <code>-o=value</code> or <code>--option=value</code></li>
|
||||
<li>Use a short and no space such as <code>-ovalue</code></li>
|
||||
</ul>
|
||||
</div>`
|
||||
},
|
||||
{ property: "index", type: "number", optional: true, description: `The positional argument index, starting at 1.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
The index refers to position according to other positional argument. It does not define position in the argument list as a whole.
|
||||
When utilized with multiple=true, only the last positional argument may be defined as multiple (i.e. the one with the highest index).
|
||||
</div>`
|
||||
},
|
||||
{ property: "multiple", optional: true, type: "boolean", description: `Specifies that the argument may appear more than once.
|
||||
For flags, this results in the number of occurrences of the flag being recorded. For example <code>-ddd</code> or <code>-d -d -d</code> would count as three occurrences.
|
||||
For options, there is a distinct difference in multiple occurrences vs multiple values. For example, <code>--opt val1 val2</code> is one occurrence, but two values. Whereas <code>--opt val1 --opt val2</code> is two occurrences.` },
|
||||
{ property: "possibleValues", optional: true, type: "string[]", description: `Specifies a list of possible values for this argument. At runtime, the CLI verifies that only one of the specified values was used, or fails with an error message.` },
|
||||
{ property: "minValues", optional: true, type: "number", description: `Specifies the minimum number of values for this argument.
|
||||
For example, if you had a -f <file> argument where you wanted at least 2 "files" you would set <code>minValues: 2</code>, and this argument would be satisfied if the user provided, 2 or more values.` },
|
||||
{ property: "maxValues", optional: true, type: "number", description: `Specifies the maximum number of values for this argument.
|
||||
For example, if you had a -f <file> argument where you wanted up to 3 "files" you would set <code>max_values: 3</code>, and this argument would be satisfied if the user provided, 1, 2, or 3 values.` },
|
||||
{ property: "required", optional: true, type: "boolean", description: `Sets whether or not the argument is required by default.
|
||||
"required by default" means it is required, when no other conflicting rules have been evaluated
|
||||
conflicting rules take precedence over being required.` },
|
||||
{ property: "requiredUnless", optional: true, type: "string", description: `Sets an arg that overrides this arg's required setting.<br/>
|
||||
i.e. this arg will be required unless this other argument is present.` },
|
||||
{ property: "requiredUnlessAll", optional: true, type: "string[]", description: `Sets args that override this arg's required setting.<br/>
|
||||
i.e. this arg will be required unless all these other arguments are present.` },
|
||||
{ property: "requiredUnlessOne", optional: true, type: "string[]", description: `Sets args that override this arg's required setting.<br/>
|
||||
i.e. this arg will be required unless at least one of these other arguments are present.` },
|
||||
{ property: "conflictsWith", optional: true, type: "string", description: `Sets a conflicting argument by name
|
||||
i.e. when using this argument, the following argument can't be present and vice versa.` },
|
||||
{ property: "conflictsWithAll", optional: true, type: "string", description: `The same as <code>"conflictsWith"</code> but allows specifying multiple two-way conflicts per argument.` },
|
||||
{ property: "requires", optional: true, type: "string", description: `Sets an argument by name that is required when this one is present.<br/>
|
||||
i.e. when using this argument, the following argument must be present.` },
|
||||
{ property: "requiresAll", optional: true, type: "string[]", description: `Sets multiple arguments by names that are required when this one is present.<br/>
|
||||
i.e. when using this argument, the following arguments must be present.` },
|
||||
{ property: "requiresIf", optional: true, type: "[string, string]", description: `Allows a conditional requirement with the signature <code>[arg: string, value: string]</code>.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
The requirement will only become valid if <code>"arg"</code>'s value equals <code>\${value}</code>.
|
||||
</div>
|
||||
` },
|
||||
{ property: "requiredIf", optional: true, type: "[string, string]", description: `Allows specifying that an argument is required conditionally with the signature <code>[arg: string, value: string]</code>.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
The requirement will only become valid if the <code>"arg"</code>'s value equals <code>\${value}</code>.
|
||||
</div>
|
||||
` },
|
||||
{ property: "requireEquals", optional: true, type: "boolean", description: `Requires that options use the <code>--option=val</code> syntax.<br/>
|
||||
i.e. an equals between the option and associated value.` },
|
||||
]} /></Array> },
|
||||
{ property: "description", optional: true, type: "string", description: `Command description which will be shown on the help information.` },
|
||||
{ property: "longDescription", optional: true, type: "string", description: `Command long description which will be shown on the help information.` },
|
||||
{ property: "beforeHelp", optional: true, type: "string", description: `Adds additional help information to be displayed in addition to auto-generated help.<br/>
|
||||
This information is displayed before the auto-generated help information.<br/>
|
||||
This is often used for header information.` },
|
||||
{ property: "afterHelp", optional: true, type: "string", description: `Adds additional help information to be displayed in addition to auto-generated help.<br/>
|
||||
This information is displayed after the auto-generated help information.<br/>
|
||||
This is often used to describe how to use the arguments, or caveats to be noted.` },
|
||||
{ property: "subcommands", optional: true, type: "{ [name: string]: CliConfig }", description: `List of subcommands of this command.<br/>
|
||||
Subcommands are effectively sub-apps, because they can contain their own arguments, subcommands, usage, etc.<br/>
|
||||
They also function just like the app command, in that they get their own auto generated help and usage.` },
|
||||
]} />
|
||||
},
|
||||
{
|
||||
property: "bundle", type: "object",
|
||||
child: <Properties anchorRoot="tauri.bundle" rows={[
|
||||
{ property: "active", optional: true, type: "boolean", description: `Whether we should build your app with tauri-bundler or plain <code>cargo build</code>.` },
|
||||
{ property: "targets", optional: true, type: "string | string[]", description: `An array of the bundles you want to generate; e.g. ["deb", "app", "msi", "appimage", "dmg"] or the string 'all' to make every supported bundle. By default we bundle everything your target supports (app/dmg on mac, deb/appimage on linux, msi on windows).` },
|
||||
{ property: "identifier", type: "string", description: `A string that uniquely identifies your application, in reverse-DNS form (for example, "com.example.appname" or "io.github.username.project"). For OS X and iOS, this is used as the bundle's CFBundleIdentifier value; for Windows, this is hashed to create an application GUID.` },
|
||||
{ property: "icon", optional: true, type: "string[]", description: `A list of (relative to src-tauri) icon paths to use for your application bundle.` },
|
||||
{ property: "resources", optional: true, type: "string[]", description: `A list of files or directories which will be copied to the resources section of the bundle. Globs are supported.` },
|
||||
{ property: "externalBin", optional: true, type: "string[]", description: `A list of—either absolute or relative—paths to binaries to embed with your application.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
Note that Tauri will look for system-specific binaries following the pattern "binary-name{-target-triple}{.system-extension}". <br/>
|
||||
E.g. you typed "my-binary":
|
||||
<ul>
|
||||
<li>"my-binary-x86_64-pc-windows-msvc.exe" for Windows</li>
|
||||
<li>"my-binary-x86_64-apple-darwin" for macOS</li>
|
||||
<li>"my-binary-x86_64-unknown-linux-gnu" for Linux</li>
|
||||
</ul>
|
||||
so don't forget to provide binaries for <strong>all targeted platforms</strong>.
|
||||
</div>` },
|
||||
{ property: "copyright", optional: true, type: "string", description: `A copyright string associated with your application.` },
|
||||
{ property: "category", optional: true, type: "string", description: `What kind of application this is.
|
||||
Should be one among the following list: <br/>
|
||||
Business, DeveloperTool, Education, Entertainment, Finance, Game, ActionGame, AdventureGame, ArcadeGame, BoardGame, CardGame, CasinoGame, DiceGame, EducationalGame, FamilyGame, KidsGame, MusicGame, PuzzleGame, RacingGame, RolePlayingGame, SimulationGame, SportsGame, StrategyGame, TriviaGame, WordGame, GraphicsAndDesign, HealthcareAndFitness, Lifestyle, Medical, Music, News, Photography, Productivity, Reference, SocialNetworking, Sports, Travel, Utility, Video, Weather.
|
||||
` },
|
||||
{ property: "shortDescription", optional: true, type: "string", description: `A short description of your application.` },
|
||||
{ property: "longDescription", optional: true, type: "string", description: `A longer, multi-line description of the application.` },
|
||||
{ property: "deb", optional: true, type: "object", child: <Properties anchorRoot="tauri.bundle.deb" rows={[
|
||||
{ property: "depends", optional: true, type: "string[]", description: `The list of deb dependencies your application relies on.` },
|
||||
{ property: "useBootstrapper", optional: true, type: "boolean", description: `Enable the <a href="/en/docs/guides/bundler/debian#bootstrapper">boostrapper script</a>.` },
|
||||
{ property: "files", optional: true, type: "{ [path: string]: string }", description: `The files to include on the package. See <a href="/en/docs/guides/bundler/debian#custom-files">the debian guide</a>.` }]} />
|
||||
},
|
||||
{ property: "windows", optional: true, type: "object", child: <Properties anchorRoot="tauri.bundle.windows" rows={[
|
||||
{ property: "digestAlgorithm", optional: true, type: "string", description: `Specifies the file digest algorithm to use for creating file signatures. Required for code signing. SHA-256 is recommended.` },
|
||||
{ property: "certificateThumbprint", optional: true, type: "string[]", description: `Specifies the SHA1 hash of the signing certificate.` },
|
||||
{ property: "timestampUrl", optional: true, type: "string[]", description: `Server to use during timestamping.` },
|
||||
{ property: "wix", optional: true, type: "object", child: <Properties anchorRoot="tauri.bundle.windows.wix" rows={[
|
||||
{ property: "language", optional: true, type: "string", description: `The installer language. See https://docs.microsoft.com/en-us/windows/win32/msi/localizing-the-error-and-actiontext-tables.` },
|
||||
{ property: "template", optional: true, type: "string", description: `A custom .wxs template to use.` },
|
||||
{ property: "fragmentPaths", optional: true, type: "string[]", description: `A list of paths to .wxs files with WiX fragments to use.` },
|
||||
{ property: "componentGroupRefs", optional: true, type: "string[]", description: `The ComponentGroup element ids you want to reference from the fragments.` },
|
||||
{ property: "componentRefs", optional: true, type: "string[]", description: `The Component element ids you want to reference from the fragments.` },
|
||||
{ property: "featureGroupRefs", optional: true, type: "string[]", description: `The FeatureGroup element ids you want to reference from the fragments.` },
|
||||
{ property: "featureRefs", optional: true, type: "string[]", description: `The Feature element ids you want to reference from the fragments.` },
|
||||
{ property: "mergeRefs", optional: true, type: "string[]", description: `The Merge element ids you want to reference from the fragments.` },
|
||||
{ property: "skipWebviewInstall", optional: true, type: "boolean", description: `Disables the Webview2 runtime installation after app install.` },
|
||||
{ property: "license", optional: true, type: "string", description: `The path to the license file to render on the installer. Must be an RTF file, so if a different extension is provided, we convert it to the RTF format.` },
|
||||
{ property: "bannerPath", optional: true, type: "string", description: `Path to a bitmap file to use as the installation user interface banner. This bitmap will appear at the top of all but the first page of the installer. The required dimensions are 493px × 58px.` },
|
||||
{ property: "dialogImagePath", optional: true, type: "string", description: `Path to a bitmap file to use on the installation user interface dialogs. It is used on the welcome and completion dialogs. The required dimensions are 493px × 312px.` }]} />
|
||||
}
|
||||
]} />
|
||||
},
|
||||
{ property: "macOS", optional: true, type: "object", child: <Properties anchorRoot="tauri.bundle.macOS" rows={[
|
||||
{ property: "frameworks", optional: true, type: "string[]", description: `A list of strings indicating any macOS X frameworks that need to be bundled with the application. If a name is used, ".framework" must be omitted and it will look for standard install locations. You may also use a path to a specific framework.` },
|
||||
{ property: "minimumSystemVersion", optional: true, type: "string", description: `A version string indicating the minimum macOS X version that the bundled application supports.` },
|
||||
{ property: "license", optional: true, type: "string", description: `The path to the license file to add to the DMG.` },
|
||||
{ property: "useBootstrapper", optional: true, type: "boolean", description: `Enable the <a href="#bootstrapper">boostrapper script</a>.` },
|
||||
{ property: "exceptionDomain", optional: true, type: "string", description: `Allows your application to communicate with the outside world.
|
||||
<div class="alert alert--info" role="alert" style="margin-top: 10px;">
|
||||
It should be a lowercase, without port and protocol domain name.
|
||||
</div>
|
||||
` },
|
||||
{ property: "signingIdentity", optional: true, type: "string", description: `Identity to use for code signing.` },
|
||||
{ property: "entitlements", optional: true, type: "string", description: `Path to the entitlements file.` },
|
||||
]} /> },
|
||||
]} />
|
||||
},
|
||||
{
|
||||
property: "allowlist", type: "object",
|
||||
child: <Properties anchorRoot="tauri.allowlist" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all API features.` },
|
||||
{
|
||||
property: "fs", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.fs" rows={[
|
||||
{ property: "scope", type: "string[]", description: `A list of glob patterns describing the paths the FS API is allowed to access. Each pattern can start with a variable that resolves to a system base directory. The variables are: \`$AUDIO\`, \`$CACHE\`, \`$CONFIG\`, \`$DATA\`, \`$LOCALDATA\`, \`$DESKTOP\`, \`$DOCUMENT\`, \`$DOWNLOAD\`, \`$EXE\`, \`$FONT\`, \`$HOME\`, \`$PICTURE\`, \`$PUBLIC\`, \`$RUNTIME\`, \`$TEMPLATE\`, \`$VIDEO\`, \`$RESOURCE\`, \`$APP\`, \`$CWD\`.` },
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all file system API features.` },
|
||||
{ property: "readTextFile", optional: true, type: "boolean", description: `Read text file from local filesystem.` },
|
||||
{ property: "readBinaryFile", optional: true, type: "boolean", description: `Read binary file from local filesystem.` },
|
||||
{ property: "writeFile", optional: true, type: "boolean", description: `Write text file to local filesystem.` },
|
||||
{ property: "writeBinaryFile", optional: true, type: "boolean", description: `Write binary file to local filesystem.` },
|
||||
{ property: "readDir", optional: true, type: "boolean", description: `Read directory from local filesystem.` },
|
||||
{ property: "copyFile", optional: true, type: "boolean", description: `Copy file from local filesystem.` },
|
||||
{ property: "createDir", optional: true, type: "boolean", description: `Create directory from local filesystem.` },
|
||||
{ property: "removeDir", optional: true, type: "boolean", description: `Remove directory from local filesystem.` },
|
||||
{ property: "removeFile", optional: true, type: "boolean", description: `Remove file from local filesystem.` },
|
||||
{ property: "renameFile", optional: true, type: "boolean", description: `Rename file from local filesystem.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "window", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.window" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all window API features.` },
|
||||
{ property: "create", optional: true, type: "boolean", description: `Allows dynamic window creation.` },
|
||||
{ property: "center", optional: true, type: "boolean", description: `Allows centering the window.` },
|
||||
{ property: "requestUserAttention", optional: true, type: "boolean", description: `Allows requesting user attention on the window.` },
|
||||
{ property: "setResizable", optional: true, type: "boolean", description: `Allows setting the resizable flag of the window.` },
|
||||
{ property: "setTitle", optional: true, type: "boolean", description: `Allows changing the window title.` },
|
||||
{ property: "maximize", optional: true, type: "boolean", description: `Allows maximizing the window.` },
|
||||
{ property: "unmaximize", optional: true, type: "boolean", description: `Allows unmaximizing the window.` },
|
||||
{ property: "minimize", optional: true, type: "boolean", description: `Allows minimizing the window.` },
|
||||
{ property: "unminimize", optional: true, type: "boolean", description: `Allows unminimizing the window.` },
|
||||
{ property: "show", optional: true, type: "boolean", description: `Allows showing the window.` },
|
||||
{ property: "hide", optional: true, type: "boolean", description: `Allows hiding the window.` },
|
||||
{ property: "close", optional: true, type: "boolean", description: `Allows closing the window.` },
|
||||
{ property: "setDecorations", optional: true, type: "boolean", description: `Allows setting the decorations flag of the window.` },
|
||||
{ property: "setAlwaysOnTop", optional: true, type: "boolean", description: `Allows setting the always_on_top flag of the window.` },
|
||||
{ property: "setSize", optional: true, type: "boolean", description: `Allows setting the window size.` },
|
||||
{ property: "setMinSize", optional: true, type: "boolean", description: `Allows setting the window minimum size.` },
|
||||
{ property: "setMaxSize", optional: true, type: "boolean", description: `Allows setting the window maximum size.` },
|
||||
{ property: "setPosition", optional: true, type: "boolean", description: `Allows changing the position of the window.` },
|
||||
{ property: "setFullscreen", optional: true, type: "boolean", description: `Allows setting the fullscreen flag of the window.` },
|
||||
{ property: "setFocus", optional: true, type: "boolean", description: `Allows focusing the window.` },
|
||||
{ property: "setIcon", optional: true, type: "boolean", description: `Allows changing the window icon.` },
|
||||
{ property: "setSkipTaskbar", optional: true, type: "boolean", description: `Allows setting the skip_taskbar flag of the window.` },
|
||||
{ property: "startDragging", optional: true, type: "boolean", description: `Allows start dragging on the window.` },
|
||||
{ property: "print", optional: true, type: "boolean", description: `Allows opening the system dialog to print the window content.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "shell", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.shell" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all shell API features.` },
|
||||
{ property: "execute", optional: true, type: "boolean", description: `Enable binary execution.` },
|
||||
{ property: "open", optional: true, type: "boolean", description: `Open URL with the user's default application.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "dialog", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.dialog" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all dialog API features.` },
|
||||
{ property: "open", optional: true, type: "boolean", description: `Open dialog window to pick files.` },
|
||||
{ property: "save", optional: true, type: "boolean", description: `Open dialog window to pick where to save files.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "http", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.http" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all HTTP API features.` },
|
||||
{ property: "request", optional: true, type: "boolean", description: `Allows making HTTP requests.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "notification", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.notification" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all notification API features.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "globalShortcut", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.globalShortcut" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all global shortcut API features.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "os", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.os" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all OS API features.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "path", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.path" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all path API features.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "protocol", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.protocol" rows={[
|
||||
{ property: "assetScope", type: "string[]", description: `A list of glob patterns describing the paths the asset protocol is allowed to access. Each pattern can start with a variable that resolves to a system base directory. The variables are: \`$AUDIO\`, \`$CACHE\`, \`$CONFIG\`, \`$DATA\`, \`$LOCALDATA\`, \`$DESKTOP\`, \`$DOCUMENT\`, \`$DOWNLOAD\`, \`$EXE\`, \`$FONT\`, \`$HOME\`, \`$PICTURE\`, \`$PUBLIC\`, \`$RUNTIME\`, \`$TEMPLATE\`, \`$VIDEO\`, \`$RESOURCE\`, \`$APP\`, \`$CWD\`.` },
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all custom protocols.` },
|
||||
{ property: "asset", optional: true, type: "boolean", description: `Enables the `asset` custom protocol, used to access files with streaming support.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "process", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.process" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all process APIs.` },
|
||||
{ property: "relaunch", optional: true, type: "boolean", description: `Enables the relaunch API.` },
|
||||
{ property: "exit", optional: true, type: "boolean", description: `Enables the exit API.` },
|
||||
]}/>
|
||||
},
|
||||
{
|
||||
property: "clipboard", optional: true, type: "object", child: <Properties anchorRoot="tauri.allowlist.clipboard" rows={[
|
||||
{ property: "all", type: "boolean", description: `Use this flag to enable all clipboard APIs.` },
|
||||
{ property: "writeText", optional: true, type: "boolean", description: `Enables the writeText API.` },
|
||||
{ property: "readText", optional: true, type: "boolean", description: `Enables the readText API.` },
|
||||
]}/>
|
||||
},
|
||||
]} />
|
||||
},
|
||||
{
|
||||
property: "windows", type: "WindowConfig[]",
|
||||
child: <Array type="WindowConfig" name="window">
|
||||
<Properties anchorRoot="tauri.windows" rows={[
|
||||
{ property: "label", type: "string", description: `Window id to reference on the codebase.` },
|
||||
{ property: "url", type: "string", description: `URL to load on the webview.` },
|
||||
{ property: "fileDropEnabled", type: "boolean", description: `Whether the file drop handler is enabled or not on the webview. Disabling it is required to use drag and drop on the frontend on Windows.` },
|
||||
{ property: "center", type: "boolean", description: `Show window in the center of the screen.` },
|
||||
{ property: "x", type: "number", description: `The horizontal position of the window's top left corner.` },
|
||||
{ property: "y", type: "number", description: `The vertical position of the window's top left corner.` },
|
||||
{ property: "width", optional: true, type: "number", description: `Initial window width.` },
|
||||
{ property: "height", optional: true, type: "number", description: `Initial window height.` },
|
||||
{ property: "minWidth", type: "number", description: `The minimum window width.` },
|
||||
{ property: "minHeight", type: "number", description: `The minimum window height.` },
|
||||
{ property: "maxWidth", type: "number", description: `The maximum window width.` },
|
||||
{ property: "maxHeight", type: "number", description: `The maximum window height.` },
|
||||
{ property: "resizable", optional: true, type: "boolean", description: `Whether the window is resizable or not..` },
|
||||
{ property: "title", type: "string", description: `Window title.` },
|
||||
{ property: "fullscreen", optional: true, type: "boolean", description: `Whether the window starts as fullscreen or not.` },
|
||||
{ property: "focus", optional: true, type: "boolean", description: `Whether the window will be initially hidden or focused.` },
|
||||
{ property: "transparent", optional: true, type: "boolean", description: `Whether the window is transparent or not.` },
|
||||
{ property: "maximized", optional: true, type: "boolean", description: `Whether the window is maximized or not.` },
|
||||
{ property: "visible", optional: true, type: "boolean", description: `Whether the window is visible or not.` },
|
||||
{ property: "decorations", optional: true, type: "boolean", description: `Whether the window should have borders and bars.` },
|
||||
{ property: "alwaysOnTop", optional: true, type: "boolean", description: `Whether the window should always be on top of other windows.` },
|
||||
{ property: "skipTaskbar", optional: true, type: "boolean", description: `Whether or not the window icon should be added to the taskbar.` },
|
||||
]}/>
|
||||
</Array>
|
||||
},
|
||||
{
|
||||
property: "security", type: "object",
|
||||
child: <Properties anchorRoot="tauri.security" rows={[
|
||||
{ property: "csp", optional: true, type: "string", description: `The Content Security Policy used on production. Also used on dev if `devCsp` is not set.
|
||||
<div class="alert alert--warning" role="alert" style="margin-top: 10px;">
|
||||
This is a really important part of the configuration since it helps you ensure your WebView is secured. See more <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" target="_blank">on Mozilla</a>.
|
||||
</div>` },
|
||||
{ property: "devCsp", optional: true, type: "string", description: `The Content Security Policy used development.
|
||||
<div class="alert alert--warning" role="alert" style="margin-top: 10px;">
|
||||
This is a really important part of the configuration since it helps you ensure your WebView is secured. See more <a href="https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP" target="_blank">on Mozilla</a>.
|
||||
</div>` },
|
||||
]} />
|
||||
},
|
||||
]} />
|
||||
|
||||
<!-- Dirty trick to have an anchor without make it appear in the table of contents -->
|
||||
<div id="bootstrapper"></div>
|
||||
|
||||
<Alert title="bootstrapper script">
|
||||
Instead of launching the app directly, we configure the bundled app to run a script that tries to expose the environment variables to the app; without that you'll have trouble using system CLI apps like Node.js.
|
||||
</Alert>
|
||||
|
||||
```js title=Example
|
||||
"tauri": {
|
||||
"cli": {
|
||||
"description": "Tauri communication example",
|
||||
"longDescription": null,
|
||||
"beforeHelp": null,
|
||||
"afterHelp": null,
|
||||
"args": [{
|
||||
"short": "c",
|
||||
"name": "config",
|
||||
"takesValue": true,
|
||||
"description": "Config path"
|
||||
}, {
|
||||
"short": "t",
|
||||
"name": "theme",
|
||||
"takesValue": true,
|
||||
"description": "App theme",
|
||||
"possibleValues": ["light", "dark", "system"]
|
||||
}, {
|
||||
"short": "v",
|
||||
"name": "verbose",
|
||||
"multipleOccurrences": true,
|
||||
"description": "Verbosity level"
|
||||
}],
|
||||
"subcommands": {
|
||||
"update": {
|
||||
"description": "Updates the app",
|
||||
"longDescription": null,
|
||||
"beforeHelp": null,
|
||||
"afterHelp": null,
|
||||
"args": [{
|
||||
"short": "b",
|
||||
"name": "background",
|
||||
"description": "Update in background"
|
||||
}],
|
||||
"subcommands": null
|
||||
}
|
||||
}
|
||||
},
|
||||
"bundle": {
|
||||
"active": true,
|
||||
"targets": ["deb"],
|
||||
"identifier": "com.tauri.dev",
|
||||
"icon": ["icons/32x32.png", "icons/128x128.png", "icons/128x128@2x.png", "icons/icon.icns", "icons/icon.ico"],
|
||||
"resources": [],
|
||||
"externalBin": [],
|
||||
"copyright": "",
|
||||
"category": "DeveloperTool",
|
||||
"shortDescription": "",
|
||||
"longDescription": "",
|
||||
"deb": {
|
||||
"depends": []
|
||||
},
|
||||
"macOS": {
|
||||
"frameworks": [],
|
||||
"minimumSystemVersion": "",
|
||||
"exceptionDomain": ""
|
||||
}
|
||||
},
|
||||
"allowlist": {
|
||||
"all": true
|
||||
},
|
||||
"windows": [{
|
||||
"title": "Tauri App",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}],
|
||||
"security": {
|
||||
"csp": "default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self'"
|
||||
}
|
||||
}
|
||||
```
|
@ -1,80 +0,0 @@
|
||||
---
|
||||
id: ci-cd
|
||||
title: CI/CD
|
||||
---
|
||||
|
||||
## Continuous Integration
|
||||
|
||||
Github Actions has two triggers of which we make heavy use: `push` and `pull_request`. Every commit that made to the repo is a `push`. When you open a pull request from a branch (call it `great_feature`) to another branch (our working branch, `dev`), each commit to `great_feature` would possibly trigger both of these events. We can use a filter to focus on the events we care about though. In our workflows, we only PR (pull request) the `dev` and `master` branches. This means that if we filter to only the `dev` and `master` branches on commit, we will only run that workflow when we _merge_ a PR. A merged PR typically only occurs once a day or less so this will be a good fit for the longer running tests, e.g. the smoke tests in our case. Below is how that might look.
|
||||
|
||||
Unit tests:
|
||||
|
||||
```yml
|
||||
# these run fast so we can have them run on any commit
|
||||
name: unit tests
|
||||
on:
|
||||
pull_request:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
```
|
||||
|
||||
Smoke tests:
|
||||
|
||||
```yml
|
||||
# these run slower so we run only on merges to dev or master branch
|
||||
name: smoke tests
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- dev
|
||||
- master
|
||||
```
|
||||
|
||||
Tauri operates off the `dev` branch as default, and merges to `master` for release. With these Github Actions set up, we will run the unit tests on every commit to an open PR (see `pull_request`). When that PR is merged into `dev`, we will run both the unit tests and the smoke tests.
|
||||
|
||||
## Continuous Deployment
|
||||
|
||||
### Introduction to immutable checksum
|
||||
|
||||
It is not only possible, but trivial to modify release notes and artifacts after it has been published on Github. While there are very valid reasons for doing this, it is not exactly a totally trustworthy method - i.e. you have no guarantee that what you are reading is really reflective of the underlying truth or the tarballs. It is technically possible to change downloads over the wire or in the box or change checksums in targeted attacks. What we are seeking to accomplish is a best case scenario where:
|
||||
|
||||
1. Human error is reduced to a minimum, but humans are still integral in the actual release
|
||||
2. Machine built assets, changelogs and attached security audits are verifiable with checksums that are published in an immutable, globally available store.
|
||||
|
||||
To this end we fashioned a workflow shown below. As it stands now, we have #3 through #6 implemented. We manually do #2 which then feeds into #3 and kicks off the rest of the automatic workflow.
|
||||
|
||||
1. a human pushes to dev through a pull request (can happen any number of times)
|
||||
- pull request includes a changeset file describing the change and required version bump
|
||||
2. a pull request is created (or updated) to include the change and version bump
|
||||
- this pull request stays open and will be force pushed until it gets merged (and published)
|
||||
- increase the version number based on changesets
|
||||
- delete all changeset files
|
||||
3. a codeowner merges the publish PR to dev (no direct push permissible for anyone)
|
||||
- all tests (unit, e2e, smoke tests) are run on the PR
|
||||
- failures prevent the publish so they must pass before merge
|
||||
4. merge to dev triggers release sequence
|
||||
- changes are squashed and a PR is opened against master
|
||||
5. when PR to master is merged...
|
||||
- vulnerability audit (crates and yarn) and output saved
|
||||
- checksums and metadata and output saved
|
||||
- packages are published on npm/cargo, tarball/zip created
|
||||
- release is created for each package that had updates (if version isn't changed, build skips the publish steps)
|
||||
- output from audit/checksums is piped into the release body
|
||||
- tarball / zip attached to release
|
||||
- async process to publish to IOTA tangle (feeless) via release tag [note: still have things to resolve here]
|
||||
6. release is complete
|
||||
- master has updated code and tagged
|
||||
- GitHub release has tarballs, checksums, and changelog (may have multiple releases if more than one package published) [note: is part of step 2 and is not yet implemented]
|
||||
|
||||
### Next Steps
|
||||
|
||||
Next steps may include transferring and publishing the built assets to additional places:
|
||||
|
||||
1. Tauri's private verdaccio
|
||||
2. IPFS
|
||||
3. PureOS Gitlab
|
||||
4. GitHub Packages
|
||||
|
||||
We can also do some interesting things like signing our releases, including a hash in the release and/or even publishing this information on a blockchain that it can be easily verified. Publishing on the blockchain is another avenue to increase the confidence that what is seen on GitHub matches what you have downloaded. The IOTA foundation created a Github Action which will publish a release to their blockchain. This has shown promise, but he gave a couple errors to tackle still.
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Cross-Platform Compilation
|
||||
---
|
||||
|
||||
How to use GH Action for Building: a glance at Tauri Action.
|
@ -1,60 +0,0 @@
|
||||
---
|
||||
title: Debugging
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Command from '@theme/Command'
|
||||
|
||||
With all the moving pieces in Tauri, you may run into a problem that requires debugging. There are a handful of locations where error details are printed, and Tauri includes some tools to make the debugging process easier.
|
||||
|
||||
## Rust Console
|
||||
|
||||
When you run a Tauri app in development mode you will have a Rust console available. This is in the terminal where you ran e.g. `tauri dev`. You can use the following code to print something to that console from within a Rust file:
|
||||
|
||||
```rust
|
||||
println!("Message from Rust: {}", msg);
|
||||
```
|
||||
|
||||
Sometimes you may have an error in your Rust code, and the Rust compiler can give you lots of information. If, for example, `tauri dev` crashes, you can rerun it like this on Linux and macOS:
|
||||
|
||||
```sh
|
||||
RUST_DEBUG=1 tauri dev
|
||||
```
|
||||
|
||||
or like this on MS Windows:
|
||||
|
||||
```sh
|
||||
set RUST_DEBUG=1
|
||||
tauri dev
|
||||
```
|
||||
|
||||
This will give you a granular stack trace. Generally speaking, the Rust compiler will help you by
|
||||
giving you detailed information about the issue, such as:
|
||||
|
||||
```
|
||||
error[E0425]: cannot find value `sun` in this scope
|
||||
--> src/main.rs:11:5
|
||||
|
|
||||
11 | sun += i.to_string().parse::<u64>().unwrap();
|
||||
| ^^^ help: a local variable with a similar name exists: `sum`
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0425`.
|
||||
```
|
||||
|
||||
## WebView JS Console
|
||||
|
||||
Right click in the WebView, and choose `Inspect Element`. This will open up a web-inspector similar to the Chrome or Firefox dev tools you are used to.
|
||||
|
||||
## Create a Debug Build
|
||||
|
||||
There are cases where you might need to inspect the JS console in the final bundle, so Tauri provides a simple command to create a debugging bundle:
|
||||
|
||||
<Command name="build --debug" />
|
||||
|
||||
Like the normal build and dev processes, the first time you run this it will take more time than subsequent runs. The final bundled app will be placed in `src-tauri/target/debug/bundle`. That app will ship with the development console enabled.
|
||||
|
||||
## Run Your App From the Terminal
|
||||
|
||||
You can also run a built app from the terminal, which will also give you the Rust compiler notes (in case of errors) or your `println` messages. Just find the file `src-tauri/target/(release|debug)/[app name]` and either double click it (but be warned, the terminal will close on errors) or just run it in directly in your console.
|
@ -1,26 +0,0 @@
|
||||
---
|
||||
title: Development Cycle
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Command from '@theme/Command'
|
||||
|
||||
### 1. Start Your Devserver
|
||||
|
||||
Now that you have everything setup, you should start your application development server provided by your UI framework or bundler (assuming you're using one, of course).
|
||||
|
||||
<Alert title="Note">
|
||||
Every framework has its own development tooling. It is outside of the scope of this document to treat them all or keep them up to date.
|
||||
</Alert>
|
||||
|
||||
### 2. Start Tauri Development Window
|
||||
|
||||
<Command name="dev" />
|
||||
|
||||
The first time you run this command, it will take several minutes for the Rust package manager to download and build all the required packages. Since they are cached, subsequent builds will be much faster, as only your code will need rebuilding.
|
||||
|
||||
Once Rust has finished building, the webview will open and it should display your web app. You can make changes to your web app, and if your tooling enables it, the webview should update automatically just like a browser. When you make changes to your Rust files, they will be rebuilt automatically and your app will restart.
|
||||
|
||||
<Alert title="A note about Cargo.toml and Source Control" icon="info-alt">
|
||||
In your project repository, you SHOULD commit the "src-tauri/Cargo.lock" along with the "src-tauri/Cargo.toml" to git because Cargo uses the lockfile to provide deterministic builds. As a result, it is recommended that all applications check in their Cargo.lock. You SHOULD NOT commit the "src-tauri/target" folder or any of its contents.
|
||||
</Alert>
|
@ -1,165 +0,0 @@
|
||||
---
|
||||
title: Integrate with Tauri
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Command from '@theme/Command'
|
||||
import Link from '@docusaurus/Link'
|
||||
|
||||
<Alert title="Please note" type="warning" icon="alert">
|
||||
You must have completed all the steps required for setting up the development environment on your machine. If you haven't done this yet, please see the <a href="/docs/get-started/intro#setting-up-your-environment"> setup page for your operating system</a>.
|
||||
</Alert>
|
||||
|
||||
There are two ways to integrate with Tauri depends on your need:
|
||||
- [Start a new Tauri project](#1-start-a-new-tauri-project)
|
||||
- Or [add Tauri to existing project](#1-add-tauri-to-existing-project)
|
||||
|
||||
### 1. Start a New Tauri Project
|
||||
|
||||
```bash
|
||||
yarn create tauri-app
|
||||
#OR
|
||||
npx create-tauri-app
|
||||
```
|
||||
|
||||
Just follow the instructions and choose the web frontend framework you prefer. `create-tauri-app` will create a template project depends on your inputs. You can go straight to [check `tauri info`](#3-check-tauri-info-to-make-sure-everything-is-set-up-properly) after this.
|
||||
|
||||
### 1. Add Tauri to Existing Project:
|
||||
|
||||
The Tauri CLI tool helps you build your project, so install it at first.
|
||||
|
||||
You can install Tauri CLI [using `Node.js`](#install-tauri-cli-package-as-a-dev-dependency) or [using `Rust`](#alternatively-install-tauri-cli-as-a-cargo-subcommand)
|
||||
|
||||
#### Install Tauri CLI package as a dev dependency:
|
||||
|
||||
```bash
|
||||
cd project-folder
|
||||
|
||||
# Not required if you already have a package.json:
|
||||
# yarn init
|
||||
# OR
|
||||
# npm init
|
||||
|
||||
yarn add -D @tauri-apps/cli
|
||||
# OR
|
||||
npm install -D @tauri-apps/cli
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
You can install Tauri as both a local and a global dependency, but we recommend installing it locally.
|
||||
</Alert>
|
||||
|
||||
If you decide to use Tauri as a local package with npm (not yarn), you will have to define a custom script to your package.json:
|
||||
|
||||
```js title=package.json
|
||||
{
|
||||
// This content is just a sample
|
||||
"scripts": {
|
||||
"tauri": "tauri"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### Alternatively, install Tauri CLI as a cargo subcommand:
|
||||
|
||||
This will install `tauri-cli` as a Cargo subcommand on the cargo binary folder (by default on `$HOME/.cargo/bin`):
|
||||
|
||||
```bash
|
||||
cargo install tauri-cli --version ^1.0.0-beta
|
||||
```
|
||||
|
||||
For more installation options, see [`cargo install`](https://doc.rust-lang.org/cargo/commands/cargo-install.html#description)
|
||||
|
||||
#### Install Tauri API Package as a Dependency (optional):
|
||||
|
||||
The `@tauri-apps/api` package is recommended for projects using ES modules or modern build tools such as Webpack or Vite. It is the most secure way to access the Tauri APIs.
|
||||
|
||||
```bash
|
||||
yarn add @tauri-apps/api
|
||||
# OR
|
||||
npm install @tauri-apps/api
|
||||
```
|
||||
|
||||
### 2. Initialize Tauri in Your App
|
||||
|
||||
<Command name="init" />
|
||||
|
||||
This command will place a new folder in your current working directory, `src-tauri`.
|
||||
|
||||
```sh
|
||||
└── src-tauri
|
||||
├── .gitignore
|
||||
├── Cargo.toml
|
||||
├── rustfmt.toml
|
||||
├── tauri.conf.json
|
||||
├── icons
|
||||
│ ├── 128x128.png
|
||||
│ ├── 128x128@2x.png
|
||||
│ ├── 32x32.png
|
||||
│ ├── Square107x107Logo.png
|
||||
│ ├── Square142x142Logo.png
|
||||
│ ├── Square150x150Logo.png
|
||||
│ ├── Square284x284Logo.png
|
||||
│ ├── Square30x30Logo.png
|
||||
│ ├── Square310x310Logo.png
|
||||
│ ├── Square44x44Logo.png
|
||||
│ ├── Square71x71Logo.png
|
||||
│ ├── Square89x89Logo.png
|
||||
│ ├── StoreLogo.png
|
||||
│ ├── icon.icns
|
||||
│ ├── icon.ico
|
||||
│ └── icon.png
|
||||
└── src
|
||||
├── build.rs
|
||||
├── cmd.rs
|
||||
└── main.rs
|
||||
```
|
||||
|
||||
### 3. Check `tauri info` to Make Sure Everything Is Set up Properly:
|
||||
|
||||
<Command name="info" />
|
||||
|
||||
Which should return something like:
|
||||
|
||||
```
|
||||
Operating System - Darwin(16.7.0) - darwin/x64
|
||||
|
||||
Node.js environment
|
||||
Node.js - 12.16.3
|
||||
@tauri-apps/cli - 1.0.0-beta.2
|
||||
@tauri-apps/api - 1.0.0-beta.1
|
||||
|
||||
Global packages
|
||||
npm - 6.14.4
|
||||
yarn - 1.22.4
|
||||
|
||||
Rust environment
|
||||
rustc - 1.52.1
|
||||
cargo - 1.52.0
|
||||
|
||||
App directory structure
|
||||
/node_modules
|
||||
/src-tauri
|
||||
/src
|
||||
/public
|
||||
|
||||
App
|
||||
tauri.rs - 1.0.0-beta.1
|
||||
build-type - bundle
|
||||
CSP - default-src blob: data: filesystem: ws: wss: http: https: tauri: 'unsafe-eval' 'unsafe-inline' 'self' img-src: 'self'
|
||||
distDir - ../public
|
||||
devPath - ../public
|
||||
framework - Svelte
|
||||
bundler - Rollup
|
||||
```
|
||||
|
||||
This information can be very helpful when triaging problems.
|
||||
|
||||
### Patterns
|
||||
|
||||
We've also defined prebuilt configurations called "Patterns". They may help you to customize Tauri to fit your needs.
|
||||
[See more about patterns](/docs/guides/patterns/about-patterns).
|
||||
|
||||
## Vue CLI Plugin Tauri
|
||||
|
||||
If you are using Vue CLI, it is recommended to use the official [CLI plugin](https://github.com/tauri-apps/vue-cli-plugin-tauri).
|
@ -1,19 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
This part of the documentation is dedicated to learning how to use Tauri.
|
||||
|
||||
Tauri provides a [CLI](/docs/api/cli), a Rust API, and a [JavaScript API](/docs/api/js/index) that you can use in your project. Because raw docs can be quite scary to newcomers (especially people who have never played with Rust before), we've created this "learn by example" section.
|
||||
|
||||
Here you will find guides and techniques to start a new project or add to your own project in order to fulfill your goals.
|
||||
|
||||
## Tauri Development Workflow
|
||||
|
||||
- [Integrate With Tauri](/docs/development/integration)
|
||||
- [Development Cycle](/docs/development/development)
|
||||
- [Debugging](/docs/development/debugging)
|
||||
- [CI/CD](/docs/development/ci-cd)
|
||||
- [Cross-Platform Compilation](/docs/development/cross-platform)
|
||||
- [App Publishing](/docs/development/publishing)
|
||||
- [Updating Packages](/docs/development/updating)
|
@ -1,22 +0,0 @@
|
||||
---
|
||||
title: App Publishing
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Command from '@theme/Command'
|
||||
|
||||
### 1. Build Your Web App
|
||||
|
||||
Now that you are ready to package your project, you will need to run your framework's or bundler's build command (assuming you're using one, of course).
|
||||
|
||||
<Alert title="Note">
|
||||
Every framework has its own publishing tooling. It is outside of the scope of this document to treat them all or keep them up to date.
|
||||
</Alert>
|
||||
|
||||
### 2. Bundle your application with Tauri
|
||||
|
||||
<Command name="build" />
|
||||
|
||||
This command will embed your web assets into a single binary with your Rust code. The binary itself will be located in `src-tauri/target/release/[app name]`, and installers will be located in `src-tauri/target/release/bundle/`.
|
||||
|
||||
Like the `tauri dev` command, the first time you run this, it will take some time to collect the Rust crates and build everything - but on subsequent runs it will only need to rebuild your code, which is much quicker.
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Signing for macOS
|
||||
---
|
||||
|
||||
Signing for macOS
|
@ -1,42 +0,0 @@
|
||||
---
|
||||
title: Updating Packages
|
||||
---
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
<Alert title="Please note" type="warning" icon="alert">
|
||||
Especially during the alpha and beta phases, you are expected to keep all Tauri dependencies and toolchains up to date. There is no support for any versions other than latest.
|
||||
</Alert>
|
||||
|
||||
## Automatic updates
|
||||
|
||||
The Tauri JS CLI has a command to install and update all needed dependencies, just run `tauri deps install` or `tauri deps update`.
|
||||
|
||||
## Manual updates
|
||||
|
||||
### Update NPM Packages
|
||||
|
||||
If you are using the `tauri` package:
|
||||
```bash
|
||||
$ yarn upgrade @tauri-apps/cli @tauri-apps/api --latest
|
||||
$ npm install @tauri-apps/cli@latest @tauri-apps/api@latest
|
||||
```
|
||||
You can also detect what the latest version of Tauri is on the command line, using:
|
||||
- `npm outdated @tauri-apps/cli`
|
||||
- `yarn outdated @tauri-apps/cli`
|
||||
|
||||
Alternatively, if you are using the `vue-cli-plugin-tauri` approach:
|
||||
```bash
|
||||
$ yarn upgrade vue-cli-plugin-tauri --latest
|
||||
$ npm install vue-cli-plugin-tauri@latest
|
||||
```
|
||||
|
||||
### Update Cargo Packages
|
||||
Go to `src-tauri/Cargo.toml` and change `tauri` to
|
||||
`tauri = { version = "%version%" }` where `%version%` is the version number shown above. (You can just use the `MAJOR.MINOR`) version, like `0.9`.
|
||||
|
||||
Then do the following:
|
||||
```bash
|
||||
$ cd src-tauri
|
||||
$ cargo update -p tauri
|
||||
```
|
||||
You can also run `cargo outdated -r tauri` to get direct information about the core library's latest version.
|
18
docs/faq.md
18
docs/faq.md
@ -1,18 +0,0 @@
|
||||
---
|
||||
title: Frequently Asked Questions
|
||||
---
|
||||
|
||||
# error: could not find native static libraryWebView2LoaderStatic, perhaps an -L flag is missing?
|
||||
|
||||
The WebView2 crate build pipeline requires `NuGet` to have a `PackageSource` to install the `Microsoft.Web.WebView2` package. If you never used `NuGet` before, you might need to create a file named `NuGet.Config` on `%APPDATA%/NuGet` folder, with the following contents:
|
||||
|
||||
```
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<configuration>
|
||||
<packageSources>
|
||||
<add key="nuget.org" value="https://api.nuget.org/v3/index.json" protocolVersion="3" />
|
||||
</packageSources>
|
||||
</configuration>
|
||||
```
|
||||
|
||||
This configuration enables the default `NuGet` registry.
|
@ -1,47 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
import OSList from '@theme/OSList'
|
||||
|
||||
Welcome to Tauri!
|
||||
|
||||
Tauri is a polyglot and generic system that is very composable and allows engineers to make a wide variety of applications. It is used for building applications for Desktop Computers using a combination of [Rust](https://www.rust-lang.org/) tools and HTML rendered in a Webview. Apps built with Tauri can ship with any number of pieces of an optional JS API / Rust API so that webviews can control the system via message passing.
|
||||
|
||||
Anything that can be displayed on a website, can be displayed in a Tauri webview app!
|
||||
|
||||
Developers are free to build the web front-end displayed in a Webview through Tauri with any web frameworks of their choice!
|
||||
**Developers can even extend the default API** with their own functionality and bridge the Webview and Rust-based backend easily!
|
||||
|
||||
The Architecture is more fully described in [Architecture](/docs/about/architecture).
|
||||
|
||||
This guide will help you create your first Tauri app. It should only take about 10 minutes, although it could take longer if you have a slower internet connection.
|
||||
|
||||
If you find an error or something unclear, or would like to propose an improvement, you have several options:
|
||||
|
||||
1. Open an issue on our [Github Repo](https://github.com/tauri-apps/tauri-docs)
|
||||
2. Visit our [Discord server](https://discord.gg/tauri) and raise your concern
|
||||
3. Request to join the education working group on Discord to gain access to its discussion channel
|
||||
|
||||
## Steps
|
||||
|
||||
1. Install and configure system prerequisites
|
||||
2. Create a web app with your frontend framework of choice
|
||||
3. Use the Tauri CLI to setup Tauri in your app
|
||||
4. Write native Rust code to add functionality or improve performance (totally optional)
|
||||
5. Use `tauri dev` to develop your app with features like hot module reloading and webview devtools
|
||||
6. Use `tauri build` to package your app into a tiny installer
|
||||
|
||||
### Setting up Your Environment
|
||||
|
||||
Before creating an app, you'll have to install and configure some developer tools. This guide assumes that you know what the command line is, how to install packages on your operating system, and generally know your way around the development side of computing.
|
||||
|
||||
Follow the platform-specific guides to get started:
|
||||
|
||||
<OSList content={{
|
||||
linux: { title: 'Linux Setup', link: '/docs/get-started/setup-linux'},
|
||||
macos: { title: 'macOS Setup', link: '/docs/get-started/setup-macos'},
|
||||
windows: { title: 'Windows Setup', link: '/docs/get-started/setup-windows'}
|
||||
}} />
|
||||
|
||||
After that, you'll be ready to [add Tauri to your project!](/docs/development/integration)
|
@ -1,149 +0,0 @@
|
||||
---
|
||||
title: Setup for Linux
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Icon from '@theme/Icon'
|
||||
import { Intro } from '@theme/SetupDocs'
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
<Intro />
|
||||
|
||||
## 1. System Dependencies <Icon title="alert" color="danger"/>
|
||||
|
||||
<Tabs
|
||||
defaultValue="debian"
|
||||
values={[
|
||||
{label: 'Debian', value: 'debian'},
|
||||
{label: 'Arch', value: 'arch'},
|
||||
{label: 'Fedora', value: 'fedora'},
|
||||
]}>
|
||||
<TabItem value="debian">
|
||||
|
||||
```sh
|
||||
$ sudo apt update && sudo apt install libwebkit2gtk-4.0-dev \
|
||||
build-essential \
|
||||
curl \
|
||||
wget \
|
||||
libssl-dev \
|
||||
libgtk-3-dev \
|
||||
libappindicator3-dev \
|
||||
patchelf \
|
||||
librsvg2-dev
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="arch">
|
||||
|
||||
```sh
|
||||
$ sudo pacman -Syu && sudo pacman -S --needed \
|
||||
webkit2gtk \
|
||||
base-devel \
|
||||
curl \
|
||||
wget \
|
||||
openssl \
|
||||
appmenu-gtk-module \
|
||||
gtk3 \
|
||||
libappindicator-gtk3 \
|
||||
patchelf \
|
||||
librsvg \
|
||||
libvips
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="fedora">
|
||||
|
||||
```sh
|
||||
$ sudo dnf check-update && sudo dnf install webkit2gtk3-devel.x86_64 \
|
||||
openssl-devel \
|
||||
curl \
|
||||
wget \
|
||||
libappindicator-gtk3 \
|
||||
patchelf \
|
||||
librsvg2-devel \
|
||||
&& sudo dnf group install "C Development Tools and Libraries"
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Optional dependencies:
|
||||
|
||||
- `libappindicator`: needed to use the system tray feature.
|
||||
- `patchelf` and `librsvg`: needed to bundle `AppImage`.
|
||||
|
||||
## 2. Node.js Runtime and Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
### Node.js (npm included)
|
||||
|
||||
We recommend using nvm to manage your Node.js runtime. It allows you to easily switch versions and update Node.js.
|
||||
|
||||
```sh
|
||||
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
We have audited this bash script, and it does what it says it is supposed to do. Nevertheless, before blindly curl-bashing a script, it is always wise to look at it first. Here is the file as a mere <a href="https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh" target="_blank">download link</a>.
|
||||
</Alert>
|
||||
|
||||
Once nvm is installed, close and reopen your terminal, then install the latest version of Node.js and npm:
|
||||
|
||||
```sh
|
||||
$ nvm install node --latest-npm
|
||||
$ nvm use node
|
||||
```
|
||||
|
||||
If you have any problems with nvm, please consult their <a href="https://github.com/nvm-sh/nvm">project readme</a>.
|
||||
|
||||
### Optional Node.js Package Manager
|
||||
|
||||
You may want to use an alternative to npm:
|
||||
|
||||
- <a href="https://yarnpkg.com/get-started" target="_blank">Yarn</a>, is preferred by Tauri's team
|
||||
- <a href="https://pnpm.js.org/en/installation" target="_blank">pnpm</a>
|
||||
|
||||
## 3. Rustc and Cargo Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
The following command will install <a href="https://rustup.rs/" target="_blank">rustup</a>, the official installer for <a href="https://www.rust-lang.org/" target="_blank">Rust</a>.
|
||||
|
||||
```bash
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
We have audited this bash script, and it does what it says it is supposed to do. Nevertheless, before blindly curl-bashing a script, it is always wise to look at it first. Here is the file as a mere <a href="https://sh.rustup.rs" target="_blank">download link</a>.
|
||||
</Alert>
|
||||
|
||||
To make sure that Rust has been installed successfully, run the following command:
|
||||
|
||||
```sh
|
||||
$ rustc --version
|
||||
latest update on 2019-12-19, rust version 1.40.0
|
||||
```
|
||||
|
||||
You may need to restart your terminal if the command does not work.
|
||||
|
||||
## 4. For Windows Subsystem for Linux (WSL) Users <Icon title="info-alt" color="info"/>
|
||||
|
||||
In order to run a graphical application with WSL, you need to download **one** of these X servers: Xming, Cygwin X, and vcXsrv.
|
||||
Since vcXsrv has been used internally, it's the one we recommend to install.
|
||||
|
||||
### WSL Version 1
|
||||
|
||||
Open the X server and then run `export DISPLAY=:0` in the terminal. You should now be able to run any graphical application via the terminal.
|
||||
|
||||
### WSL Version 2
|
||||
|
||||
You'll need to run a command that is slightly more complex than WSL 1: `export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | awk '{print $2}'):0` and you need to add `-ac` to the X server as an argument. Note: if for some reason this command doesn't work you can use an alternative command such as: `export DISPLAY=$(cat /etc/resolv.conf | grep nameserver | sed 's/.* //g'):0` or you can manually find the Address using `cat /etc/resolve.conf | grep nameserver`.
|
||||
|
||||
<Alert type="info" title="Note">
|
||||
|
||||
Don't forget that you'll have to use the "export" command anytime you want to use a graphical application, for each newly opened terminal.
|
||||
|
||||
You can download some examples to try with `sudo apt-get install x11-apps`. xeyes is always a good one. It can be handy when troubleshooting WSL issues.
|
||||
</Alert>
|
||||
|
||||
## Continue
|
||||
|
||||
Now that you have set up the Linux-specific dependencies for Tauri, learn how to [add Tauri to your project](/docs/development/integration).
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Setup for macOS
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import { Intro } from '@theme/SetupDocs'
|
||||
import Icon from '@theme/Icon'
|
||||
|
||||
<Intro />
|
||||
|
||||
## 1. System Dependencies <Icon title="alert" color="danger"/>
|
||||
|
||||
|
||||
You will need to have <a href="https://brew.sh/" target="_blank">Homebrew</a> installed to run the following command.
|
||||
|
||||
```sh
|
||||
$ brew install gcc
|
||||
```
|
||||
|
||||
You will also need to make sure `xcode` is installed.
|
||||
|
||||
```sh
|
||||
$ xcode-select --install
|
||||
```
|
||||
|
||||
## 2. Node.js Runtime and Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
### Node.js (npm included)
|
||||
|
||||
We recommend using nvm to manage your Node.js runtime. It allows you to easily switch versions and update Node.js.
|
||||
|
||||
```sh
|
||||
$ curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh | bash
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
We have audited this bash script, and it does what it says it is supposed to do. Nevertheless, before blindly curl-bashing a script, it is always wise to look at it first. Here is the file as a mere <a href="https://raw.githubusercontent.com/nvm-sh/nvm/v0.35.2/install.sh" target="_blank">download link</a>.
|
||||
</Alert>
|
||||
|
||||
Once nvm is installed, close and reopen your terminal, then install the latest version of Node.js and npm:
|
||||
|
||||
```sh
|
||||
$ nvm install node --latest-npm
|
||||
$ nvm use node
|
||||
```
|
||||
|
||||
If you have any problems with nvm, please consult their <a href="https://github.com/nvm-sh/nvm">project readme</a>.
|
||||
|
||||
### Optional Node.js Package Manager
|
||||
|
||||
You may want to use an alternative to npm:
|
||||
|
||||
- <a href="https://yarnpkg.com/get-started" target="_blank">Yarn</a>, is preferred by Tauri's team
|
||||
- <a href="https://pnpm.js.org/en/installation" target="_blank">pnpm</a>
|
||||
|
||||
## 3. Rustc and Cargo Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
The following command will install <a href="https://rustup.rs/" target="_blank">rustup</a>, the official installer for <a href="https://www.rust-lang.org/" target="_blank">Rust</a>.
|
||||
|
||||
```
|
||||
$ curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
We have audited this bash script, and it does what it says it is supposed to do. Nevertheless, before blindly curl-bashing a script, it is always wise to look at it first. Here is the file as a mere <a href="https://sh.rustup.rs" target="_blank">download link</a>.
|
||||
</Alert>
|
||||
|
||||
To make sure that Rust has been installed successfully, run the following command:
|
||||
|
||||
```sh
|
||||
$ rustc --version
|
||||
latest update on 2019-12-19, rust version 1.40.0
|
||||
```
|
||||
|
||||
You may need to restart your terminal if the command does not work.
|
||||
|
||||
## Continue
|
||||
|
||||
Now that you have set up the macOS-specific dependencies for Tauri, learn how to [add Tauri to your project](/docs/development/integration).
|
@ -1,75 +0,0 @@
|
||||
---
|
||||
title: Setup for Windows
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import Icon from '@theme/Icon'
|
||||
import { Intro } from '@theme/SetupDocs'
|
||||
|
||||
<Alert title="Note">
|
||||
|
||||
For those using the Windows Subsystem for Linux (WSL) please refer to our [Linux specific instructions](/docs/get-started/setup-linux) instead.
|
||||
</Alert>
|
||||
|
||||
<Intro />
|
||||
|
||||
## 1. System Dependencies <Icon title="alert" color="danger"/>
|
||||
|
||||
You'll need to install Microsoft Visual Studio C++ build tools. <a href="https://visualstudio.microsoft.com/visual-cpp-build-tools/" target="_blank">Download the installer here</a>, and then run it. When it asks you what packages you would like to install, select C++ Build Tools and make sure the Windows SDK is selected.
|
||||
|
||||
<Alert title="Note">
|
||||
This is a big download (over 1GB) and takes the most time, so go grab a coffee.
|
||||
</Alert>
|
||||
|
||||
<Alert type="warning">
|
||||
You may need to uninstall the 2017 version of the build tools if you have them. There are reports of Tauri not working with both the 2017 and 2019 versions installed.
|
||||
</Alert>
|
||||
|
||||
## 2. Node.js Runtime and Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
### Node.js (npm included)
|
||||
|
||||
We recommend using <a href="https://github.com/coreybutler/nvm-windows#installation--upgrades" target="_blank">nvm-windows</a> to manage your Node.js runtime. It allows you to easily switch versions and update Node.js.
|
||||
|
||||
Then run the following from an Administrative PowerShell and press Y when prompted:
|
||||
|
||||
```powershell
|
||||
# BE SURE YOU ARE IN AN ADMINISTRATIVE PowerShell!
|
||||
nvm install latest
|
||||
nvm use {{latest}} # Replace with your latest downloaded version
|
||||
```
|
||||
|
||||
This will install the most recent version of Node.js with npm.
|
||||
|
||||
### Optional Node.js Package Manager
|
||||
|
||||
You may want to use an alternative to npm:
|
||||
|
||||
- <a href="https://yarnpkg.com/get-started" target="_blank">Yarn</a>, is preferred by Tauri's team
|
||||
- <a href="https://pnpm.js.org/en/installation" target="_blank">pnpm</a>
|
||||
|
||||
## 3. Rustc and Cargo Package Manager <Icon title="control-skip-forward" color="warning"/>
|
||||
|
||||
Now you will need to install <a href="https://www.rust-lang.org/" target="_blank">Rust</a>. The easiest way to do this is to use <a href="https://rustup.rs/" target="_blank">rustup</a>, the official installer.
|
||||
|
||||
- <a href="https://win.rustup.rs/x86_64" target="_blank">64-bit download link</a>
|
||||
- <a href="https://win.rustup.rs/i686" target="_blank">32-bit download link</a>
|
||||
|
||||
Download and install the proper variant for your computer's architecture.
|
||||
|
||||
|
||||
## 4. Install WebView2
|
||||
|
||||
<Alert title="Note">
|
||||
WebView2 is pre-installed in Windows 11.
|
||||
</Alert>
|
||||
|
||||
Finally, you will need to install WebView2. The best way to do this is to download and run the Evergreen Bootstrapper from [this page](https://developer.microsoft.com/en-us/microsoft-edge/webview2/#download-section).
|
||||
|
||||
<Alert type="Note">
|
||||
If you have problems of any kind after following these instructions, we recommend that you reboot your computer before developing a Tauri project to ensure that everything works as expected.
|
||||
</Alert>
|
||||
|
||||
## Continue
|
||||
|
||||
Now that you have set up the Windows-specific dependencies for Tauri, learn how to [add Tauri to your project](/docs/development/integration).
|
@ -1,107 +0,0 @@
|
||||
---
|
||||
title: Anti Bloat
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
The following links have tutorials on reducing the size of your installers:
|
||||
|
||||
- https://github.com/RazrFalcon/cargo-bloat
|
||||
- https://lifthrasiir.github.io/rustlog/why-is-a-rust-executable-large.html
|
||||
- https://doc.rust-lang.org/cargo/reference/manifest.html#the-profile-sections
|
||||
|
||||
### Rust Compression Features
|
||||
|
||||
Add this to your `src-tauri/Cargo.toml`
|
||||
|
||||
[profile.release]
|
||||
panic = "abort"
|
||||
codegen-units = 1
|
||||
lto = true
|
||||
incremental = false
|
||||
opt-level = "s"
|
||||
|
||||
<Alert title="Note">
|
||||
|
||||
There is also `opt-level = "z"` available to try to reduce the resulting binary size. `"s"` and `"z"` can sometimes be smaller than the other, so test it with your own application!
|
||||
|
||||
We've seen smaller binary sizes from `"s"` for Tauri example applications, but real world applications can always differ.
|
||||
</Alert>
|
||||
|
||||
#### Unstable Rust Compression Features
|
||||
|
||||
<Alert type="warning" title="Warning" icon="alert">
|
||||
The following suggestions are all unstable features and require a nightly toolchain. See the <a href="https://doc.rust-lang.org/cargo/reference/unstable.html#unstable-features">Unstable Features</a> documentation for more information of what this involves.
|
||||
</Alert>
|
||||
|
||||
The following methods involve using unstable compiler features and require having a rust nightly toolchain installed. If you don't have the nightly toolchain + `rust-src` nightly component added, try the following:
|
||||
|
||||
$ rustup toolchain install nightly
|
||||
$ rustup component add rust-src --toolchain nightly
|
||||
|
||||
The Rust Standard Library comes precompiled. You can instead apply the optimization options used for the rest of your binary + dependencies to the std with an unstable flag. This flag requires specifying your target, so know the target triple that you are targeting.
|
||||
|
||||
$ cargo +nightly build --release -Z build-std --target x86_64-unknown-linux-gnu
|
||||
|
||||
If you are using `panic = "abort"` in your release profile optimizations, then you need to make sure the `panic_abort` crate is compiled with std. Additionally, an extra std feature can be used to further reduce the binary size. The following applies both:
|
||||
|
||||
$ cargo +nightly build --release -Z build-std=std,panic_abort -Z build-std-features=panic_immediate_abort --target x86_64-unknown-linux-gnu
|
||||
|
||||
See the unstable documentation for more details about [`-Z build-std`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std) and [`-Z build-std-features`](https://doc.rust-lang.org/cargo/reference/unstable.html#build-std-features).
|
||||
|
||||
### Stripping
|
||||
|
||||
Binary size can easily be reduced by stripping out debugging information from binaries that ship to end users. This is not good for debuggable builds, but means good binary size savings for end user binaries. The easiest way is to use the famous `strip` utility to remove this debugging information.
|
||||
|
||||
$ strip target/release/my_application
|
||||
|
||||
See your local `strip` manpage for more information and flags that can be used to specify what information gets stripped out from the binary.
|
||||
|
||||
### Allowlist config
|
||||
|
||||
You can also reduce the application size with the `allowlist` config, and only enabling what you need. Sometimes this is useful with Tauri's [Bridge-Pattern](/docs/guides/patterns/bridge) or others, depending on needs.
|
||||
|
||||
For example in `tauri.conf.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false,
|
||||
"fs": {
|
||||
"writeFile": true,
|
||||
"writeBinaryFile": true
|
||||
},
|
||||
"shell": {
|
||||
"execute": true
|
||||
},
|
||||
"dialog": {
|
||||
"save": true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### UPX
|
||||
|
||||
UPX, **Ultimate Packer for eXecutables**, is a dinosaur amongst the binary packers. This 23-year old, well-maintained piece of kit is GPL-v2 licensed with a pretty liberal usage declaration. Our understanding of the licensing is that you can use it for any purposes (commercial or otherwise) without needing to change your license unless you modify the source code of UPX.
|
||||
|
||||
Basically it compresses the binary and decompresses it at runtime. It should work for pretty much any binary type out there. Read more: https://github.com/upx/upx
|
||||
|
||||
<Alert type="warning" title="Warning" icon="alert">
|
||||
You should know that this technique might flag your binary as a virus on Windows and macOS - so use at your own discretion, and as always validate with [Frida](https://frida.re/docs/home/) and do real distribution testing!
|
||||
</Alert>
|
||||
|
||||
#### Usage on macOS
|
||||
|
||||
$ brew install upx
|
||||
$ yarn tauri build
|
||||
$ upx --ultra-brute src-tauri/target/release/bundle/macos/app.app/Contents/macOS/app
|
||||
Ultimate Packer for eXecutables
|
||||
Copyright (C) 1996 - 2018
|
||||
UPX 3.95 Markus Oberhumer, Laszlo Molnar & John Reiser Aug 26th 2018
|
||||
|
||||
File size Ratio Format Name
|
||||
-------------------- ------ ----------- -----------
|
||||
963140 -> 274448 28.50% macho/amd64 app
|
@ -1,34 +0,0 @@
|
||||
---
|
||||
title: Debian packages
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
Tauri allows your app to be packaged as a `.deb` (Debian package) file.
|
||||
|
||||
# Bootstrapper
|
||||
|
||||
Instead of launching the app directly, you can configure the bundled app to run a script that tries to expose the environment variables to the app; without that you'll have trouble using system programs because the `PATH` environment variable isn't correct. Enable it with the <a href="/docs/api/config#tauri.bundle.deb.useBootstrapper">`useBootstrapper`</a> config.
|
||||
|
||||
# Custom files
|
||||
|
||||
To include custom files to the debian package, you can configure a mapping on `tauri.conf.json > tauri > bundle > deb > files` as follows:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"deb": {
|
||||
"files": {
|
||||
"/usr/lib/README.md": "../README.md", // copies the README.md file to /usr/lib/README.md
|
||||
"usr/lib/assets": "../public/" // copies the entire public directory to /usr/lib/assets
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Alert title="Note" icon="info-alt">
|
||||
Each `files` object key is the path on the debian package, and the value is a path to a file or directory relative to the `tauri.conf.json` file.
|
||||
</Alert>
|
@ -1,11 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
---
|
||||
|
||||
The Tauri Bundler is a Rust harness for compiling your binary, packaging assets, and preparing a final bundle.
|
||||
|
||||
It will detect your operating system and build a bundle accordingly. It currently supports:
|
||||
|
||||
- Linux: .deb, .appimage
|
||||
- macOS: .app, .dmg
|
||||
- Windows: .exe, .msi
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: Sidecar (Embedding External Binaries)
|
||||
sidebar_label: Sidecar
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
You may need to embed depending binaries in order to make your application work or to prevent users having to install additional dependencies (e.g. Node.js, Python, etc).
|
||||
|
||||
To bundle the binaries of your choice, you can add the `externalBin` property to the `tauri > bundle` object in your `tauri.conf.json`.
|
||||
|
||||
See more about tauri.conf.json configuration <a href="/docs/api/config#tauri.bundle">here</a>.
|
||||
|
||||
`externalBin` expects a list of strings targeting binaries either with absolute or relative paths.
|
||||
|
||||
Here is a sample to illustrate the configuration, this is not a complete `tauri.conf.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"externalBin": ["/absolute/path/to/app", "relative/path/to/binary", "bin/python"]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
A binary with the same name and a `-$TARGET_TRIPLE` suffix must exist on the specified path. For instance, `"externalBin": ["bin/python"]` requires a `src-tauri/bin/python-x86_64-unknown-linux-gnu` executable on Linux. You can find the current platform's target triple running the following command:
|
||||
|
||||
```bash
|
||||
rustc -Vv | grep host | cut -f2 -d' '
|
||||
```
|
||||
|
||||
Here's a Node.js script to append the target triple to a binary:
|
||||
|
||||
```javascript
|
||||
const execa = require('execa')
|
||||
const fs = require('fs')
|
||||
|
||||
let extension = ''
|
||||
if (process.platform === 'win32') {
|
||||
extension = '.exe'
|
||||
}
|
||||
|
||||
async function main() {
|
||||
const rustInfo = (await execa('rustc', ['-vV'])).stdout
|
||||
const targetTriple = /host: (\S+)/g.exec(rustInfo)[1]
|
||||
if (!targetTriple) {
|
||||
console.error('Failed to determine platform target triple')
|
||||
}
|
||||
fs.renameSync(
|
||||
`src-tauri/binaries/app${extension}`,
|
||||
`src-tauri/binaries/app-${targetTriple}${extension}`
|
||||
)
|
||||
}
|
||||
|
||||
main().catch((e) => {
|
||||
throw e
|
||||
})
|
||||
|
||||
```
|
||||
|
||||
## Running the sidecar binary on JavaScript
|
||||
|
||||
On the JavaScript code, import the `Command` class on the `shell` module and use the `sidecar` static method:
|
||||
|
||||
```javascript
|
||||
import { Command } from '@tauri-apps/api/shell'
|
||||
// alternatively, use `window.__TAURI__.shell.Command`
|
||||
// `my-sidecar` is the value specified on `tauri.conf.json > tauri > bundle > externalBin`
|
||||
const command = Command.sidecar('my-sidecar')
|
||||
const output = await command.execute()
|
||||
```
|
||||
|
||||
## Running the sidecar binary on Rust
|
||||
|
||||
On the Rust code, import the `Command` struct from the `tauri::api::process` module:
|
||||
|
||||
```rust
|
||||
let (mut rx, mut child) = Command::new_sidecar("my-sidecar")
|
||||
.expect("failed to create `my-sidecar` binary command")
|
||||
.spawn()
|
||||
.expect("Failed to spawn sidecar");
|
||||
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// read events such as stdout
|
||||
while let Some(event) = rx.recv().await {
|
||||
if let CommandEvent::Stdout(line) = event {
|
||||
window
|
||||
.emit("message", Some(format!("'{}'", line)))
|
||||
.expect("failed to emit event");
|
||||
// write to stdin
|
||||
child.write("message from Rust\n".as_bytes()).unwrap();
|
||||
}
|
||||
}
|
||||
});
|
||||
```
|
||||
|
||||
## Using Node.js on a sidecar
|
||||
|
||||
The Tauri [sidecar example](https://github.com/tauri-apps/tauri/tree/dev/examples/sidecar) demonstrates how to use the sidecar API to run a Node.js application on Tauri.
|
||||
It compiles the Node.js code using [pkg](https://github.com/vercel/pkg) and uses the scripts above to run it.
|
@ -1,104 +0,0 @@
|
||||
---
|
||||
title: How to code-sign and notorize a OSX .dmg file with GitHub Actions
|
||||
sidebar_label: OSX Code-signing with GitHub Actions
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
## Intro
|
||||
|
||||
Tauri has a smooth code-signing & notarization functionality built directly into the bundler and configured via the `tauri.conf.json`
|
||||
|
||||
This guide will give a brief overview of how to sign an application, and how to get the app notarized with Apple. All in a GitHub action.
|
||||
|
||||
## Prerequisits
|
||||
- OSX - This will be needed to create/export the certificate.
|
||||
- [Apple Developer Program](https://developer.apple.com/programs/) subscription
|
||||
- [Developer ID Application](https://developer.apple.com/developer-id/) certificate
|
||||
- see [this](https://localazy.com/blog/how-to-automatically-sign-macos-apps-using-github-actions#reference) guide for additional help
|
||||
- Working Tauri application, being built and published via GitHub Actions, as shown in [tauri-action](https://github.com/tauri-apps/tauri-action)
|
||||
|
||||
<Alert title="Note" icon="info-alt">
|
||||
If you are not utilizing GitHub Actions to perform builds of OSX DMGs, you will need to ensure the environment variable `CI=true` exists. For more information refer to [Issue #592](https://github.com/tauri-apps/tauri/issues/592).
|
||||
</Alert>
|
||||
|
||||
## GitHub Secrets
|
||||
|
||||
We will need to add a few GitHub secrets for the proper configuration of the GitHub Action. These can be named however you would like, but we must assign them to the correct Tauri variables, so keep them as relevant as possible.
|
||||
- You can view [this](https://docs.github.com/en/actions/reference/encrypted-secrets) guide for how to add GitHub secrets.
|
||||
|
||||
The secrets I used are as follows
|
||||
|
||||
| GitHub Secrets | Value for Variable |
|
||||
| :---: | :---: |
|
||||
|APPLE_CERTIFICATE| Base64 encoded version of your .p12 certificate. You can find a guide [here](https://localazy.com/blog/how-to-automatically-sign-macos-apps-using-github-actions#reference)|
|
||||
|APPLE_CERTIFICATE_PASSWORD|Certificate password used on creation of certificate|
|
||||
|APPLE_IDENTITY_ID|"Developer ID Application: Your Company, Inc (XXXXXXXXX)" shown in your keychain. you can also use `security find-identity -v -p codesigning` on OSX to find this identity |
|
||||
|APPLE_ID|this is the email used to request the certificate|
|
||||
APPLE_PASSWORD|This is an app-specific password, that must also be created by the same account used to request the certificate. Guide [here](https://support.apple.com/en-ca/HT204397)|
|
||||
|
||||
Once we have established the GitHub Secrets we will need to make some modifications to our GitHub publish action in `.github/workflows/main.yml`
|
||||
|
||||
|
||||
---
|
||||
### Workflow Modifications
|
||||
|
||||
All we will have to do from here is assign the GitHub secrets to the proper environment variables.
|
||||
```
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
```
|
||||
|
||||
If you are using the tauri-action publish template, then your result should look similar the the `env:` portion below.
|
||||
```
|
||||
name: "publish"
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- release
|
||||
|
||||
jobs:
|
||||
publish-tauri:
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
platform: [macos-latest, ubuntu-latest, windows-latest]
|
||||
|
||||
runs-on: ${{ matrix.platform }}
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: setup node
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 12
|
||||
- name: install Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
- name: install webkit2gtk (ubuntu only)
|
||||
if: matrix.platform == 'ubuntu-latest'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y webkit2gtk-4.0
|
||||
- name: install app dependencies and build it
|
||||
run: yarn && yarn build
|
||||
- uses: tauri-apps/tauri-action@v0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
ENABLE_CODE_SIGNING: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE: ${{ secrets.APPLE_CERTIFICATE }}
|
||||
APPLE_CERTIFICATE_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
|
||||
APPLE_SIGNING_IDENTITY: ${{ secrets.APPLE_IDENTITY_ID }}
|
||||
APPLE_ID: ${{ secrets.APPLE_ID }}
|
||||
APPLE_PASSWORD: ${{ secrets.APPLE_PASSWORD }}
|
||||
with:
|
||||
tagName: app-v__VERSION__ # the action automatically replaces \_\_VERSION\_\_ with the app version
|
||||
releaseName: "App v__VERSION__"
|
||||
releaseBody: "See the assets to download this version and install."
|
||||
releaseDraft: true
|
||||
prerelease: false
|
||||
```
|
@ -1,162 +0,0 @@
|
||||
---
|
||||
title: Make your own CLI
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
Tauri enables your app to have a CLI through <a href="https://github.com/clap-rs/clap" target="_blank">clap</a>, a robust command line argument parser. With a simple CLI definition in your `tauri.conf.json` file, you can define your interface and read its argument matches map on JavaScript and/or Rust.
|
||||
|
||||
## Base Configuration
|
||||
|
||||
Under `tauri.conf.json`, you have the following structure to configure the interface:
|
||||
|
||||
```js title=src-tauri/tauri.conf.json
|
||||
{
|
||||
"tauri": {
|
||||
"cli": {
|
||||
"description": "", // command description that's shown on help
|
||||
"longDescription": "", // command long description that's shown on help
|
||||
"beforeHelp": "", // content to show before the help text
|
||||
"afterHelp": "", // content to show after the help text
|
||||
"args": [], // list of arguments of the command, we'll explain it later
|
||||
"subcommands": {
|
||||
"subcommand-name": {
|
||||
// configures a subcommand that is accessible
|
||||
// with `$ ./app subcommand-name --arg1 --arg2 --etc`
|
||||
// configuration as above, with "description", "args", etc.
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Alert title="Note">
|
||||
All JSON configurations here are just samples, many other fields have been omitted for the sake of clarity.
|
||||
</Alert>
|
||||
|
||||
## Adding Arguments
|
||||
|
||||
The `args` array represents the list of arguments accepted by its command or subcommand. You can find more details about the way to configure them <a href="/docs/api/config#tauri">here</a>.
|
||||
|
||||
### Positional Arguments
|
||||
|
||||
A positional argument is identified by its position in the list of arguments. With the following configuration:
|
||||
|
||||
```json title=src-tauri/tauri.conf.json:tauri.cli
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "source",
|
||||
"index": 1,
|
||||
"takesValue": true
|
||||
},
|
||||
{
|
||||
"name": "destination",
|
||||
"index": 2,
|
||||
"takesValue": true
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Users can run your app as `$ ./app tauri.txt dest.txt` and the arg matches map will define `source` as `"tauri.txt"` and `destination` as `"dest.txt"`.
|
||||
|
||||
### Named Arguments
|
||||
|
||||
A named argument is a (key, value) pair where the key identifies the value. With the following configuration:
|
||||
|
||||
```json title=src-tauri/tauri.conf.json:tauri.cli
|
||||
{
|
||||
"args": [
|
||||
{
|
||||
"name": "type",
|
||||
"short": "t",
|
||||
"takesValue": true,
|
||||
"multiple": true,
|
||||
"possibleValues": ["foo", "bar"]
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Users can run your app as `$ ./app --type foo bar`, `$ ./app -t foo -t bar` or `$ ./app --type=foo,bar` and the arg matches map will define `type` as `["foo", "bar"]`.
|
||||
|
||||
### Flag Arguments
|
||||
|
||||
A flag argument is a standalone key whose presence or absence provides information to your application. With the following configuration:
|
||||
|
||||
```js title=src-tauri/tauri.conf.json:tauri.cli
|
||||
{
|
||||
"args": [
|
||||
"name": "verbose",
|
||||
"short": "v",
|
||||
"multipleOccurrences": true
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
Users can run your app as `$ ./app -v -v -v`, `$ ./app --verbose --verbose --verbose` or `$ ./app -vvv` and the arg matches map will define `verbose` as `true`, with `occurrences = 3`.
|
||||
|
||||
## Subcommands
|
||||
|
||||
Some CLI applications has additional interfaces as subcommands. For instance, the `git` CLI has `git branch`, `git commit` and `git push`. You can define additional nested interfaces with the `subcommands` array:
|
||||
|
||||
```js title=src-tauri/tauri.conf.json:tauri
|
||||
{
|
||||
"cli": {
|
||||
...
|
||||
"subcommands": {
|
||||
"branch": {
|
||||
"args": []
|
||||
},
|
||||
"push": {
|
||||
"args": []
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Its configuration is the same as the root application configuration, with the `description`, `longDescription`, `args`, etc.
|
||||
|
||||
## Reading the matches
|
||||
|
||||
### Rust
|
||||
|
||||
```rust
|
||||
use tauri::api::cli::get_matches;
|
||||
|
||||
fn main() {
|
||||
let context = tauri::generate_context!();
|
||||
let cli_config = context.config().tauri.cli.clone().unwrap();
|
||||
|
||||
match get_matches(&cli_config) {
|
||||
// `matches` here is a Struct with { args, subcommand }.
|
||||
// `args` is `HashMap<String, ArgData>` where `ArgData` is a struct with { value, occurances }.
|
||||
// `subcommand` is `Option<Box<SubcommandMatches>>` where `SubcommandMatches` is a struct with { name, matches }.
|
||||
Ok(matches) => {
|
||||
println!("{:?}", matches)
|
||||
}
|
||||
Err(_) => {}
|
||||
};
|
||||
|
||||
tauri::Builder::default()
|
||||
.run(context)
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
```js
|
||||
import { getMatches } from '@tauri-apps/api/cli'
|
||||
|
||||
getMatches().then((matches) => {
|
||||
// do something with the { args, subcommand } matches
|
||||
})
|
||||
```
|
||||
|
||||
## Complete documentation
|
||||
|
||||
You can find more about the CLI configuration <a href="/docs/api/config#tauri">here</a>.
|
@ -1,256 +0,0 @@
|
||||
---
|
||||
title: Create Rust Commands
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
Tauri provides a simple yet powerful "command" system for calling Rust functions from your web app. Commands can accept arguments and return values. They can also return errors and be `async`.
|
||||
|
||||
## Basic Example
|
||||
|
||||
Commands are defined in your `src-tauri/src/main.rs` file. To create a command, just add a function and annotate it with `#[tauri::command]`:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn my_custom_command() {
|
||||
println!("I was invoked from JS!");
|
||||
}
|
||||
```
|
||||
|
||||
You will have to provide a list of your commands to the builder function like so:
|
||||
|
||||
```rust
|
||||
// Also in main.rs
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
// This is where you pass in your commands
|
||||
.invoke_handler(tauri::generate_handler![my_custom_command])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
```
|
||||
|
||||
Now, you can invoke the command from your JS code:
|
||||
|
||||
```js
|
||||
// With the Tauri API npm package:
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
// With the Tauri global script, enabled when `tauri.conf.json > build > withGlobalTauri` is set to true:
|
||||
const invoke = window.__TAURI__.invoke
|
||||
|
||||
// Invoke the command
|
||||
invoke('my_custom_command')
|
||||
```
|
||||
|
||||
## Passing Arguments
|
||||
|
||||
Your command handlers can take arguments:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn my_custom_command(invoke_message: String) {
|
||||
println!("I was invoked from JS, with this message: {}", invoke_message);
|
||||
}
|
||||
```
|
||||
|
||||
Arguments should be passed as a JSON object with camelCase keys:
|
||||
|
||||
```js
|
||||
invoke('my_custom_command', { invokeMessage: 'Hello!' })
|
||||
```
|
||||
|
||||
Arguments can be of any type, as long as they implement [serde::Deserialize](https://serde.rs/derive.html).
|
||||
|
||||
## Returning Data
|
||||
|
||||
Command handlers can return data as well:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn my_custom_command() -> String {
|
||||
"Hello from Rust!".into()
|
||||
}
|
||||
```
|
||||
|
||||
The `invoke` function returns a promise that resolves with the returned value:
|
||||
|
||||
```js
|
||||
invoke('my_custom_command').then((message) => console.log(message))
|
||||
```
|
||||
|
||||
Returned data can be of any type, as long as it implements [Serde::Serialize](https://serde.rs/derive.html).
|
||||
|
||||
## Error Handling
|
||||
|
||||
If your handler could fail and needs to be able to return an error, have the function return a `Result`:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn my_custom_command() -> Result<String, String> {
|
||||
// If something fails
|
||||
Err("This failed!".into())
|
||||
// If it worked
|
||||
Ok("This worked!".into())
|
||||
}
|
||||
```
|
||||
|
||||
If the command returns an error, the promise will reject, otherwise it resolves:
|
||||
|
||||
```js
|
||||
invoke('my_custom_command')
|
||||
.then((message) => console.log(message))
|
||||
.catch((error) => console.error(error))
|
||||
```
|
||||
|
||||
## Async Commands
|
||||
|
||||
<Alert title="Note">
|
||||
Async commands are executed on a separate thread using the <a href="https://tauri.studio/en/docs/api/rust/tauri/async_runtime/fn.spawn">async runtime</a>.
|
||||
Commands without the <i>async</i> keyword are executed on the main thread, unless defined with <i>#[tauri::command(async)]</i>.
|
||||
</Alert>
|
||||
|
||||
If your command needs to run asynchronously, simply declare it as `async`:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
async fn my_custom_command() {
|
||||
// Call another async function and wait for it to finish
|
||||
let result = some_async_function().await;
|
||||
println!("Result: {}", result);
|
||||
}
|
||||
```
|
||||
|
||||
Since invoking the command from JS already returns a promise, it works just like any other command:
|
||||
|
||||
```js
|
||||
invoke('my_custom_command').then(() => console.log('Completed!'))
|
||||
```
|
||||
|
||||
## Accessing the Window in Commands
|
||||
|
||||
Commands can access the `Window` instance that invoked the message:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
async fn my_custom_command(window: tauri::Window) {
|
||||
println!("Window: {}", window.label());
|
||||
}
|
||||
```
|
||||
|
||||
## Accessing an AppHandle in Commands
|
||||
|
||||
Commands can access an `AppHandle` instance:
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
async fn my_custom_command(app_handle: tauri::AppHandle) {
|
||||
let app_dir = app_handle.path_resolver().app_dir();
|
||||
use tauri::GlobalShortcutManager;
|
||||
app_handle.global_shortcut_manager().register("CTRL + U", move || {});
|
||||
}
|
||||
```
|
||||
|
||||
## Accessing managed state
|
||||
|
||||
Tauri can manage state using the `manage` function on `tauri::Builder`.
|
||||
The state can be accessed on a command using `tauri::State`:
|
||||
|
||||
```rust
|
||||
struct MyState(String);
|
||||
|
||||
#[tauri::command]
|
||||
fn my_custom_command(state: tauri::State<MyState>) {
|
||||
assert_eq!(state.0 == "some state value", true);
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(MyState("some state value".into()))
|
||||
.invoke_handler(tauri::generate_handler![my_custom_command])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
## Creating Multiple Commands
|
||||
|
||||
The `tauri::generate_handler!` macro takes an array of commands. To register
|
||||
multiple commands, you cannot call invoke_handler multiple times. Only the last
|
||||
call will be used. You must pass each command to a single call of
|
||||
`tauri::generate_handler!`.
|
||||
|
||||
```rust
|
||||
#[tauri::command]
|
||||
fn cmd_a() -> String {
|
||||
"Command a"
|
||||
}
|
||||
#[tauri::command]
|
||||
fn cmd_b() -> String {
|
||||
"Command b"
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.invoke_handler(tauri::generate_handler![cmd_a, cmd_b])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
Any or all of the above features can be combined:
|
||||
|
||||
```rust title=main.rs
|
||||
// Definition in main.rs
|
||||
|
||||
struct Database;
|
||||
|
||||
#[derive(serde::Serialize)]
|
||||
struct CustomResponse {
|
||||
message: String,
|
||||
other_val: usize,
|
||||
}
|
||||
|
||||
async fn some_other_function() -> Option<String> {
|
||||
Some("response".into())
|
||||
}
|
||||
|
||||
#[tauri::command]
|
||||
async fn my_custom_command(
|
||||
window: tauri::Window,
|
||||
number: usize,
|
||||
database: tauri::State<'_, Database>,
|
||||
) -> Result<CustomResponse, String> {
|
||||
println!("Called from {}", window.label());
|
||||
let result: Option<String> = some_other_function().await;
|
||||
if let Some(message) = result {
|
||||
Ok(CustomResponse {
|
||||
message,
|
||||
other_val: 42 + number,
|
||||
})
|
||||
} else {
|
||||
Err("No result".into())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.manage(Database {})
|
||||
.invoke_handler(tauri::generate_handler![my_custom_command])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
```js
|
||||
// Invocation from JS
|
||||
|
||||
invoke('my_custom_command', {
|
||||
number: 42,
|
||||
})
|
||||
.then((res) =>
|
||||
console.log(`Message: ${res.message}, Other Val: ${res.other_val}`)
|
||||
)
|
||||
.catch((e) => console.error(e))
|
||||
```
|
@ -1,52 +0,0 @@
|
||||
---
|
||||
title: Contributor Guide
|
||||
---
|
||||
|
||||
todo: make this friendlier and more complete
|
||||
|
||||
Tauri is a polyglot system that uses:
|
||||
|
||||
- git
|
||||
- Node.js
|
||||
- Rust
|
||||
- GitHub actions
|
||||
|
||||
It can be developed on macOS, Linux and Windows.
|
||||
|
||||
## Contribution Flow
|
||||
|
||||
1. File an Issue
|
||||
2. Fork the Repository
|
||||
3. Make Your Changes
|
||||
4. Make a PR
|
||||
|
||||
### A Note About Contributions to the Rust Libraries
|
||||
|
||||
When contributing to the Rust libraries `tauri`, `tauri-api`, and `tauri-updater`; you will want to setup an environment for RLS (the Rust Language Server). In the Tauri root directory, there is a `.scripts` folder that contains a set of scripts to automate adding a couple temporary environment variables to your shell/terminal. These environment variables point to directories in the test fixture which will prevent RLS from crashing on compile-time. This is a necessary step for setting up a development environment for Tauri's Rust libraries.
|
||||
|
||||
##### _Example Instructions_
|
||||
|
||||
1. Navigate to the Tauri Root directory.
|
||||
2. Execute a script based on your Operating System from this folder: `.scripts/init_env.bat` for Windows Cmd, `.scripts/init_env.ps1` for Windows Powershell, `. .scripts/init_env.sh` for Linux/macOS bash (note the first `.` in this command).
|
||||
3. Open your text editor/IDE from this shell/terminal.
|
||||
|
||||
## Hands On Example
|
||||
|
||||
Let's make a new example. That's a great way to learn. We are going to assume you are on a nixy type of environment like Linux or macOS and have all of your development dependencies like rust and node already sorted out.
|
||||
|
||||
```sh
|
||||
git clone git@github.com:tauri-apps/tauri.git
|
||||
cd tauri/cli/tauri.js
|
||||
yarn
|
||||
mkdir ../../examples/vanillajs && cd "$_"
|
||||
```
|
||||
|
||||
```json
|
||||
"tauri:source": "node ../../../cli/tauri.js/bin/tauri",
|
||||
```
|
||||
|
||||
```ini
|
||||
[dependencies.tauri]
|
||||
path = "../../../../core/tauri"
|
||||
features = [ "all-api" ]
|
||||
```
|
@ -1,132 +0,0 @@
|
||||
---
|
||||
title: Events
|
||||
---
|
||||
|
||||
The Tauri event system is a multi-producer multi-consumer communication primitive that allows message passing between the frontend and the backend.
|
||||
It is analogous to the command system, but payload type check must be written on the event handler and it simplifies communication from the backend to the frontend, working like a channel.
|
||||
|
||||
A Tauri application can listen and emit to global and window-specific events. Usage from the frontend and the backend are described below.
|
||||
|
||||
## Frontend
|
||||
|
||||
The event system is accessible on the frontend on the `event` and `window` modules of the `@tauri-apps/api` package.
|
||||
|
||||
### Global events
|
||||
|
||||
To use the global event channel, import the `event` module and use the `emit` and `listen` functions:
|
||||
|
||||
```ts
|
||||
import { emit, listen } from '@tauri-apps/api/event'
|
||||
|
||||
// listen to the `click` event and get a function to remove the event listener
|
||||
// there's also a `once` function that subscribes to an event and automatically unsubscribes the listener on the first event
|
||||
const unlisten = await listen('click', event => {
|
||||
// event.event is the event name (useful if you want to use a single callback fn for multiple event types)
|
||||
// event.payload is the payload object
|
||||
})
|
||||
|
||||
// emits the `click` event with the object payload
|
||||
emit('click', {
|
||||
theMessage: 'Tauri is awesome!'
|
||||
})
|
||||
```
|
||||
|
||||
### Window-specific events
|
||||
|
||||
Window-specific events are exposed on the `window` module.
|
||||
|
||||
```ts
|
||||
import { appWindow, WebviewWindow } from '@tauri-apps/api/window'
|
||||
|
||||
// emit an event that are only visible to the current window
|
||||
appWindow.emit('event', { message: 'Tauri is awesome!' })
|
||||
|
||||
// create a new webview window and emit an event only to that window
|
||||
const webview = new WebviewWindow('window')
|
||||
webview.emit('event')
|
||||
|
||||
```
|
||||
|
||||
## Backend
|
||||
|
||||
On the backend, the global event channel is exposed on the `App` struct, and window-specific events can be emitted using the `Window` trait.
|
||||
|
||||
### Global events
|
||||
|
||||
```rust
|
||||
use tauri::Manager;
|
||||
|
||||
// the payload type must implement `Serialize`.
|
||||
// for global events, it also must implement `Clone`.
|
||||
#[derive(Clone, serde::Serialize)]
|
||||
struct Payload {
|
||||
message: String,
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
// listen to the `event-name` (emitted on any window)
|
||||
let id = app.listen_global("event-name", |event| {
|
||||
println!("got event-name with payload {:?}", event.payload());
|
||||
});
|
||||
// unlisten to the event using the `id` returned on the `listen_global` function
|
||||
// an `once_global` API is also exposed on the `App` struct
|
||||
app.unlisten(id);
|
||||
|
||||
// emit the `event-name` event to all webview windows on the frontend
|
||||
app.emit_all("event-name", Payload { message: "Tauri is awesome!".into() }).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
```
|
||||
|
||||
### Window-specific events
|
||||
|
||||
To use the window-specific event channel, a `Window` object can be obtained on a command handler or with the `get_window` function:
|
||||
|
||||
```rust
|
||||
use tauri::{Manager, Window};
|
||||
|
||||
// the payload type must implement `Serialize`.
|
||||
#[derive(serde::Serialize)]
|
||||
struct Payload {
|
||||
message: String,
|
||||
}
|
||||
|
||||
// init a background process on the command, and emit periodic events only to the window that used the command
|
||||
#[tauri::command]
|
||||
fn init_process(window: Window) {
|
||||
std::thread::spawn(move || {
|
||||
loop {
|
||||
window.emit("event-name", Payload { message: "Tauri is awesome!".into() }).unwrap();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
// `main` here is the window label; it is defined on the window creation or under `tauri.conf.json`
|
||||
// the default value is `main`. note that it must be unique
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
|
||||
// listen to the `event-name` (emitted on the `main` window)
|
||||
let id = main_window.listen("event-name", |event| {
|
||||
println!("got window event-name with payload {:?}", event.payload());
|
||||
});
|
||||
// unlisten to the event using the `id` returned on the `listen` function
|
||||
// an `once` API is also exposed on the `Window` struct
|
||||
main_window.unlisten(id);
|
||||
|
||||
// emit the `event-name` event to the `main` window
|
||||
main_window.emit("event-name", Payload { message: "Tauri is awesome!".into() }).unwrap();
|
||||
Ok(())
|
||||
})
|
||||
.invoke_handler(tauri::generate_handler![init_process])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
```
|
@ -1,47 +0,0 @@
|
||||
---
|
||||
title: Icons
|
||||
---
|
||||
|
||||
import Command from '@theme/Command'
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
Tauri ships with a default iconset based on its logo. This is probably NOT what you want when you ship your application. To remedy this common situation, Tauri provides the `icon` command that will take an input file ("./app-icon.png" by default) and create all the icons needed for the various platforms:
|
||||
|
||||
<Command name="icon"/>
|
||||
|
||||
```sh
|
||||
Options
|
||||
--help, -h Displays this message
|
||||
--log, l Logging [boolean]
|
||||
--icon, i Source icon (png, 1240x1240 with transparency)
|
||||
--target, t Target folder (default: 'src-tauri/icons')
|
||||
--compression, c Compression type [pngquant|optipng|zopfli]
|
||||
```
|
||||
|
||||
These will be placed in your `src-tauri/icons` folder where they will automatically be included in your built app.
|
||||
|
||||
If you need to source your icons from some other location, you can edit this part of the `src-tauri/tauri.conf.json` file:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"icon": [
|
||||
"icons/32x32.png",
|
||||
"icons/128x128.png",
|
||||
"icons/128x128@2x.png",
|
||||
"icons/icon.icns",
|
||||
"icons/icon.ico"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
<Alert type="info" icon="info-alt" title="Note on filetypes">
|
||||
|
||||
- icon.icns = macOS
|
||||
- icon.ico = MS Windows
|
||||
- \*.png = Linux
|
||||
|
||||
</Alert>
|
@ -1,158 +0,0 @@
|
||||
---
|
||||
title: Window Menu
|
||||
---
|
||||
|
||||
Native application menus can be attached to a window.
|
||||
|
||||
### Creating a menu
|
||||
|
||||
To create a native window menu, import the `Menu`, `Submenu`, `MenuItem` and `CustomMenuItem` types.
|
||||
The `MenuItem` enum contains a collection of platform-specific items (currently not implemented on Windows).
|
||||
The `CustomMenuItem` allows you to create your own menu items and add special functionality to them.
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
|
||||
```
|
||||
|
||||
Create a `Menu` instance:
|
||||
|
||||
```rust
|
||||
// here `"quit".to_string()` defines the menu item id, and the second parameter is the menu item label.
|
||||
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
|
||||
let close = CustomMenuItem::new("close".to_string(), "Close");
|
||||
let submenu = Submenu::new("File", Menu::new().add_item(quit).add_item(close));
|
||||
let menu = Menu::new()
|
||||
.add_native_item(MenuItem::Copy)
|
||||
.add_item(CustomMenuItem::new("hide", "Hide"))
|
||||
.add_submenu(submenu);
|
||||
// alternatively, using the `with_items` constructor, useful if you end up using conditional compilation
|
||||
let menu = Menu::with_items([
|
||||
MenuItem::Copy.into(),
|
||||
CustomMenuItem::new("hide", "Hide").into(),
|
||||
submenu.into(),
|
||||
])
|
||||
```
|
||||
|
||||
### Adding the menu to all windows
|
||||
|
||||
The defined menu can be set to all windows using the `menu` API on the `tauri::Builder` struct:
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
|
||||
|
||||
fn main() {
|
||||
let menu = Menu::new(); // configure the menu
|
||||
tauri::Builder::default()
|
||||
.menu(menu)
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### Adding the menu to a specific window
|
||||
|
||||
You can create a window and set the menu to be used. This allows defining a specific menu set for each application window.
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem, Submenu};
|
||||
use tauri::WindowBuilder;
|
||||
|
||||
fn main() {
|
||||
let menu = Menu::new(); // configure the menu
|
||||
tauri::Builder::default()
|
||||
.create_window(
|
||||
"main-window".to_string(),
|
||||
tauri::WindowUrl::App("index.html".into()),
|
||||
move |window_builder, webview_attributes| {
|
||||
(window_builder.menu(menu), webview_attributes)
|
||||
},
|
||||
)
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### Listening to events on custom menu items
|
||||
|
||||
Each `CustomMenuItem` triggers an event when clicked. Use the `on_menu_event` API to handle them, either on the global `tauri::Builder` or on an specific window.
|
||||
|
||||
#### Listening to events on global menus
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem};
|
||||
|
||||
fn main() {
|
||||
let menu = vec![]; // insert the menu array here
|
||||
tauri::Builder::default()
|
||||
.menu(menu)
|
||||
.on_menu_event(|event| {
|
||||
match event.menu_item_id() {
|
||||
"quit" => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
"close" => {
|
||||
event.window().close().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
#### Listening to events on window menus
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, Menu, MenuItem};
|
||||
use tauri::{Manager, WindowBuilder};
|
||||
|
||||
fn main() {
|
||||
let menu = vec![]; // insert the menu array here
|
||||
tauri::Builder::default()
|
||||
.create_window(
|
||||
"main-window".to_string(),
|
||||
tauri::WindowUrl::App("index.html".into()),
|
||||
move |window_builder, webview_attributes| {
|
||||
(window_builder.menu(menu), webview_attributes)
|
||||
},
|
||||
)
|
||||
.setup(|app| {
|
||||
let window = app.get_window("main-window").unwrap();
|
||||
let window_ = window.clone();
|
||||
window.on_menu_event(move |event| {
|
||||
match event.menu_item_id().as_str() {
|
||||
"quit" => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
"close" => {
|
||||
window_.close().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### Updating menu items
|
||||
|
||||
The `Window` struct has a `menu_handle` method, which allows updating menu items:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
let menu_handle = main_window.menu_handle();
|
||||
std::thread::spawn(move || {
|
||||
// you can also `set_selected`, `set_enabled` and `set_native_image` (macOS only).
|
||||
menu_handle.get_item("item_id").set_title("New title");
|
||||
})
|
||||
Ok(())
|
||||
})
|
||||
}
|
||||
```
|
@ -1,335 +0,0 @@
|
||||
---
|
||||
title: Migrating from 0.x
|
||||
---
|
||||
|
||||
First of all if you still have `tauri` as dependency in your `package.json`
|
||||
replace it with a recent version of `@tauri-apps/cli` (make sure to also change
|
||||
the import path in your JavaScript/TypeScript files, see [JavaScript](#javascript)).
|
||||
|
||||
For example:
|
||||
|
||||
```diff
|
||||
- "tauri": "^0.14.1"
|
||||
+ "@tauri-apps/cli": "^1.0.0-beta-rc.4"
|
||||
```
|
||||
|
||||
Next update your `Cargo.toml`:
|
||||
|
||||
- add `tauri-build` as a new build-dependency and remove `winres`, e.g.:
|
||||
|
||||
```diff
|
||||
+ [build-dependencies]
|
||||
+ tauri-build = { version = "1.0.0-beta-rc.0" }
|
||||
|
||||
- [target."cfg(windows)".build-dependencies]
|
||||
- winres = "0.1"
|
||||
```
|
||||
|
||||
- update the version of `tauri` to e.g. `1.0.0-beta-rc.4`
|
||||
- remove all old features of the `tauri` dependency
|
||||
- remove all features, that tauri added and add `custom-protocol` as a new one:
|
||||
|
||||
```diff
|
||||
[features]
|
||||
- embedded-server = [ "tauri/embedded-server" ]
|
||||
- no-server = [ "tauri/no-server" ]
|
||||
+ custom-protocol = [ "tauri/custom-protocol" ]
|
||||
+ default = [ "custom-protocol" ]
|
||||
```
|
||||
|
||||
Update your `tauri.conf.json` like this:
|
||||
|
||||
- remove `ctx`
|
||||
- remove the `embeddedServer`
|
||||
- rename `osx` to `macOS` and add some fields:
|
||||
- `"exceptionDomain": ""`
|
||||
- `"signingIdentity": null`
|
||||
- `"entitlements": null`
|
||||
- remove the `exceptionDomain`
|
||||
- add a configuration for `windows`:
|
||||
- `"certificateThumbprint": null`
|
||||
- `"digestAlgorithm": "sha256"`
|
||||
- `"timestampUrl": ""`
|
||||
- make the `window` definition into an array and call it `windows`
|
||||
- remove `inliner`
|
||||
|
||||
> for more information about the config see [here](../../api/config.md)
|
||||
|
||||
```diff
|
||||
{
|
||||
- "ctx": {},
|
||||
"tauri": {
|
||||
- "embeddedServer": {
|
||||
- "active": true
|
||||
- },
|
||||
"bundle": {
|
||||
- "osx": {
|
||||
+ "macOS": {
|
||||
"frameworks": [],
|
||||
"minimumSystemVersion": "",
|
||||
- "useBootstrapper": false
|
||||
+ "useBootstrapper": false,
|
||||
+ "exceptionDomain": "",
|
||||
+ "signingIdentity": null,
|
||||
+ "entitlements": null
|
||||
},
|
||||
- "exceptionDomain": ""
|
||||
+ "windows": {
|
||||
+ "certificateThumbprint": null,
|
||||
+ "digestAlgorithm": "sha256",
|
||||
+ "timestampUrl": ""
|
||||
+ }
|
||||
},
|
||||
+ "updater": {
|
||||
+ "active": false
|
||||
+ },
|
||||
- "window": {
|
||||
+ "windows": [
|
||||
{
|
||||
"title": "Calciumdibromid",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
+ ],
|
||||
- "inliner": {
|
||||
- "active": true
|
||||
- }
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Commands
|
||||
|
||||
The following example is taken from the previous documentation.
|
||||
|
||||
In the new version of Tauri there is no distinction between synchronous and
|
||||
asynchronous commands, the only difference in your code is a call of
|
||||
`tauri::execute_promise()`, that isn't there in a synchronous command.
|
||||
|
||||
### Rust
|
||||
|
||||
Here is the complete example code of the "old" version:
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DoSomethingPayload {
|
||||
state: String,
|
||||
data: u64,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(tag = "cmd", rename_all = "camelCase")]
|
||||
enum Cmd {
|
||||
DoSomething {
|
||||
count: u64,
|
||||
payload: DoSomethingPayload,
|
||||
callback: String,
|
||||
error: String,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Response<'a> {
|
||||
value: u64,
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone)]
|
||||
struct CommandError<'a> {
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> CommandError<'a> {
|
||||
fn new(message: &'a str) -> Self {
|
||||
Self { message }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for CommandError<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::error::Error for CommandError<'a> {}
|
||||
|
||||
fn main() {
|
||||
tauri::AppBuilder::new()
|
||||
.invoke_handler(|_webview, arg| {
|
||||
use Cmd::*;
|
||||
match serde_json::from_str(arg) {
|
||||
Err(e) => Err(e.to_string()),
|
||||
Ok(command) => {
|
||||
match command {
|
||||
DoSomething { count, payload, callback, error } => tauri::execute_promise(
|
||||
_webview,
|
||||
move || {
|
||||
if count > 5 {
|
||||
let response = Response {
|
||||
value: 5,
|
||||
message: "async response!",
|
||||
};
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(CommandError::new("count should be > 5").into())
|
||||
}
|
||||
},
|
||||
callback,
|
||||
error,
|
||||
),
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
})
|
||||
.build()
|
||||
.run();
|
||||
}
|
||||
```
|
||||
|
||||
Complete the following steps to migrate your code:
|
||||
|
||||
- create a new function for every `Cmd` enum variant
|
||||
- wrap the new function with the `#[tauri::command]` macro
|
||||
- use the fields of the enum as arguments (`callback` and `error` can be deleted)
|
||||
- as function body use the code inside the `match` block of the enum variant
|
||||
- add a return type
|
||||
- rename `AppBuilder` to `Builder` in `main()`
|
||||
- replace the big `invoke_handler` with the new syntax
|
||||
|
||||
The old example code should look like this now:
|
||||
|
||||
```rust
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct DoSomethingPayload {
|
||||
state: String,
|
||||
data: u64,
|
||||
}
|
||||
|
||||
#[derive(Serialize)]
|
||||
struct Response<'a> {
|
||||
value: u64,
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
struct CommandError<'a> {
|
||||
message: &'a str,
|
||||
}
|
||||
|
||||
impl<'a> CommandError<'a> {
|
||||
fn new(message: &'a str) -> Self {
|
||||
Self { message }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::fmt::Display for CommandError<'a> {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
write!(f, "{}", self.message)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> std::error::Error for CommandError<'a> {}
|
||||
|
||||
#[tauri::command]
|
||||
fn do_something(count: u64, payload: DoSomethingPayload) -> Result<Response, CommandError> {
|
||||
if count > 5 {
|
||||
let response = Response {
|
||||
value: 5,
|
||||
message: "async response!",
|
||||
};
|
||||
Ok(response)
|
||||
} else {
|
||||
Err(CommandError::new("count should be > 5").into())
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
tauri::Builder::new()
|
||||
.invoke_handler(tauri::generate_handler![do_something])
|
||||
.run(tauri::generate_context!());
|
||||
}
|
||||
```
|
||||
|
||||
### JavaScript
|
||||
|
||||
Like mentioned above there is also no distinction between synchronous and
|
||||
asynchronous commands in JavaScript.
|
||||
You only have to use `invoke` and optionally use the results.
|
||||
|
||||
Here is an example of the "old" code:
|
||||
|
||||
```js
|
||||
invoke({
|
||||
cmd: 'doSomething',
|
||||
count: 5,
|
||||
payload: {
|
||||
state: 'some string data',
|
||||
data: 17
|
||||
}
|
||||
});
|
||||
|
||||
promisified({
|
||||
cmd: 'doSomething',
|
||||
count: 5,
|
||||
payload: {
|
||||
state: 'some string data',
|
||||
data: 17
|
||||
}
|
||||
}).then(response => {
|
||||
console.log(response);
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
```
|
||||
|
||||
Complete the following steps to migrate your code:
|
||||
|
||||
- replace all `promisified`-calls with `invoke`-calls
|
||||
- extract the `cmd` attribute of the argument object as first parameter
|
||||
(you may have to rename it to `snake_case` as the `cmd` parameter is now the
|
||||
name of the function in Rust)
|
||||
- if you import parts of the tauri-api with `tauri/api/*` replace it with `@tauri-apps/api/*`, e.g.:
|
||||
|
||||
```diff
|
||||
- import { invoke } from 'tauri/api/tauri';
|
||||
+ import { invoke } from '@tauri-apps/api/tauri';
|
||||
```
|
||||
|
||||
The old example code should look like this now:
|
||||
|
||||
```js
|
||||
invoke(
|
||||
'do_something',
|
||||
{
|
||||
count: 5,
|
||||
payload: {
|
||||
state: 'some string data',
|
||||
data: 17
|
||||
}
|
||||
}
|
||||
);
|
||||
|
||||
invoke(
|
||||
'do_something',
|
||||
{
|
||||
count: 5,
|
||||
payload: {
|
||||
state: 'some string data',
|
||||
data: 17
|
||||
}
|
||||
}
|
||||
).then(response => {
|
||||
console.log(response);
|
||||
}).catch(error => {
|
||||
console.error(error);
|
||||
});
|
||||
```
|
||||
|
||||
For more information on commands read [Create Rust Commands](command.md).
|
@ -1,5 +0,0 @@
|
||||
---
|
||||
title: Multiwindow
|
||||
---
|
||||
|
||||
Manage multiple windows on a single application.
|
@ -1,9 +0,0 @@
|
||||
---
|
||||
id: about-patterns
|
||||
title: "A word on patterns"
|
||||
sidebar_label: A word on patterns
|
||||
---
|
||||
|
||||
Tauri patterns are descriptions of use-cases that are entirely configurable within the `src-tauri/tauri.conf.json` file. These are not the limits of what Tauri can do, and there are probably more out there. If you discover one, why not get in touch and help us update this collection!
|
||||
|
||||
If you haven't read about the general design of Tauri, then it would make the most sense for you to visit the ["Get started"](/docs/get-started/intro) and become familiar with the basic architecture and terminology used in these patterns.
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: Bridge
|
||||
---
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="3"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="4"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="4"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Bridge.png')} alt="Bridge" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Highly configurable</li>
|
||||
<li>No Rust skills required</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>Some WebAPIs unavailable</li>
|
||||
<li>Challenge to implement</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The Bridge recipe is a secure pattern where messages are passed between brokers via an implicit bridge using the API. It isolates functionality to scope and passes messages instead of functionality.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph TD
|
||||
H==>F
|
||||
subgraph WEBVIEW
|
||||
F-.-E
|
||||
end
|
||||
D-->E
|
||||
E-->D
|
||||
B-->D
|
||||
D-->B
|
||||
subgraph RUST
|
||||
A==>H
|
||||
A-->B
|
||||
B-.-C
|
||||
B-.-G
|
||||
end
|
||||
A[Binary]
|
||||
B{Rust Broker}
|
||||
C[Subprocess 2]
|
||||
G[Subprocess 1]
|
||||
D(( API BRIDGE ))
|
||||
E{JS Broker}
|
||||
F[Window]
|
||||
H{Bootstrap}
|
||||
style D fill:#ccc,stroke:#333,stroke-width:4px,color:white
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px`} />
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": { // all API values are default false
|
||||
"all": false, // use this flag to enable all API features
|
||||
"shell": {
|
||||
"execute": false, // enable application execution
|
||||
"open": false, // open link/path in the default app
|
||||
},
|
||||
"fs": {
|
||||
"listFiles": false, // list files in a directory
|
||||
"readBinaryFile": false, // read binary file from local filesystem
|
||||
"readTextFile": false, // read text file from local filesystem
|
||||
"setTitle": false, // set the window title
|
||||
"writeFile": false // write file to local filesystem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: Cloudbridge
|
||||
---
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="1"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="3"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="2"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Cloudbridge.png')} alt="Cloudbridge" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>All available features</li>
|
||||
<li>No Rust skills required</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>Largest bundle size</li>
|
||||
<li>Hard to separate concerns</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The Cloudbridge recipe combines the flexibility of a localhost and the security of the bridge. With so many features, it can be easy to get lost.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph TD
|
||||
H==>F2
|
||||
H==>D2
|
||||
D2-->F2
|
||||
F2-->D2
|
||||
B-->D
|
||||
D-->B
|
||||
E2-->D
|
||||
D-->E2
|
||||
subgraph WEBVIEW
|
||||
F2
|
||||
E2
|
||||
end
|
||||
subgraph SERVER
|
||||
D2
|
||||
E-->D2
|
||||
end
|
||||
subgraph RUST
|
||||
A==>H
|
||||
A-->B
|
||||
B-.-C
|
||||
end
|
||||
A[Binary]
|
||||
B{Rust Broker}
|
||||
C[Subprocess]
|
||||
D(( API BRIDGE ))
|
||||
E{JS Broker}
|
||||
D2(( localhost ))
|
||||
E[bundled resources]
|
||||
E2{JS Broker}
|
||||
F2[Window]
|
||||
H{Bootstrap}
|
||||
style D fill:#ccc,stroke:#333,stroke-width:4px,color:white
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px
|
||||
style SERVER fill:#49A24A,stroke:#2B6063,stroke-width:4px
|
||||
`} />
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": true // enable entire API
|
||||
}
|
||||
}
|
||||
```
|
@ -1,89 +0,0 @@
|
||||
---
|
||||
title: Cloudish
|
||||
---
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="3"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="3"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="2"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Cloudish.png')} alt="Cloudish" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Similar to a SPA web-app</li>
|
||||
<li>No Rust skills required</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>No access to Rust API</li>
|
||||
<li>Uses a localhost server</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The Cloudish recipe is a pattern for maximum flexibility and app performance. It uses a localhost server, which means that your app will technically be available to other processes, like browsers and potentially other devices on the network. All of your assets are baked into the binary, but served as if they were distinct files.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph TD
|
||||
H==>F
|
||||
H==>D
|
||||
D-->F
|
||||
F-->D
|
||||
subgraph RUST
|
||||
A==>H
|
||||
end
|
||||
subgraph WEBVIEW
|
||||
F
|
||||
end
|
||||
subgraph SERVER
|
||||
D
|
||||
E-->D
|
||||
end
|
||||
A[Binary]
|
||||
D(( localhost ))
|
||||
E[bundled resources]
|
||||
F[Window]
|
||||
H{Bootstrap}
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px
|
||||
style SERVER fill:#49A24A,stroke:#2B6063,stroke-width:4px`} />
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false // disable entire API
|
||||
}
|
||||
}
|
||||
|
||||
```
|
@ -1,91 +0,0 @@
|
||||
---
|
||||
title: GLUI
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<Alert type="warning" icon="info-alt" title="Please note">
|
||||
This pattern is not available for now.
|
||||
</Alert>
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="0"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="0"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="0"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/GLUI.png')} alt="GLUI" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Framebuffer FTW</li>
|
||||
<li>Window events rigged</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>Broken on your machine</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The GLUI is a research pattern that we will use internally to test approaches using a GLUTIN window. We’re not sure yet if it will make the final cut as a bona fide alternative to WebView, although early tests with transparent and multiwindow are exciting.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph TD
|
||||
A==>H
|
||||
H==>G
|
||||
A-->D
|
||||
D-->G
|
||||
subgraph GLUTIN
|
||||
G
|
||||
end
|
||||
subgraph RUST
|
||||
A
|
||||
end
|
||||
A[Binary]
|
||||
D(Framebuffer)
|
||||
G[GL Window]
|
||||
H{Bootstrap}
|
||||
style GLUTIN stroke:${colors.blue.dark},stroke-width:4px
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px`} />
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": { // all API endpoints are default false
|
||||
"all": false, // disable the api
|
||||
},
|
||||
"window": { // not yet normative
|
||||
"glutin": true,
|
||||
"webview": false
|
||||
}
|
||||
}
|
||||
```
|
@ -1,79 +0,0 @@
|
||||
---
|
||||
title: Hermit
|
||||
---
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="0"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Hermit.png')} alt="Hermit" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Quick to make</li>
|
||||
<li>Smallest size</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>No remote resources</li>
|
||||
<li>No access to API</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The Hermit recipe is a pattern for ultimate application isolation where all logic is self-contained in the Window and the binary exists merely to bootstrap the Window. There is no communication back to Rust from the Window, there is no localhost server, and the Window has no access to any remote resources. The Hermit is great for interactive Kiosk Mode and standalone HTML based games.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph LR
|
||||
A==>H
|
||||
H==>F
|
||||
subgraph WEBVIEW
|
||||
F
|
||||
end
|
||||
subgraph RUST
|
||||
A
|
||||
end
|
||||
A[fa:fa-cog Binary ]
|
||||
F[fa:fa-window-maximize Window]
|
||||
H{Bootstrap}
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px`} />
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": {
|
||||
"all": false, // disable and tree-shake all api functions
|
||||
}
|
||||
}
|
||||
```
|
@ -1,82 +0,0 @@
|
||||
---
|
||||
title: Lockdown
|
||||
---
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="2"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="4"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="5" color="#fff04d"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Lockdown.png')} alt="Lockdown" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Highest security rating</li>
|
||||
<li>Elegant and powerful</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>Rust skills required</li>
|
||||
<li>No remote resources</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
## Description
|
||||
|
||||
The Lockdown recipe is a minimal usage of the [Bridge pattern](/docs/guides/patterns/bridge), which only allows interaction between Rust and the Window via expiring JS Promise Closures that are injected into the Window by Rust and nulled as part of the callback.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph TD
|
||||
H==>F
|
||||
G-.->B
|
||||
B-->G
|
||||
subgraph WEBVIEW
|
||||
G-->F
|
||||
end
|
||||
subgraph RUST
|
||||
A-->B
|
||||
A==>H
|
||||
end
|
||||
A[Binary]
|
||||
B[API:Event]
|
||||
F[Window]
|
||||
G((Promise Closure))
|
||||
H{Bootstrap}
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px`} />
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": {} // all API endpoints are default false
|
||||
}
|
||||
```
|
@ -1,92 +0,0 @@
|
||||
---
|
||||
title: Multiwin
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
import useBaseUrl from '@docusaurus/useBaseUrl'
|
||||
|
||||
import Rater from '@theme/Rater'
|
||||
|
||||
<div className="row">
|
||||
<div className="col col--4">
|
||||
<table>
|
||||
<tr>
|
||||
<td>Ease of Use</td>
|
||||
<td><Rater value="4"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Extensibility</td>
|
||||
<td><Rater value="4"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Performance</td>
|
||||
<td><Rater value="3"/></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>Security</td>
|
||||
<td><Rater value="5"/></td>
|
||||
</tr>
|
||||
</table>
|
||||
</div>
|
||||
<div className="col col--4 pattern-logo">
|
||||
<img src={useBaseUrl('img/patterns/Multiwin.png')} alt="Multiwin" />
|
||||
</div>
|
||||
<div className="col col--4">
|
||||
Pros:
|
||||
<ul>
|
||||
<li>Windows can be spawned or destroyed at runtime</li>
|
||||
<li>Separation of concerns</li>
|
||||
</ul>
|
||||
Cons:
|
||||
<ul>
|
||||
<li>Somewhat complex</li>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
## Description
|
||||
|
||||
The Multiwin recipe will allow you to have multiple windows.
|
||||
|
||||
## Diagram
|
||||
|
||||
import Mermaid, { colors } from '@theme/Mermaid'
|
||||
|
||||
<Mermaid chart={`graph LR
|
||||
A==>H
|
||||
H==>F
|
||||
H==>G
|
||||
subgraph WEBVIEW
|
||||
F
|
||||
end
|
||||
subgraph WINIT
|
||||
G
|
||||
end
|
||||
subgraph RUST
|
||||
A
|
||||
end
|
||||
A[Binary]
|
||||
F[Window]
|
||||
G[Window]
|
||||
H{Bootstrap}
|
||||
style WINIT stroke:${colors.blue.dark},stroke-width:4px
|
||||
style RUST fill:${colors.orange.light},stroke:${colors.orange.dark},stroke-width:4px
|
||||
style WEBVIEW fill:${colors.blue.light},stroke:${colors.blue.dark},stroke-width:4px`} />
|
||||
|
||||
|
||||
## Configuration
|
||||
|
||||
Here's what you need to add to your tauri.conf.json file:
|
||||
```json
|
||||
"tauri": {
|
||||
"allowlist": {}, // all API endpoints are default false
|
||||
"windows": [{
|
||||
"title": "Window1",
|
||||
"label": "main",
|
||||
}, {
|
||||
"title": "Splash",
|
||||
"label": "splashscreen"
|
||||
}]
|
||||
}
|
||||
|
||||
```
|
@ -1,102 +0,0 @@
|
||||
---
|
||||
title: Write Tauri Plugins
|
||||
---
|
||||
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
<Alert title="Note" icon="info-alt">
|
||||
The Tauri CLI can bootstrap a Plugin project with the `$ tauri init plugin --name your-plugin-name` command.
|
||||
It setups the recommended folder structure, optionally adding a TypeScript API wrapper with the `--api` flag.
|
||||
</Alert>
|
||||
|
||||
Plugins allow you to hook into the Tauri application lifecycle and introduce new commands.
|
||||
|
||||
## Writing a Plugin
|
||||
|
||||
To write a plugin you just need to implement the `tauri::plugin::Plugin` trait:
|
||||
|
||||
```rust
|
||||
use tauri::{plugin::{Plugin, Result as PluginResult}, Runtime, PageLoadPayload, Window, Invoke, AppHandle};
|
||||
|
||||
struct MyAwesomePlugin<R: Runtime> {
|
||||
invoke_handler: Box<dyn Fn(Invoke<R>) + Send + Sync>,
|
||||
// plugin state, configuration fields
|
||||
}
|
||||
|
||||
// the plugin custom command handlers if you choose to extend the API.
|
||||
#[tauri::command]
|
||||
// this will be accessible with `invoke('plugin:awesome|initialize')`.
|
||||
// where `awesome` is the plugin name.
|
||||
fn initialize() {}
|
||||
|
||||
#[tauri::command]
|
||||
// this will be accessible with `invoke('plugin:awesome|do_something')`.
|
||||
fn do_something() {}
|
||||
|
||||
impl<R: Runtime> MyAwesomePlugin<R> {
|
||||
// you can add configuration fields here,
|
||||
// see https://doc.rust-lang.org/1.0.0/style/ownership/builders.html
|
||||
pub fn new() -> Self {
|
||||
Self {
|
||||
invoke_handler: Box::new(tauri::generate_handler![initialize, do_something]),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Plugin<R> for MyAwesomePlugin<R> {
|
||||
/// The plugin name. Must be defined and used on the `invoke` calls.
|
||||
fn name(&self) -> &'static str {
|
||||
"awesome"
|
||||
}
|
||||
|
||||
/// The JS script to evaluate on initialization.
|
||||
/// Useful when your plugin is accessible through `window`
|
||||
/// or needs to perform a JS task on app initialization
|
||||
/// e.g. "window.awesomePlugin = { ... the plugin interface }"
|
||||
fn initialization_script(&self) -> Option<String> {
|
||||
None
|
||||
}
|
||||
|
||||
/// initialize plugin with the config provided on `tauri.conf.json > plugins > $yourPluginName` or the default value.
|
||||
fn initialize(&mut self, app: &AppHandle<R>, config: serde_json::Value) -> PluginResult<()> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Callback invoked when the Window is created.
|
||||
fn created(&mut self, window: Window<R>) {}
|
||||
|
||||
/// Callback invoked when the webview performs a navigation.
|
||||
fn on_page_load(&mut self, window: Window<R>, payload: PageLoadPayload) {}
|
||||
|
||||
/// Extend the invoke handler.
|
||||
fn extend_api(&mut self, message: Invoke<R>) {
|
||||
(self.invoke_handler)(message)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
Note that each function on the `Plugin` trait is optional, except the `name` function.
|
||||
|
||||
## Using a plugin
|
||||
|
||||
To use a plugin, just pass an instance of the `MyAwesomePlugin` struct to the App's `plugin` method:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
let awesome_plugin = MyAwesomePlugin::new();
|
||||
tauri::Builder::default()
|
||||
.plugin(awesome_plugin)
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
```
|
||||
|
||||
## Official Tauri Plugins
|
||||
|
||||
- [Stronghold](https://github.com/tauri-apps/tauri-plugin-stronghold): Local Storage on native webview is not encrypted. Stronghold offers secure encrypted database service.
|
||||
- [Authenticator](https://github.com/tauri-apps/tauri-plugin-authenticator)
|
||||
- [Logging](https://github.com/tauri-apps/tauri-plugin-log)
|
||||
- [SQL](https://github.com/tauri-apps/tauri-plugin-sql)
|
||||
- [WebSocket](https://github.com/tauri-apps/tauri-plugin-websocket)
|
||||
- [Restoring window state](https://github.com/tauri-apps/tauri-plugin-window-state)
|
||||
- [Store](https://github.com/tauri-apps/tauri-plugin-store)
|
@ -1,106 +0,0 @@
|
||||
---
|
||||
title: Splashscreen
|
||||
---
|
||||
|
||||
import Link from '@docusaurus/Link'
|
||||
|
||||
If your webpage could take some time to load, or if you need to run an initialization procedure in Rust before displaying your main window, a splashscreen could improve the loading experience for the user.
|
||||
|
||||
### Setup
|
||||
|
||||
First, create a `splashscreen.html` in your `distDir` that contains the HTML code for a splashscreen. Then, update your `tauri.conf.json` like so:
|
||||
|
||||
```diff
|
||||
"windows": [
|
||||
{
|
||||
"title": "Tauri App",
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false,
|
||||
+ "visible": false // Hide the main window by default
|
||||
},
|
||||
// Add the splashscreen window
|
||||
+ {
|
||||
+ "width": 400,
|
||||
+ "height": 200,
|
||||
+ "decorations": false,
|
||||
+ "url": "splashscreen.html",
|
||||
+ "label": "splashscreen"
|
||||
+ }
|
||||
]
|
||||
```
|
||||
|
||||
Now, your main window will be hidden and the splashscreen window will show when your app is launched. Next, you'll need a way to close the splashscreen and show the main window when your app is ready. How you do this depends on what you are waiting for before closing the splashscreen.
|
||||
|
||||
### Waiting for Webpage
|
||||
|
||||
If you are waiting for your web code, you'll want to create a `close_splashscreen` [command](../command.md).
|
||||
|
||||
```rust title=src-tauri/main.rs
|
||||
use tauri::Manager;
|
||||
// Create the command:
|
||||
#[tauri::command]
|
||||
fn close_splashscreen(window: tauri::Window) {
|
||||
// Close splashscreen
|
||||
if let Some(splashscreen) = window.get_window("splashscreen") {
|
||||
splashscreen.close().unwrap();
|
||||
}
|
||||
// Show main window
|
||||
window.get_window("main").unwrap().show().unwrap();
|
||||
}
|
||||
|
||||
// Register the command:
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
// Add this line
|
||||
.invoke_handler(tauri::generate_handler![close_splashscreen])
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
|
||||
```
|
||||
|
||||
Then, you can call it from your JS:
|
||||
|
||||
```js
|
||||
// With the Tauri API npm package:
|
||||
import { invoke } from '@tauri-apps/api/tauri'
|
||||
// With the Tauri global script:
|
||||
const invoke = window.__TAURI__.invoke
|
||||
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// This will wait for the window to load, but you could
|
||||
// run this function on whatever trigger you want
|
||||
invoke('close_splashscreen')
|
||||
})
|
||||
```
|
||||
|
||||
### Waiting for Rust
|
||||
|
||||
If you are waiting for Rust code to run, put it in the `setup` function handler so you have access to the `App` instance:
|
||||
|
||||
```rust title=src-tauri/main.rs
|
||||
use tauri::Manager;
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.setup(|app| {
|
||||
let splashscreen_window = app.get_window("splashscreen").unwrap();
|
||||
let main_window = app.get_window("main").unwrap();
|
||||
// we perform the initialization code on a new task so the app doesn't freeze
|
||||
tauri::async_runtime::spawn(async move {
|
||||
// initialize your app here instead of sleeping :)
|
||||
println!("Initializing...");
|
||||
std::thread::sleep(std::time::Duration::from_secs(2));
|
||||
println!("Done initializing.");
|
||||
|
||||
// After it's done, close the splashscreen and display the main window
|
||||
splashscreen_window.close().unwrap();
|
||||
main_window.show().unwrap();
|
||||
});
|
||||
Ok(())
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("failed to run app");
|
||||
}
|
||||
```
|
@ -1,184 +0,0 @@
|
||||
---
|
||||
title: System Tray
|
||||
---
|
||||
|
||||
Native application system tray.
|
||||
|
||||
### Setup
|
||||
|
||||
Configure the `systemTray` object on `tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"tauri": {
|
||||
"systemTray": {
|
||||
"iconPath": "icons/icon.png",
|
||||
"iconAsTemplate": true
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
The `iconPath` is pointed to a PNG file on macOS and Linux, and a `.ico` file must exist for Windows support.
|
||||
|
||||
The `iconAsTemplate` is a boolean value that determines whether the image represents a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc) image on macOS.
|
||||
|
||||
|
||||
### Creating a system tray
|
||||
|
||||
To create a native system tray, import the `SystemTray` type:
|
||||
|
||||
```rust
|
||||
use tauri::SystemTray;
|
||||
```
|
||||
|
||||
Initialize a new tray instance:
|
||||
|
||||
```rust
|
||||
let tray = SystemTray::new();
|
||||
```
|
||||
|
||||
### Configuring a system tray context menu
|
||||
|
||||
Optionally you can add a context menu that is visible when the tray icon is right clicked. Import the `SystemTrayMenu`, `SystemTrayMenuItem` and `CustomMenuItem` types:
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, SystemTrayMenu, SystemTrayMenuItem};
|
||||
```
|
||||
|
||||
Create the `SystemTrayMenu`:
|
||||
|
||||
```rust
|
||||
// here `"quit".to_string()` defines the menu item id, and the second parameter is the menu item label.
|
||||
let quit = CustomMenuItem::new("quit".to_string(), "Quit");
|
||||
let hide = CustomMenuItem::new("hide".to_string(), "Hide");
|
||||
let tray_menu = SystemTrayMenu::new()
|
||||
.add_item(quit)
|
||||
.add_native_item(SystemTrayMenuItem::Separator)
|
||||
.add_item(hide);
|
||||
```
|
||||
|
||||
Add the tray menu to the `SystemTray` instance:
|
||||
|
||||
```rust
|
||||
let tray = SystemTray::new().with_menu(tray_menu);
|
||||
```
|
||||
|
||||
### Configure the app system tray
|
||||
|
||||
The created `SystemTray` instance can be set using the `system_tray` API on the `tauri::Builder` struct:
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
|
||||
fn main() {
|
||||
let tray_menu = SystemTrayMenu::new(); // insert the menu items here
|
||||
let system_tray = SystemTray::new()
|
||||
.with_menu(tray_menu);
|
||||
tauri::Builder::default()
|
||||
.system_tray(system_tray)
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### Listening to system tray events
|
||||
|
||||
Each `CustomMenuItem` triggers an event when clicked.
|
||||
Also, Tauri emits tray icon click events.
|
||||
Use the `on_system_tray_event` API to handle them:
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
let tray_menu = SystemTrayMenu::new(); // insert the menu items here
|
||||
tauri::Builder::default()
|
||||
.system_tray(SystemTray::new().with_menu(tray_menu))
|
||||
.on_system_tray_event(|app, event| match event {
|
||||
SystemTrayEvent::LeftClick {
|
||||
position: _,
|
||||
size: _,
|
||||
..
|
||||
} => {
|
||||
println!("system tray received a left click");
|
||||
}
|
||||
SystemTrayEvent::RightClick {
|
||||
position: _,
|
||||
size: _,
|
||||
..
|
||||
} => {
|
||||
println!("system tray received a right click");
|
||||
}
|
||||
SystemTrayEvent::DoubleClick {
|
||||
position: _,
|
||||
size: _,
|
||||
..
|
||||
} => {
|
||||
println!("system tray received a double click");
|
||||
}
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
match id.as_str() {
|
||||
"quit" => {
|
||||
std::process::exit(0);
|
||||
}
|
||||
"hide" => {
|
||||
let window = app.get_window("main").unwrap();
|
||||
window.hide().unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
### Updating system tray
|
||||
|
||||
The `AppHandle` struct has a `tray_handle` method, which returns a handle to the system tray allowing updating tray icon and context menu items:
|
||||
|
||||
#### Updating context menu items
|
||||
|
||||
```rust
|
||||
use tauri::{CustomMenuItem, SystemTray, SystemTrayMenu};
|
||||
use tauri::Manager;
|
||||
|
||||
fn main() {
|
||||
let tray_menu = SystemTrayMenu::new(); // insert the menu items here
|
||||
tauri::Builder::default()
|
||||
.system_tray(SystemTray::new().with_menu(tray_menu))
|
||||
.on_system_tray_event(|app, event| match event {
|
||||
SystemTrayEvent::MenuItemClick { id, .. } => {
|
||||
// get a handle to the clicked menu item
|
||||
// note that `tray_handle` can be called anywhere,
|
||||
// just get a `AppHandle` instance with `app.handle()` on the setup hook
|
||||
// and move it to another function or thread
|
||||
let item_handle = app.tray_handle().get_item(&id);
|
||||
match id.as_str() {
|
||||
"hide" => {
|
||||
let window = app.get_window("main").unwrap();
|
||||
window.hide().unwrap();
|
||||
// you can also `set_selected`, `set_enabled` and `set_native_image` (macOS only).
|
||||
item_handle.set_title("Show").unwrap();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
})
|
||||
.run(tauri::generate_context!())
|
||||
.expect("error while running tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
#### Updating tray icon
|
||||
|
||||
Note that `tauri::Icon` must be a `Path` variant on Linux, and `Raw` variant on Windows and macOS.
|
||||
|
||||
```rust
|
||||
app.tray_handle().set_icon(tauri::Icon::Raw(include_bytes!("../path/to/myicon.ico"))).unwrap();
|
||||
```
|
@ -1,308 +0,0 @@
|
||||
---
|
||||
title: Updater
|
||||
---
|
||||
|
||||
# Configuration
|
||||
|
||||
Once you have your Tauri project ready, you need to configure the updater.
|
||||
|
||||
Add this in tauri.conf.json
|
||||
```json
|
||||
"updater": {
|
||||
"active": true,
|
||||
"endpoints": [
|
||||
"https://releases.myapp.com/{{target}}/{{current_version}}"
|
||||
],
|
||||
"dialog": true,
|
||||
"pubkey": ""
|
||||
}
|
||||
```
|
||||
|
||||
The required keys are "active" and "endpoints", others are optional.
|
||||
|
||||
"active" must be a boolean. By default, it's set to false.
|
||||
|
||||
"endpoints" must be an array. The string `{{target}}` and `{{current_version}}` are automatically replaced in the URL allowing you determine [server-side](#update-server-json-format) if an update is available. If multiple endpoints are specified, the updater will fallback if a server is not responding within the pre-defined timeout.
|
||||
|
||||
"dialog" if present must be a boolean. By default, it's set to true. If enabled, [events](#events) are turned-off as the updater will handle everything. If you need the custom events, you MUST turn off the built-in dialog.
|
||||
|
||||
"pubkey" if present must be a valid public-key generated with Tauri cli. See [Signing updates](#signing-updates).
|
||||
|
||||
## Update Requests
|
||||
|
||||
Tauri is indifferent to the request the client application provides for update checking.
|
||||
|
||||
`Accept: application/json` is added to the request headers because Tauri is responsible for parsing the response.
|
||||
|
||||
For the requirements imposed on the responses and the body format of an update, response see [Server Support](#server-support).
|
||||
|
||||
Your update request must *at least* include a version identifier so that the server can determine whether an update for this specific version is required.
|
||||
|
||||
It may also include other identifying criteria such as operating system version, to allow the server to deliver as fine-grained an update as you would like.
|
||||
|
||||
How you include the version identifier or other criteria is specific to the server that you are requesting updates from. A common approach is to use query parameters, [Configuration](#configuration) shows an example of this.
|
||||
|
||||
## Built-in dialog
|
||||
|
||||
By default, updater uses a built-in dialog API from Tauri.
|
||||
|
||||
![New Update](https://i.imgur.com/UMilB5A.png)
|
||||
|
||||
The dialog release notes is represented by the update `note` provided by the [server](#server-support).
|
||||
|
||||
If the user accepts, the download and install are initialized. The user will be then prompted to restart the application.
|
||||
|
||||
## Javascript API
|
||||
|
||||
**Attention, you need to _disable built-in dialog_ in your [tauri configuration](#configuration), otherwise, events aren't emitted and the javascript API will NOT work.**
|
||||
|
||||
|
||||
```js
|
||||
import { checkUpdate, installUpdate } from "@tauri-apps/api/updater";
|
||||
import { relaunch } from "@tauri-apps/api/process";
|
||||
try {
|
||||
const {shouldUpdate, manifest} = await checkUpdate();
|
||||
if (shouldUpdate) {
|
||||
// display dialog
|
||||
await installUpdate();
|
||||
// install complete, restart app
|
||||
await relaunch();
|
||||
}
|
||||
} catch(error) {
|
||||
console.log(error);
|
||||
}
|
||||
```
|
||||
|
||||
## Events
|
||||
|
||||
**Attention, you need to _disable built-in dialog_ in your [tauri configuration](#configuration), otherwise, events aren't emitted.**
|
||||
|
||||
To know when an update is ready to be installed, you can subscribe to these events:
|
||||
|
||||
### Initialize updater and check if a new version is available
|
||||
|
||||
#### If a new version is available, the event `tauri://update-available` is emitted.
|
||||
|
||||
Event: `tauri://update`
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
window.emit("tauri://update".to_string(), None);
|
||||
```
|
||||
|
||||
### Javascript
|
||||
```js
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
emit("tauri://update");
|
||||
```
|
||||
|
||||
### Listen New Update Available
|
||||
|
||||
Event: `tauri://update-available`
|
||||
|
||||
Emitted data:
|
||||
```none
|
||||
version Version announced by the server
|
||||
date Date announced by the server
|
||||
body Note announced by the server
|
||||
```
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
window.listen("tauri://update-available".to_string(), move |msg| {
|
||||
println!("New version available: {:?}", msg);
|
||||
})
|
||||
```
|
||||
|
||||
### Javascript
|
||||
```js
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
listen("tauri://update-available", function (res) {
|
||||
console.log("New version available: ", res);
|
||||
});
|
||||
```
|
||||
|
||||
### Emit Install and Download
|
||||
|
||||
You need to emit this event to initialize the download and listen to the [install progress](#listen-install-progress).
|
||||
|
||||
Event: `tauri://update-install`
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
window.emit("tauri://update-install".to_string(), None);
|
||||
```
|
||||
|
||||
### Javascript
|
||||
```js
|
||||
import { emit } from "@tauri-apps/api/event";
|
||||
emit("tauri://update-install");
|
||||
```
|
||||
|
||||
### Listen Install Progress
|
||||
|
||||
Event: `tauri://update-status`
|
||||
|
||||
Emitted data:
|
||||
```none
|
||||
status [ERROR/PENDING/DONE]
|
||||
error String/null
|
||||
```
|
||||
|
||||
PENDING is emitted when the download is started and DONE when the install is complete. You can then ask to restart the application.
|
||||
|
||||
ERROR is emitted when there is an error with the updater. We suggest to listen to this event even if the dialog is enabled.
|
||||
|
||||
### Rust
|
||||
```rust
|
||||
window.listen("tauri://update-status".to_string(), move |msg| {
|
||||
println!("New status: {:?}", msg);
|
||||
})
|
||||
```
|
||||
|
||||
### Javascript
|
||||
```js
|
||||
import { listen } from "@tauri-apps/api/event";
|
||||
listen("tauri://update-status", function (res) {
|
||||
console.log("New status: ", res);
|
||||
});
|
||||
```
|
||||
|
||||
# Server Support
|
||||
|
||||
Your server should determine whether an update is required based on the [Update Request](#update-requests) your client issues.
|
||||
|
||||
If an update is required your server should respond with a status code of [200 OK](http://tools.ietf.org/html/rfc2616#section-10.2.1) and include the [update JSON](#update-server-json-format) in the body. To save redundantly downloading the same version multiple times your server must not inform the client to update.
|
||||
|
||||
If no update is required your server must respond with a status code of [204 No Content](http://tools.ietf.org/html/rfc2616#section-10.2.5).
|
||||
|
||||
## Update Server JSON Format
|
||||
|
||||
When an update is available, Tauri expects the following schema in response to the update request provided:
|
||||
|
||||
```json
|
||||
{
|
||||
"url": "https://mycompany.example.com/myapp/releases/myrelease.tar.gz",
|
||||
"version": "0.0.1",
|
||||
"notes": "Theses are some release notes",
|
||||
"pub_date": "2020-09-18T12:29:53+01:00",
|
||||
"signature": ""
|
||||
}
|
||||
```
|
||||
|
||||
The only required keys are "url" and "version", the others are optional.
|
||||
|
||||
"pub_date" if present must be formatted according to ISO 8601.
|
||||
|
||||
"signature" if present must be a valid signature generated with Tauri cli. See [Signing updates](#signing-updates).
|
||||
|
||||
## Update File JSON Format
|
||||
|
||||
The alternate update technique uses a plain JSON file meaning you can store your update metadata on S3, gist, or another static file store. Tauri will check against the name/version field and if the version is smaller than the current one and the platform is available, the update will be triggered. The format of this file is detailed below:
|
||||
|
||||
```json
|
||||
{
|
||||
"name":"v1.0.0",
|
||||
"notes":"Test version",
|
||||
"pub_date":"2020-06-22T19:25:57Z",
|
||||
"platforms": {
|
||||
"darwin": {
|
||||
"signature":"",
|
||||
"url":"https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.app.tar.gz"
|
||||
},
|
||||
"linux": {
|
||||
"signature":"",
|
||||
"url":"https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.AppImage.tar.gz"
|
||||
},
|
||||
"win64": {
|
||||
"signature":"",
|
||||
"url":"https://github.com/lemarier/tauri-test/releases/download/v1.0.0/app.x64.msi.zip"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
# Bundler (Artifacts)
|
||||
|
||||
The Tauri bundler will automatically generate update artifacts if the updater is enabled in `tauri.conf.json`
|
||||
|
||||
If the bundler can locate your private and pubkey, your update artifacts will be automatically signed.
|
||||
|
||||
The signature can be found in the `sig` file. The signature can be uploaded to GitHub safely or made public as long as your private key is secure.
|
||||
|
||||
You can see how it's [bundled with the CI](https://github.com/tauri-apps/tauri/blob/5b6c7bb6ee3661f5a42917ce04a89d94f905c949/.github/workflows/artifacts-updater.yml#L44) and a [sample tauri.conf.json](https://github.com/tauri-apps/tauri/blob/5b6c7bb6ee3661f5a42917ce04a89d94f905c949/examples/updater/src-tauri/tauri.conf.json#L52)
|
||||
|
||||
## macOS
|
||||
|
||||
On MACOS we create a .tar.gz from the whole application. (.app)
|
||||
|
||||
```none
|
||||
target/release/bundle
|
||||
└── osx
|
||||
└── app.app
|
||||
└── app.app.tar.gz (update bundle)
|
||||
└── app.app.tar.gz.sig (if signature enabled)
|
||||
```
|
||||
|
||||
## Windows
|
||||
|
||||
On Windows we create a .zip from the MSI, when downloaded and validated, we run the MSI install.
|
||||
|
||||
```none
|
||||
target/release
|
||||
└── app.x64.msi
|
||||
└── app.x64.msi.zip (update bundle)
|
||||
└── app.x64.msi.zip.sig (if signature enabled)
|
||||
```
|
||||
|
||||
## Linux
|
||||
|
||||
On Linux, we create a .tar.gz from the AppImage.
|
||||
|
||||
```none
|
||||
target/release/bundle
|
||||
└── appimage
|
||||
└── app.AppImage
|
||||
└── app.AppImage.tar.gz (update bundle)
|
||||
└── app.AppImage.tar.gz.sig (if signature enabled)
|
||||
```
|
||||
|
||||
# Signing updates
|
||||
|
||||
We offer a built-in signature to ensure your update is safe to be installed.
|
||||
|
||||
To sign your updates, you need two things.
|
||||
|
||||
The *Public-key* (pubkey) should be added inside your `tauri.conf.json` to validate the update archive before installing.
|
||||
|
||||
The *Private key* (privkey) is used to sign your update and should NEVER be shared with anyone. Also, if you lost this key, you'll NOT be able to publish a new update to the current user base (if pubkey is set in tauri.conf.json). It's important to save it at a safe place and you can always access it.
|
||||
|
||||
To generate your keys you need to use the Tauri cli.
|
||||
|
||||
```bash
|
||||
tauri signer sign -g -w ~/.tauri/myapp.key
|
||||
```
|
||||
|
||||
You have multiple options available
|
||||
```bash
|
||||
Tauri updates signer.
|
||||
USAGE:
|
||||
tauri signer sign [FLAGS] [OPTIONS]
|
||||
FLAGS:
|
||||
--force Overwrite private key even if it exists on the specified path
|
||||
-g, --generate Generate keypair to sign files
|
||||
-h, --help Prints help information
|
||||
--no-password Set empty password for your private key
|
||||
-V, --version Prints version information
|
||||
OPTIONS:
|
||||
-p, --password <password> Set private key password when signing
|
||||
-k, --private-key <private-key> Load the private key from a string
|
||||
-f, --private-key-path <private-key-path> Load the private key from a file
|
||||
--sign-file <sign-file> Sign the specified file
|
||||
-w, --write-keys <write-keys> Write private key to a file
|
||||
```
|
||||
***
|
||||
Environment variables used to sign with the Tauri `bundler`:
|
||||
If they are set, and `tauri.conf.json` expose the public key, the bundler will automatically generate and sign the updater artifacts.
|
||||
`TAURI_PRIVATE_KEY` Path or String of your private key
|
||||
`TAURI_KEY_PASSWORD` Your private key password (optional)
|
@ -1,100 +0,0 @@
|
||||
---
|
||||
title: Continuous Integration
|
||||
---
|
||||
|
||||
Utilizing Linux and some programs to create a fake display, it is possible to run [WebDriver] tests with
|
||||
[`tauri-driver`] on your CI. The following example will use the [WebdriverIO] example we [previously built together] and
|
||||
GitHub Actions.
|
||||
|
||||
This means the following assumptions:
|
||||
|
||||
1. The Tauri application is in the repository root and the binary builds when running `cargo build --release`.
|
||||
2. The [WebDriverIO] test runner is in the `webdriver/webdriverio` directory and runs when `yarn test` is used in that
|
||||
directory.
|
||||
|
||||
The following is a commented GitHub Actions workflow file at `.github/workflows/webdriver.yml`
|
||||
|
||||
```yaml
|
||||
# run this action when the repository is pushed to
|
||||
on: [ push ]
|
||||
|
||||
# the name of our workflow
|
||||
name: WebDriver
|
||||
|
||||
jobs:
|
||||
# a single job named test
|
||||
test:
|
||||
# the display name the test job
|
||||
name: WebDriverIO Test Runner
|
||||
|
||||
# we want to run on the latest linux environment
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
# the steps our job runs **in order**
|
||||
steps:
|
||||
# checkout the code on the workflow runner
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
# install system dependencies that Tauri needs to compile on Linux.
|
||||
# note the extra dependencies for `tauri-driver` to run which are `webkit2gtk-driver` and `xvfb`
|
||||
- name: Tauri dependencies
|
||||
run: >-
|
||||
sudo apt-get update &&
|
||||
sudo apt-get install -y
|
||||
libgtk-3-dev
|
||||
libgtksourceview-3.0-dev
|
||||
webkit2gtk-4.0
|
||||
libappindicator3-dev
|
||||
webkit2gtk-driver
|
||||
xvfb
|
||||
|
||||
# install the latest Rust stable
|
||||
- name: Rust stable
|
||||
uses: actions-rs/toolchain@v1
|
||||
with:
|
||||
toolchain: stable
|
||||
|
||||
# we run our rust tests before the webdriver tests to avoid testing a broken application
|
||||
- name: Cargo test
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: test
|
||||
|
||||
# build a release build of our application to be used during our WebdriverIO tests
|
||||
- name: Cargo build
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: build
|
||||
args: --release
|
||||
|
||||
# install the latest stable node version at the time of writing
|
||||
- name: Node v16
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: 16.x
|
||||
|
||||
# install our Node.js dependencies with Yarn
|
||||
- name: Yarn install
|
||||
run: yarn install
|
||||
working-directory: webdriver/webdriverio
|
||||
|
||||
# install the latest version of `tauri-driver`.
|
||||
# note: the tauri-driver version is independent of any other Tauri versions
|
||||
- name: Install tauri-driver
|
||||
uses: actions-rs/cargo@v1
|
||||
with:
|
||||
command: install
|
||||
args: tauri-driver
|
||||
|
||||
# run the WebdriverIO test suite.
|
||||
# we run it through `xvfb-run` (the dependency we installed earlier) to have a fake
|
||||
# display server which allows our application to run headless without any changes to the code
|
||||
- name: WebdriverIO
|
||||
run: xvfb-run yarn test
|
||||
working-directory: webdriver/webdriverio
|
||||
```
|
||||
|
||||
[WebDriver]: https://www.w3.org/TR/webdriver/
|
||||
[`tauri-driver`]: https://crates.io/crates/tauri-driver
|
||||
[WebdriverIO]: https://webdriver.io/
|
||||
[previously built together]: example/webdriverio
|
@ -1,256 +0,0 @@
|
||||
---
|
||||
title: Selenium
|
||||
---
|
||||
import Alert from '@theme/Alert'
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
<Alert title="Example Application" type="info" icon="info-alt">
|
||||
|
||||
This [Selenium] guide expects you to have already gone through the [example Application setup] in order to follow
|
||||
step-by-step. The general information may still be useful otherwise.
|
||||
</Alert>
|
||||
|
||||
This WebDriver testing example will use [Selenium] and a popular Node.js testing suite. It is expected to already have
|
||||
Node.js installed, along with `npm` or `yarn` although the [finished example project] uses `yarn`.
|
||||
|
||||
## Create a Directory for the Tests
|
||||
|
||||
Let's start off by creating a space in our project to write these tests. We are going to be using a nested directory for
|
||||
this example project as we will later also go over other frameworks, but typically you will only need to use one. Create
|
||||
the directory we will use with `mkdir -p webdriver/selenium`. The rest of this guide will assume you are inside the
|
||||
`webdriver/selenium` directory.
|
||||
|
||||
## Initializing a Selenium Project
|
||||
|
||||
We will be using a pre-existing `package.json` to bootstrap this test suite because we have already chosen specific
|
||||
dependencies to use and want to showcase a simple working solution. The bottom of this section has a collapsed
|
||||
guide on how to set it up from scratch.
|
||||
|
||||
`package.json`:
|
||||
```json
|
||||
{
|
||||
"name": "selenium",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "mocha"
|
||||
},
|
||||
"dependencies": {
|
||||
"chai": "^4.3.4",
|
||||
"mocha": "^9.0.3",
|
||||
"selenium-webdriver": "^4.0.0-beta.4"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We have a script which runs [Mocha] as a test framework exposed as the `test` command. We also have various dependencies
|
||||
that we will be using to run the tests. [Mocha] as the testing framework, [Chai] as the assertion library, and
|
||||
[`selenium-webdriver`] which is the Node.js [Selenium] package.
|
||||
|
||||
<details><summary>Click me if you want to see how to set a project up from scratch</summary>
|
||||
|
||||
If you wanted to install the dependencies from scratch, just run the following command.
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm install mocha chai selenium-webdriver
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn add mocha chai selenium-webdriver
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
I suggest also adding a `"test": "mocha"` item in the `package.json` `"scripts"` key so that running mocha can be called
|
||||
simply with
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
## Testing
|
||||
|
||||
Unlike the [WebdriverIO Test Suite](webdriverio#config), Selenium does not come out of the box with a Test Suite and
|
||||
leaves it up to the developer to build those out. We chose [Mocha] which is pretty neutral, and not related to WebDrivers
|
||||
at all, so our script will need to do a bit of work to set up everything for us in the right order. [Mocha] expects a
|
||||
testing file at `test/test.js` by default, so let's create that file now.
|
||||
|
||||
`test/test.js`:
|
||||
```js
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const { expect } = require("chai");
|
||||
const { spawn, spawnSync } = require("child_process");
|
||||
const { Builder, By, Capabilities } = require("selenium-webdriver");
|
||||
|
||||
// create the path to the expected application binary
|
||||
const application = path.resolve(
|
||||
__dirname,
|
||||
"..",
|
||||
"..",
|
||||
"..",
|
||||
"target",
|
||||
"release",
|
||||
"hello-tauri-webdriver"
|
||||
);
|
||||
|
||||
// keep track of the webdriver instance we create
|
||||
let driver;
|
||||
|
||||
// keep track of the tauri-driver process we start
|
||||
let tauriDriver;
|
||||
|
||||
before(async function() {
|
||||
// set timeout to 2 minutes to allow the program to build if it needs to
|
||||
this.timeout(120000)
|
||||
|
||||
// ensure the program has been built
|
||||
spawnSync("cargo", ["build", "--release"]);
|
||||
|
||||
// start tauri-driver
|
||||
tauriDriver = spawn(
|
||||
path.resolve(os.homedir(), ".cargo", "bin", "tauri-driver"),
|
||||
[],
|
||||
{ stdio: [null, process.stdout, process.stderr] }
|
||||
);
|
||||
|
||||
const capabilities = new Capabilities();
|
||||
capabilities.set("tauri:options", { application, args: ["app", "cli", "args"] });
|
||||
capabilities.setBrowserName("wry");
|
||||
|
||||
// start the webdriver client
|
||||
driver = await new Builder()
|
||||
.withCapabilities(capabilities)
|
||||
.usingServer("http://localhost:4444/")
|
||||
.build();
|
||||
});
|
||||
|
||||
after(async function() {
|
||||
// stop the webdriver session
|
||||
await driver.quit();
|
||||
|
||||
// kill the tauri-driver process
|
||||
tauriDriver.kill();
|
||||
});
|
||||
|
||||
describe("Hello Tauri", () => {
|
||||
it("should be cordial", async () => {
|
||||
const text = await driver.findElement(By.css("body > h1")).getText();
|
||||
expect(text).to.match(/^[hH]ello/);
|
||||
});
|
||||
|
||||
it("should be excited", async () => {
|
||||
const text = await driver.findElement(By.css("body > h1")).getText();
|
||||
expect(text).to.match(/!$/);
|
||||
});
|
||||
|
||||
it("should be easy on the eyes", async () => {
|
||||
// selenium returns color css values as rgb(r, g, b)
|
||||
const text = await driver.findElement(By.css("body")).getCssValue("background-color");
|
||||
|
||||
const rgb = text.match(/^rgb\((?<r>\d+), (?<g>\d+), (?<b>\d+)\)$/).groups;
|
||||
expect(rgb).to.have.all.keys('r','g','b');
|
||||
|
||||
const luma = 0.2126 * rgb.r + 0.7152 * rgb.g + 0.0722 * rgb.b ;
|
||||
expect(luma).to.be.lessThan(100)
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
If you are familiar with JS testing frameworks, `describe`, `it`, and `expect` should look familiar. We also have
|
||||
semi-complex `before()` and `after()` callbacks to setup and teardown mocha. Lines that are not the tests themselves
|
||||
have comments explaining what the setup and teardown code is doing. If you were familiar with the Spec file from the
|
||||
[WebdriverIO example](webdriverio#spec), you will notice a lot more code that isn't tests, as we have to set up a few
|
||||
more WebDriver related items.
|
||||
|
||||
## Running the Test Suite
|
||||
|
||||
Now that we are all set up with our dependencies and our test script, lets run it!
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
We should see output the following output:
|
||||
|
||||
```text
|
||||
➜ selenium git:(main) ✗ yarn test
|
||||
yarn run v1.22.11
|
||||
$ mocha
|
||||
|
||||
|
||||
Hello Tauri
|
||||
✔ should be cordial (120ms)
|
||||
✔ should be excited
|
||||
✔ should be easy on the eyes
|
||||
|
||||
|
||||
3 passing (588ms)
|
||||
|
||||
Done in 0.93s.
|
||||
```
|
||||
|
||||
We can see that our `Hello Tauri` sweet we created with `decribe` had all 3 items we created with `it` pass their
|
||||
tests!
|
||||
|
||||
With [Selenium] and some hooking up to a test suite, we just enabled e2e testing without modifying our Tauri
|
||||
application at all!
|
||||
|
||||
|
||||
[Selenium]: https://selenium.dev/
|
||||
[finished example project]: https://github.com/chippers/hello_tauri
|
||||
[example Application setup]: setup
|
||||
[Mocha]: https://mochajs.org/
|
||||
[Chai]: https://www.chaijs.com/
|
||||
[`selenium-webdriver`]: https://www.npmjs.com/package/selenium-webdriver
|
@ -1,191 +0,0 @@
|
||||
---
|
||||
title: Setup Example
|
||||
---
|
||||
|
||||
import HelloTauriWebdriver from '@site/static/img/webdriver/hello-tauri-webdriver.png'
|
||||
|
||||
This example application is going to solely focus on adding WebDriver testing to an already existing project. To have a
|
||||
project to test on in the next two sections, we are going to set up an extremely minimal Tauri application for use in
|
||||
our testing. We will not use the Tauri CLI, any frontend dependencies or build steps, and not be bundling the
|
||||
application afterwards. This is to showcase exactly a minimal suite to show off adding WebDriver testing to an existing
|
||||
application.
|
||||
|
||||
If you just want to see the finished example project that utilizes what will be shown in this example guide, then you
|
||||
can see https://github.com/chippers/hello_tauri.
|
||||
|
||||
## Initializing a Cargo Project
|
||||
|
||||
We want to create a new binary Cargo project to house this example application. We can easily do this from the command
|
||||
line with `cargo new hello-tauri-webdriver --bin` which will scaffold a minimal binary Cargo project for us. This
|
||||
directory will serve as the working directory for the rest of this guide, so make sure commands you run are inside this
|
||||
new `hello-tauri-webdriver/` directory.
|
||||
|
||||
## Creating a Minimal Frontend
|
||||
|
||||
We will create a minimal HTML file to act as the frontend to our example application. We will also be using a few things
|
||||
from this frontend later during our WebDriver tests.
|
||||
|
||||
First, let's create our Tauri `distDir` that we know we will need once building the Tauri portion of the application.
|
||||
`mkdir dist` should create a new directory called `dist/` in which we will be placing the following `index.html` file.
|
||||
|
||||
`dist/index.html`:
|
||||
|
||||
```html
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<title>Hello Tauri!</title>
|
||||
<style>
|
||||
body {
|
||||
/* Add a nice colorscheme */
|
||||
background-color: #222831;
|
||||
color: #ececec;
|
||||
|
||||
/* Make the body the exact size of the window */
|
||||
margin: 0;
|
||||
height: 100vh;
|
||||
width: 100vw;
|
||||
|
||||
/* Vertically and horizontally center children of the body tag */
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Hello, Tauri!</h1>
|
||||
</body>
|
||||
</html>
|
||||
```
|
||||
|
||||
## Adding Tauri to the Cargo Project
|
||||
|
||||
Next, we will add some necessary items to make our Cargo project into a Tauri project. First, is adding the dependencies
|
||||
to the Cargo Manifest (`Cargo.toml`) so that Cargo knows to pull in our dependencies while building.
|
||||
|
||||
`Cargo.toml`:
|
||||
|
||||
```toml
|
||||
[package]
|
||||
name = "hello-tauri-webdriver"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
rust-version = "1.57"
|
||||
|
||||
# Needed to set up some things for Tauri at build time
|
||||
[build-dependencies]
|
||||
tauri-build = "1.0.0-beta.4"
|
||||
|
||||
# The actual Tauri dependency, along with `custom-protocol` to serve the pages.
|
||||
[dependencies]
|
||||
tauri = { version = "1.0.0-beta.6", features = ["custom-protocol"] }
|
||||
|
||||
# Make --release build a binary that is small (opt-level = "s") and fast (lto = true).
|
||||
# This is completely optional, but shows that testing the application as close to the
|
||||
# typical release settings is possible. Note: this will slow down compilation.
|
||||
[profile.release]
|
||||
incremental = false
|
||||
codegen-units = 1
|
||||
panic = "abort"
|
||||
opt-level = "s"
|
||||
lto = true
|
||||
```
|
||||
|
||||
As you may have noticed, we added a `[build-dependency]`. To use the build dependency, we must use it from a build
|
||||
script. We will create one now at `build.rs`.
|
||||
|
||||
`build.rs`:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
// Only watch the `dist/` directory for recompiling, preventing unnecessary
|
||||
// changes when we change files in other project subdirectories.
|
||||
println!("cargo:rerun-if-changed=dist");
|
||||
|
||||
// Run the Tauri build-time helpers
|
||||
tauri_build::build()
|
||||
}
|
||||
```
|
||||
|
||||
With all that setup, our Cargo Project now knows how to pull in and build our Tauri dependencies. Let's finish making
|
||||
this minimal example a Tauri application by setting up Tauri in the actual project code. We will be editing
|
||||
the `src/main.rs`
|
||||
file to add this Tauri functionality.
|
||||
|
||||
`src/main.rs`:
|
||||
|
||||
```rust
|
||||
fn main() {
|
||||
tauri::Builder::default()
|
||||
.run(tauri::generate_context!())
|
||||
.expect("unable to run Tauri application");
|
||||
}
|
||||
```
|
||||
|
||||
Pretty simple, right?
|
||||
|
||||
## Tauri Configuration
|
||||
|
||||
We are going to need 2 things to successfully build the application. First, we need an icon file. You can use any PNG
|
||||
for this next part and copy it into `icon.png`. Typically, this will be provided as part of the scaffolding when you use
|
||||
the Tauri CLI to create a project. To get the default Tauri icon, we can download the icon used by the Hello Tauri
|
||||
example repository with the
|
||||
command `curl -L "https://github.com/chippers/hello_tauri/raw/main/icon.png" --output icon.png`.
|
||||
|
||||
The second thing we will need is a `tauri.conf.json` to specify some important configuration values to Tauri. Again,
|
||||
this would typically come from the `tauri init` scaffolding command, but we will be creating our own minimal config
|
||||
here.
|
||||
|
||||
`tauri.conf.json`:
|
||||
|
||||
```json
|
||||
{
|
||||
"build": {
|
||||
"distDir": "dist"
|
||||
},
|
||||
"tauri": {
|
||||
"bundle": {
|
||||
"identifier": "studio.tauri.hello_tauri_webdriver",
|
||||
"icon": [
|
||||
"icon.png"
|
||||
]
|
||||
},
|
||||
"allowlist": {
|
||||
"all": false
|
||||
},
|
||||
"windows": [
|
||||
{
|
||||
"width": 800,
|
||||
"height": 600,
|
||||
"resizable": true,
|
||||
"fullscreen": false
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
I'll go over some of these. You can see the `dist/` directory we created earlier specified as the `distDir` property. We
|
||||
set a bundle identifier so that the built application has a unique id, along with setting the `icon.png` as the only
|
||||
icon. We aren't using any Tauri apis or features, so we just disable all them in `allowlist` by setting `"all": false`.
|
||||
The window values just sets a single window to be created with some reasonable default values.
|
||||
|
||||
At this point, we have a basic Hello World application that when ran, should display a simple greeting.
|
||||
|
||||
## Running the Example Application
|
||||
|
||||
To make sure we did it right, let's build this application! We will run this as a `--release` application because we
|
||||
will also run our WebDriver tests with a release profile. Run `cargo run --release` and after some compiling we should
|
||||
see the following application pop up.
|
||||
|
||||
<div style={{textAlign: 'center'}}>
|
||||
<img src={HelloTauriWebdriver}/>
|
||||
</div>
|
||||
|
||||
_Note: If you are modifying the application and want to use the Devtools, then run it without `--release` and "Inspect
|
||||
Element" should be available in the right click menu._
|
||||
|
||||
We should now be ready to start testing this application with some WebDriver frameworks. This guide will go over both
|
||||
[WebdriverIO](webdriverio) and [Selenium](selenium) in that order.
|
@ -1,276 +0,0 @@
|
||||
---
|
||||
title: WebdriverIO
|
||||
---
|
||||
import Alert from '@theme/Alert'
|
||||
import Tabs from '@theme/Tabs'
|
||||
import TabItem from '@theme/TabItem'
|
||||
|
||||
<Alert title="Example Application" type="info" icon="info-alt">
|
||||
|
||||
This [WebdriverIO] guide expects you to have already gone through the [example Application setup] in order to follow
|
||||
step-by-step. The general information may still be useful otherwise.
|
||||
</Alert>
|
||||
|
||||
This WebDriver testing example will use [WebdriverIO] and its testing suite. It is expected to already have Node.js
|
||||
installed, along with `npm` or `yarn` although the [finished example project] uses `yarn`.
|
||||
|
||||
## Create a Directory for the Tests
|
||||
|
||||
Let's start off by creating a space in our project to write these tests. We are going to be using a nested directory for
|
||||
this example project as we will later also go over other frameworks, but typically you will only need to use one. Create
|
||||
the directory we will use with `mkdir -p webdriver/webdriverio`. The rest of this guide will assume you are inside the
|
||||
`webdriver/webdriverio` directory.
|
||||
|
||||
## Initializing a WebdriverIO Project
|
||||
|
||||
We will be using a pre-existing `package.json` to bootstrap this test suite because we have already chosen specific
|
||||
[WebdriverIO] config options and want to showcase a simple working solution. The bottom of this section has a collapsed
|
||||
guide on how to set it up from scratch.
|
||||
|
||||
`package.json`:
|
||||
```json
|
||||
{
|
||||
"name": "webdriverio",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"test": "wdio run wdio.conf.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@wdio/cli": "^7.9.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@wdio/local-runner": "^7.9.1",
|
||||
"@wdio/mocha-framework": "^7.9.1",
|
||||
"@wdio/spec-reporter": "^7.9.0"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
We have a script which runs a [WebdriverIO] config as a test suite exposed as the `test` command. We also have various
|
||||
dependencies that were added by the `@wdio/cli` command when we first set it up. In short, these dependencies are for
|
||||
the most simple setup using a local WebDriver runner, [Mocha] as the test framework, and a simple Spec Reporter.
|
||||
|
||||
<details><summary>Click me if you want to see how to set a project up from scratch</summary>
|
||||
|
||||
The CLI is interactive, and you may choose the tools to work with yourself. Note that you will likely diverge from
|
||||
the rest of the guide, and need to set up the differences yourself.
|
||||
|
||||
Let's add the [WebdriverIO] CLI to this npm project.
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm install @wdio/cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn add @wdio/cli
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
To then run the interactive config command to set up a [WebdriverIO] test suite, you can then run:
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npx wdio config
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn wdio config
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
</details>
|
||||
|
||||
## Config
|
||||
|
||||
You may have noticed that the `test` script in our `package.json` mentions a file `wdio.conf.js`. That's the [WebdriverIO]
|
||||
config file which controls most aspects of our testing suite.
|
||||
|
||||
`wdio.conf.js`:
|
||||
```js
|
||||
const os = require("os");
|
||||
const path = require("path");
|
||||
const { spawn, spawnSync } = require("child_process");
|
||||
|
||||
// keep track of the `tauri-driver` child process
|
||||
let tauriDriver;
|
||||
|
||||
exports.config = {
|
||||
specs: ["./test/specs/**/*.js"],
|
||||
maxInstances: 1,
|
||||
capabilities: [
|
||||
{
|
||||
maxInstances: 1,
|
||||
"tauri:options": {
|
||||
application: "../../target/release/hello-tauri-webdriver",
|
||||
args: ["app", "cli", "args"]
|
||||
},
|
||||
},
|
||||
],
|
||||
reporters: ["spec"],
|
||||
framework: "mocha",
|
||||
mochaOpts: {
|
||||
ui: "bdd",
|
||||
timeout: 60000,
|
||||
},
|
||||
|
||||
// ensure the rust project is built since we expect this binary to exist for the webdriver sessions
|
||||
onPrepare: () => spawnSync("cargo", ["build", "--release"]),
|
||||
|
||||
// ensure we are running `tauri-driver` before the session starts so that we can proxy the webdriver requests
|
||||
beforeSession: () =>
|
||||
(tauriDriver = spawn(
|
||||
path.resolve(os.homedir(), ".cargo", "bin", "tauri-driver"),
|
||||
[],
|
||||
{ stdio: [null, process.stdout, process.stderr] }
|
||||
)),
|
||||
|
||||
// clean up the `tauri-driver` process we spawned at the start of the session
|
||||
afterSession: () => tauriDriver.kill(),
|
||||
};
|
||||
```
|
||||
|
||||
If you are interested in the properties on `exports.config` object, then I [suggest reading the documentation] for it.
|
||||
For non-WDIO specific items, there are comments explaining why we are running commands in `onPrepare`, `beforeSession`,
|
||||
and `afterSession`. We also have our specs set to `"./test/specs/**/*.js"`, so let's create a spec now.
|
||||
|
||||
## Spec
|
||||
|
||||
A spec contains the code that is testing your actual application. The test runner will load these specs and automatically
|
||||
run them as it sees fit. Let's create our spec now in the directory we specified.
|
||||
|
||||
`test/specs/example.e2e.js`:
|
||||
```js
|
||||
// calculates the luma from a hex color `#abcdef`
|
||||
function luma(hex) {
|
||||
if (hex.startsWith("#")) {
|
||||
hex = hex.substring(1);
|
||||
}
|
||||
|
||||
const rgb = parseInt(hex, 16);
|
||||
const r = (rgb >> 16) & 0xff;
|
||||
const g = (rgb >> 8) & 0xff;
|
||||
const b = (rgb >> 0) & 0xff;
|
||||
return 0.2126 * r + 0.7152 * g + 0.0722 * b;
|
||||
}
|
||||
|
||||
describe("Hello Tauri", () => {
|
||||
it("should be cordial", async () => {
|
||||
const header = await $("body > h1");
|
||||
const text = await header.getText();
|
||||
expect(text).toMatch(/^[hH]ello/);
|
||||
});
|
||||
|
||||
it("should be excited", async () => {
|
||||
const header = await $("body > h1");
|
||||
const text = await header.getText();
|
||||
expect(text).toMatch(/!$/);
|
||||
});
|
||||
|
||||
it("should be easy on the eyes", async () => {
|
||||
const body = await $("body");
|
||||
const backgroundColor = await body.getCSSProperty("background-color");
|
||||
expect(luma(backgroundColor.parsed.hex)).toBeLessThan(100);
|
||||
});
|
||||
});
|
||||
```
|
||||
|
||||
The `luma` function on top is just a helper function for one of our tests and is not related to the actual testing of
|
||||
the application. If you are familiar with other testing frameworks, you may notice similar functions being exposed that
|
||||
are used such as `describe`, `it`, and `expect`. The other APIs, such as items like `$` and the methods it exposes is
|
||||
covered by the [WebdriverIO API docs](https://webdriver.io/docs/api).
|
||||
|
||||
## Running the Test Suite
|
||||
|
||||
Now that we are all set up with a config and a spec, let's run it!
|
||||
|
||||
<Tabs groupId="package-manager"
|
||||
defaultValue="yarn"
|
||||
values={[
|
||||
{label: 'npm', value: 'npm'}, {label: 'Yarn', value: 'yarn'},
|
||||
]}>
|
||||
<TabItem value="npm">
|
||||
|
||||
```sh
|
||||
npm test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
||||
<TabItem value="yarn">
|
||||
|
||||
```sh
|
||||
yarn test
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
We should see output the following output:
|
||||
|
||||
```text
|
||||
➜ webdriverio git:(main) ✗ yarn test
|
||||
yarn run v1.22.11
|
||||
$ wdio run wdio.conf.js
|
||||
|
||||
Execution of 1 workers started at 2021-08-17T08:06:10.279Z
|
||||
|
||||
[0-0] RUNNING in undefined - /test/specs/example.e2e.js
|
||||
[0-0] PASSED in undefined - /test/specs/example.e2e.js
|
||||
|
||||
"spec" Reporter:
|
||||
------------------------------------------------------------------
|
||||
[wry 0.12.1 linux #0-0] Running: wry (v0.12.1) on linux
|
||||
[wry 0.12.1 linux #0-0] Session ID: 81e0107b-4d38-4eed-9b10-ee80ca47bb83
|
||||
[wry 0.12.1 linux #0-0]
|
||||
[wry 0.12.1 linux #0-0] » /test/specs/example.e2e.js
|
||||
[wry 0.12.1 linux #0-0] Hello Tauri
|
||||
[wry 0.12.1 linux #0-0] ✓ should be cordial
|
||||
[wry 0.12.1 linux #0-0] ✓ should be excited
|
||||
[wry 0.12.1 linux #0-0] ✓ should be easy on the eyes
|
||||
[wry 0.12.1 linux #0-0]
|
||||
[wry 0.12.1 linux #0-0] 3 passing (244ms)
|
||||
|
||||
|
||||
Spec Files: 1 passed, 1 total (100% completed) in 00:00:01
|
||||
|
||||
Done in 1.98s.
|
||||
```
|
||||
|
||||
We see the Spec Reporter tell us that all 3 tests from the `test/specs/example.e2e.js` file, along with the final report
|
||||
`Spec Files: 1 passed, 1 total (100% completed) in 00:00:01`.
|
||||
|
||||
Using the [WebdriverIO] test suite, we just easily enabled e2e testing for our Tauri application from just a few lines
|
||||
of configuration and a single command to run it! Even better, we didn't have to modify the application at all.
|
||||
|
||||
|
||||
[WebdriverIO]: https://webdriver.io/
|
||||
[finished example project]: https://github.com/chippers/hello_tauri
|
||||
[example Application setup]: setup
|
||||
[Mocha]: https://mochajs.org/
|
||||
[suggest reading the documentation]: (https://webdriver.io/docs/configurationfile)
|
@ -1,56 +0,0 @@
|
||||
---
|
||||
title: Introduction
|
||||
---
|
||||
import Alert from '@theme/Alert'
|
||||
|
||||
<Alert title="Currently in pre-alpha" type="info" icon="info-alt">
|
||||
|
||||
Webdriver support for Tauri is still in pre-alpha. Tooling that is dedicated to it such as [tauri-driver] is still in
|
||||
active development and may change as necessary over time. Additionally, only Windows and Linux are currently supported.
|
||||
</Alert>
|
||||
|
||||
[WebDriver] is a standardized interface to interact with web documents that is primarily intended for automated testing.
|
||||
Tauri supports the [WebDriver] interface by leveraging the native platform's [WebDriver] server underneath a
|
||||
cross-platform wrapper [`tauri-driver`].
|
||||
|
||||
## System Dependencies
|
||||
|
||||
Install the latest [`tauri-driver`] or update an existing installation by running:
|
||||
|
||||
```sh
|
||||
cargo install tauri-driver
|
||||
```
|
||||
|
||||
Because we currently utilize the platform's native [WebDriver] server, there are some requirements for running
|
||||
[`tauri-driver`] on supported platforms. Platform support is currently limited to Linux and Windows.
|
||||
|
||||
### Linux
|
||||
|
||||
We use `WebKitWebDriver` on linux platforms. Check if this binary exists already (command `which WebKitWebDriver`) as
|
||||
some distributions bundle it with the regular webkit package. Other platforms may have a separate package for them such
|
||||
as `webkit2gtk-driver` on Debian based distributions.
|
||||
|
||||
### Windows
|
||||
|
||||
Make sure to grab the version of [Microsoft Edge Driver] that matches your Windows' Edge version that the application is
|
||||
being built and tested on. On up-to-date Window installs, this should almost always be the latest stable version. If the
|
||||
two versions do not match, you may experience your WebDriver testing suite hanging while trying to connect.
|
||||
|
||||
The download contains a binary called `msedgedriver.exe`. [`tauri-driver`] looks for that binary in the `$PATH` so make
|
||||
sure it's either available on the path or use the `--native-driver` option on [`tauri-driver`]. On Windows CI machines,
|
||||
you may want to download this automatically as part of the CI setup process to ensure the Edge and Edge Driver versions
|
||||
stay in sync. A guide on how to do this may be added at a later date.
|
||||
|
||||
## Example Application
|
||||
|
||||
The [next section](example/setup) of the guide will show step-by-step how to create a minimal example application that
|
||||
is tested with WebDriver.
|
||||
|
||||
If you prefer to just see the result of the guide and look over a finished minimal codebase that utilizes it then you
|
||||
can look at https://github.com/chippers/hello_tauri. That example also comes with a CI script to test with GitHub
|
||||
actions, but you may still be interested in the [WebDriver CI](ci) guide as it explains the concept a bit more.
|
||||
|
||||
[WebDriver]: https://www.w3.org/TR/webdriver/
|
||||
[`tauri-driver`]: https://crates.io/crates/tauri-driver
|
||||
[tauri-driver]: https://crates.io/crates/tauri-driver
|
||||
[Microsoft Edge Driver]: https://developer.microsoft.com/en-us/microsoft-edge/tools/webdriver/
|
@ -1,89 +0,0 @@
|
||||
---
|
||||
title: Window Customization
|
||||
---
|
||||
|
||||
Tauri provides lots of options for customizing the look and feel of your app's window. You can create custom titlebars, have transparent windows, enforce size constraints, and more.
|
||||
|
||||
## Configuration
|
||||
|
||||
There are three ways to change the window configuration:
|
||||
|
||||
- [Through tauri.conf.json](https://tauri.studio/en/docs/api/config/#tauri.windows)
|
||||
- [Through the JS API](https://tauri.studio/en/docs/api/js/classes/window.windowmanager)
|
||||
- [Through the Window in Rust](https://tauri.studio/en/docs/api/rust/tauri/window/struct.window)
|
||||
|
||||
## Creating a Custom Titlebar
|
||||
|
||||
A common use of these window features is creating a custom titlebar. This short tutorial will guide you through that process.
|
||||
|
||||
### CSS
|
||||
|
||||
You'll need to add some CSS for the titlebar to keep it at the top of the screen and style the buttons:
|
||||
|
||||
```css
|
||||
.titlebar {
|
||||
height: 30px;
|
||||
background: #329ea3;
|
||||
user-select: none;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
}
|
||||
.titlebar-button {
|
||||
display: inline-flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
}
|
||||
.titlebar-button:hover {
|
||||
background: #5bbec3;
|
||||
}
|
||||
```
|
||||
|
||||
### HTML
|
||||
|
||||
Now, you'll need to add the HTML for the titlebar. Put this at the top of your `<body>` tag:
|
||||
|
||||
```html
|
||||
<div data-tauri-drag-region class="titlebar">
|
||||
<div class="titlebar-button" id="titlebar-minimize">
|
||||
<img
|
||||
src="https://api.iconify.design/mdi:window-minimize.svg"
|
||||
alt="minimize"
|
||||
/>
|
||||
</div>
|
||||
<div class="titlebar-button" id="titlebar-maximize">
|
||||
<img
|
||||
src="https://api.iconify.design/mdi:window-maximize.svg"
|
||||
alt="maximize"
|
||||
/>
|
||||
</div>
|
||||
<div class="titlebar-button" id="titlebar-close">
|
||||
<img src="https://api.iconify.design/mdi:close.svg" alt="close" />
|
||||
</div>
|
||||
</div>
|
||||
```
|
||||
|
||||
Note that you may need to move the rest of your content down so that the titlebar doesn't cover it.
|
||||
|
||||
### JS
|
||||
|
||||
Finally, you'll need to make the buttons work:
|
||||
|
||||
|
||||
```js
|
||||
import { appWindow } from '@tauri-apps/api/window'
|
||||
document
|
||||
.getElementById('titlebar-minimize')
|
||||
.addEventListener('click', () => appWindow.minimize())
|
||||
document
|
||||
.getElementById('titlebar-maximize')
|
||||
.addEventListener('click', () => appWindow.toggleMaximize())
|
||||
document
|
||||
.getElementById('titlebar-close')
|
||||
.addEventListener('click', () => appWindow.close())
|
||||
```
|
@ -1,109 +0,0 @@
|
||||
[
|
||||
{
|
||||
"label": "Get started",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"get-started/intro",
|
||||
"get-started/setup-linux",
|
||||
"get-started/setup-macos",
|
||||
"get-started/setup-windows"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Development",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"development/intro",
|
||||
"development/integration",
|
||||
"development/development",
|
||||
"development/debugging",
|
||||
"development/ci-cd",
|
||||
"development/cross-platform",
|
||||
"development/publishing",
|
||||
"development/updating"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Guides",
|
||||
"type": "category",
|
||||
"items": [
|
||||
{
|
||||
"label": "Patterns",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"guides/patterns/about-patterns",
|
||||
"guides/patterns/hermit",
|
||||
"guides/patterns/bridge",
|
||||
"guides/patterns/cloudish",
|
||||
"guides/patterns/cloudbridge",
|
||||
"guides/patterns/lockdown",
|
||||
"guides/patterns/multiwin",
|
||||
"guides/patterns/glui"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Bundler",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"guides/bundler/introduction",
|
||||
"guides/bundler/anti-bloat",
|
||||
"guides/bundler/sidecar",
|
||||
"guides/bundler/debian",
|
||||
"guides/bundler/sign-osx"
|
||||
]
|
||||
},
|
||||
"guides/cli",
|
||||
"guides/command",
|
||||
"guides/events",
|
||||
"guides/plugin",
|
||||
"guides/updater",
|
||||
"guides/icons",
|
||||
"guides/splashscreen",
|
||||
"guides/window-customization",
|
||||
"guides/menu",
|
||||
"guides/system-tray",
|
||||
{
|
||||
"label": "WebDriver Testing",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"guides/webdriver/introduction",
|
||||
{
|
||||
"label": "Example Application",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"guides/webdriver/example/setup",
|
||||
"guides/webdriver/example/webdriverio",
|
||||
"guides/webdriver/example/selenium"
|
||||
]
|
||||
},
|
||||
"guides/webdriver/ci"
|
||||
]
|
||||
},
|
||||
"guides/migration",
|
||||
"guides/contributor-guide"
|
||||
]
|
||||
},
|
||||
{
|
||||
"label": "Tips",
|
||||
"type": "category",
|
||||
"items": []
|
||||
},
|
||||
{
|
||||
"label": "API",
|
||||
"type": "category",
|
||||
"items": [
|
||||
"api/config",
|
||||
"api/cli",
|
||||
{
|
||||
"label": "Rust",
|
||||
"type": "category",
|
||||
"items": []
|
||||
},
|
||||
{
|
||||
"label": "JavaScript",
|
||||
"type": "category",
|
||||
"items": []
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
39
examples/api/dist/assets/index.4b6f33f7.js
vendored
39
examples/api/dist/assets/index.4b6f33f7.js
vendored
File diff suppressed because one or more lines are too long
39
examples/api/dist/assets/index.f9e7ab94.js
vendored
Normal file
39
examples/api/dist/assets/index.f9e7ab94.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2
examples/api/dist/index.html
vendored
2
examples/api/dist/index.html
vendored
@ -5,7 +5,7 @@
|
||||
<link rel="stylesheet" href="/global.css" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + Vite App</title>
|
||||
<script type="module" crossorigin src="/assets/index.4b6f33f7.js"></script>
|
||||
<script type="module" crossorigin src="/assets/index.f9e7ab94.js"></script>
|
||||
<link rel="modulepreload" href="/assets/vendor.3a672f03.js">
|
||||
<link rel="stylesheet" href="/assets/index.b706bb41.css">
|
||||
</head>
|
||||
|
@ -123,7 +123,8 @@ fn main() {
|
||||
};
|
||||
item_handle.set_title(new_title).unwrap();
|
||||
}
|
||||
"new" => app
|
||||
"new" => {
|
||||
app
|
||||
.create_window(
|
||||
"new",
|
||||
WindowUrl::App("index.html".into()),
|
||||
@ -131,7 +132,8 @@ fn main() {
|
||||
(window_builder.title("Tauri"), webview_attributes)
|
||||
},
|
||||
)
|
||||
.unwrap(),
|
||||
.unwrap();
|
||||
},
|
||||
#[cfg(target_os = "macos")]
|
||||
"icon_1" => {
|
||||
app.tray_handle().set_icon_as_template(true).unwrap();
|
||||
|
@ -118,7 +118,7 @@
|
||||
<div class="flex row noselect just-around" style="margin=1em;" data-tauri-drag-region>
|
||||
<img class="logo" src="tauri logo.png" height="60" on:click={onLogoClick} alt="logo" />
|
||||
<div>
|
||||
<a class="dark-link" target="_blank" href="https://tauri.studio/en/docs/getting-started/intro">
|
||||
<a class="dark-link" target="_blank" href="https://tauri.studio/en/docs/get-started/intro">
|
||||
Documentation
|
||||
</a>
|
||||
<a class="dark-link" target="_blank" href="https://github.com/tauri-apps/tauri">
|
||||
|
@ -23,6 +23,7 @@
|
||||
|
||||
function openDialog() {
|
||||
open({
|
||||
title: 'My wonderful open dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
@ -70,6 +71,7 @@
|
||||
|
||||
function saveDialog() {
|
||||
save({
|
||||
title: 'My wonderful save dialog',
|
||||
defaultPath,
|
||||
filters: filter
|
||||
? [
|
||||
|
@ -605,9 +605,11 @@ node-abi@^2.7.0:
|
||||
semver "^5.4.1"
|
||||
|
||||
node-fetch@^2.6.1:
|
||||
version "2.6.1"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.1.tgz#045bd323631f76ed2e2b55573394416b639a0052"
|
||||
integrity sha512-V4aYg89jEoVRxRb2fJdAg8FHvI7cEyYdVAh94HH0UIK8oJxUfkjlDQN9RbMx+bEjP7+ggMiFRprSti032Oipxw==
|
||||
version "2.6.7"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.7.tgz#24de9fba827e3b4ae44dc8b20256a379160052ad"
|
||||
integrity sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==
|
||||
dependencies:
|
||||
whatwg-url "^5.0.0"
|
||||
|
||||
noop-logger@^0.1.1:
|
||||
version "0.1.1"
|
||||
@ -1018,6 +1020,11 @@ to-regex-range@^5.0.1:
|
||||
dependencies:
|
||||
is-number "^7.0.0"
|
||||
|
||||
tr46@~0.0.3:
|
||||
version "0.0.3"
|
||||
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
|
||||
integrity sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o=
|
||||
|
||||
tslib@2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.1.0.tgz#da60860f1c2ecaa5703ab7d39bc05b6bf988b97a"
|
||||
@ -1047,6 +1054,19 @@ util-deprecate@^1.0.1, util-deprecate@~1.0.1:
|
||||
resolved "https://registry.yarnpkg.com/util-deprecate/-/util-deprecate-1.0.2.tgz#450d4dc9fa70de732762fbd2d4a28981419a0ccf"
|
||||
integrity sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=
|
||||
|
||||
webidl-conversions@^3.0.0:
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/webidl-conversions/-/webidl-conversions-3.0.1.tgz#24534275e2a7bc6be7bc86611cc16ae0a5654871"
|
||||
integrity sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE=
|
||||
|
||||
whatwg-url@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/whatwg-url/-/whatwg-url-5.0.0.tgz#966454e8765462e37644d3626f6742ce8b70965d"
|
||||
integrity sha1-lmRU6HZUYuN2RNNib2dCzotwll0=
|
||||
dependencies:
|
||||
tr46 "~0.0.3"
|
||||
webidl-conversions "^3.0.0"
|
||||
|
||||
which-pm-runs@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/which-pm-runs/-/which-pm-runs-1.0.0.tgz#670b3afbc552e0b55df6b7780ca74615f23ad1cb"
|
||||
|
3
tooling/api/.gitignore
vendored
3
tooling/api/.gitignore
vendored
@ -62,3 +62,6 @@ typings/
|
||||
debug.log
|
||||
package-lock.json
|
||||
.vscode/settings.json
|
||||
|
||||
# Documentation output
|
||||
docs
|
@ -18,7 +18,8 @@
|
||||
"lint-fix": "eslint --fix --ext ts \"./src/**/*.ts\"",
|
||||
"lint:lockfile": "lockfile-lint --path yarn.lock --type yarn --validate-https --allowed-hosts npm yarn",
|
||||
"format": "prettier --write --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
|
||||
"format:check": "prettier --check --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore"
|
||||
"format:check": "prettier --check --end-of-line=auto \"./**/*.{cjs,js,jsx,ts,tsx,html,css,json}\" --ignore-path .gitignore",
|
||||
"generate-docs": "typedoc src --githubPages false --readme none --entryDocument index.md --hideInPageTOC true"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -43,6 +43,8 @@ interface DialogFilter {
|
||||
|
||||
/** Options for the open dialog. */
|
||||
interface OpenDialogOptions {
|
||||
/** The title of the dialog window. */
|
||||
title?: string
|
||||
/** The filters of the dialog. */
|
||||
filters?: DialogFilter[]
|
||||
/** Initial directory or file path. */
|
||||
@ -55,6 +57,8 @@ interface OpenDialogOptions {
|
||||
|
||||
/** Options for the save dialog. */
|
||||
interface SaveDialogOptions {
|
||||
/** The title of the dialog window. */
|
||||
title?: string
|
||||
/** The filters of the dialog. */
|
||||
filters?: DialogFilter[]
|
||||
/**
|
||||
|
@ -16,7 +16,7 @@ import { LiteralUnion } from 'type-fest'
|
||||
|
||||
interface Event<T> {
|
||||
/** Event name */
|
||||
event: string
|
||||
event: EventName
|
||||
/** Event identifier used to unlisten */
|
||||
id: number
|
||||
/** Event payload */
|
||||
|
@ -37,7 +37,7 @@ enum ResponseType {
|
||||
Binary = 3
|
||||
}
|
||||
|
||||
type Part = 'string' | number[]
|
||||
type Part = string | number[]
|
||||
|
||||
/** The body object to be used on POST and PUT requests. */
|
||||
class Body {
|
||||
|
@ -5,7 +5,7 @@
|
||||
/**
|
||||
* Provides operating system-related utility methods and properties.
|
||||
*
|
||||
* This package is also accessible with `window.__TAURI__.fs` when `tauri.conf.json > build > withGlobalTauri` is set to true.
|
||||
* This package is also accessible with `window.__TAURI__.os` when `tauri.conf.json > build > withGlobalTauri` is set to true.
|
||||
*
|
||||
* The APIs must be allowlisted on `tauri.conf.json`:
|
||||
* ```json
|
||||
|
@ -514,6 +514,7 @@ class WindowManager extends WebviewWindowHandle {
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** `null` has no effect.
|
||||
* - **Linux:** Urgency levels have the same effect.
|
||||
*
|
||||
* @param resizable
|
||||
* @returns A promise indicating the success or failure of the operation.
|
||||
|
@ -39,7 +39,7 @@ toml = "0.5.8"
|
||||
walkdir = "2"
|
||||
handlebars = { version = "4.2" }
|
||||
zip = { version = "0.5" }
|
||||
tempfile = "3.2.0"
|
||||
tempfile = "3.3.0"
|
||||
regex = "1"
|
||||
|
||||
[target."cfg(target_os = \"windows\")".dependencies]
|
||||
|
@ -76,6 +76,7 @@ These settings are used only when bundling `app` and `dmg` packages.
|
||||
* `license`: Path to the license file for the DMG bundle.
|
||||
* `exception_domain`: The exception domain to use on the macOS .app bundle. Allows communication to the outside world e.g. a web server you're shipping.
|
||||
* `use_bootstrapper`: Enables the bootstrapper script, which allows access to the environment variables.
|
||||
* `provider_short_name`: If your Apple ID is connected to multiple teams, you have to specify the provider short name of the team you want to use to notarize your app. See [Customizing the notarization workflow](https://developer.apple.com/documentation/security/notarizing_macos_software_before_distribution/customizing_the_notarization_workflow) and search for `--list-providers` for more information how to obtain your provider short name.
|
||||
|
||||
### Example `tauri.conf.json`:
|
||||
|
||||
|
@ -33,4 +33,10 @@ wget -q -4 -O linuxdeploy-x86_64.AppImage https://github.com/linuxdeploy/linuxde
|
||||
chmod +x linuxdeploy-plugin-gtk.sh
|
||||
chmod +x linuxdeploy-x86_64.AppImage
|
||||
|
||||
OUTPUT="{{appimage_filename}}" ./linuxdeploy-x86_64.AppImage --appdir "{{app_name}}.AppDir" --plugin gtk --output appimage
|
||||
args=()
|
||||
if ! lsmod | grep -q 'fuse'
|
||||
then
|
||||
args+=( '--appimage-extract-and-run' )
|
||||
fi
|
||||
|
||||
OUTPUT="{{appimage_filename}}" ./linuxdeploy-x86_64.AppImage "${args[@]}" --appdir "{{app_name}}.AppDir" --plugin gtk --output appimage
|
||||
|
@ -258,7 +258,7 @@ pub fn notarize(
|
||||
sign(zip_path.clone(), identity, &settings, false)?;
|
||||
};
|
||||
|
||||
let notarize_args = vec![
|
||||
let mut notarize_args = vec![
|
||||
"altool",
|
||||
"--notarize-app",
|
||||
"-f",
|
||||
@ -268,6 +268,12 @@ pub fn notarize(
|
||||
"--primary-bundle-id",
|
||||
identifier,
|
||||
];
|
||||
|
||||
if let Some(provider_short_name) = &settings.macos().provider_short_name {
|
||||
notarize_args.push("--asc-provider");
|
||||
notarize_args.push(provider_short_name);
|
||||
}
|
||||
|
||||
common::print_info("notarizing app")?;
|
||||
let output = Command::new("xcrun")
|
||||
.args(notarize_args)
|
||||
|
@ -168,6 +168,8 @@ pub struct MacOsSettings {
|
||||
pub exception_domain: Option<String>,
|
||||
/// Code signing identity.
|
||||
pub signing_identity: Option<String>,
|
||||
/// Provider short name for notarization.
|
||||
pub provider_short_name: Option<String>,
|
||||
/// Path to the entitlements.plist file.
|
||||
pub entitlements: Option<String>,
|
||||
/// Path to the Info.plist file for the bundle.
|
||||
|
@ -387,27 +387,32 @@ pub fn build_wix_app_installer(
|
||||
.find(|bin| bin.main())
|
||||
.ok_or_else(|| anyhow::anyhow!("Failed to get main binary"))?;
|
||||
let app_exe_source = settings.binary_path(main_binary);
|
||||
let try_sign = |file_path: &PathBuf| -> crate::Result<()> {
|
||||
if let Some(certificate_thumbprint) = &settings.windows().certificate_thumbprint {
|
||||
common::print_info(&format!("signing {}", file_path.display()))?;
|
||||
sign(
|
||||
&file_path,
|
||||
&SignParams {
|
||||
digest_algorithm: settings
|
||||
.windows()
|
||||
.digest_algorithm
|
||||
.as_ref()
|
||||
.map(|algorithm| algorithm.to_string())
|
||||
.unwrap_or_else(|| "sha256".to_string()),
|
||||
certificate_thumbprint: certificate_thumbprint.to_string(),
|
||||
timestamp_url: settings
|
||||
.windows()
|
||||
.timestamp_url
|
||||
.as_ref()
|
||||
.map(|url| url.to_string()),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
Ok(())
|
||||
};
|
||||
|
||||
if let Some(certificate_thumbprint) = &settings.windows().certificate_thumbprint {
|
||||
common::print_info("signing app")?;
|
||||
sign(
|
||||
&app_exe_source,
|
||||
&SignParams {
|
||||
digest_algorithm: settings
|
||||
.windows()
|
||||
.digest_algorithm
|
||||
.as_ref()
|
||||
.map(|algorithm| algorithm.to_string())
|
||||
.unwrap_or_else(|| "sha256".to_string()),
|
||||
certificate_thumbprint: certificate_thumbprint.to_string(),
|
||||
timestamp_url: settings
|
||||
.windows()
|
||||
.timestamp_url
|
||||
.as_ref()
|
||||
.map(|url| url.to_string()),
|
||||
},
|
||||
)?;
|
||||
}
|
||||
common::print_info("trying to sign app")?;
|
||||
try_sign(&app_exe_source)?;
|
||||
|
||||
// ensure that `target/{release, debug}/wix` folder exists
|
||||
std::fs::create_dir_all(settings.project_out_directory().join("wix"))?;
|
||||
@ -655,6 +660,7 @@ pub fn build_wix_app_installer(
|
||||
settings,
|
||||
)?;
|
||||
rename(&msi_output_path, &msi_path)?;
|
||||
try_sign(&msi_path)?;
|
||||
|
||||
Ok(msi_path)
|
||||
}
|
||||
|
@ -75,12 +75,12 @@
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "7.16.7",
|
||||
"@babel/preset-env": "7.16.7",
|
||||
"@babel/preset-env": "7.16.8",
|
||||
"@babel/preset-typescript": "7.16.7",
|
||||
"@jest/globals": "27.4.6",
|
||||
"@rollup/plugin-babel": "5.3.0",
|
||||
"@rollup/plugin-commonjs": "21.0.1",
|
||||
"@rollup/plugin-node-resolve": "13.1.2",
|
||||
"@rollup/plugin-node-resolve": "13.1.3",
|
||||
"@rollup/plugin-replace": "3.0.1",
|
||||
"@rollup/plugin-typescript": "8.3.0",
|
||||
"@types/cross-spawn": "6.0.2",
|
||||
@ -93,8 +93,8 @@
|
||||
"@types/ms": "0.7.31",
|
||||
"@types/semver": "7.3.9",
|
||||
"@types/sharp": "0.29.5",
|
||||
"@typescript-eslint/eslint-plugin": "5.9.0",
|
||||
"@typescript-eslint/parser": "5.9.0",
|
||||
"@typescript-eslint/eslint-plugin": "5.9.1",
|
||||
"@typescript-eslint/parser": "5.9.1",
|
||||
"babel-jest": "27.4.6",
|
||||
"eslint": "8.6.0",
|
||||
"eslint-config-prettier": "8.3.0",
|
||||
@ -105,7 +105,7 @@
|
||||
"eslint-plugin-promise": "5.2.0",
|
||||
"eslint-plugin-security": "1.4.0",
|
||||
"is-running": "2.1.0",
|
||||
"jest": "27.4.6",
|
||||
"jest": "27.4.7",
|
||||
"jest-transform-toml": "1.0.0",
|
||||
"lockfile-lint": "4.6.2",
|
||||
"prettier": "2.5.1",
|
||||
|
@ -26,7 +26,12 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.4.tgz#081d6bbc336ec5c2435c6346b2ae1fb98b5ac68e"
|
||||
integrity sha512-1o/jo7D+kC9ZjHX5v+EHrdjl3PhxMrLSOTGsOdHJ+KL8HCaEK6ehrVL2RS6oHDZp+L7xLirLrPmQtEng769J/Q==
|
||||
|
||||
"@babel/core@7.16.7", "@babel/core@^7.12.3":
|
||||
"@babel/compat-data@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/compat-data/-/compat-data-7.16.8.tgz#31560f9f29fdf1868de8cb55049538a1b9732a60"
|
||||
integrity sha512-m7OkX0IdKLKPpBlJtF561YJal5y/jyI5fNfWbPxh2D/nbzzGI4qRyrD8xO2jB24u7l+5I2a43scCG2IrfjC50Q==
|
||||
|
||||
"@babel/core@7.16.7", "@babel/core@^7.12.3", "@babel/core@^7.8.0":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.16.7.tgz#db990f931f6d40cb9b87a0dc7d2adc749f1dcbcf"
|
||||
integrity sha512-aeLaqcqThRNZYmbMqtulsetOQZ/5gbR/dWruUCJcpas4Qoyy+QeagfDsPdMrqwsPRDNxJvBlRiZxxX7THO7qtA==
|
||||
@ -86,6 +91,15 @@
|
||||
jsesc "^2.5.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/generator@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.16.8.tgz#359d44d966b8cd059d543250ce79596f792f2ebe"
|
||||
integrity sha512-1ojZwE9+lOXzcWdWmO6TbUzDfqLD39CmEhN8+2cX9XkDo5yW1OpgfejfliysR2AWLpMamTiOiAp/mtroaymhpw==
|
||||
dependencies:
|
||||
"@babel/types" "^7.16.8"
|
||||
jsesc "^2.5.1"
|
||||
source-map "^0.5.0"
|
||||
|
||||
"@babel/helper-annotate-as-pure@^7.14.5":
|
||||
version "7.14.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.14.5.tgz#7bf478ec3b71726d56a8ca5775b046fc29879e61"
|
||||
@ -311,14 +325,14 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-plugin-utils/-/helper-plugin-utils-7.16.7.tgz#aa3a8ab4c3cceff8e65eb9e73d87dc4ff320b2f5"
|
||||
integrity sha512-Qg3Nk7ZxpgMrsox6HreY1ZNKdBq7K72tDSliA6dCl5f007jR4ne8iD5UzuNnCJH2xBf2BEEVGr+/OL6Gdp7RxA==
|
||||
|
||||
"@babel/helper-remap-async-to-generator@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.7.tgz#5ce2416990d55eb6e099128338848ae8ffa58a9a"
|
||||
integrity sha512-C3o117GnP/j/N2OWo+oepeWbFEKRfNaay+F1Eo5Mj3A1SRjyx+qaFhm23nlipub7Cjv2azdUUiDH+VlpdwUFRg==
|
||||
"@babel/helper-remap-async-to-generator@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-remap-async-to-generator/-/helper-remap-async-to-generator-7.16.8.tgz#29ffaade68a367e2ed09c90901986918d25e57e3"
|
||||
integrity sha512-fm0gH7Flb8H51LqJHy3HJ3wnE1+qtYR2A99K06ahwrawLdOFsCEWjZOrYricXJHoPSudNKxrMBUPEIPxiIIvBw==
|
||||
dependencies:
|
||||
"@babel/helper-annotate-as-pure" "^7.16.7"
|
||||
"@babel/helper-wrap-function" "^7.16.7"
|
||||
"@babel/types" "^7.16.7"
|
||||
"@babel/helper-wrap-function" "^7.16.8"
|
||||
"@babel/types" "^7.16.8"
|
||||
|
||||
"@babel/helper-replace-supers@^7.14.5":
|
||||
version "7.14.5"
|
||||
@ -401,15 +415,15 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-validator-option/-/helper-validator-option-7.16.7.tgz#b203ce62ce5fe153899b617c08957de860de4d23"
|
||||
integrity sha512-TRtenOuRUVo9oIQGPC5G9DgK4743cdxvtOw0weQNpZXaS16SCBi5MNjZF8vba3ETURjZpTbVn7Vvcf2eAwFozQ==
|
||||
|
||||
"@babel/helper-wrap-function@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.7.tgz#8ddf9eaa770ed43de4bc3687f3f3b0d6d5ecf014"
|
||||
integrity sha512-7a9sABeVwcunnztZZ7WTgSw6jVYLzM1wua0Z4HIXm9S3/HC96WKQTkFgGEaj5W06SHHihPJ6Le6HzS5cGOQMNw==
|
||||
"@babel/helper-wrap-function@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/helper-wrap-function/-/helper-wrap-function-7.16.8.tgz#58afda087c4cd235de92f7ceedebca2c41274200"
|
||||
integrity sha512-8RpyRVIAW1RcDDGTA+GpPAwV22wXCfKOoM9bet6TLkGIFTkRQSkH1nMQ5Yet4MpoXe1ZwHPVtNasc2w0uZMqnw==
|
||||
dependencies:
|
||||
"@babel/helper-function-name" "^7.16.7"
|
||||
"@babel/template" "^7.16.7"
|
||||
"@babel/traverse" "^7.16.7"
|
||||
"@babel/types" "^7.16.7"
|
||||
"@babel/traverse" "^7.16.8"
|
||||
"@babel/types" "^7.16.8"
|
||||
|
||||
"@babel/helpers@^7.14.6":
|
||||
version "7.14.6"
|
||||
@ -457,6 +471,11 @@
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.7.tgz#d372dda9c89fcec340a82630a9f533f2fe15877e"
|
||||
integrity sha512-sR4eaSrnM7BV7QPzGfEX5paG/6wrZM3I0HDzfIAK06ESvo9oy3xBuVBxE3MbQaKNhvg8g/ixjMWo2CGpzpHsDA==
|
||||
|
||||
"@babel/parser@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.16.8.tgz#61c243a3875f7d0b0962b0543a33ece6ff2f1f17"
|
||||
integrity sha512-i7jDUfrVBWc+7OKcBzEe5n7fbv3i2fWtxKzzCvOjnzSxMfWMigAhtfJ7qzZNGFNMsCCd67+uz553dYKWXPvCKw==
|
||||
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression/-/plugin-bugfix-safari-id-destructuring-collision-in-function-expression-7.16.7.tgz#4eda6d6c2a0aa79c70fa7b6da67763dfe2141050"
|
||||
@ -473,13 +492,13 @@
|
||||
"@babel/helper-skip-transparent-expression-wrappers" "^7.16.0"
|
||||
"@babel/plugin-proposal-optional-chaining" "^7.16.7"
|
||||
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.7.tgz#739adc1212a9e4892de440cd7dfffb06172df78d"
|
||||
integrity sha512-TTXBT3A5c11eqRzaC6beO6rlFT3Mo9C2e8eB44tTr52ESXSK2CIc2fOp1ynpAwQA8HhBMho+WXhMHWlAe3xkpw==
|
||||
"@babel/plugin-proposal-async-generator-functions@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.16.8.tgz#3bdd1ebbe620804ea9416706cd67d60787504bc8"
|
||||
integrity sha512-71YHIvMuiuqWJQkebWJtdhQTfd4Q4mF76q2IX37uZPkG9+olBxsX+rH1vkhFto4UeJZ9dPY2s+mDvhDm1u2BGQ==
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
"@babel/helper-remap-async-to-generator" "^7.16.7"
|
||||
"@babel/helper-remap-async-to-generator" "^7.16.8"
|
||||
"@babel/plugin-syntax-async-generators" "^7.8.4"
|
||||
|
||||
"@babel/plugin-proposal-class-properties@^7.16.7":
|
||||
@ -742,14 +761,14 @@
|
||||
dependencies:
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
|
||||
"@babel/plugin-transform-async-to-generator@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.7.tgz#646e1262ac341b587ff5449844d4492dbb10ac4b"
|
||||
integrity sha512-pFEfjnK4DfXCfAlA5I98BYdDJD8NltMzx19gt6DAmfE+2lXRfPUoa0/5SUjT4+TDE1W/rcxU/1lgN55vpAjjdg==
|
||||
"@babel/plugin-transform-async-to-generator@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-async-to-generator/-/plugin-transform-async-to-generator-7.16.8.tgz#b83dff4b970cf41f1b819f8b49cc0cfbaa53a808"
|
||||
integrity sha512-MtmUmTJQHCnyJVrScNzNlofQJ3dLFuobYn3mwOTKHnSCMtbNsqvF71GQmJfFjdrXSsAA7iysFmYWw4bXZ20hOg==
|
||||
dependencies:
|
||||
"@babel/helper-module-imports" "^7.16.7"
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
"@babel/helper-remap-async-to-generator" "^7.16.7"
|
||||
"@babel/helper-remap-async-to-generator" "^7.16.8"
|
||||
|
||||
"@babel/plugin-transform-block-scoped-functions@^7.16.7":
|
||||
version "7.16.7"
|
||||
@ -863,10 +882,10 @@
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
babel-plugin-dynamic-import-node "^2.3.3"
|
||||
|
||||
"@babel/plugin-transform-modules-commonjs@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.7.tgz#fd119e6a433c527d368425b45df361e1e95d3c1a"
|
||||
integrity sha512-h2RP2kE7He1ZWKyAlanMZrAbdv+Acw1pA8dQZhE025WJZE2z0xzFADAinXA9fxd5bn7JnM+SdOGcndGx1ARs9w==
|
||||
"@babel/plugin-transform-modules-commonjs@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.16.8.tgz#cdee19aae887b16b9d331009aa9a219af7c86afe"
|
||||
integrity sha512-oflKPvsLT2+uKQopesJt3ApiaIS2HW+hzHFcwRNtyDGieAeC/dIHZX8buJQ2J2X1rxGPy4eRcUijm3qcSPjYcA==
|
||||
dependencies:
|
||||
"@babel/helper-module-transforms" "^7.16.7"
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
@ -892,10 +911,10 @@
|
||||
"@babel/helper-module-transforms" "^7.16.7"
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.7.tgz#749d90d94e73cf62c60a0cc8d6b94d29305a81f2"
|
||||
integrity sha512-kFy35VwmwIQwCjwrAQhl3+c/kr292i4KdLPKp5lPH03Ltc51qnFlIADoyPxc/6Naz3ok3WdYKg+KK6AH+D4utg==
|
||||
"@babel/plugin-transform-named-capturing-groups-regex@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/plugin-transform-named-capturing-groups-regex/-/plugin-transform-named-capturing-groups-regex-7.16.8.tgz#7f860e0e40d844a02c9dcf9d84965e7dfd666252"
|
||||
integrity sha512-j3Jw+n5PvpmhRR+mrgIh04puSANCk/T/UA3m3P1MjJkhlK906+ApHhDIqBQDdOgL/r1UYpz4GNclTXxyZrYGSw==
|
||||
dependencies:
|
||||
"@babel/helper-create-regexp-features-plugin" "^7.16.7"
|
||||
|
||||
@ -1002,18 +1021,18 @@
|
||||
"@babel/helper-create-regexp-features-plugin" "^7.16.7"
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
|
||||
"@babel/preset-env@7.16.7":
|
||||
version "7.16.7"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.7.tgz#c491088856d0b3177822a2bf06cb74d76327aa56"
|
||||
integrity sha512-urX3Cee4aOZbRWOSa3mKPk0aqDikfILuo+C7qq7HY0InylGNZ1fekq9jmlr3pLWwZHF4yD7heQooc2Pow2KMyQ==
|
||||
"@babel/preset-env@7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/preset-env/-/preset-env-7.16.8.tgz#e682fa0bcd1cf49621d64a8956318ddfb9a05af9"
|
||||
integrity sha512-9rNKgVCdwHb3z1IlbMyft6yIXIeP3xz6vWvGaLHrJThuEIqWfHb0DNBH9VuTgnDfdbUDhkmkvMZS/YMCtP7Elg==
|
||||
dependencies:
|
||||
"@babel/compat-data" "^7.16.4"
|
||||
"@babel/compat-data" "^7.16.8"
|
||||
"@babel/helper-compilation-targets" "^7.16.7"
|
||||
"@babel/helper-plugin-utils" "^7.16.7"
|
||||
"@babel/helper-validator-option" "^7.16.7"
|
||||
"@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression" "^7.16.7"
|
||||
"@babel/plugin-bugfix-v8-spread-parameters-in-optional-chaining" "^7.16.7"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.16.7"
|
||||
"@babel/plugin-proposal-async-generator-functions" "^7.16.8"
|
||||
"@babel/plugin-proposal-class-properties" "^7.16.7"
|
||||
"@babel/plugin-proposal-class-static-block" "^7.16.7"
|
||||
"@babel/plugin-proposal-dynamic-import" "^7.16.7"
|
||||
@ -1043,7 +1062,7 @@
|
||||
"@babel/plugin-syntax-private-property-in-object" "^7.14.5"
|
||||
"@babel/plugin-syntax-top-level-await" "^7.14.5"
|
||||
"@babel/plugin-transform-arrow-functions" "^7.16.7"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.16.7"
|
||||
"@babel/plugin-transform-async-to-generator" "^7.16.8"
|
||||
"@babel/plugin-transform-block-scoped-functions" "^7.16.7"
|
||||
"@babel/plugin-transform-block-scoping" "^7.16.7"
|
||||
"@babel/plugin-transform-classes" "^7.16.7"
|
||||
@ -1057,10 +1076,10 @@
|
||||
"@babel/plugin-transform-literals" "^7.16.7"
|
||||
"@babel/plugin-transform-member-expression-literals" "^7.16.7"
|
||||
"@babel/plugin-transform-modules-amd" "^7.16.7"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.16.7"
|
||||
"@babel/plugin-transform-modules-commonjs" "^7.16.8"
|
||||
"@babel/plugin-transform-modules-systemjs" "^7.16.7"
|
||||
"@babel/plugin-transform-modules-umd" "^7.16.7"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.16.7"
|
||||
"@babel/plugin-transform-named-capturing-groups-regex" "^7.16.8"
|
||||
"@babel/plugin-transform-new-target" "^7.16.7"
|
||||
"@babel/plugin-transform-object-super" "^7.16.7"
|
||||
"@babel/plugin-transform-parameters" "^7.16.7"
|
||||
@ -1075,11 +1094,11 @@
|
||||
"@babel/plugin-transform-unicode-escapes" "^7.16.7"
|
||||
"@babel/plugin-transform-unicode-regex" "^7.16.7"
|
||||
"@babel/preset-modules" "^0.1.5"
|
||||
"@babel/types" "^7.16.7"
|
||||
"@babel/types" "^7.16.8"
|
||||
babel-plugin-polyfill-corejs2 "^0.3.0"
|
||||
babel-plugin-polyfill-corejs3 "^0.4.0"
|
||||
babel-plugin-polyfill-corejs3 "^0.5.0"
|
||||
babel-plugin-polyfill-regenerator "^0.3.0"
|
||||
core-js-compat "^3.19.1"
|
||||
core-js-compat "^3.20.2"
|
||||
semver "^6.3.0"
|
||||
|
||||
"@babel/preset-modules@^0.1.5":
|
||||
@ -1165,6 +1184,22 @@
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/traverse@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/traverse/-/traverse-7.16.8.tgz#bab2f2b09a5fe8a8d9cad22cbfe3ba1d126fef9c"
|
||||
integrity sha512-xe+H7JlvKsDQwXRsBhSnq1/+9c+LlQcCK3Tn/l5sbx02HYns/cn7ibp9+RV1sIUqu7hKg91NWsgHurO9dowITQ==
|
||||
dependencies:
|
||||
"@babel/code-frame" "^7.16.7"
|
||||
"@babel/generator" "^7.16.8"
|
||||
"@babel/helper-environment-visitor" "^7.16.7"
|
||||
"@babel/helper-function-name" "^7.16.7"
|
||||
"@babel/helper-hoist-variables" "^7.16.7"
|
||||
"@babel/helper-split-export-declaration" "^7.16.7"
|
||||
"@babel/parser" "^7.16.8"
|
||||
"@babel/types" "^7.16.8"
|
||||
debug "^4.1.0"
|
||||
globals "^11.1.0"
|
||||
|
||||
"@babel/types@^7.0.0", "@babel/types@^7.14.5", "@babel/types@^7.3.0", "@babel/types@^7.3.3", "@babel/types@^7.4.4":
|
||||
version "7.14.5"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.14.5.tgz#3bb997ba829a2104cedb20689c4a5b8121d383ff"
|
||||
@ -1189,6 +1224,14 @@
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@babel/types@^7.16.8":
|
||||
version "7.16.8"
|
||||
resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.16.8.tgz#0ba5da91dd71e0a4e7781a30f22770831062e3c1"
|
||||
integrity sha512-smN2DQc5s4M7fntyjGtyIPbRJv6wW4rU/94fmYJ7PKQuZkC0qGMHXJbg6sNGt12JmVr4k5YaptI/XtiLJBnmIg==
|
||||
dependencies:
|
||||
"@babel/helper-validator-identifier" "^7.16.7"
|
||||
to-fast-properties "^2.0.0"
|
||||
|
||||
"@bcoe/v8-coverage@^0.2.3":
|
||||
version "0.2.3"
|
||||
resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
|
||||
@ -1251,10 +1294,10 @@
|
||||
jest-util "^27.4.2"
|
||||
slash "^3.0.0"
|
||||
|
||||
"@jest/core@^27.4.6":
|
||||
version "27.4.6"
|
||||
resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.6.tgz#5cc4e714602324dc1561029224fd593e70ab4c7d"
|
||||
integrity sha512-2XvkAguDxaSAg6+Rznq7VZeIs3eV4owk3dud0zL4FH0d8mX7whkAUnO9rb0keMGStazfekR1ec9Yf9BFt4m02Q==
|
||||
"@jest/core@^27.4.7":
|
||||
version "27.4.7"
|
||||
resolved "https://registry.yarnpkg.com/@jest/core/-/core-27.4.7.tgz#84eabdf42a25f1fa138272ed229bcf0a1b5e6913"
|
||||
integrity sha512-n181PurSJkVMS+kClIFSX/LLvw9ExSb+4IMtD6YnfxZVerw9ANYtW0bPrm0MJu2pfe9SY9FJ9FtQ+MdZkrZwjg==
|
||||
dependencies:
|
||||
"@jest/console" "^27.4.6"
|
||||
"@jest/reporters" "^27.4.6"
|
||||
@ -1268,7 +1311,7 @@
|
||||
exit "^0.1.2"
|
||||
graceful-fs "^4.2.4"
|
||||
jest-changed-files "^27.4.2"
|
||||
jest-config "^27.4.6"
|
||||
jest-config "^27.4.7"
|
||||
jest-haste-map "^27.4.6"
|
||||
jest-message-util "^27.4.6"
|
||||
jest-regex-util "^27.4.0"
|
||||
@ -1740,10 +1783,10 @@
|
||||
magic-string "^0.25.7"
|
||||
resolve "^1.17.0"
|
||||
|
||||
"@rollup/plugin-node-resolve@13.1.2":
|
||||
version "13.1.2"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.2.tgz#f05cbdce79e6d5fe1c4249a33ea961ea21cec258"
|
||||
integrity sha512-xyqbuf1vyOPC60jEKhx3DBHunymnCJswzjNTKfX4Jz7zCPar1UqbRZCNY1u5QaXh97beaFTWdoUUWiV4qX8o/g==
|
||||
"@rollup/plugin-node-resolve@13.1.3":
|
||||
version "13.1.3"
|
||||
resolved "https://registry.yarnpkg.com/@rollup/plugin-node-resolve/-/plugin-node-resolve-13.1.3.tgz#2ed277fb3ad98745424c1d2ba152484508a92d79"
|
||||
integrity sha512-BdxNk+LtmElRo5d06MGY4zoepyrXX1tkzX2hrnPEZ53k78GuOMWLqmJDGIIOPwVRIFZrLQOo+Yr6KtCuLIA0AQ==
|
||||
dependencies:
|
||||
"@rollup/pluginutils" "^3.1.0"
|
||||
"@types/resolve" "1.17.1"
|
||||
@ -2072,14 +2115,14 @@
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
|
||||
"@typescript-eslint/eslint-plugin@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.0.tgz#382182d5cb062f52aac54434cfc47c28898c8006"
|
||||
integrity sha512-qT4lr2jysDQBQOPsCCvpPUZHjbABoTJW8V9ZzIYKHMfppJtpdtzszDYsldwhFxlhvrp7aCHeXD1Lb9M1zhwWwQ==
|
||||
"@typescript-eslint/eslint-plugin@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.9.1.tgz#e5a86d7e1f9dc0b3df1e6d94feaf20dd838d066c"
|
||||
integrity sha512-Xv9tkFlyD4MQGpJgTo6wqDqGvHIRmRgah/2Sjz1PUnJTawjHWIwBivUE9x0QtU2WVii9baYgavo/bHjrZJkqTw==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "5.9.0"
|
||||
"@typescript-eslint/scope-manager" "5.9.0"
|
||||
"@typescript-eslint/type-utils" "5.9.0"
|
||||
"@typescript-eslint/experimental-utils" "5.9.1"
|
||||
"@typescript-eslint/scope-manager" "5.9.1"
|
||||
"@typescript-eslint/type-utils" "5.9.1"
|
||||
debug "^4.3.2"
|
||||
functional-red-black-tree "^1.0.1"
|
||||
ignore "^5.1.8"
|
||||
@ -2087,26 +2130,26 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/experimental-utils@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.0.tgz#652762d37d6565ef07af285021b8347b6c79a827"
|
||||
integrity sha512-ZnLVjBrf26dn7ElyaSKa6uDhqwvAi4jBBmHK1VxuFGPRAxhdi18ubQYSGA7SRiFiES3q9JiBOBHEBStOFkwD2g==
|
||||
"@typescript-eslint/experimental-utils@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/experimental-utils/-/experimental-utils-5.9.1.tgz#8c407c4dd5ffe522329df6e4c9c2b52206d5f7f1"
|
||||
integrity sha512-cb1Njyss0mLL9kLXgS/eEY53SZQ9sT519wpX3i+U457l2UXRDuo87hgKfgRazmu9/tQb0x2sr3Y0yrU+Zz0y+w==
|
||||
dependencies:
|
||||
"@types/json-schema" "^7.0.9"
|
||||
"@typescript-eslint/scope-manager" "5.9.0"
|
||||
"@typescript-eslint/types" "5.9.0"
|
||||
"@typescript-eslint/typescript-estree" "5.9.0"
|
||||
"@typescript-eslint/scope-manager" "5.9.1"
|
||||
"@typescript-eslint/types" "5.9.1"
|
||||
"@typescript-eslint/typescript-estree" "5.9.1"
|
||||
eslint-scope "^5.1.1"
|
||||
eslint-utils "^3.0.0"
|
||||
|
||||
"@typescript-eslint/parser@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.9.0.tgz#fdbb08767a4caa6ca6ccfed5f9ffe9387f0c7d97"
|
||||
integrity sha512-/6pOPz8yAxEt4PLzgbFRDpZmHnXCeZgPDrh/1DaVKOjvn/UPMlWhbx/gA96xRi2JxY1kBl2AmwVbyROUqys5xQ==
|
||||
"@typescript-eslint/parser@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-5.9.1.tgz#b114011010a87e17b3265ca715e16c76a9834cef"
|
||||
integrity sha512-PLYO0AmwD6s6n0ZQB5kqPgfvh73p0+VqopQQLuNfi7Lm0EpfKyDalchpVwkE+81k5HeiRrTV/9w1aNHzjD7C4g==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "5.9.0"
|
||||
"@typescript-eslint/types" "5.9.0"
|
||||
"@typescript-eslint/typescript-estree" "5.9.0"
|
||||
"@typescript-eslint/scope-manager" "5.9.1"
|
||||
"@typescript-eslint/types" "5.9.1"
|
||||
"@typescript-eslint/typescript-estree" "5.9.1"
|
||||
debug "^4.3.2"
|
||||
|
||||
"@typescript-eslint/parser@^4.0.0":
|
||||
@ -2127,20 +2170,20 @@
|
||||
"@typescript-eslint/types" "4.27.0"
|
||||
"@typescript-eslint/visitor-keys" "4.27.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.9.0.tgz#02dfef920290c1dcd7b1999455a3eaae7a1a3117"
|
||||
integrity sha512-DKtdIL49Qxk2a8icF6whRk7uThuVz4A6TCXfjdJSwOsf+9ree7vgQWcx0KOyCdk0i9ETX666p4aMhrRhxhUkyg==
|
||||
"@typescript-eslint/scope-manager@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-5.9.1.tgz#6c27be89f1a9409f284d95dfa08ee3400166fe69"
|
||||
integrity sha512-8BwvWkho3B/UOtzRyW07ffJXPaLSUKFBjpq8aqsRvu6HdEuzCY57+ffT7QoV4QXJXWSU1+7g3wE4AlgImmQ9pQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.9.0"
|
||||
"@typescript-eslint/visitor-keys" "5.9.0"
|
||||
"@typescript-eslint/types" "5.9.1"
|
||||
"@typescript-eslint/visitor-keys" "5.9.1"
|
||||
|
||||
"@typescript-eslint/type-utils@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.9.0.tgz#fd5963ead04bc9b7af9c3a8e534d8d39f1ce5f93"
|
||||
integrity sha512-uVCb9dJXpBrK1071ri5aEW7ZHdDHAiqEjYznF3HSSvAJXyrkxGOw2Ejibz/q6BXdT8lea8CMI0CzKNFTNI6TEQ==
|
||||
"@typescript-eslint/type-utils@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-5.9.1.tgz#c6832ffe655b9b1fec642d36db1a262d721193de"
|
||||
integrity sha512-tRSpdBnPRssjlUh35rE9ug5HrUvaB9ntREy7gPXXKwmIx61TNN7+l5YKgi1hMKxo5NvqZCfYhA5FvyuJG6X6vg==
|
||||
dependencies:
|
||||
"@typescript-eslint/experimental-utils" "5.9.0"
|
||||
"@typescript-eslint/experimental-utils" "5.9.1"
|
||||
debug "^4.3.2"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
@ -2149,10 +2192,10 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-4.27.0.tgz#712b408519ed699baff69086bc59cd2fc13df8d8"
|
||||
integrity sha512-I4ps3SCPFCKclRcvnsVA/7sWzh7naaM/b4pBO2hVxnM3wrU51Lveybdw5WoIktU/V4KfXrTt94V9b065b/0+wA==
|
||||
|
||||
"@typescript-eslint/types@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.9.0.tgz#e5619803e39d24a03b3369506df196355736e1a3"
|
||||
integrity sha512-mWp6/b56Umo1rwyGCk8fPIzb9Migo8YOniBGPAQDNC6C52SeyNGN4gsVwQTAR+RS2L5xyajON4hOLwAGwPtUwg==
|
||||
"@typescript-eslint/types@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-5.9.1.tgz#1bef8f238a2fb32ebc6ff6d75020d9f47a1593c6"
|
||||
integrity sha512-SsWegWudWpkZCwwYcKoDwuAjoZXnM1y2EbEerTHho19Hmm+bQ56QG4L4jrtCu0bI5STaRTvRTZmjprWlTw/5NQ==
|
||||
|
||||
"@typescript-eslint/typescript-estree@4.27.0":
|
||||
version "4.27.0"
|
||||
@ -2167,13 +2210,13 @@
|
||||
semver "^7.3.5"
|
||||
tsutils "^3.21.0"
|
||||
|
||||
"@typescript-eslint/typescript-estree@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.0.tgz#0e5c6f03f982931abbfbc3c1b9df5fbf92a3490f"
|
||||
integrity sha512-kxo3xL2mB7XmiVZcECbaDwYCt3qFXz99tBSuVJR4L/sR7CJ+UNAPrYILILktGj1ppfZ/jNt/cWYbziJUlHl1Pw==
|
||||
"@typescript-eslint/typescript-estree@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-5.9.1.tgz#d5b996f49476495070d2b8dd354861cf33c005d6"
|
||||
integrity sha512-gL1sP6A/KG0HwrahVXI9fZyeVTxEYV//6PmcOn1tD0rw8VhUWYeZeuWHwwhnewnvEMcHjhnJLOBhA9rK4vmb8A==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.9.0"
|
||||
"@typescript-eslint/visitor-keys" "5.9.0"
|
||||
"@typescript-eslint/types" "5.9.1"
|
||||
"@typescript-eslint/visitor-keys" "5.9.1"
|
||||
debug "^4.3.2"
|
||||
globby "^11.0.4"
|
||||
is-glob "^4.0.3"
|
||||
@ -2188,12 +2231,12 @@
|
||||
"@typescript-eslint/types" "4.27.0"
|
||||
eslint-visitor-keys "^2.0.0"
|
||||
|
||||
"@typescript-eslint/visitor-keys@5.9.0":
|
||||
version "5.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.0.tgz#7585677732365e9d27f1878150fab3922784a1a6"
|
||||
integrity sha512-6zq0mb7LV0ThExKlecvpfepiB+XEtFv/bzx7/jKSgyXTFD7qjmSu1FoiS0x3OZaiS+UIXpH2vd9O89f02RCtgw==
|
||||
"@typescript-eslint/visitor-keys@5.9.1":
|
||||
version "5.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-5.9.1.tgz#f52206f38128dd4f675cf28070a41596eee985b7"
|
||||
integrity sha512-Xh37pNz9e9ryW4TVdwiFzmr4hloty8cFj8GTWMXh3Z8swGwyQWeCcNgF0hm6t09iZd6eiZmIf4zHedQVP6TVtg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "5.9.0"
|
||||
"@typescript-eslint/types" "5.9.1"
|
||||
eslint-visitor-keys "^3.0.0"
|
||||
|
||||
"@yarnpkg/lockfile@^1.1.0":
|
||||
@ -2475,13 +2518,13 @@ babel-plugin-polyfill-corejs2@^0.3.0:
|
||||
"@babel/helper-define-polyfill-provider" "^0.3.0"
|
||||
semver "^6.1.1"
|
||||
|
||||
babel-plugin-polyfill-corejs3@^0.4.0:
|
||||
version "0.4.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.4.0.tgz#0b571f4cf3d67f911512f5c04842a7b8e8263087"
|
||||
integrity sha512-YxFreYwUfglYKdLUGvIF2nJEsGwj+RhWSX/ije3D2vQPOXuyMLMtg/cCGMDpOA7Nd+MwlNdnGODbd2EwUZPlsw==
|
||||
babel-plugin-polyfill-corejs3@^0.5.0:
|
||||
version "0.5.0"
|
||||
resolved "https://registry.yarnpkg.com/babel-plugin-polyfill-corejs3/-/babel-plugin-polyfill-corejs3-0.5.0.tgz#f81371be3fe499d39e074e272a1ef86533f3d268"
|
||||
integrity sha512-Hcrgnmkf+4JTj73GbK3bBhlVPiLL47owUAnoJIf69Hakl3q+KfodbDXiZWGMM7iqCZTxCG3Z2VRfPNYES4rXqQ==
|
||||
dependencies:
|
||||
"@babel/helper-define-polyfill-provider" "^0.3.0"
|
||||
core-js-compat "^3.18.0"
|
||||
core-js-compat "^3.20.0"
|
||||
|
||||
babel-plugin-polyfill-regenerator@^0.3.0:
|
||||
version "0.3.0"
|
||||
@ -2657,13 +2700,13 @@ browserslist@^4.17.5:
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
|
||||
browserslist@^4.17.6:
|
||||
version "4.17.6"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.17.6.tgz#c76be33e7786b497f66cad25a73756c8b938985d"
|
||||
integrity sha512-uPgz3vyRTlEiCv4ee9KlsKgo2V6qPk7Jsn0KAn2OBqbqKo3iNcPEC1Ti6J4dwnz+aIRfEEEuOzC9IBk8tXUomw==
|
||||
browserslist@^4.19.1:
|
||||
version "4.19.1"
|
||||
resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.19.1.tgz#4ac0435b35ab655896c31d53018b6dd5e9e4c9a3"
|
||||
integrity sha512-u2tbbG5PdKRTUoctO3NBD8FQ5HdPh1ZXPHzp1rwaa5jTc+RV9/+RlWiAIKmjRPQF+xbGM9Kklj5bZQFa2s/38A==
|
||||
dependencies:
|
||||
caniuse-lite "^1.0.30001274"
|
||||
electron-to-chromium "^1.3.886"
|
||||
caniuse-lite "^1.0.30001286"
|
||||
electron-to-chromium "^1.4.17"
|
||||
escalade "^3.1.1"
|
||||
node-releases "^2.0.1"
|
||||
picocolors "^1.0.0"
|
||||
@ -2806,16 +2849,16 @@ caniuse-lite@^1.0.30001219:
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001237.tgz#4b7783661515b8e7151fc6376cfd97f0e427b9e5"
|
||||
integrity sha512-pDHgRndit6p1NR2GhzMbQ6CkRrp4VKuSsqbcLeOQppYPKOYkKT/6ZvZDvKJUqcmtyWIAHuZq3SVS2vc1egCZzw==
|
||||
|
||||
caniuse-lite@^1.0.30001274:
|
||||
version "1.0.30001275"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001275.tgz#26f5076629fe4e52bbd245f9046ad7b90aafdf57"
|
||||
integrity sha512-ihJVvj8RX0kn9GgP43HKhb5q9s2XQn4nEQhdldEJvZhCsuiB2XOq6fAMYQZaN6FPWfsr2qU0cdL0CSbETwbJAg==
|
||||
|
||||
caniuse-lite@^1.0.30001280:
|
||||
version "1.0.30001281"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001281.tgz#fe92459012197185263c1e5362e10df51ddad8c2"
|
||||
integrity sha512-1ktfDV2+4LYxOGeAZygMyvhiPBJKV1gsqPmfGGwRbwYOzDWbsQHgBFo+2lpRTMO2gWjPWJrCMLWa2fnSVnxn0A==
|
||||
|
||||
caniuse-lite@^1.0.30001286:
|
||||
version "1.0.30001298"
|
||||
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001298.tgz#0e690039f62e91c3ea581673d716890512e7ec52"
|
||||
integrity sha512-AcKqikjMLlvghZL/vfTHorlQsLDhGRalYf1+GmWCf5SCMziSGjRYQW/JEksj14NaYHIR6KIhrFAy0HV5C25UzQ==
|
||||
|
||||
caw@^2.0.0, caw@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/caw/-/caw-2.0.1.tgz#6c3ca071fc194720883c2dc5da9b074bfc7e9e95"
|
||||
@ -3061,12 +3104,12 @@ convert-source-map@^1.4.0, convert-source-map@^1.6.0, convert-source-map@^1.7.0:
|
||||
dependencies:
|
||||
safe-buffer "~5.1.1"
|
||||
|
||||
core-js-compat@^3.18.0, core-js-compat@^3.19.1:
|
||||
version "3.19.1"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.19.1.tgz#fe598f1a9bf37310d77c3813968e9f7c7bb99476"
|
||||
integrity sha512-Q/VJ7jAF/y68+aUsQJ/afPOewdsGkDtcMb40J8MbuWKlK3Y+wtHq8bTHKPj2WKWLIqmS5JhHs4CzHtz6pT2W6g==
|
||||
core-js-compat@^3.20.0, core-js-compat@^3.20.2:
|
||||
version "3.20.2"
|
||||
resolved "https://registry.yarnpkg.com/core-js-compat/-/core-js-compat-3.20.2.tgz#d1ff6936c7330959b46b2e08b122a8b14e26140b"
|
||||
integrity sha512-qZEzVQ+5Qh6cROaTPFLNS4lkvQ6mBzE3R6A6EEpssj7Zr2egMHgsy4XapdifqJDGC9CBiNv7s+ejI96rLNQFdg==
|
||||
dependencies:
|
||||
browserslist "^4.17.6"
|
||||
browserslist "^4.19.1"
|
||||
semver "7.0.0"
|
||||
|
||||
core-util-is@~1.0.0:
|
||||
@ -3428,16 +3471,16 @@ electron-to-chromium@^1.3.723:
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.752.tgz#0728587f1b9b970ec9ffad932496429aef750d09"
|
||||
integrity sha512-2Tg+7jSl3oPxgsBsWKh5H83QazTkmWG/cnNwJplmyZc7KcN61+I10oUgaXSVk/NwfvN3BdkKDR4FYuRBQQ2v0A==
|
||||
|
||||
electron-to-chromium@^1.3.886:
|
||||
version "1.3.887"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.887.tgz#b36aeed12a28aaa19460a467823f5bbe1f3c6f06"
|
||||
integrity sha512-QQUumrEjFDKSVYVdaeBmFdyQGoaV+fCSMyWHvfx/u22bRHSTeBQYt6P4jMY+gFd4kgKB9nqk7RMtWkDB49OYPA==
|
||||
|
||||
electron-to-chromium@^1.3.896:
|
||||
version "1.3.900"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.900.tgz#5be2c5818a2a012c511b4b43e87b6ab7a296d4f5"
|
||||
integrity sha512-SuXbQD8D4EjsaBaJJxySHbC+zq8JrFfxtb4GIr4E9n1BcROyMcRrJCYQNpJ9N+Wjf5mFp7Wp0OHykd14JNEzzQ==
|
||||
|
||||
electron-to-chromium@^1.4.17:
|
||||
version "1.4.42"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.42.tgz#fe3ee7c71f0b5302da12e730cd9cd395609625df"
|
||||
integrity sha512-JJLT8bjdswJzk8sNRnQjee0MGtO4zTn1t7eWwYPr8gPTadQgNRR/wFRKLGD6HZVZby39yHERkvuCVKNm10r7Dg==
|
||||
|
||||
emittery@^0.8.1:
|
||||
version "0.8.1"
|
||||
resolved "https://registry.yarnpkg.com/emittery/-/emittery-0.8.1.tgz#bb23cc86d03b30aa75a7f734819dee2e1ba70860"
|
||||
@ -5109,29 +5152,30 @@ jest-circus@^27.4.6:
|
||||
stack-utils "^2.0.3"
|
||||
throat "^6.0.1"
|
||||
|
||||
jest-cli@^27.4.6:
|
||||
version "27.4.6"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.6.tgz#b81e053a753111bddf0782c1a1a86b9b910f5b86"
|
||||
integrity sha512-SFfUC7jMHPGwsNSYBnJMNtjoSDrb34T+SEH5psDeGNVuTVov5Zi1RQpfJYwzpK2ex3OmnsNsiqLe3/SCc8S01Q==
|
||||
jest-cli@^27.4.7:
|
||||
version "27.4.7"
|
||||
resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-27.4.7.tgz#d00e759e55d77b3bcfea0715f527c394ca314e5a"
|
||||
integrity sha512-zREYhvjjqe1KsGV15mdnxjThKNDgza1fhDT+iUsXWLCq3sxe9w5xnvyctcYVT5PcdLSjv7Y5dCwTS3FCF1tiuw==
|
||||
dependencies:
|
||||
"@jest/core" "^27.4.6"
|
||||
"@jest/core" "^27.4.7"
|
||||
"@jest/test-result" "^27.4.6"
|
||||
"@jest/types" "^27.4.2"
|
||||
chalk "^4.0.0"
|
||||
exit "^0.1.2"
|
||||
graceful-fs "^4.2.4"
|
||||
import-local "^3.0.2"
|
||||
jest-config "^27.4.6"
|
||||
jest-config "^27.4.7"
|
||||
jest-util "^27.4.2"
|
||||
jest-validate "^27.4.6"
|
||||
prompts "^2.0.1"
|
||||
yargs "^16.2.0"
|
||||
|
||||
jest-config@^27.4.6:
|
||||
version "27.4.6"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.6.tgz#0970f6c92702ca7878120cea638791d00a3996ee"
|
||||
integrity sha512-3SGoFbaanQVg7MK5w/z8LnCMF6aZc2I7EQxS4s8fTfZpVYnWNDN34llcaViToIB62DFMhwHWTPX9X2O+4aDL1g==
|
||||
jest-config@^27.4.7:
|
||||
version "27.4.7"
|
||||
resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-27.4.7.tgz#4f084b2acbd172c8b43aa4cdffe75d89378d3972"
|
||||
integrity sha512-xz/o/KJJEedHMrIY9v2ParIoYSrSVY6IVeE4z5Z3i101GoA5XgfbJz+1C8EYPsv7u7f39dS8F9v46BHDhn0vlw==
|
||||
dependencies:
|
||||
"@babel/core" "^7.8.0"
|
||||
"@jest/test-sequencer" "^27.4.6"
|
||||
"@jest/types" "^27.4.2"
|
||||
babel-jest "^27.4.6"
|
||||
@ -5485,14 +5529,14 @@ jest-worker@^27.4.6:
|
||||
merge-stream "^2.0.0"
|
||||
supports-color "^8.0.0"
|
||||
|
||||
jest@27.4.6:
|
||||
version "27.4.6"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.6.tgz#5557fad2ab1d2b709e86d2332ea2552dc021a4d9"
|
||||
integrity sha512-BRbYo0MeujnnJIo206WRsfsr3gIMraR+LO9vZJsdG2/298aKYQJbS3wHG0KN3Z7SWIcf6JaSMM4E8X6cIdG9AA==
|
||||
jest@27.4.7:
|
||||
version "27.4.7"
|
||||
resolved "https://registry.yarnpkg.com/jest/-/jest-27.4.7.tgz#87f74b9026a1592f2da05b4d258e57505f28eca4"
|
||||
integrity sha512-8heYvsx7nV/m8m24Vk26Y87g73Ba6ueUd0MWed/NXMhSZIm62U/llVbS0PJe1SHunbyXjJ/BqG1z9bFjGUIvTg==
|
||||
dependencies:
|
||||
"@jest/core" "^27.4.6"
|
||||
"@jest/core" "^27.4.7"
|
||||
import-local "^3.0.2"
|
||||
jest-cli "^27.4.6"
|
||||
jest-cli "^27.4.7"
|
||||
|
||||
jimp@^0.16.1:
|
||||
version "0.16.1"
|
||||
|
161
tooling/cli.rs/Cargo.lock
generated
161
tooling/cli.rs/Cargo.lock
generated
@ -20,7 +20,7 @@ version = "0.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0b613b8e1e3cf911a086f53f03bf286f52fd7a7258e4fa606f0ef220d39d8877"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -60,9 +60,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "anyhow"
|
||||
version = "1.0.52"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "84450d0b4a8bd1ba4144ce8ce718fbc5d071358b1e5384bace6536b3d1f2d5b3"
|
||||
checksum = "94a45b455c14666b85fc40a019e8ab9eb75e3a124e05494f5397122bc9eb06e0"
|
||||
|
||||
[[package]]
|
||||
name = "ar"
|
||||
@ -153,7 +153,7 @@ version = "0.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1d36a02058e76b040de25a4464ba1c80935655595b661505c8b39b664828b95"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -264,9 +264,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap"
|
||||
version = "3.0.4"
|
||||
version = "3.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d01c9347757e131122b19cd19a05c85805b68c2352a97b623efdc3c295290299"
|
||||
checksum = "1957aa4a5fb388f0a0a73ce7556c5b42025b874e5cdc2c670775e346e97adec0"
|
||||
dependencies = [
|
||||
"atty",
|
||||
"bitflags",
|
||||
@ -281,9 +281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "clap_derive"
|
||||
version = "3.0.5"
|
||||
version = "3.0.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "41a0645a430ec9136d2d701e54a95d557de12649a9dd7109ced3187e648ac824"
|
||||
checksum = "9a1132dc3944b31c20dd8b906b3a9f0a5d0243e092d59171414969657ac6aa85"
|
||||
dependencies = [
|
||||
"heck",
|
||||
"proc-macro-error",
|
||||
@ -311,9 +311,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "combine"
|
||||
version = "4.6.2"
|
||||
version = "4.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b2b2f5d0ee456f3928812dfc8c6d9a1d592b98678f6d56db9b0cd2b7bc6c8db5"
|
||||
checksum = "50b727aacc797f9fc28e355d21f34709ac4fc9adecfe470ad07b8f4464f53062"
|
||||
dependencies = [
|
||||
"bytes",
|
||||
"memchr",
|
||||
@ -367,9 +367,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.0"
|
||||
version = "1.3.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "738c290dfaea84fc1ca15ad9c168d083b05a714e1efddd8edaab678dc28d2836"
|
||||
checksum = "a2209c310e29876f7f0b2721e7e26b84aff178aa3da5d091f9bfbf47669e60e3"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
]
|
||||
@ -558,7 +558,7 @@ version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d3dd60d1080a57a05ab032377049e0591415d2b31afd7028356dbf3cc6dcb066"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -569,7 +569,7 @@ checksum = "b697d66081d42af4fba142d56918a3cb21dc8eb63372c6b85d14f44fb9c5979b"
|
||||
dependencies = [
|
||||
"block-buffer 0.10.0",
|
||||
"crypto-common",
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@ -635,9 +635,9 @@ checksum = "e88a8acf291dafb59c2d96e8f59828f3838bb1a70398823ade51a84de6a6deed"
|
||||
|
||||
[[package]]
|
||||
name = "fastrand"
|
||||
version = "1.6.0"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "779d043b6a0b90cc4c0ed7ee380a6504394cee7efd7db050e3774eee387324b2"
|
||||
checksum = "c3fcf0cee53519c866c09b5de1f6c56ff9d647101f81c1964fa632e148896cdf"
|
||||
dependencies = [
|
||||
"instant",
|
||||
]
|
||||
@ -783,9 +783,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "getrandom"
|
||||
version = "0.2.3"
|
||||
version = "0.2.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcd999463524c52659517fe2cea98493cfe485d10565e7b0fb07dbba7ad2753"
|
||||
checksum = "418d37c8b1d42553c93648be529cb70f920d3baf8ef469b74b9638df426e0b4c"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"libc",
|
||||
@ -820,9 +820,9 @@ checksum = "9b919933a397b79c37e33b77bb2aa3dc8eb6e165ad809e58ff75bc7db2e34574"
|
||||
|
||||
[[package]]
|
||||
name = "handlebars"
|
||||
version = "4.2.0"
|
||||
version = "4.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2483bce82dd3ed52509d0117e4a30a488bd608be250ed7a0185301314239ed31"
|
||||
checksum = "25546a65e5cf1f471f3438796fc634650b31d7fcde01d444c309aeb28b92e3a8"
|
||||
dependencies = [
|
||||
"log",
|
||||
"pest",
|
||||
@ -1044,9 +1044,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "js-sys"
|
||||
version = "0.3.55"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7cc9ffccd38c451a86bf13657df244e9c3f37493cce8e5e21e940963777acc84"
|
||||
checksum = "a38fc24e30fd564ce974c02bf1d337caddff65be6cc4735a1f7eab22a7440f04"
|
||||
dependencies = [
|
||||
"wasm-bindgen",
|
||||
]
|
||||
@ -1126,15 +1126,15 @@ checksum = "830d08ce1d1d941e6b30645f1a0eb5643013d835ce3779a5fc208261dbe10f55"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.112"
|
||||
version = "0.2.117"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1b03d17f364a3a042d5e5d46b053bbbf82c92c9430c592dd4c064dc6ee997125"
|
||||
checksum = "e74d72e0f9b65b5b4ca49a346af3976df0f9c61d550727f349ecd559f251a26c"
|
||||
|
||||
[[package]]
|
||||
name = "libflate"
|
||||
version = "1.1.1"
|
||||
version = "1.1.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "16364af76ebb39b5869bb32c81fa93573267cd8c62bb3474e28d78fac3fb141e"
|
||||
checksum = "d2d57e534717ac3e0b8dc459fe338bdfb4e29d7eea8fd0926ba649ddd3f4765f"
|
||||
dependencies = [
|
||||
"adler32",
|
||||
"crc32fast",
|
||||
@ -1152,9 +1152,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.5"
|
||||
version = "0.4.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712a4d093c9976e24e7dbca41db895dabcbac38eb5f4045393d17a95bdfb1109"
|
||||
checksum = "88943dd7ef4a2e5a4bfa2753aaab3013e34ce2533d1996fb18ef591e315e2b3b"
|
||||
dependencies = [
|
||||
"scopeguard",
|
||||
]
|
||||
@ -1227,7 +1227,7 @@ version = "0.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "43935b78ea0886357ab9259bd227879a54b12a83de261c3270aad584500cba2f"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
"getrandom 0.2.4",
|
||||
"rpassword",
|
||||
"scrypt",
|
||||
]
|
||||
@ -1438,9 +1438,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "openssl-probe"
|
||||
version = "0.1.4"
|
||||
version = "0.1.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "28988d872ab76095a6e6ac88d99b54fd267702734fd7ffe610ca27f533ddb95a"
|
||||
checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf"
|
||||
|
||||
[[package]]
|
||||
name = "openssl-sys"
|
||||
@ -1457,9 +1457,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "os_info"
|
||||
version = "3.0.9"
|
||||
version = "3.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b89dd55b8d8d97dabd0d1adc625d188378fcf87632825bfe9c956acc9a11a72a"
|
||||
checksum = "198e392be7e882f0c2836f425e430f81d9a0e99651e4646311347417cddbfd43"
|
||||
dependencies = [
|
||||
"log",
|
||||
"serde",
|
||||
@ -1755,9 +1755,9 @@ checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3"
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.14"
|
||||
version = "1.0.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "47aa80447ce4daf1717500037052af176af5d38cc3e571d9ec1c7353fc10c87d"
|
||||
checksum = "864d3e96a899863136fc6e99f3d7cae289dafe43bf2c5ac19b70df7210c0a145"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
@ -1823,7 +1823,7 @@ version = "0.6.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d34f1408f55294453790c48b2f1ebbb1c5b4b7563eb1f418bcfcfdbb06ebb4e7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
"getrandom 0.2.4",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -1893,7 +1893,7 @@ version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "528532f3d801c87aec9def2add9ca802fe569e44a544afe633765267840abe64"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
"getrandom 0.2.4",
|
||||
"redox_syscall",
|
||||
]
|
||||
|
||||
@ -1940,9 +1940,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "rle-decode-fast"
|
||||
version = "1.0.1"
|
||||
version = "1.0.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cabe4fa914dec5870285fa7f71f602645da47c486e68486d2b4ceb4a343e90ac"
|
||||
checksum = "3582f63211428f83597b51b2ddb88e2a91a9d52d12831f9d08f5e624e8977422"
|
||||
|
||||
[[package]]
|
||||
name = "rpassword"
|
||||
@ -2076,9 +2076,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework"
|
||||
version = "2.4.2"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "525bc1abfda2e1998d152c45cf13e696f76d0a4972310b22fac1658b05df7c87"
|
||||
checksum = "3fed7948b6c68acbb6e20c334f55ad635dc0f75506963de4464289fbd3b051ac"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"core-foundation",
|
||||
@ -2089,9 +2089,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "security-framework-sys"
|
||||
version = "2.4.2"
|
||||
version = "2.6.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a9dd14d83160b528b7bfd66439110573efcfbe281b17fc2ca9f39f550d619c7e"
|
||||
checksum = "a57321bf8bc2362081b2599912d2961fe899c0efadf1b4b2f8d48b3e253bb96c"
|
||||
dependencies = [
|
||||
"core-foundation-sys",
|
||||
"libc",
|
||||
@ -2125,18 +2125,18 @@ checksum = "568a8e6258aa33c13358f81fd834adb854c6f7c9468520910a9b1e8fac068012"
|
||||
|
||||
[[package]]
|
||||
name = "serde"
|
||||
version = "1.0.133"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "97565067517b60e2d1ea8b268e59ce036de907ac523ad83a0475da04e818989a"
|
||||
checksum = "ce31e24b01e1e524df96f1c2fdd054405f8d7376249a5110886fb4b658484789"
|
||||
dependencies = [
|
||||
"serde_derive",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "serde_derive"
|
||||
version = "1.0.133"
|
||||
version = "1.0.136"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ed201699328568d8d08208fdd080e3ff594e6c422e438b6705905da01005d537"
|
||||
checksum = "08597e7152fcd306f41838ed3e37be9eaeed2b61c42e2117266a554fab4662f9"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2156,9 +2156,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "serde_json"
|
||||
version = "1.0.74"
|
||||
version = "1.0.78"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee2bb9cd061c5865d345bb02ca49fcef1391741b672b54a0bf7b679badec3142"
|
||||
checksum = "d23c1ba4cf0efd44be32017709280b32d1cea5c3f1275c3b6d9e8bc54f758085"
|
||||
dependencies = [
|
||||
"itoa 1.0.1",
|
||||
"ryu",
|
||||
@ -2232,9 +2232,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "sha1"
|
||||
version = "0.6.0"
|
||||
version = "0.6.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2579985fda508104f7587689507983eadd6a6e84dd35d6d115361f530916fa0d"
|
||||
checksum = "c1da05c97445caa12d05e848c4a4fcbbea29e748ac28f7e80e9b010392063770"
|
||||
dependencies = [
|
||||
"sha1_smol",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "sha1_smol"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ae1a47186c03a32177042e55dbc5fd5aee900b8e0069a8d70fba96a9375cd012"
|
||||
|
||||
[[package]]
|
||||
name = "sha2"
|
||||
@ -2272,9 +2281,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "siphasher"
|
||||
version = "0.3.7"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "533494a8f9b724d33625ab53c6c4800f7cc445895924a8ef649222dcb76e938b"
|
||||
checksum = "a86232ab60fa71287d7f2ddae4a7073f6b7aac33631c3015abb556f08c6d0a3e"
|
||||
|
||||
[[package]]
|
||||
name = "slab"
|
||||
@ -2284,9 +2293,9 @@ checksum = "9def91fd1e018fe007022791f865d0ccc9b3a0d5001e01aabb8b40e46000afb5"
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.7.0"
|
||||
version = "1.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1ecab6c735a6bb4139c0caafd0cc3635748bbb3acf4550e8138122099251f309"
|
||||
checksum = "f2dd574626839106c320a323308629dcb1acfc96e32a8cba364ddc61ac23ee83"
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
@ -2340,9 +2349,9 @@ checksum = "6bdef32e8150c2a081110b42772ffe7d7c9032b606bc226c8260fd97e0976601"
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "1.0.85"
|
||||
version = "1.0.86"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a684ac3dcd8913827e18cd09a68384ee66c1de24157e3c556c9ab16d85695fb7"
|
||||
checksum = "8a65b3f4ffa0092e9887669db0eae07941f023991ab58ea44da8fe8e2d511c6b"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2394,7 +2403,7 @@ dependencies = [
|
||||
"regex",
|
||||
"serde",
|
||||
"serde_json",
|
||||
"sha2 0.9.9",
|
||||
"sha2 0.10.1",
|
||||
"strsim",
|
||||
"tar",
|
||||
"tempfile",
|
||||
@ -2595,9 +2604,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "toml_edit"
|
||||
version = "0.12.4"
|
||||
version = "0.12.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "30c244d60366ca501c0549e418cef09d581afbc7f18cd0b646b26484a9fae005"
|
||||
checksum = "94217a6d6a89fb646cbc9f28283cf4745f77eb0b04c4f680c4b3ae4450e3a473"
|
||||
dependencies = [
|
||||
"combine",
|
||||
"indexmap",
|
||||
@ -2668,7 +2677,7 @@ version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9f214e8f697e925001e66ec2c6e37a4ef93f0f78c2eed7814394e10c62025b05"
|
||||
dependencies = [
|
||||
"generic-array 0.14.4",
|
||||
"generic-array 0.14.5",
|
||||
"subtle",
|
||||
]
|
||||
|
||||
@ -2729,7 +2738,7 @@ version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc5cf98d8186244414c848017f0e2676b3fcb46807f6668a97dfe67359a3c4b7"
|
||||
dependencies = [
|
||||
"getrandom 0.2.3",
|
||||
"getrandom 0.2.4",
|
||||
"sha1",
|
||||
]
|
||||
|
||||
@ -2792,9 +2801,9 @@ checksum = "fd6fbd9a79829dd1ad0cc20627bf1ed606756a7f77edff7b66b7064f9cb327c6"
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "632f73e236b219150ea279196e54e610f5dbafa5d61786303d4da54f84e47fce"
|
||||
checksum = "25f1af7423d8588a3d840681122e72e6a24ddbcb3f0ec385cac0d12d24256c06"
|
||||
dependencies = [
|
||||
"cfg-if 1.0.0",
|
||||
"wasm-bindgen-macro",
|
||||
@ -2802,9 +2811,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-backend"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a317bf8f9fba2476b4b2c85ef4c4af8ff39c3c7f0cdfeed4f82c34a880aa837b"
|
||||
checksum = "8b21c0df030f5a177f3cba22e9bc4322695ec43e7257d865302900290bcdedca"
|
||||
dependencies = [
|
||||
"bumpalo",
|
||||
"lazy_static",
|
||||
@ -2817,9 +2826,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d56146e7c495528bf6587663bea13a8eb588d39b36b679d83972e1a2dbbdacf9"
|
||||
checksum = "2f4203d69e40a52ee523b2529a773d5ffc1dc0071801c87b3d270b471b80ed01"
|
||||
dependencies = [
|
||||
"quote",
|
||||
"wasm-bindgen-macro-support",
|
||||
@ -2827,9 +2836,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-macro-support"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7803e0eea25835f8abdc585cd3021b3deb11543c6fe226dcd30b228857c5c5ab"
|
||||
checksum = "bfa8a30d46208db204854cadbb5d4baf5fcf8071ba5bf48190c3e59937962ebc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
@ -2840,15 +2849,15 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "wasm-bindgen-shared"
|
||||
version = "0.2.78"
|
||||
version = "0.2.79"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0237232789cf037d5480773fe568aac745bfe2afbc11a863e97901780a6b47cc"
|
||||
checksum = "3d958d035c4438e28c70e4321a2911302f10135ce78a9c7834c0cab4123d06a2"
|
||||
|
||||
[[package]]
|
||||
name = "web-sys"
|
||||
version = "0.3.55"
|
||||
version = "0.3.56"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "38eb105f1c59d9eaa6b5cdc92b859d85b926e82cb2e0945cd0c9259faa6fe9fb"
|
||||
checksum = "c060b319f29dd25724f09a2ba1418f142f539b2be99fbf4d2d5a8f7330afb8eb"
|
||||
dependencies = [
|
||||
"js-sys",
|
||||
"wasm-bindgen",
|
||||
@ -2958,9 +2967,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "zeroize"
|
||||
version = "1.4.3"
|
||||
version = "1.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d68d9dcec5f9b43a30d38c49f91dfedfaac384cb8f085faca366c26207dd1619"
|
||||
checksum = "7c88870063c39ee00ec285a2f8d6a966e5b6fb2becc4e8dac77ed0d370ed6006"
|
||||
|
||||
[[package]]
|
||||
name = "zip"
|
||||
|
@ -18,7 +18,7 @@ name = "cargo-tauri"
|
||||
path = "src/main.rs"
|
||||
|
||||
[dependencies]
|
||||
clap = { version = "=3.0.4", features = [ "derive" ] }
|
||||
clap = { version = "=3.0.6", features = [ "derive" ] }
|
||||
anyhow = "1.0"
|
||||
tauri-bundler = { version = "1.0.0-beta.4", path = "../bundler" }
|
||||
colored = "2.0"
|
||||
@ -34,7 +34,7 @@ tauri-utils = { version = "1.0.0-beta.3", path = "../../core/tauri-utils", featu
|
||||
schemars = { version = "0.8", features = ["url"] }
|
||||
toml = "0.5"
|
||||
valico = "3.6"
|
||||
handlebars = "4.1"
|
||||
handlebars = "4.2"
|
||||
include_dir = "0.7"
|
||||
minisign = "0.7"
|
||||
base64 = "0.13.0"
|
||||
|
@ -1107,6 +1107,13 @@
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"providerShortName": {
|
||||
"description": "Provider short name for notarization.",
|
||||
"type": [
|
||||
"string",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"signingIdentity": {
|
||||
"description": "Identity to use for code signing.",
|
||||
"type": [
|
||||
|
@ -44,17 +44,17 @@ pub fn execute_with_output(cmd: &mut Command) -> crate::Result<()> {
|
||||
pub fn command_env(debug: bool) -> HashMap<String, String> {
|
||||
let mut map = HashMap::new();
|
||||
|
||||
map.insert("PLATFORM".into(), std::env::consts::OS.into());
|
||||
map.insert("ARCH".into(), std::env::consts::ARCH.into());
|
||||
map.insert("FAMILY".into(), std::env::consts::FAMILY.into());
|
||||
map.insert("VERSION".into(), os_info::get().version().to_string());
|
||||
map.insert("TAURI_PLATFORM".into(), std::env::consts::OS.into());
|
||||
map.insert("TAURI_ARCH".into(), std::env::consts::ARCH.into());
|
||||
map.insert("TAURI_FAMILY".into(), std::env::consts::FAMILY.into());
|
||||
map.insert("TAURI_PLATFORM_VERSION".into(), os_info::get().version().to_string());
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
map.insert("PLATFORM_TYPE".into(), "Linux".into());
|
||||
map.insert("TAURI_PLATFORM_TYPE".into(), "Linux".into());
|
||||
#[cfg(target_os = "windows")]
|
||||
map.insert("PLATFORM_TYPE".into(), "Windows_NT".into());
|
||||
map.insert("TAURI_PLATFORM_TYPE".into(), "Windows_NT".into());
|
||||
#[cfg(target_os = "macos")]
|
||||
map.insert("PLATFORM_TYPE".into(), "Darwin".into());
|
||||
map.insert("TAURI_PLATFORM_TYPE".into(), "Darwin".into());
|
||||
|
||||
if debug {
|
||||
map.insert("TAURI_DEBUG".into(), "true".to_string());
|
||||
|
@ -104,7 +104,7 @@ impl Options {
|
||||
|
||||
self.dist_dir = self.dist_dir
|
||||
.or(request_input(
|
||||
r#"Whe re are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created?"#,
|
||||
r#"Where are your web assets (HTML/CSS/JS) located, relative to the "<current dir>/src-tauri/tauri.conf.json" file that will be created?"#,
|
||||
init_defaults.framework.as_ref().map(|f| f.dist_dir()),
|
||||
self.ci)?);
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user