refactor(core): remove window endpoints (#6947)

This commit is contained in:
Lucas Fernandes Nogueira 2023-05-12 04:18:00 -07:00 committed by GitHub
parent 5a9307d11c
commit 9a79dc0858
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
29 changed files with 76 additions and 3893 deletions

View File

@ -0,0 +1,6 @@
---
"tauri": patch
"tauri-utils": patch
---
Remove `enable_tauri_api` from the IPC scope.

View File

@ -0,0 +1,5 @@
---
"tauri-macros": patch
---
Removed the module command macros.

View File

@ -0,0 +1,6 @@
---
"tauri": patch
"api": patch
---
Moved the `window` JS APIs to its own plugin in the plugins-workspace repository.

View File

@ -2508,11 +2508,6 @@
"items": {
"type": "string"
}
},
"enableTauriAPI": {
"description": "Enables access to the Tauri API.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false

View File

@ -1,303 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use heck::{ToLowerCamelCase, ToSnakeCase};
use proc_macro::TokenStream;
use proc_macro2::{Span, TokenStream as TokenStream2};
use quote::{format_ident, quote, quote_spanned};
use syn::{
parse::{Parse, ParseStream},
parse_quote,
spanned::Spanned,
Data, DeriveInput, Error, Fields, Ident, ItemFn, LitStr, Token,
};
pub(crate) fn generate_command_enum(mut input: DeriveInput) -> TokenStream {
let mut deserialize_functions = TokenStream2::new();
let mut errors = TokenStream2::new();
input.attrs.push(parse_quote!(#[allow(dead_code)]));
match &mut input.data {
Data::Enum(data_enum) => {
for variant in &mut data_enum.variants {
let mut feature: Option<Ident> = None;
let mut error_message: Option<String> = None;
for attr in &variant.attrs {
if attr.path.is_ident("cmd") {
let r = attr
.parse_args_with(|input: ParseStream| {
if let Ok(f) = input.parse::<Ident>() {
feature.replace(f);
input.parse::<Token![,]>()?;
let error_message_raw: LitStr = input.parse()?;
error_message.replace(error_message_raw.value());
}
Ok(quote!())
})
.unwrap_or_else(syn::Error::into_compile_error);
errors.extend(r);
}
}
if let Some(f) = feature {
let error_message = if let Some(e) = error_message {
let e = e.to_string();
quote!(#e)
} else {
quote!("This API is not enabled in the allowlist.")
};
let deserialize_function_name = quote::format_ident!("__{}_deserializer", variant.ident);
deserialize_functions.extend(quote! {
#[cfg(not(#f))]
#[allow(non_snake_case)]
fn #deserialize_function_name<'de, D, T>(deserializer: D) -> ::std::result::Result<T, D::Error>
where
D: ::serde::de::Deserializer<'de>,
{
::std::result::Result::Err(::serde::de::Error::custom(crate::Error::ApiNotAllowlisted(#error_message.into()).to_string()))
}
});
let deserialize_function_name = deserialize_function_name.to_string();
variant
.attrs
.push(parse_quote!(#[cfg_attr(not(#f), serde(deserialize_with = #deserialize_function_name))]));
}
}
}
_ => {
return Error::new(
Span::call_site(),
"`command_enum` is only implemented for enums",
)
.to_compile_error()
.into()
}
};
TokenStream::from(quote! {
#errors
#input
#deserialize_functions
})
}
pub(crate) fn generate_run_fn(input: DeriveInput) -> TokenStream {
let name = &input.ident;
let data = &input.data;
let mut errors = TokenStream2::new();
let mut is_async = false;
let attrs = input.attrs;
for attr in attrs {
if attr.path.is_ident("cmd") {
let r = attr
.parse_args_with(|input: ParseStream| {
if let Ok(token) = input.parse::<Ident>() {
is_async = token == "async";
}
Ok(quote!())
})
.unwrap_or_else(syn::Error::into_compile_error);
errors.extend(r);
}
}
let maybe_await = if is_async { quote!(.await) } else { quote!() };
let maybe_async = if is_async { quote!(async) } else { quote!() };
let mut matcher;
match data {
Data::Enum(data_enum) => {
matcher = TokenStream2::new();
for variant in &data_enum.variants {
let variant_name = &variant.ident;
let mut feature = None;
for attr in &variant.attrs {
if attr.path.is_ident("cmd") {
let r = attr
.parse_args_with(|input: ParseStream| {
if let Ok(f) = input.parse::<Ident>() {
feature.replace(f);
input.parse::<Token![,]>()?;
let _: LitStr = input.parse()?;
}
Ok(quote!())
})
.unwrap_or_else(syn::Error::into_compile_error);
errors.extend(r);
}
}
let maybe_feature_check = if let Some(f) = feature {
quote!(#[cfg(#f)])
} else {
quote!()
};
let (fields_in_variant, variables) = match &variant.fields {
Fields::Unit => (quote_spanned! { variant.span() => }, quote!()),
Fields::Unnamed(fields) => {
let mut variables = TokenStream2::new();
for i in 0..fields.unnamed.len() {
let variable_name = format_ident!("value{}", i);
variables.extend(quote!(#variable_name,));
}
(quote_spanned! { variant.span() => (#variables) }, variables)
}
Fields::Named(fields) => {
let mut variables = TokenStream2::new();
for field in &fields.named {
let ident = field.ident.as_ref().unwrap();
variables.extend(quote!(#ident,));
}
(
quote_spanned! { variant.span() => { #variables } },
variables,
)
}
};
let mut variant_execute_function_name = format_ident!(
"{}",
variant_name.to_string().to_snake_case().to_lowercase()
);
variant_execute_function_name.set_span(variant_name.span());
matcher.extend(quote_spanned! {
variant.span() => #maybe_feature_check #name::#variant_name #fields_in_variant => #name::#variant_execute_function_name(context, #variables)#maybe_await.map(Into::into),
});
}
matcher.extend(quote! {
_ => Err(crate::error::into_anyhow("API not in the allowlist (https://tauri.app/docs/api/config#tauri.allowlist)")),
});
}
_ => {
return Error::new(
Span::call_site(),
"CommandModule is only implemented for enums",
)
.to_compile_error()
.into()
}
};
let (impl_generics, ty_generics, where_clause) = input.generics.split_for_impl();
let expanded = quote! {
#errors
impl #impl_generics #name #ty_generics #where_clause {
pub #maybe_async fn run<R: crate::Runtime>(self, context: crate::endpoints::InvokeContext<R>) -> super::Result<crate::endpoints::InvokeResponse> {
match self {
#matcher
}
}
}
};
TokenStream::from(expanded)
}
/// Attributes for the module enum variant handler.
pub struct HandlerAttributes {
allowlist: Ident,
}
impl Parse for HandlerAttributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
Ok(Self {
allowlist: input.parse()?,
})
}
}
pub enum AllowlistCheckKind {
Runtime,
Serde,
}
pub struct HandlerTestAttributes {
allowlist: Ident,
error_message: String,
allowlist_check_kind: AllowlistCheckKind,
}
impl Parse for HandlerTestAttributes {
fn parse(input: ParseStream) -> syn::Result<Self> {
let allowlist = input.parse()?;
input.parse::<Token![,]>()?;
let error_message_raw: LitStr = input.parse()?;
let error_message = error_message_raw.value();
let allowlist_check_kind =
if let (Ok(_), Ok(i)) = (input.parse::<Token![,]>(), input.parse::<Ident>()) {
if i == "runtime" {
AllowlistCheckKind::Runtime
} else {
AllowlistCheckKind::Serde
}
} else {
AllowlistCheckKind::Serde
};
Ok(Self {
allowlist,
error_message,
allowlist_check_kind,
})
}
}
pub fn command_handler(attributes: HandlerAttributes, function: ItemFn) -> TokenStream2 {
let allowlist = attributes.allowlist;
quote!(
#[cfg(#allowlist)]
#function
)
}
pub fn command_test(attributes: HandlerTestAttributes, function: ItemFn) -> TokenStream2 {
let allowlist = attributes.allowlist;
let error_message = attributes.error_message.as_str();
let signature = function.sig.clone();
let enum_variant_name = function.sig.ident.to_string().to_lower_camel_case();
let response = match attributes.allowlist_check_kind {
AllowlistCheckKind::Runtime => {
let test_name = function.sig.ident.clone();
quote!(super::Cmd::#test_name(crate::test::mock_invoke_context()))
}
AllowlistCheckKind::Serde => quote! {
serde_json::from_str::<super::Cmd>(&format!(r#"{{ "cmd": "{}", "data": null }}"#, #enum_variant_name))
},
};
quote!(
#[cfg(#allowlist)]
#function
#[cfg(not(#allowlist))]
#[allow(unused_variables)]
#[quickcheck_macros::quickcheck]
#signature {
if let Err(e) = #response {
assert!(e.to_string().contains(#error_message));
} else {
panic!("unexpected response");
}
}
)
}

View File

@ -4,10 +4,9 @@
use crate::context::ContextItems;
use proc_macro::TokenStream;
use syn::{parse_macro_input, DeriveInput, ItemFn};
use syn::{parse_macro_input, DeriveInput};
mod command;
mod command_module;
mod mobile;
mod runtime;
@ -81,40 +80,3 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre
let input = parse_macro_input!(input as DeriveInput);
runtime::default_runtime(attributes, input).into()
}
/// Prepares the command module enum.
#[doc(hidden)]
#[proc_macro_derive(CommandModule, attributes(cmd))]
pub fn derive_command_module(input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
command_module::generate_run_fn(input)
}
/// Adds a `run` method to an enum (one of the tauri endpoint modules).
/// The `run` method takes a `tauri::endpoints::InvokeContext`
/// and returns a `tauri::Result<tauri::endpoints::InvokeResponse>`.
/// It matches on each enum variant and call a method with name equal to the variant name, lowercased and snake_cased,
/// passing the context and the variant's fields as arguments.
/// That function must also return the same `Result<InvokeResponse>`.
#[doc(hidden)]
#[proc_macro_attribute]
pub fn command_enum(_: TokenStream, input: TokenStream) -> TokenStream {
let input = parse_macro_input!(input as DeriveInput);
command_module::generate_command_enum(input)
}
#[doc(hidden)]
#[proc_macro_attribute]
pub fn module_command_handler(attributes: TokenStream, input: TokenStream) -> TokenStream {
let attributes = parse_macro_input!(attributes as command_module::HandlerAttributes);
let input = parse_macro_input!(input as ItemFn);
command_module::command_handler(attributes, input).into()
}
#[doc(hidden)]
#[proc_macro_attribute]
pub fn module_command_test(attributes: TokenStream, input: TokenStream) -> TokenStream {
let attributes = parse_macro_input!(attributes as command_module::HandlerTestAttributes);
let input = parse_macro_input!(input as ItemFn);
command_module::command_test(attributes, input).into()
}

View File

@ -1019,9 +1019,6 @@ pub struct RemoteDomainAccessScope {
/// The list of plugins that are allowed in this scope.
#[serde(default)]
pub plugins: Vec<String>,
/// Enables access to the Tauri API.
#[serde(default, rename = "enableTauriAPI", alias = "enable-tauri-api")]
pub enable_tauri_api: bool,
}
/// Security configuration.
@ -3344,7 +3341,6 @@ mod build {
let domain = str_lit(&self.domain);
let windows = vec_lit(&self.windows, str_lit);
let plugins = vec_lit(&self.plugins, str_lit);
let enable_tauri_api = self.enable_tauri_api;
literal_struct!(
tokens,
@ -3352,8 +3348,7 @@ mod build {
scheme,
domain,
windows,
plugins,
enable_tauri_api
plugins
);
}
}

File diff suppressed because one or more lines are too long

View File

@ -1,116 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
use crate::{
hooks::{InvokeError, InvokeMessage, InvokeResolver},
Config, Invoke, PackageInfo, Runtime, Window,
};
pub use anyhow::Result;
use serde::{Deserialize, Serialize};
use serde_json::Value as JsonValue;
use std::sync::Arc;
mod window;
/// The context passed to the invoke handler.
pub struct InvokeContext<R: Runtime> {
pub window: Window<R>,
pub config: Arc<Config>,
pub package_info: PackageInfo,
}
#[cfg(test)]
impl<R: Runtime> Clone for InvokeContext<R> {
fn clone(&self) -> Self {
Self {
window: self.window.clone(),
config: self.config.clone(),
package_info: self.package_info.clone(),
}
}
}
/// The response for a JS `invoke` call.
pub struct InvokeResponse {
json: Result<JsonValue>,
}
impl<T: Serialize> From<T> for InvokeResponse {
fn from(value: T) -> Self {
Self {
json: serde_json::to_value(value).map_err(Into::into),
}
}
}
#[derive(Deserialize)]
#[serde(tag = "module", content = "message")]
enum Module {
Window(Box<window::Cmd>),
}
impl Module {
fn run<R: Runtime>(
self,
window: Window<R>,
resolver: InvokeResolver<R>,
config: Arc<Config>,
package_info: PackageInfo,
) {
let context = InvokeContext {
window,
config,
package_info,
};
match self {
Self::Window(cmd) => resolver.respond_async(async move {
cmd
.run(context)
.await
.and_then(|r| r.json)
.map_err(InvokeError::from_anyhow)
}),
}
}
}
pub(crate) fn handle<R: Runtime>(
module: String,
invoke: Invoke<R>,
config: Arc<Config>,
package_info: &PackageInfo,
) {
let Invoke { message, resolver } = invoke;
let InvokeMessage {
mut payload,
window,
..
} = message;
if let JsonValue::Object(ref mut obj) = payload {
obj.insert("module".to_string(), JsonValue::String(module.clone()));
}
match serde_json::from_value::<Module>(payload) {
Ok(module) => module.run(window, resolver, config, package_info.clone()),
Err(e) => {
let message = e.to_string();
if message.starts_with("unknown variant") {
let mut s = message.split('`');
s.next();
if let Some(unknown_variant_name) = s.next() {
if unknown_variant_name == module {
return resolver.reject(format!(
"The `{module}` module is not enabled. You must enable one of its APIs in the allowlist.",
));
} else if module == "Window" {
return resolver.reject(window::into_allowlist_error(unknown_variant_name).to_string());
}
}
}
resolver.reject(message);
}
}
}

View File

@ -1,364 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
#![allow(unused_imports)]
use super::{InvokeContext, InvokeResponse};
#[cfg(window_create)]
use crate::runtime::{webview::WindowBuilder, Dispatch};
use crate::{
runtime::{
window::dpi::{Position, Size},
UserAttentionType,
},
utils::config::WindowConfig,
CursorIcon, Icon, Manager, Runtime,
};
use serde::Deserialize;
use tauri_macros::{command_enum, module_command_handler, CommandModule};
#[derive(Deserialize)]
#[serde(untagged)]
pub enum IconDto {
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
File(std::path::PathBuf),
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
Raw(Vec<u8>),
Rgba {
rgba: Vec<u8>,
width: u32,
height: u32,
},
}
impl From<IconDto> for Icon {
fn from(icon: IconDto) -> Self {
match icon {
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
IconDto::File(path) => Self::File(path),
#[cfg(any(feature = "icon-png", feature = "icon-ico"))]
IconDto::Raw(raw) => Self::Raw(raw),
IconDto::Rgba {
rgba,
width,
height,
} => Self::Rgba {
rgba,
width,
height,
},
}
}
}
/// Window management API descriptor.
#[derive(Deserialize)]
#[serde(tag = "type", content = "payload", rename_all = "camelCase")]
pub enum WindowManagerCmd {
// Getters
ScaleFactor,
InnerPosition,
OuterPosition,
InnerSize,
OuterSize,
IsFullscreen,
IsMinimized,
IsMaximized,
IsDecorated,
IsResizable,
IsVisible,
Title,
CurrentMonitor,
PrimaryMonitor,
AvailableMonitors,
Theme,
// Setters
#[cfg(window_center)]
Center,
#[cfg(window_request_user_attention)]
RequestUserAttention(Option<UserAttentionType>),
#[cfg(window_set_resizable)]
SetResizable(bool),
#[cfg(window_set_title)]
SetTitle(String),
#[cfg(window_maximize)]
Maximize,
#[cfg(window_unmaximize)]
Unmaximize,
#[cfg(all(window_maximize, window_unmaximize))]
ToggleMaximize,
#[cfg(window_minimize)]
Minimize,
#[cfg(window_unminimize)]
Unminimize,
#[cfg(window_show)]
Show,
#[cfg(window_hide)]
Hide,
#[cfg(window_close)]
Close,
#[cfg(window_set_decorations)]
SetDecorations(bool),
#[cfg(window_set_shadow)]
SetShadow(bool),
#[cfg(window_set_always_on_top)]
#[serde(rename_all = "camelCase")]
SetAlwaysOnTop(bool),
#[cfg(window_set_content_protected)]
SetContentProtected(bool),
#[cfg(window_set_size)]
SetSize(Size),
#[cfg(window_set_min_size)]
SetMinSize(Option<Size>),
#[cfg(window_set_max_size)]
SetMaxSize(Option<Size>),
#[cfg(window_set_position)]
SetPosition(Position),
#[cfg(window_set_fullscreen)]
SetFullscreen(bool),
#[cfg(window_set_focus)]
SetFocus,
#[cfg(window_set_icon)]
SetIcon {
icon: IconDto,
},
#[cfg(window_set_skip_taskbar)]
SetSkipTaskbar(bool),
#[cfg(window_set_cursor_grab)]
SetCursorGrab(bool),
#[cfg(window_set_cursor_visible)]
SetCursorVisible(bool),
#[cfg(window_set_cursor_icon)]
SetCursorIcon(CursorIcon),
#[cfg(window_set_cursor_position)]
SetCursorPosition(Position),
#[cfg(window_set_ignore_cursor_events)]
SetIgnoreCursorEvents(bool),
#[cfg(window_start_dragging)]
StartDragging,
#[cfg(window_print)]
Print,
// internals
#[cfg(all(window_maximize, window_unmaximize))]
#[serde(rename = "__toggleMaximize")]
InternalToggleMaximize,
#[cfg(any(debug_assertions, feature = "devtools"))]
#[serde(rename = "__toggleDevtools")]
InternalToggleDevtools,
}
pub fn into_allowlist_error(variant: &str) -> crate::Error {
match variant {
"center" => crate::Error::ApiNotAllowlisted("window > center".to_string()),
"requestUserAttention" => {
crate::Error::ApiNotAllowlisted("window > requestUserAttention".to_string())
}
"setResizable" => crate::Error::ApiNotAllowlisted("window > setResizable".to_string()),
"setTitle" => crate::Error::ApiNotAllowlisted("window > setTitle".to_string()),
"maximize" => crate::Error::ApiNotAllowlisted("window > maximize".to_string()),
"unmaximize" => crate::Error::ApiNotAllowlisted("window > unmaximize".to_string()),
"toggleMaximize" => {
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
}
"minimize" => crate::Error::ApiNotAllowlisted("window > minimize".to_string()),
"unminimize" => crate::Error::ApiNotAllowlisted("window > unminimize".to_string()),
"show" => crate::Error::ApiNotAllowlisted("window > show".to_string()),
"hide" => crate::Error::ApiNotAllowlisted("window > hide".to_string()),
"close" => crate::Error::ApiNotAllowlisted("window > close".to_string()),
"setDecorations" => crate::Error::ApiNotAllowlisted("window > setDecorations".to_string()),
"setShadow" => crate::Error::ApiNotAllowlisted("window > setShadow".to_string()),
"setAlwaysOnTop" => crate::Error::ApiNotAllowlisted("window > setAlwaysOnTop".to_string()),
"setContentProtected" => {
crate::Error::ApiNotAllowlisted("window > setContentProtected".to_string())
}
"setSize" => crate::Error::ApiNotAllowlisted("window > setSize".to_string()),
"setMinSize" => crate::Error::ApiNotAllowlisted("window > setMinSize".to_string()),
"setMaxSize" => crate::Error::ApiNotAllowlisted("window > setMaxSize".to_string()),
"setPosition" => crate::Error::ApiNotAllowlisted("window > setPosition".to_string()),
"setFullscreen" => crate::Error::ApiNotAllowlisted("window > setFullscreen".to_string()),
"setIcon" => crate::Error::ApiNotAllowlisted("window > setIcon".to_string()),
"setSkipTaskbar" => crate::Error::ApiNotAllowlisted("window > setSkipTaskbar".to_string()),
"setCursorGrab" => crate::Error::ApiNotAllowlisted("window > setCursorGrab".to_string()),
"setCursorVisible" => crate::Error::ApiNotAllowlisted("window > setCursorVisible".to_string()),
"setCursorIcon" => crate::Error::ApiNotAllowlisted("window > setCursorIcon".to_string()),
"setCursorPosition" => {
crate::Error::ApiNotAllowlisted("window > setCursorPosition".to_string())
}
"setIgnoreCursorEvents" => {
crate::Error::ApiNotAllowlisted("window > setIgnoreCursorEvents".to_string())
}
"startDragging" => crate::Error::ApiNotAllowlisted("window > startDragging".to_string()),
"print" => crate::Error::ApiNotAllowlisted("window > print".to_string()),
"__toggleMaximize" => {
crate::Error::ApiNotAllowlisted("window > maximize and window > unmaximize".to_string())
}
"__toggleDevtools" => crate::Error::ApiNotAllowlisted("devtools".to_string()),
_ => crate::Error::ApiNotAllowlisted("window".to_string()),
}
}
/// The API descriptor.
#[command_enum]
#[derive(Deserialize, CommandModule)]
#[cmd(async)]
#[serde(tag = "cmd", content = "data", rename_all = "camelCase")]
pub enum Cmd {
#[cmd(window_create, "window > create")]
CreateWebview { options: Box<WindowConfig> },
Manage {
label: Option<String>,
cmd: WindowManagerCmd,
},
}
impl Cmd {
#[module_command_handler(window_create)]
async fn create_webview<R: Runtime>(
context: InvokeContext<R>,
mut options: Box<WindowConfig>,
) -> super::Result<()> {
options.additional_browser_args = None;
crate::window::WindowBuilder::from_config(&context.window, *options)
.build()
.map_err(crate::error::into_anyhow)?;
Ok(())
}
async fn manage<R: Runtime>(
context: InvokeContext<R>,
label: Option<String>,
cmd: WindowManagerCmd,
) -> super::Result<InvokeResponse> {
Self::_manage(context, label, cmd)
.await
.map_err(crate::error::into_anyhow)
}
async fn _manage<R: Runtime>(
context: InvokeContext<R>,
label: Option<String>,
cmd: WindowManagerCmd,
) -> crate::Result<InvokeResponse> {
let window = match label {
Some(l) if !l.is_empty() => context
.window
.get_window(&l)
.ok_or(crate::Error::WebviewNotFound)?,
_ => context.window,
};
match cmd {
// Getters
WindowManagerCmd::ScaleFactor => return Ok(window.scale_factor()?.into()),
WindowManagerCmd::InnerPosition => return Ok(window.inner_position()?.into()),
WindowManagerCmd::OuterPosition => return Ok(window.outer_position()?.into()),
WindowManagerCmd::InnerSize => return Ok(window.inner_size()?.into()),
WindowManagerCmd::OuterSize => return Ok(window.outer_size()?.into()),
WindowManagerCmd::IsFullscreen => return Ok(window.is_fullscreen()?.into()),
WindowManagerCmd::IsMinimized => return Ok(window.is_minimized()?.into()),
WindowManagerCmd::IsMaximized => return Ok(window.is_maximized()?.into()),
WindowManagerCmd::IsDecorated => return Ok(window.is_decorated()?.into()),
WindowManagerCmd::IsResizable => return Ok(window.is_resizable()?.into()),
WindowManagerCmd::IsVisible => return Ok(window.is_visible()?.into()),
WindowManagerCmd::Title => return Ok(window.title()?.into()),
WindowManagerCmd::CurrentMonitor => return Ok(window.current_monitor()?.into()),
WindowManagerCmd::PrimaryMonitor => return Ok(window.primary_monitor()?.into()),
WindowManagerCmd::AvailableMonitors => return Ok(window.available_monitors()?.into()),
WindowManagerCmd::Theme => return Ok(window.theme()?.into()),
// Setters
#[cfg(all(desktop, window_center))]
WindowManagerCmd::Center => window.center()?,
#[cfg(all(desktop, window_request_user_attention))]
WindowManagerCmd::RequestUserAttention(request_type) => {
window.request_user_attention(request_type)?
}
#[cfg(all(desktop, window_set_resizable))]
WindowManagerCmd::SetResizable(resizable) => window.set_resizable(resizable)?,
#[cfg(all(desktop, window_set_title))]
WindowManagerCmd::SetTitle(title) => window.set_title(&title)?,
#[cfg(all(desktop, window_maximize))]
WindowManagerCmd::Maximize => window.maximize()?,
#[cfg(all(desktop, window_unmaximize))]
WindowManagerCmd::Unmaximize => window.unmaximize()?,
#[cfg(all(desktop, window_maximize, window_unmaximize))]
WindowManagerCmd::ToggleMaximize => match window.is_maximized()? {
true => window.unmaximize()?,
false => window.maximize()?,
},
#[cfg(all(desktop, window_minimize))]
WindowManagerCmd::Minimize => window.minimize()?,
#[cfg(all(desktop, window_unminimize))]
WindowManagerCmd::Unminimize => window.unminimize()?,
#[cfg(all(desktop, window_show))]
WindowManagerCmd::Show => window.show()?,
#[cfg(all(desktop, window_hide))]
WindowManagerCmd::Hide => window.hide()?,
#[cfg(all(desktop, window_close))]
WindowManagerCmd::Close => window.close()?,
#[cfg(all(desktop, window_set_decorations))]
WindowManagerCmd::SetDecorations(decorations) => window.set_decorations(decorations)?,
#[cfg(all(desktop, window_set_shadow))]
WindowManagerCmd::SetShadow(enable) => window.set_shadow(enable)?,
#[cfg(all(desktop, window_set_always_on_top))]
WindowManagerCmd::SetAlwaysOnTop(always_on_top) => window.set_always_on_top(always_on_top)?,
#[cfg(all(desktop, window_set_content_protected))]
WindowManagerCmd::SetContentProtected(protected) => {
window.set_content_protected(protected)?
}
#[cfg(all(desktop, window_set_size))]
WindowManagerCmd::SetSize(size) => window.set_size(size)?,
#[cfg(all(desktop, window_set_min_size))]
WindowManagerCmd::SetMinSize(size) => window.set_min_size(size)?,
#[cfg(all(desktop, window_set_max_size))]
WindowManagerCmd::SetMaxSize(size) => window.set_max_size(size)?,
#[cfg(all(desktop, window_set_position))]
WindowManagerCmd::SetPosition(position) => window.set_position(position)?,
#[cfg(all(desktop, window_set_fullscreen))]
WindowManagerCmd::SetFullscreen(fullscreen) => window.set_fullscreen(fullscreen)?,
#[cfg(all(desktop, window_set_focus))]
WindowManagerCmd::SetFocus => window.set_focus()?,
#[cfg(all(desktop, window_set_icon))]
WindowManagerCmd::SetIcon { icon } => window.set_icon(icon.into())?,
#[cfg(all(desktop, window_set_skip_taskbar))]
WindowManagerCmd::SetSkipTaskbar(skip) => window.set_skip_taskbar(skip)?,
#[cfg(all(desktop, window_set_cursor_grab))]
WindowManagerCmd::SetCursorGrab(grab) => window.set_cursor_grab(grab)?,
#[cfg(all(desktop, window_set_cursor_visible))]
WindowManagerCmd::SetCursorVisible(visible) => window.set_cursor_visible(visible)?,
#[cfg(all(desktop, window_set_cursor_icon))]
WindowManagerCmd::SetCursorIcon(icon) => window.set_cursor_icon(icon)?,
#[cfg(all(desktop, window_set_cursor_position))]
WindowManagerCmd::SetCursorPosition(position) => window.set_cursor_position(position)?,
#[cfg(all(desktop, window_set_ignore_cursor_events))]
WindowManagerCmd::SetIgnoreCursorEvents(ignore_cursor) => {
window.set_ignore_cursor_events(ignore_cursor)?
}
#[cfg(all(desktop, window_start_dragging))]
WindowManagerCmd::StartDragging => window.start_dragging()?,
#[cfg(all(desktop, window_print))]
WindowManagerCmd::Print => window.print()?,
// internals
#[cfg(all(desktop, window_maximize, window_unmaximize))]
WindowManagerCmd::InternalToggleMaximize => {
if window.is_resizable()? {
match window.is_maximized()? {
true => window.unmaximize()?,
false => window.maximize()?,
}
}
}
#[cfg(all(desktop, any(debug_assertions, feature = "devtools")))]
WindowManagerCmd::InternalToggleDevtools => {
if window.is_devtools_open() {
window.close_devtools();
} else {
window.open_devtools();
}
}
#[allow(unreachable_patterns)]
_ => (),
}
#[allow(unreachable_code)]
Ok(().into())
}
}

View File

@ -51,10 +51,7 @@ pub enum Error {
AssetNotFound(String),
/// Failed to serialize/deserialize.
#[error("JSON error: {0}")]
Json(serde_json::Error),
/// Unknown API type.
#[error("unknown API: {0:?}")]
UnknownApi(Option<serde_json::Error>),
Json(#[from] serde_json::Error),
/// Failed to execute tauri API.
#[error("failed to execute API: {0}")]
FailedToExecuteApi(#[from] crate::api::Error),
@ -105,24 +102,3 @@ pub enum Error {
#[error("jni error: {0}")]
Jni(#[from] jni::errors::Error),
}
pub(crate) fn into_anyhow<T: std::fmt::Display>(err: T) -> anyhow::Error {
anyhow::anyhow!(err.to_string())
}
impl Error {
#[allow(dead_code)]
pub(crate) fn into_anyhow(self) -> anyhow::Error {
anyhow::anyhow!(self.to_string())
}
}
impl From<serde_json::Error> for Error {
fn from(error: serde_json::Error) -> Self {
if error.to_string().contains("unknown variant") {
Self::UnknownApi(Some(error))
} else {
Self::Json(error)
}
}
}

View File

@ -61,9 +61,6 @@ impl PageLoadPayload {
pub struct InvokePayload {
/// The invoke command.
pub cmd: String,
#[serde(rename = "__tauriModule")]
#[doc(hidden)]
pub tauri_module: Option<String>,
/// The success callback.
pub callback: CallbackFn,
/// The error callback.

View File

@ -179,8 +179,6 @@ pub mod api;
pub(crate) mod app;
pub mod async_runtime;
pub mod command;
/// The Tauri API endpoints.
mod endpoints;
mod error;
mod event;
mod hooks;

View File

@ -14,7 +14,6 @@ pub struct RemoteDomainAccessScope {
domain: String,
windows: Vec<String>,
plugins: Vec<String>,
enable_tauri_api: bool,
}
impl RemoteDomainAccessScope {
@ -25,7 +24,6 @@ impl RemoteDomainAccessScope {
domain: domain.into(),
windows: Vec::new(),
plugins: Vec::new(),
enable_tauri_api: false,
}
}
@ -47,9 +45,13 @@ impl RemoteDomainAccessScope {
self
}
/// Enables access to the Tauri API.
pub fn enable_tauri_api(mut self) -> Self {
self.enable_tauri_api = true;
/// Adds the given list of plugins to the allowed plugin list.
pub fn add_plugins<I, S>(mut self, plugins: I) -> Self
where
I: IntoIterator<Item = S>,
S: Into<String>,
{
self.plugins.extend(plugins.into_iter().map(Into::into));
self
}
@ -67,11 +69,6 @@ impl RemoteDomainAccessScope {
pub fn plugins(&self) -> &Vec<String> {
&self.plugins
}
/// Whether this scope enables Tauri API access or not.
pub fn enables_tauri_api(&self) -> bool {
self.enable_tauri_api
}
}
pub(crate) struct RemoteAccessError {
@ -99,7 +96,6 @@ impl Scope {
domain: s.domain,
windows: s.windows,
plugins: s.plugins,
enable_tauri_api: s.enable_tauri_api,
})
.collect();
@ -119,7 +115,7 @@ impl Scope {
/// app.ipc_scope().configure_remote_access(
/// RemoteDomainAccessScope::new("tauri.app")
/// .add_window("main")
/// .enable_tauri_api()
/// .add_plugins(["path", "event"])
/// );
/// Ok(())
/// });
@ -238,7 +234,6 @@ mod tests {
InvokePayload {
cmd: "plugin:path|is_absolute".into(),
tauri_module: None,
callback,
error,
inner: serde_json::Value::Object(payload),
@ -251,7 +246,6 @@ mod tests {
InvokePayload {
cmd: format!("plugin:{PLUGIN_NAME}|doSomething"),
tauri_module: None,
callback,
error,
inner: Default::default(),
@ -262,8 +256,7 @@ mod tests {
fn scope_not_defined() {
let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("app.tauri.app")
.add_window("other")
.add_plugin("path")
.enable_tauri_api()]);
.add_plugin("path")]);
window.navigate("https://tauri.app".parse().unwrap());
assert_ipc_response::<()>(
@ -280,8 +273,7 @@ mod tests {
fn scope_not_defined_for_window() {
let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app")
.add_window("second")
.add_plugin("path")
.enable_tauri_api()]);
.add_plugin("path")]);
window.navigate("https://tauri.app".parse().unwrap());
assert_ipc_response::<()>(
@ -295,8 +287,7 @@ mod tests {
fn scope_not_defined_for_url() {
let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("github.com")
.add_window("main")
.add_plugin("path")
.enable_tauri_api()]);
.add_plugin("path")]);
window.navigate("https://tauri.app".parse().unwrap());
assert_ipc_response::<()>(
@ -313,12 +304,10 @@ mod tests {
let (_app, mut window) = test_context(vec![
RemoteDomainAccessScope::new("tauri.app")
.add_window("main")
.add_plugin("path")
.enable_tauri_api(),
.add_plugin("path"),
RemoteDomainAccessScope::new("sub.tauri.app")
.add_window("main")
.add_plugin("path")
.enable_tauri_api(),
.add_plugin("path"),
]);
window.navigate("https://tauri.app".parse().unwrap());
@ -352,8 +341,7 @@ mod tests {
fn subpath_is_allowed() {
let (_app, window) = test_context(vec![RemoteDomainAccessScope::new("tauri.app")
.add_window("main")
.add_plugin("path")
.enable_tauri_api()]);
.add_plugin("path")]);
window.navigate("https://tauri.app/inner/path".parse().unwrap());
assert_ipc_response(&window, path_is_absolute_payload(), Ok(true));

View File

@ -9,7 +9,7 @@ pub use mock_runtime::*;
use std::{borrow::Cow, sync::Arc};
use crate::{Manager, Pattern, WindowBuilder};
use crate::{Pattern, WindowBuilder};
use tauri_utils::{
assets::{AssetKey, Assets, CspHash},
config::{Config, PatternKind, TauriConfig, WindowUrl},
@ -81,13 +81,3 @@ pub fn mock_app() -> crate::App<MockRuntime> {
app
}
#[allow(dead_code)]
pub(crate) fn mock_invoke_context() -> crate::endpoints::InvokeContext<MockRuntime> {
let app = mock_app();
crate::endpoints::InvokeContext {
window: app.get_window("main").unwrap(),
config: app.config(),
package_info: app.package_info().clone(),
}
}

View File

@ -1516,18 +1516,7 @@ impl<R: Runtime> Window<R> {
return Ok(());
}
if let Some(module) = &payload.tauri_module {
if !is_local && scope.map(|s| !s.enables_tauri_api()).unwrap_or_default() {
invoke.resolver.reject(IPC_SCOPE_DOES_NOT_ALLOW);
return Ok(());
}
crate::endpoints::handle(
module.to_string(),
invoke,
manager.config(),
manager.package_info(),
);
} else if payload.cmd.starts_with("plugin:") {
if payload.cmd.starts_with("plugin:") {
if !is_local {
let command = invoke.message.command.replace("plugin:", "");
let plugin_name = command.split('|').next().unwrap().to_string();

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -52,11 +52,6 @@ pub fn run() {
.content_protected(true);
}
#[cfg(target_os = "windows")]
{
window_builder = window_builder.transparent(true).decorations(false);
}
let window = window_builder.build().unwrap();
#[cfg(debug_assertions)]

View File

@ -1,19 +1,11 @@
<script>
import { writable } from 'svelte/store'
import { appWindow, getCurrent } from '@tauri-apps/api/window'
import Welcome from './views/Welcome.svelte'
import Cli from './views/Cli.svelte'
import Communication from './views/Communication.svelte'
import Window from './views/Window.svelte'
import WebRTC from './views/WebRTC.svelte'
import { onMount } from 'svelte'
import { listen } from '@tauri-apps/api/event'
appWindow.onFileDropEvent((event) => {
onMessage(`File drop: ${JSON.stringify(event.payload)}`)
})
const userAgent = navigator.userAgent.toLowerCase()
const isMobile = userAgent.includes('android') || userAgent.includes('iphone')
@ -29,16 +21,6 @@
component: Communication,
icon: 'i-codicon-radio-tower'
},
!isMobile && {
label: 'CLI',
component: Cli,
icon: 'i-codicon-terminal'
},
!isMobile && {
label: 'Window',
component: Window,
icon: 'i-codicon-window'
},
{
label: 'WebRTC',
component: WebRTC,
@ -51,25 +33,6 @@
selected = view
}
// Window controls
let isWindowMaximized
onMount(async () => {
const window = getCurrent()
isWindowMaximized = await window.isMaximized()
listen('tauri://resize', async () => {
isWindowMaximized = await window.isMaximized()
})
})
function minimize() {
getCurrent().minimize()
}
async function toggleMaximize() {
const window = getCurrent()
;(await window.isMaximized()) ? window.unmaximize() : window.maximize()
}
// dark/light
let isDark
onMount(() => {
@ -141,8 +104,6 @@
document.addEventListener('mousemove', moveHandler)
}
const isWindows = navigator.appVersion.includes('Win')
// mobile
let isSideBarOpen = false
let sidebar
@ -211,52 +172,6 @@
}
</script>
<!-- custom titlebar for Windows -->
{#if isWindows}
<div
class="w-screen select-none h-8 pl-2 flex justify-between items-center absolute text-primaryText dark:text-darkPrimaryText"
data-tauri-drag-region
>
<span class="lt-sm:pl-10 text-darkPrimaryText">Tauri API Validation</span>
<span
class="
h-100%
children:h-100% children:w-12 children:inline-flex
children:items-center children:justify-center"
>
<span
title={isDark ? 'Switch to Light mode' : 'Switch to Dark mode'}
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleDark}
>
{#if isDark}
<div class="i-ph-sun" />
{:else}
<div class="i-ph-moon" />
{/if}
</span>
<span
title="Minimize"
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={minimize}
>
<div class="i-codicon-chrome-minimize" />
</span>
<span
title={isWindowMaximized ? 'Restore' : 'Maximize'}
class="hover:bg-hoverOverlay active:bg-hoverOverlayDarker dark:hover:bg-darkHoverOverlay dark:active:bg-darkHoverOverlayDarker"
on:click={toggleMaximize}
>
{#if isWindowMaximized}
<div class="i-codicon-chrome-restore" />
{:else}
<div class="i-codicon-chrome-maximize" />
{/if}
</span>
</span>
</div>
{/if}
<!-- Sidebar toggle, only visible on small screens -->
<div
id="sidebarToggle"
@ -283,20 +198,18 @@
src="tauri_logo.png"
alt="Tauri logo"
/>
{#if !isWindows}
<a href="##" class="nv justify-between h-8" on:click={toggleDark}>
{#if isDark}
Switch to Light mode
<div class="i-ph-sun" />
{:else}
Switch to Dark mode
<div class="i-ph-moon" />
{/if}
</a>
<br />
<div class="bg-white/5 h-2px" />
<br />
{/if}
<a href="##" class="nv justify-between h-8" on:click={toggleDark}>
{#if isDark}
Switch to Light mode
<div class="i-ph-sun" />
{:else}
Switch to Dark mode
<div class="i-ph-moon" />
{/if}
</a>
<br />
<div class="bg-white/5 h-2px" />
<br />
<a
class="nv justify-between h-8"

View File

@ -1,29 +0,0 @@
<script>
import { invoke } from '@tauri-apps/api/tauri'
export let onMessage
function cliMatches() {
invoke('plugin:cli|cli_matches').then(onMessage).catch(onMessage)
}
</script>
<p>
This binary can be run from the terminal and takes the following arguments:
<code class="code-block flex flex-wrap my-2">
<pre>
--config &lt;PATH&gt;
--theme &lt;light|dark|system&gt;
--verbose</pre>
</code>
Additionally, it has a <code>update --background</code> subcommand.
</p>
<br />
<div class="note">
Note that the arguments are only parsed, not implemented.
</div>
<br />
<br />
<button class="btn" id="cli-matches" on:click={cliMatches}>
Get matches
</button>

View File

@ -1,438 +0,0 @@
<script>
import {
appWindow,
WebviewWindow,
LogicalSize,
UserAttentionType,
PhysicalSize,
PhysicalPosition
} from '@tauri-apps/api/window'
let selectedWindow = appWindow.label
const windowMap = {
[appWindow.label]: appWindow
}
const cursorIconOptions = [
'default',
'crosshair',
'hand',
'arrow',
'move',
'text',
'wait',
'help',
'progress',
// something cannot be done
'notAllowed',
'contextMenu',
'cell',
'verticalText',
'alias',
'copy',
'noDrop',
// something can be grabbed
'grab',
/// something is grabbed
'grabbing',
'allScroll',
'zoomIn',
'zoomOut',
// edge is to be moved
'eResize',
'nResize',
'neResize',
'nwResize',
'sResize',
'seResize',
'swResize',
'wResize',
'ewResize',
'nsResize',
'neswResize',
'nwseResize',
'colResize',
'rowResize'
]
export let onMessage
let newWindowLabel
let urlValue = 'https://tauri.app'
let resizable = true
let maximized = false
let decorations = true
let alwaysOnTop = false
let contentProtected = true
let fullscreen = false
let width = null
let height = null
let minWidth = null
let minHeight = null
let maxWidth = null
let maxHeight = null
let x = null
let y = null
let scaleFactor = 1
let innerPosition = new PhysicalPosition(x, y)
let outerPosition = new PhysicalPosition(x, y)
let innerSize = new PhysicalSize(width, height)
let outerSize = new PhysicalSize(width, height)
let resizeEventUnlisten
let moveEventUnlisten
let cursorGrab = false
let cursorVisible = true
let cursorX = null
let cursorY = null
let cursorIcon = 'default'
let cursorIgnoreEvents = false
let windowTitle = 'Awesome Tauri Example!'
function setTitle_() {
windowMap[selectedWindow].setTitle(windowTitle)
}
function hide_() {
windowMap[selectedWindow].hide()
setTimeout(windowMap[selectedWindow].show, 2000)
}
function minimize_() {
windowMap[selectedWindow].minimize()
setTimeout(windowMap[selectedWindow].unminimize, 2000)
}
function createWindow() {
if (!newWindowLabel) return
const webview = new WebviewWindow(newWindowLabel)
windowMap[newWindowLabel] = webview
webview.once('tauri://error', function () {
onMessage('Error creating new webview')
})
}
function loadWindowSize() {
windowMap[selectedWindow].innerSize().then((response) => {
innerSize = response
width = innerSize.width
height = innerSize.height
})
windowMap[selectedWindow].outerSize().then((response) => {
outerSize = response
})
}
function loadWindowPosition() {
windowMap[selectedWindow].innerPosition().then((response) => {
innerPosition = response
})
windowMap[selectedWindow].outerPosition().then((response) => {
outerPosition = response
x = outerPosition.x
y = outerPosition.y
})
}
async function addWindowEventListeners(window) {
if (!window) return
if (resizeEventUnlisten) {
resizeEventUnlisten()
}
if (moveEventUnlisten) {
moveEventUnlisten()
}
moveEventUnlisten = await window.listen('tauri://move', loadWindowPosition)
resizeEventUnlisten = await window.listen('tauri://resize', loadWindowSize)
}
async function requestUserAttention_() {
await windowMap[selectedWindow].minimize()
await windowMap[selectedWindow].requestUserAttention(
UserAttentionType.Critical
)
await new Promise((resolve) => setTimeout(resolve, 3000))
await windowMap[selectedWindow].requestUserAttention(null)
}
$: {
windowMap[selectedWindow]
loadWindowPosition()
loadWindowSize()
}
$: windowMap[selectedWindow]?.setResizable(resizable)
$: maximized
? windowMap[selectedWindow]?.maximize()
: windowMap[selectedWindow]?.unmaximize()
$: windowMap[selectedWindow]?.setDecorations(decorations)
$: windowMap[selectedWindow]?.setAlwaysOnTop(alwaysOnTop)
$: windowMap[selectedWindow]?.setContentProtected(contentProtected)
$: windowMap[selectedWindow]?.setFullscreen(fullscreen)
$: width &&
height &&
windowMap[selectedWindow]?.setSize(new PhysicalSize(width, height))
$: minWidth && minHeight
? windowMap[selectedWindow]?.setMinSize(
new LogicalSize(minWidth, minHeight)
)
: windowMap[selectedWindow]?.setMinSize(null)
$: maxWidth > 800 && maxHeight > 400
? windowMap[selectedWindow]?.setMaxSize(
new LogicalSize(maxWidth, maxHeight)
)
: windowMap[selectedWindow]?.setMaxSize(null)
$: x !== null &&
y !== null &&
windowMap[selectedWindow]?.setPosition(new PhysicalPosition(x, y))
$: windowMap[selectedWindow]
?.scaleFactor()
.then((factor) => (scaleFactor = factor))
$: addWindowEventListeners(windowMap[selectedWindow])
$: windowMap[selectedWindow]?.setCursorGrab(cursorGrab)
$: windowMap[selectedWindow]?.setCursorVisible(cursorVisible)
$: windowMap[selectedWindow]?.setCursorIcon(cursorIcon)
$: cursorX !== null &&
cursorY !== null &&
windowMap[selectedWindow]?.setCursorPosition(
new PhysicalPosition(cursorX, cursorY)
)
$: windowMap[selectedWindow]?.setIgnoreCursorEvents(cursorIgnoreEvents)
</script>
<div class="flex flex-col children:grow gap-2">
<div class="flex gap-1">
<input
class="input grow"
type="text"
placeholder="New Window label.."
bind:value={newWindowLabel}
/>
<button class="btn" on:click={createWindow}>New window</button>
</div>
<br />
{#if Object.keys(windowMap).length >= 1}
<span class="font-700 text-sm">Selected window:</span>
<select class="input" bind:value={selectedWindow}>
<option value="" disabled selected>Choose a window...</option>
{#each Object.keys(windowMap) as label}
<option value={label}>{label}</option>
{/each}
</select>
{/if}
{#if windowMap[selectedWindow]}
<br />
<div class="flex flex-wrap gap-2">
<button
class="btn"
title="Unminimizes after 2 seconds"
on:click={() => windowMap[selectedWindow].center()}
>
Center
</button>
<button
class="btn"
title="Unminimizes after 2 seconds"
on:click={minimize_}
>
Minimize
</button>
<button
class="btn"
title="Visible again after 2 seconds"
on:click={hide_}
>
Hide
</button>
<button
class="btn"
on:click={requestUserAttention_}
title="Minimizes the window, requests attention for 3s and then resets it"
>Request attention</button
>
</div>
<br />
<div class="flex flex-wrap gap-2">
<label>
Maximized
<input type="checkbox" bind:checked={maximized} />
</label>
<label>
Resizable
<input type="checkbox" bind:checked={resizable} />
</label>
<label>
Has decorations
<input type="checkbox" bind:checked={decorations} />
</label>
<label>
Always on top
<input type="checkbox" bind:checked={alwaysOnTop} />
</label>
<label>
Content protected
<input type="checkbox" bind:checked={contentProtected} />
</label>
<label>
Fullscreen
<input type="checkbox" bind:checked={fullscreen} />
</label>
</div>
<br />
<div class="flex flex-row gap-2 flex-wrap">
<div class="flex children:grow flex-col">
<div>
X
<input class="input" type="number" bind:value={x} min="0" />
</div>
<div>
Y
<input class="input" type="number" bind:value={y} min="0" />
</div>
</div>
<div class="flex children:grow flex-col">
<div>
Width
<input class="input" type="number" bind:value={width} min="400" />
</div>
<div>
Height
<input class="input" type="number" bind:value={height} min="400" />
</div>
</div>
<div class="flex children:grow flex-col">
<div>
Min width
<input class="input" type="number" bind:value={minWidth} />
</div>
<div>
Min height
<input class="input" type="number" bind:value={minHeight} />
</div>
</div>
<div class="flex children:grow flex-col">
<div>
Max width
<input class="input" type="number" bind:value={maxWidth} min="800" />
</div>
<div>
Max height
<input class="input" type="number" bind:value={maxHeight} min="400" />
</div>
</div>
</div>
<br />
<div>
<div class="flex">
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Inner Size
</div>
<span>Width: {innerSize.width}</span>
<span>Height: {innerSize.height}</span>
</div>
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Outer Size
</div>
<span>Width: {outerSize.width}</span>
<span>Height: {outerSize.height}</span>
</div>
</div>
<div class="flex">
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Inner Logical Size
</div>
<span>Width: {innerSize.toLogical(scaleFactor).width}</span>
<span>Height: {innerSize.toLogical(scaleFactor).height}</span>
</div>
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Outer Logical Size
</div>
<span>Width: {outerSize.toLogical(scaleFactor).width}</span>
<span>Height: {outerSize.toLogical(scaleFactor).height}</span>
</div>
</div>
<div class="flex">
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Inner Position
</div>
<span>x: {innerPosition.x}</span>
<span>y: {innerPosition.y}</span>
</div>
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Outer Position
</div>
<span>x: {outerPosition.x}</span>
<span>y: {outerPosition.y}</span>
</div>
</div>
<div class="flex">
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Inner Logical Position
</div>
<span>x: {innerPosition.toLogical(scaleFactor).x}</span>
<span>y: {innerPosition.toLogical(scaleFactor).y}</span>
</div>
<div class="grow">
<div class="text-accent dark:text-darkAccent font-700">
Outer Logical Position
</div>
<span>x: {outerPosition.toLogical(scaleFactor).x}</span>
<span>y: {outerPosition.toLogical(scaleFactor).y}</span>
</div>
</div>
</div>
<br />
<h4 class="mb-2">Cursor</h4>
<div class="flex gap-2">
<label>
<input type="checkbox" bind:checked={cursorGrab} />
Grab
</label>
<label>
<input type="checkbox" bind:checked={cursorVisible} />
Visible
</label>
<label>
<input type="checkbox" bind:checked={cursorIgnoreEvents} />
Ignore events
</label>
</div>
<div class="flex gap-2">
<label>
Icon
<select class="input" bind:value={cursorIcon}>
{#each cursorIconOptions as kind}
<option value={kind}>{kind}</option>
{/each}
</select>
</label>
<label>
X position
<input class="input" type="number" bind:value={cursorX} />
</label>
<label>
Y position
<input class="input" type="number" bind:value={cursorY} />
</label>
</div>
<br />
<div class="flex flex-col gap-1">
<form class="flex gap-1" on:submit|preventDefault={setTitle_}>
<input class="input grow" id="title" bind:value={windowTitle} />
<button class="btn" type="submit">Set title</button>
</form>
</div>
{/if}
</div>

File diff suppressed because one or more lines are too long

View File

@ -2,7 +2,6 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
import { type WindowLabel } from '../window'
import { invoke, transformCallback } from '../tauri'
import { type EventName } from '../event'
@ -46,7 +45,7 @@ async function _unlisten(event: string, eventId: number): Promise<void> {
*/
async function emit(
event: string,
windowLabel?: WindowLabel,
windowLabel?: string,
payload?: unknown
): Promise<void> {
await invoke('plugin:event|emit', {

View File

@ -1,37 +0,0 @@
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/** @ignore */
import { invoke } from '../tauri'
type TauriModule =
| 'App'
| 'Fs'
| 'Path'
| 'Os'
| 'Window'
| 'Shell'
| 'Event'
| 'Internal'
| 'Dialog'
| 'Cli'
| 'Notification'
| 'Http'
| 'GlobalShortcut'
| 'Process'
| 'Clipboard'
interface TauriCommand {
__tauriModule: TauriModule
[key: string]: unknown
}
async function invokeTauriCommand<T>(command: TauriCommand): Promise<T> {
return invoke('tauri', command)
}
export type { TauriModule, TauriCommand }
export { invokeTauriCommand }

View File

@ -16,9 +16,8 @@
import * as event from './event'
import * as path from './path'
import * as tauri from './tauri'
import * as window from './window'
/** @ignore */
const invoke = tauri.invoke
export { invoke, event, path, tauri, window }
export { invoke, event, path, tauri }

View File

@ -2,6 +2,21 @@
// SPDX-License-Identifier: Apache-2.0
// SPDX-License-Identifier: MIT
/** @ignore */
declare global {
interface Window {
__TAURI_METADATA__: {
__windows: WindowDef[]
__currentWindow: WindowDef
}
}
}
/** @ignore */
interface WindowDef {
label: string
}
interface IPCMessage {
cmd: string
callback: number

File diff suppressed because it is too large Load Diff

View File

@ -2508,11 +2508,6 @@
"items": {
"type": "string"
}
},
"enableTauriAPI": {
"description": "Enables access to the Tauri API.",
"default": false,
"type": "boolean"
}
},
"additionalProperties": false