Browse Source

feat: move `window` plugin back to core (#8007)

Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
Amr Bashir 1 year ago
parent
commit
c9a9246c37

+ 5 - 0
.changes/api-window.md

@@ -0,0 +1,5 @@
+---
+"@tauri-apps/api": 'minor:feat'
+---
+
+Add the `window` module back

+ 5 - 0
.changes/window-plugin-core.md

@@ -0,0 +1,5 @@
+---
+"tauri": patch:changes
+---
+
+Added the `window` plugin back into core.

File diff suppressed because it is too large
+ 0 - 0
core/tauri/scripts/bundle.global.js


+ 1 - 0
core/tauri/src/app.rs

@@ -807,6 +807,7 @@ impl<R: Runtime> App<R> {
   fn register_core_plugins(&self) -> crate::Result<()> {
     self.handle.plugin(crate::path::init())?;
     self.handle.plugin(crate::event::init())?;
+    self.handle.plugin(crate::window::plugin::init())?;
     Ok(())
   }
 

+ 3 - 0
core/tauri/src/error.rs

@@ -107,4 +107,7 @@ pub enum Error {
   #[cfg(all(desktop, feature = "tray-icon"))]
   #[cfg_attr(doc_cfg, doc(cfg(all(desktop, feature = "tray-icon"))))]
   BadTrayIcon(#[from] tray_icon::BadIcon),
+  /// window not found.
+  #[error("window not found")]
+  WindowNotFound,
 }

+ 3 - 1
core/tauri/src/window.rs → core/tauri/src/window/mod.rs

@@ -4,6 +4,8 @@
 
 //! The Tauri window types and functions.
 
+pub(crate) mod plugin;
+
 use http::HeaderMap;
 pub use tauri_utils::{config::Color, WindowEffect as Effect, WindowEffectState as EffectState};
 use url::Url;
@@ -2461,7 +2463,7 @@ impl<R: Runtime> Window<R> {
   ///
   /// This listener only receives events that are triggered using the
   /// [`trigger`](Window#method.trigger) and [`emit_and_trigger`](Window#method.emit_and_trigger) methods or
-  /// the `emit` function from the window plugin (`@tauri-apps/plugin-window` package).
+  /// the `emit` function from the window plugin (`@tauri-apps/api/window` package).
   ///
   /// # Examples
   /// ```

+ 322 - 0
core/tauri/src/window/plugin.rs

@@ -0,0 +1,322 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+//! The tauri plugin to create and manipulate windows from JS.
+
+use crate::{
+  plugin::{Builder, TauriPlugin},
+  Runtime,
+};
+
+#[cfg(desktop)]
+mod desktop_commands {
+  use serde::Deserialize;
+
+  use super::*;
+  use crate::{
+    command,
+    utils::config::{WindowConfig, WindowEffectsConfig},
+    AppHandle, CursorIcon, Icon, Manager, Monitor, PhysicalPosition, PhysicalSize, Position, Size,
+    Theme, UserAttentionType, Window, WindowBuilder,
+  };
+
+  #[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,
+        },
+      }
+    }
+  }
+
+  #[command(root = "crate")]
+  pub async fn create<R: Runtime>(app: AppHandle<R>, options: WindowConfig) -> crate::Result<()> {
+    WindowBuilder::from_config(&app, options).build()?;
+    Ok(())
+  }
+
+  fn get_window<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<Window<R>> {
+    match label {
+      Some(l) if !l.is_empty() => window.get_window(&l).ok_or(crate::Error::WindowNotFound),
+      _ => Ok(window),
+    }
+  }
+
+  macro_rules! getter {
+    ($cmd: ident, $ret: ty) => {
+      #[command(root = "crate")]
+      pub async fn $cmd<R: Runtime>(
+        window: Window<R>,
+        label: Option<String>,
+      ) -> crate::Result<$ret> {
+        get_window(window, label)?.$cmd().map_err(Into::into)
+      }
+    };
+  }
+
+  macro_rules! setter {
+    ($cmd: ident) => {
+      #[command(root = "crate")]
+      pub async fn $cmd<R: Runtime>(window: Window<R>, label: Option<String>) -> crate::Result<()> {
+        get_window(window, label)?.$cmd().map_err(Into::into)
+      }
+    };
+
+    ($cmd: ident, $input: ty) => {
+      #[command(root = "crate")]
+      pub async fn $cmd<R: Runtime>(
+        window: Window<R>,
+        label: Option<String>,
+        value: $input,
+      ) -> crate::Result<()> {
+        get_window(window, label)?.$cmd(value).map_err(Into::into)
+      }
+    };
+  }
+
+  getter!(scale_factor, f64);
+  getter!(inner_position, PhysicalPosition<i32>);
+  getter!(outer_position, PhysicalPosition<i32>);
+  getter!(inner_size, PhysicalSize<u32>);
+  getter!(outer_size, PhysicalSize<u32>);
+  getter!(is_fullscreen, bool);
+  getter!(is_minimized, bool);
+  getter!(is_maximized, bool);
+  getter!(is_focused, bool);
+  getter!(is_decorated, bool);
+  getter!(is_resizable, bool);
+  getter!(is_maximizable, bool);
+  getter!(is_minimizable, bool);
+  getter!(is_closable, bool);
+  getter!(is_visible, bool);
+  getter!(title, String);
+  getter!(current_monitor, Option<Monitor>);
+  getter!(primary_monitor, Option<Monitor>);
+  getter!(available_monitors, Vec<Monitor>);
+  getter!(theme, Theme);
+
+  setter!(center);
+  setter!(request_user_attention, Option<UserAttentionType>);
+  setter!(set_resizable, bool);
+  setter!(set_maximizable, bool);
+  setter!(set_minimizable, bool);
+  setter!(set_closable, bool);
+  setter!(set_title, &str);
+  setter!(maximize);
+  setter!(unmaximize);
+  setter!(minimize);
+  setter!(unminimize);
+  setter!(show);
+  setter!(hide);
+  setter!(close);
+  setter!(set_decorations, bool);
+  setter!(set_shadow, bool);
+  setter!(set_effects, Option<WindowEffectsConfig>);
+  setter!(set_always_on_top, bool);
+  setter!(set_content_protected, bool);
+  setter!(set_size, Size);
+  setter!(set_min_size, Option<Size>);
+  setter!(set_max_size, Option<Size>);
+  setter!(set_position, Position);
+  setter!(set_fullscreen, bool);
+  setter!(set_focus);
+  setter!(set_skip_taskbar, bool);
+  setter!(set_cursor_grab, bool);
+  setter!(set_cursor_visible, bool);
+  setter!(set_cursor_icon, CursorIcon);
+  setter!(set_cursor_position, Position);
+  setter!(set_ignore_cursor_events, bool);
+  setter!(start_dragging);
+  setter!(print);
+
+  #[command(root = "crate")]
+  pub async fn set_icon<R: Runtime>(
+    window: Window<R>,
+    label: Option<String>,
+    value: IconDto,
+  ) -> crate::Result<()> {
+    get_window(window, label)?
+      .set_icon(value.into())
+      .map_err(Into::into)
+  }
+
+  #[command(root = "crate")]
+  pub async fn toggle_maximize<R: Runtime>(
+    window: Window<R>,
+    label: Option<String>,
+  ) -> crate::Result<()> {
+    let window = get_window(window, label)?;
+    match window.is_maximized()? {
+      true => window.unmaximize()?,
+      false => window.maximize()?,
+    };
+    Ok(())
+  }
+
+  #[command(root = "crate")]
+  pub async fn internal_toggle_maximize<R: Runtime>(
+    window: Window<R>,
+    label: Option<String>,
+  ) -> crate::Result<()> {
+    let window = get_window(window, label)?;
+    if window.is_resizable()? {
+      match window.is_maximized()? {
+        true => window.unmaximize()?,
+        false => window.maximize()?,
+      };
+    }
+    Ok(())
+  }
+
+  #[cfg(any(debug_assertions, feature = "devtools"))]
+  #[command(root = "crate")]
+  pub async fn internal_toggle_devtools<R: Runtime>(
+    window: Window<R>,
+    label: Option<String>,
+  ) -> crate::Result<()> {
+    let window = get_window(window, label)?;
+    if window.is_devtools_open() {
+      window.close_devtools();
+    } else {
+      window.open_devtools();
+    }
+    Ok(())
+  }
+}
+
+/// Initializes the plugin.
+pub fn init<R: Runtime>() -> TauriPlugin<R> {
+  let mut init_script = String::new();
+  // window.print works on Linux/Windows; need to use the API on macOS
+  #[cfg(any(target_os = "macos", target_os = "ios"))]
+  {
+    init_script.push_str(include_str!("./scripts/print.js"));
+  }
+  init_script.push_str(include_str!("./scripts/drag.js"));
+
+  #[cfg(any(debug_assertions, feature = "devtools"))]
+  {
+    use serialize_to_javascript::{default_template, DefaultTemplate, Template};
+
+    #[derive(Template)]
+    #[default_template("./scripts/toggle-devtools.js")]
+    struct Devtools<'a> {
+      os_name: &'a str,
+    }
+
+    init_script.push_str(
+      &Devtools {
+        os_name: std::env::consts::OS,
+      }
+      .render_default(&Default::default())
+      .unwrap()
+      .into_string(),
+    );
+  }
+
+  Builder::new("window")
+    .js_init_script(init_script)
+    .invoke_handler(|invoke| {
+      #[cfg(desktop)]
+      {
+        let handler: Box<dyn Fn(crate::ipc::Invoke<R>) -> bool> =
+          Box::new(crate::generate_handler![
+            desktop_commands::create,
+            // getters
+            desktop_commands::scale_factor,
+            desktop_commands::inner_position,
+            desktop_commands::outer_position,
+            desktop_commands::inner_size,
+            desktop_commands::outer_size,
+            desktop_commands::is_fullscreen,
+            desktop_commands::is_minimized,
+            desktop_commands::is_maximized,
+            desktop_commands::is_focused,
+            desktop_commands::is_decorated,
+            desktop_commands::is_resizable,
+            desktop_commands::is_maximizable,
+            desktop_commands::is_minimizable,
+            desktop_commands::is_closable,
+            desktop_commands::is_visible,
+            desktop_commands::title,
+            desktop_commands::current_monitor,
+            desktop_commands::primary_monitor,
+            desktop_commands::available_monitors,
+            desktop_commands::theme,
+            // setters
+            desktop_commands::center,
+            desktop_commands::request_user_attention,
+            desktop_commands::set_resizable,
+            desktop_commands::set_maximizable,
+            desktop_commands::set_minimizable,
+            desktop_commands::set_closable,
+            desktop_commands::set_title,
+            desktop_commands::maximize,
+            desktop_commands::unmaximize,
+            desktop_commands::minimize,
+            desktop_commands::unminimize,
+            desktop_commands::show,
+            desktop_commands::hide,
+            desktop_commands::close,
+            desktop_commands::set_decorations,
+            desktop_commands::set_shadow,
+            desktop_commands::set_effects,
+            desktop_commands::set_always_on_top,
+            desktop_commands::set_content_protected,
+            desktop_commands::set_size,
+            desktop_commands::set_min_size,
+            desktop_commands::set_max_size,
+            desktop_commands::set_position,
+            desktop_commands::set_fullscreen,
+            desktop_commands::set_focus,
+            desktop_commands::set_skip_taskbar,
+            desktop_commands::set_cursor_grab,
+            desktop_commands::set_cursor_visible,
+            desktop_commands::set_cursor_icon,
+            desktop_commands::set_cursor_position,
+            desktop_commands::set_ignore_cursor_events,
+            desktop_commands::start_dragging,
+            desktop_commands::print,
+            desktop_commands::set_icon,
+            desktop_commands::toggle_maximize,
+            desktop_commands::internal_toggle_maximize,
+            #[cfg(any(debug_assertions, feature = "devtools"))]
+            desktop_commands::internal_toggle_devtools,
+          ]);
+        #[allow(clippy::needless_return)]
+        return handler(invoke);
+      }
+      #[cfg(mobile)]
+      {
+        invoke.resolver.reject("Window API not available on mobile");
+        return true;
+      }
+    })
+    .build()
+}

+ 17 - 0
core/tauri/src/window/scripts/drag.js

@@ -0,0 +1,17 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+document.addEventListener("mousedown", (e) => {
+  if (e.target.hasAttribute("data-tauri-drag-region") && e.button === 0) {
+    // prevents text cursor
+    e.preventDefault();
+    // fix #2549: double click on drag region edge causes content to maximize without window sizing change
+    // https://github.com/tauri-apps/tauri/issues/2549#issuecomment-1250036908
+    e.stopImmediatePropagation();
+
+    // start dragging if the element has a `tauri-drag-region` data attribute and maximize on double-clicking it
+    const cmd = e.detail === 2 ? "internal_toggle_maximize" : "start_dragging";
+    window.__TAURI_INVOKE__("plugin:window|" + cmd);
+  }
+});

+ 7 - 0
core/tauri/src/window/scripts/print.js

@@ -0,0 +1,7 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+window.print = function () {
+  return window.__TAURI_INVOKE__("plugin:window|print");
+};

+ 29 - 0
core/tauri/src/window/scripts/toggle-devtools.js

@@ -0,0 +1,29 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+;(function () {
+  function toggleDevtoolsHotkey() {
+    const osName = __TEMPLATE_os_name__
+
+    const isHotkey =
+      osName === 'macos'
+        ? (event) => event.metaKey && event.altKey && event.key === 'I'
+        : (event) => event.ctrlKey && event.shiftKey && event.key === 'I'
+
+    document.addEventListener('keydown', (event) => {
+      if (isHotkey(event)) {
+        window.__TAURI_INVOKE__('plugin:window|internal_toggle_devtools')
+      }
+    })
+  }
+
+  if (
+    document.readyState === 'complete' ||
+    document.readyState === 'interactive'
+  ) {
+    toggleDevtoolsHotkey()
+  } else {
+    window.addEventListener('DOMContentLoaded', toggleDevtoolsHotkey, true)
+  }
+})()

File diff suppressed because it is too large
+ 0 - 0
examples/api/dist/assets/index.css


File diff suppressed because it is too large
+ 0 - 0
examples/api/dist/assets/index.js


+ 2 - 2
examples/api/src-tauri/Cargo.lock

@@ -3333,9 +3333,9 @@ dependencies = [
 
 [[package]]
 name = "tao"
-version = "0.22.0"
+version = "0.22.3"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "60279ecb16c33a6cef45cd37a9602455c190942d20e360bd8499bff49f2a48f3"
+checksum = "f5d30690a6746dfbb26d4f41522757da1ebfd277514b58e27a80b7e55e511e52"
 dependencies = [
  "bitflags 1.3.2",
  "cairo-rs",

+ 6 - 0
examples/api/src/App.svelte

@@ -5,6 +5,7 @@
 
   import Welcome from './views/Welcome.svelte'
   import Communication from './views/Communication.svelte'
+  import Window from './views/Window.svelte'
   import WebRTC from './views/WebRTC.svelte'
 
   document.addEventListener('keydown', (event) => {
@@ -24,6 +25,11 @@
       component: Communication,
       icon: 'i-codicon-radio-tower'
     },
+    {
+      label: 'Window',
+      component: Window,
+      icon: 'i-codicon-window'
+    },
     {
       label: 'WebRTC',
       component: WebRTC,

+ 611 - 0
examples/api/src/views/Window.svelte

@@ -0,0 +1,611 @@
+<script>
+  import {
+    getCurrent,
+    LogicalSize,
+    UserAttentionType,
+    PhysicalSize,
+    PhysicalPosition,
+    Effect,
+    EffectState,
+    Window
+  } from "@tauri-apps/api/window";
+  import { invoke } from "@tauri-apps/api/tauri";
+
+  const appWindow = getCurrent();
+
+  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",
+  ];
+
+  const windowsEffects = ["mica", "blur", "acrylic"];
+  const isWindows = navigator.appVersion.includes("windows");
+  const isMacOS = navigator.appVersion.includes("macos");
+  let effectOptions = isWindows
+    ? windowsEffects
+    : Object.keys(Effect)
+        .map((effect) => Effect[effect])
+        .filter((e) => !windowsEffects.includes(e));
+  const effectStateOptions = Object.keys(EffectState).map(
+    (state) => EffectState[state]
+  );
+
+  export let onMessage;
+  const mainEl = document.querySelector("main");
+
+  let newWindowLabel;
+
+  let urlValue = "https://tauri.app";
+  let resizable = true;
+  let maximizable = true;
+  let minimizable = true;
+  let closable = 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!";
+
+  let effects = [];
+  let selectedEffect;
+  let effectState;
+  let effectRadius;
+  let effectR, effectG, effectB, effectA;
+
+  let windowIconPath;
+
+  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 changeIcon() {
+    windowMap[selectedWindow].setIcon(path);
+  }
+
+  function createWindow() {
+    if (!newWindowLabel) return;
+
+    const webview = new Window(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);
+  }
+
+  async function addEffect() {
+    if (!effects.includes(selectedEffect)) {
+      effects = [...effects, selectedEffect];
+    }
+
+    const payload = {
+      effects,
+      state: effectState,
+      radius: effectRadius,
+    };
+    if (
+      Number.isInteger(effectR) &&
+      Number.isInteger(effectG) &&
+      Number.isInteger(effectB) &&
+      Number.isInteger(effectA)
+    ) {
+      payload.color = [effectR, effectG, effectB, effectA];
+    }
+
+    mainEl.classList.remove("bg-primary");
+    mainEl.classList.remove("dark:bg-darkPrimary");
+    await windowMap[selectedWindow].clearEffects();
+    await windowMap[selectedWindow].setEffects(payload);
+  }
+
+  async function clearEffects() {
+    effects = [];
+    await windowMap[selectedWindow].clearEffects();
+    mainEl.classList.add("bg-primary");
+    mainEl.classList.add("dark:bg-darkPrimary");
+  }
+
+  $: {
+    windowMap[selectedWindow];
+    loadWindowPosition();
+    loadWindowSize();
+  }
+  $: windowMap[selectedWindow]?.setResizable(resizable);
+  $: windowMap[selectedWindow]?.setMaximizable(maximizable);
+  $: windowMap[selectedWindow]?.setMinimizable(minimizable);
+  $: windowMap[selectedWindow]?.setClosable(closable);
+  $: 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 gap-1 items-center">
+       <label>
+        Icon path
+      </label>
+      <form class="flex gap-1 grow" on:submit|preventDefault={setTitle_}>
+        <input class="input grow" bind:value={windowIconPath} />
+        <button class="btn" type="submit"> Change window icon </button>
+      </form>
+    </div>
+    <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>
+
+    <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>
+        Maximizable
+        <input type="checkbox" bind:checked={maximizable} />
+      </label>
+      <label>
+        Minimizable
+        <input type="checkbox" bind:checked={minimizable} />
+      </label>
+      <label>
+        Closable
+        <input type="checkbox" bind:checked={closable} />
+      </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>
+
+    <br />
+
+    {#if isWindows || isMacOS}
+      <div class="flex flex-col gap-1">
+        <div class="flex">
+          <label>
+            Effect
+            <select class="input" bind:value={selectedEffect}>
+              {#each effectOptions as effect}
+                <option value={effect}>{effect}</option>
+              {/each}
+            </select>
+          </label>
+
+          <label>
+            State
+            <select class="input" bind:value={effectState}>
+              {#each effectStateOptions as state}
+                <option value={state}>{state}</option>
+              {/each}
+            </select>
+          </label>
+
+          <label>
+            Radius
+            <input class="input" type="number" bind:value={effectRadius} />
+          </label>
+        </div>
+
+        <div class="flex">
+          <label>
+            Color
+            <div class="flex">
+              <input
+                style="max-width: 120px;"
+                class="input"
+                type="number"
+                placeholder="R"
+                bind:value={effectR}
+              />
+              <input
+                style="max-width: 120px;"
+                class="input"
+                type="number"
+                placeholder="G"
+                bind:value={effectG}
+              />
+              <input
+                style="max-width: 120px;"
+                class="input"
+                type="number"
+                placeholder="B"
+                bind:value={effectB}
+              />
+              <input
+                style="max-width: 120px;"
+                class="input"
+                type="number"
+                placeholder="A"
+                bind:value={effectA}
+              />
+            </div>
+          </label>
+        </div>
+
+        <div class="flex">
+          <button class="btn" style="width: 80px;" on:click={addEffect}
+            >Add</button
+          >
+        </div>
+
+        <div class="flex">
+          <div>
+            Applied effects: {effects.length ? effects.join(",") : "None"}
+          </div>
+
+          <button class="btn" style="width: 80px;" on:click={clearEffects}
+            >Clear</button
+          >
+        </div>
+      </div>
+    {/if}
+  {/if}
+</div>

File diff suppressed because it is too large
+ 0 - 0
tooling/api/docs/js-api.json


+ 99 - 0
tooling/api/src/dpi.ts

@@ -0,0 +1,99 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+/**
+ * A size represented in logical pixels.
+ *
+ * @since 2.0.0
+ */
+class LogicalSize {
+  type = 'Logical'
+  width: number
+  height: number
+
+  constructor(width: number, height: number) {
+    this.width = width
+    this.height = height
+  }
+}
+
+/**
+ * A size represented in physical pixels.
+ *
+ * @since 2.0.0
+ */
+class PhysicalSize {
+  type = 'Physical'
+  width: number
+  height: number
+
+  constructor(width: number, height: number) {
+    this.width = width
+    this.height = height
+  }
+
+  /**
+   * Converts the physical size to a logical one.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const appWindow = getCurrent();
+   * const factor = await appWindow.scaleFactor();
+   * const size = await appWindow.innerSize();
+   * const logical = size.toLogical(factor);
+   * ```
+   *  */
+  toLogical(scaleFactor: number): LogicalSize {
+    return new LogicalSize(this.width / scaleFactor, this.height / scaleFactor)
+  }
+}
+
+/**
+ *  A position represented in logical pixels.
+ *
+ * @since 2.0.0
+ */
+class LogicalPosition {
+  type = 'Logical'
+  x: number
+  y: number
+
+  constructor(x: number, y: number) {
+    this.x = x
+    this.y = y
+  }
+}
+
+/**
+ *  A position represented in physical pixels.
+ *
+ * @since 2.0.0
+ */
+class PhysicalPosition {
+  type = 'Physical'
+  x: number
+  y: number
+
+  constructor(x: number, y: number) {
+    this.x = x
+    this.y = y
+  }
+
+  /**
+   * Converts the physical position to a logical one.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const appWindow = getCurrent();
+   * const factor = await appWindow.scaleFactor();
+   * const position = await appWindow.innerPosition();
+   * const logical = position.toLogical(factor);
+   * ```
+   * */
+  toLogical(scaleFactor: number): LogicalPosition {
+    return new LogicalPosition(this.x / scaleFactor, this.y / scaleFactor)
+  }
+}
+
+export { LogicalPosition, LogicalSize, PhysicalPosition, PhysicalSize }

+ 4 - 2
tooling/api/src/index.ts

@@ -8,16 +8,18 @@
  * This module exposes all other modules as an object where the key is the module name, and the value is the module exports.
  * @example
  * ```typescript
- * import { app, dialog, event, fs, globalShortcut } from '@tauri-apps/api'
+ * import { event, window, path } from '@tauri-apps/api'
  * ```
  * @module
  */
 
 import * as event from './event'
 import * as tauri from './tauri'
+import * as window from './window'
 import * as path from './path'
+import * as dpi from './dpi'
 
 /** @ignore */
 const invoke = tauri.invoke
 
-export { invoke, event, path, tauri }
+export { invoke, event, path, tauri, window, dpi }

+ 2234 - 0
tooling/api/src/window.ts

@@ -0,0 +1,2234 @@
+// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
+// SPDX-License-Identifier: Apache-2.0
+// SPDX-License-Identifier: MIT
+
+/**
+ * Provides APIs to create windows, communicate with other windows and manipulate the current window.
+ *
+ * ## Window events
+ *
+ * Events can be listened to using {@link Window.listen}:
+ * ```typescript
+ * import { getCurrent } from "@tauri-apps/api/window";
+ * getCurrent().listen("my-window-event", ({ event, payload }) => { });
+ * ```
+ *
+ * @module
+ */
+
+import {
+  LogicalPosition,
+  LogicalSize,
+  PhysicalPosition,
+  PhysicalSize
+} from './dpi'
+import type { Event, EventName, EventCallback, UnlistenFn } from './event'
+import { TauriEvent, emit, listen, once } from './event'
+import { invoke } from './tauri'
+
+/**
+ * Allows you to retrieve information about a given monitor.
+ *
+ * @since 2.0.0
+ */
+export interface Monitor {
+  /** Human-readable name of the monitor */
+  name: string | null
+  /** The monitor's resolution. */
+  size: PhysicalSize
+  /** the Top-left corner position of the monitor relative to the larger full screen area. */
+  position: PhysicalPosition
+  /** The scale factor that can be used to map physical pixels to logical pixels. */
+  scaleFactor: number
+}
+
+type Theme = 'light' | 'dark'
+type TitleBarStyle = 'visible' | 'transparent' | 'overlay'
+
+/**
+ * The payload for the `scaleChange` event.
+ *
+ * @since 2.0.0
+ */
+interface ScaleFactorChanged {
+  /** The new window scale factor. */
+  scaleFactor: number
+  /** The new window size */
+  size: PhysicalSize
+}
+
+/** The file drop event types. */
+type FileDropEvent =
+  | { type: 'hover'; paths: string[] }
+  | { type: 'drop'; paths: string[] }
+  | { type: 'cancel' }
+
+/** @ignore */
+interface WindowDef {
+  label: string
+}
+
+/** @ignore */
+declare global {
+  interface Window {
+    __TAURI_METADATA__: {
+      __windows: WindowDef[]
+      __currentWindow: WindowDef
+    }
+  }
+}
+
+/**
+ * Attention type to request on a window.
+ *
+ * @since 2.0.0
+ */
+enum UserAttentionType {
+  /**
+   * #### Platform-specific
+   * - **macOS:** Bounces the dock icon until the application is in focus.
+   * - **Windows:** Flashes both the window and the taskbar button until the application is in focus.
+   */
+  Critical = 1,
+  /**
+   * #### Platform-specific
+   * - **macOS:** Bounces the dock icon once.
+   * - **Windows:** Flashes the taskbar button until the application is in focus.
+   */
+  Informational
+}
+
+class CloseRequestedEvent {
+  /** Event name */
+  event: EventName
+  /** The label of the window that emitted this event. */
+  windowLabel: string
+  /** Event identifier used to unlisten */
+  id: number
+  private _preventDefault = false
+
+  constructor(event: Event<null>) {
+    this.event = event.event
+    this.windowLabel = event.windowLabel
+    this.id = event.id
+  }
+
+  preventDefault(): void {
+    this._preventDefault = true
+  }
+
+  isPreventDefault(): boolean {
+    return this._preventDefault
+  }
+}
+
+export type CursorIcon =
+  | 'default'
+  | 'crosshair'
+  | 'hand'
+  | 'arrow'
+  | 'move'
+  | 'text'
+  | 'wait'
+  | 'help'
+  | 'progress'
+  // something cannot be done
+  | 'notAllowed'
+  | 'contextMenu'
+  | 'cell'
+  | 'verticalText'
+  | 'alias'
+  | 'copy'
+  | 'noDrop'
+  // something can be grabbed
+  | 'grab'
+  /// something is grabbed
+  | 'grabbing'
+  | 'allScroll'
+  | 'zoomIn'
+  | 'zoomOut'
+  // edge is to be moved
+  | 'eResize'
+  | 'nResize'
+  | 'neResize'
+  | 'nwResize'
+  | 'sResize'
+  | 'seResize'
+  | 'swResize'
+  | 'wResize'
+  | 'ewResize'
+  | 'nsResize'
+  | 'neswResize'
+  | 'nwseResize'
+  | 'colResize'
+  | 'rowResize'
+
+/**
+ * Get an instance of `Window` for the current window.
+ *
+ * @since 2.0.0
+ */
+function getCurrent(): Window {
+  return new Window(window.__TAURI_METADATA__.__currentWindow.label, {
+    // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
+    skip: true
+  })
+}
+
+/**
+ * Gets a list of instances of `Window` for all available windows.
+ *
+ * @since 2.0.0
+ */
+function getAll(): Window[] {
+  return window.__TAURI_METADATA__.__windows.map(
+    (w) =>
+      new Window(w.label, {
+        // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
+        skip: true
+      })
+  )
+}
+
+/** @ignore */
+// events that are emitted right here instead of by the created webview
+const localTauriEvents = ['tauri://created', 'tauri://error']
+/** @ignore */
+export type WindowLabel = string
+
+/**
+ * Create new webview window or get a handle to an existing one.
+ *
+ * Windows are identified by a *label*  a unique identifier that can be used to reference it later.
+ * It may only contain alphanumeric characters `a-zA-Z` plus the following special characters `-`, `/`, `:` and `_`.
+ *
+ * @example
+ * ```typescript
+ * // loading embedded asset:
+ * const appWindow = new Window('theUniqueLabel', {
+ *   url: 'path/to/page.html'
+ * });
+ * // alternatively, load a remote URL:
+ * const appWindow = new Window('theUniqueLabel', {
+ *   url: 'https://github.com/tauri-apps/tauri'
+ * });
+ *
+ * appWindow.once('tauri://created', function () {
+ *  // window successfully created
+ * });
+ * appWindow.once('tauri://error', function (e) {
+ *  // an error happened creating the window
+ * });
+ *
+ * // emit an event to the backend
+ * await appWindow.emit("some event", "data");
+ * // listen to an event from the backend
+ * const unlisten = await appWindow.listen("event name", e => {});
+ * unlisten();
+ * ```
+ *
+ * @since 2.0.0
+ */
+class Window {
+  /** The window label. It is a unique identifier for the window, can be used to reference it later. */
+  label: WindowLabel
+  /** Local event listeners. */
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  listeners: Record<string, Array<EventCallback<any>>>
+
+  /**
+   * Creates a new Window.
+   * @example
+   * ```typescript
+   * import { Window } from '@tauri-apps/api/window';
+   * const appWindow = new Window('my-label', {
+   *   url: 'https://github.com/tauri-apps/tauri'
+   * });
+   * appWindow.once('tauri://created', function () {
+   *  // window successfully created
+   * });
+   * appWindow.once('tauri://error', function (e) {
+   *  // an error happened creating the window
+   * });
+   * ```
+   *
+   * @param label The unique webview window label. Must be alphanumeric: `a-zA-Z-/:_`.
+   * @returns The {@link Window} instance to communicate with the webview.
+   *
+   * @since 2.0.0
+   */
+  constructor(label: WindowLabel, options: WindowOptions = {}) {
+    this.label = label
+    // eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
+    this.listeners = Object.create(null)
+
+    // @ts-expect-error `skip` is not a public API so it is not defined in WindowOptions
+    if (!options?.skip) {
+      invoke('plugin:window|create', {
+        options: {
+          ...options,
+          label
+        }
+      })
+        .then(async () => this.emit('tauri://created'))
+        .catch(async (e: string) => this.emit('tauri://error', e))
+    }
+  }
+
+  /**
+   * Gets the Window for the webview associated with the given label.
+   * @example
+   * ```typescript
+   * import { Window } from '@tauri-apps/api/window';
+   * const mainWindow = Window.getByLabel('main');
+   * ```
+   *
+   * @param label The webview window label.
+   * @returns The Window instance to communicate with the webview or null if the webview doesn't exist.
+   *
+   * @since 2.0.0
+   */
+  static getByLabel(label: string): Window | null {
+    if (getAll().some((w) => w.label === label)) {
+      // @ts-expect-error `skip` is not defined in the public API but it is handled by the constructor
+      return new Window(label, { skip: true })
+    }
+    return null
+  }
+
+  /**
+   * Get an instance of `Window` for the current window.
+   *
+   * @since 2.0.0
+   */
+  static getCurrent(): Window {
+    return getCurrent()
+  }
+
+  /**
+   * Gets a list of instances of `Window` for all available windows.
+   *
+   * @since 2.0.0
+   */
+  static getAll(): Window[] {
+    return getAll()
+  }
+
+  /**
+   *  Gets the focused window.
+   * @example
+   * ```typescript
+   * import { Window } from '@tauri-apps/api/window';
+   * const focusedWindow = Window.getFocusedWindow();
+   * ```
+   *
+   * @returns The Window instance to communicate with the webview or `undefined` if there is not any focused window.
+   *
+   * @since 1.4
+   */
+  static async getFocusedWindow(): Promise<Window | null> {
+    for (const w of getAll()) {
+      if (await w.isFocused()) {
+        return w
+      }
+    }
+    return null
+  }
+
+  /**
+   * Listen to an event emitted by the backend that is tied to the webview window.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const unlisten = await getCurrent().listen<string>('state-changed', (event) => {
+   *   console.log(`Got error: ${payload}`);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
+   * @param handler Event handler.
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async listen<T>(
+    event: EventName,
+    handler: EventCallback<T>
+  ): Promise<UnlistenFn> {
+    if (this._handleTauriEvent(event, handler)) {
+      return Promise.resolve(() => {
+        // eslint-disable-next-line @typescript-eslint/no-unsafe-member-access, security/detect-object-injection
+        const listeners = this.listeners[event]
+        listeners.splice(listeners.indexOf(handler), 1)
+      })
+    }
+    return listen(event, handler, { target: this.label })
+  }
+
+  /**
+   * Listen to an one-off event emitted by the backend that is tied to the webview window.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const unlisten = await getCurrent().once<null>('initialized', (event) => {
+   *   console.log(`Window initialized!`);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
+   * @param handler Event handler.
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async once<T>(event: string, handler: EventCallback<T>): Promise<UnlistenFn> {
+    if (this._handleTauriEvent(event, handler)) {
+      return Promise.resolve(() => {
+        // eslint-disable-next-line security/detect-object-injection
+        const listeners = this.listeners[event]
+        listeners.splice(listeners.indexOf(handler), 1)
+      })
+    }
+    return once(event, handler, { target: this.label })
+  }
+
+  /**
+   * Emits an event to the backend, tied to the webview window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().emit('window-loaded', { loggedIn: true, token: 'authToken' });
+   * ```
+   *
+   * @param event Event name. Must include only alphanumeric characters, `-`, `/`, `:` and `_`.
+   * @param payload Event payload.
+   */
+  async emit(event: string, payload?: unknown): Promise<void> {
+    if (localTauriEvents.includes(event)) {
+      // eslint-disable-next-line
+      for (const handler of this.listeners[event] || []) {
+        handler({ event, id: -1, windowLabel: this.label, payload })
+      }
+      return Promise.resolve()
+    }
+    return emit(event, payload, { target: this.label })
+  }
+
+  /** @ignore */
+  _handleTauriEvent<T>(event: string, handler: EventCallback<T>): boolean {
+    if (localTauriEvents.includes(event)) {
+      if (!(event in this.listeners)) {
+        // eslint-disable-next-line
+        this.listeners[event] = [handler]
+      } else {
+        // eslint-disable-next-line
+        this.listeners[event].push(handler)
+      }
+      return true
+    }
+    return false
+  }
+
+  // Getters
+  /**
+   * The scale factor that can be used to map physical pixels to logical pixels.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const factor = await getCurrent().scaleFactor();
+   * ```
+   *
+   * @returns The window's monitor scale factor.
+   *
+   * @since 2.0.0
+   * */
+  async scaleFactor(): Promise<number> {
+    return invoke('plugin:window|scale_factor', {
+      label: this.label
+    })
+  }
+
+  /**
+   * The position of the top-left hand corner of the window's client area relative to the top-left hand corner of the desktop.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const position = await getCurrent().innerPosition();
+   * ```
+   *
+   * @returns The window's inner position.
+   *
+   * @since 2.0.0
+   *  */
+  async innerPosition(): Promise<PhysicalPosition> {
+    return invoke<{ x: number; y: number }>('plugin:window|inner_position', {
+      label: this.label
+    }).then(({ x, y }) => new PhysicalPosition(x, y))
+  }
+
+  /**
+   * The position of the top-left hand corner of the window relative to the top-left hand corner of the desktop.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const position = await getCurrent().outerPosition();
+   * ```
+   *
+   * @returns The window's outer position.
+   *
+   * @since 2.0.0
+   *  */
+  async outerPosition(): Promise<PhysicalPosition> {
+    return invoke<{ x: number; y: number }>('plugin:window|outer_position', {
+      label: this.label
+    }).then(({ x, y }) => new PhysicalPosition(x, y))
+  }
+
+  /**
+   * The physical size of the window's client area.
+   * The client area is the content of the window, excluding the title bar and borders.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const size = await getCurrent().innerSize();
+   * ```
+   *
+   * @returns The window's inner size.
+   *
+   * @since 2.0.0
+   */
+  async innerSize(): Promise<PhysicalSize> {
+    return invoke<{ width: number; height: number }>(
+      'plugin:window|inner_size',
+      {
+        label: this.label
+      }
+    ).then(({ width, height }) => new PhysicalSize(width, height))
+  }
+
+  /**
+   * The physical size of the entire window.
+   * These dimensions include the title bar and borders. If you don't want that (and you usually don't), use inner_size instead.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const size = await getCurrent().outerSize();
+   * ```
+   *
+   * @returns The window's outer size.
+   *
+   * @since 2.0.0
+   */
+  async outerSize(): Promise<PhysicalSize> {
+    return invoke<{ width: number; height: number }>(
+      'plugin:window|outer_size',
+      {
+        label: this.label
+      }
+    ).then(({ width, height }) => new PhysicalSize(width, height))
+  }
+
+  /**
+   * Gets the window's current fullscreen state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const fullscreen = await getCurrent().isFullscreen();
+   * ```
+   *
+   * @returns Whether the window is in fullscreen mode or not.
+   *
+   * @since 2.0.0
+   *  */
+  async isFullscreen(): Promise<boolean> {
+    return invoke('plugin:window|is_fullscreen', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current minimized state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const minimized = await getCurrent().isMinimized();
+   * ```
+   *
+   * @since 2.0.0
+   * */
+  async isMinimized(): Promise<boolean> {
+    return invoke('plugin:window|is_minimized', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current maximized state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const maximized = await getCurrent().isMaximized();
+   * ```
+   *
+   * @returns Whether the window is maximized or not.
+   *
+   * @since 2.0.0
+   * */
+  async isMaximized(): Promise<boolean> {
+    return invoke('plugin:window|is_maximized', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current focus state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const focused = await getCurrent().isFocused();
+   * ```
+   *
+   * @returns Whether the window is focused or not.
+   *
+   * @since 2.0.0
+   * */
+  async isFocused(): Promise<boolean> {
+    return invoke('plugin:window|is_focused', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current decorated state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const decorated = await getCurrent().isDecorated();
+   * ```
+   *
+   * @returns Whether the window is decorated or not.
+   *
+   * @since 2.0.0
+   *  */
+  async isDecorated(): Promise<boolean> {
+    return invoke('plugin:window|is_decorated', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current resizable state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const resizable = await getCurrent().isResizable();
+   * ```
+   *
+   * @returns Whether the window is resizable or not.
+   *
+   * @since 2.0.0
+   *  */
+  async isResizable(): Promise<boolean> {
+    return invoke('plugin:window|is_resizable', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window’s native maximize button state.
+   *
+   * #### Platform-specific
+   *
+   * - **Linux / iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const maximizable = await getCurrent().isMaximizable();
+   * ```
+   *
+   * @returns Whether the window's native maximize button is enabled or not.
+   *  */
+  async isMaximizable(): Promise<boolean> {
+    return invoke('plugin:window|is_maximizable', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window’s native minimize button state.
+   *
+   * #### Platform-specific
+   *
+   * - **Linux / iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const minimizable = await getCurrent().isMinimizable();
+   * ```
+   *
+   * @returns Whether the window's native minimize button is enabled or not.
+   *  */
+  async isMinimizable(): Promise<boolean> {
+    return invoke('plugin:window|is_minimizable', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window’s native close button state.
+   *
+   * #### Platform-specific
+   *
+   * - **iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const closable = await getCurrent().isClosable();
+   * ```
+   *
+   * @returns Whether the window's native close button is enabled or not.
+   *  */
+  async isClosable(): Promise<boolean> {
+    return invoke('plugin:window|is_closable', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current visible state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const visible = await getCurrent().isVisible();
+   * ```
+   *
+   * @returns Whether the window is visible or not.
+   *
+   * @since 2.0.0
+   *  */
+  async isVisible(): Promise<boolean> {
+    return invoke('plugin:window|is_visible', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current title.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const title = await getCurrent().title();
+   * ```
+   *
+   * @since 2.0.0
+   * */
+  async title(): Promise<string> {
+    return invoke('plugin:window|title', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Gets the window's current theme.
+   *
+   * #### Platform-specific
+   *
+   * - **macOS:** Theme was introduced on macOS 10.14. Returns `light` on macOS 10.13 and below.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * const theme = await getCurrent().theme();
+   * ```
+   *
+   * @returns The window theme.
+   *
+   * @since 2.0.0
+   * */
+  async theme(): Promise<Theme | null> {
+    return invoke('plugin:window|theme', {
+      label: this.label
+    })
+  }
+
+  // Setters
+
+  /**
+   * Centers the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().center();
+   * ```
+   *
+   * @param resizable
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async center(): Promise<void> {
+    return invoke('plugin:window|center', {
+      label: this.label
+    })
+  }
+
+  /**
+   *  Requests user attention to the window, this has no effect if the application
+   * is already focused. How requesting for user attention manifests is platform dependent,
+   * see `UserAttentionType` for details.
+   *
+   * Providing `null` will unset the request for user attention. Unsetting the request for
+   * user attention might not be done automatically by the WM when the window receives input.
+   *
+   * #### Platform-specific
+   *
+   * - **macOS:** `null` has no effect.
+   * - **Linux:** Urgency levels have the same effect.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().requestUserAttention();
+   * ```
+   *
+   * @param requestType
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async requestUserAttention(
+    requestType: UserAttentionType | null
+  ): Promise<void> {
+    let requestType_ = null
+    if (requestType) {
+      if (requestType === UserAttentionType.Critical) {
+        requestType_ = { type: 'Critical' }
+      } else {
+        requestType_ = { type: 'Informational' }
+      }
+    }
+
+    return invoke('plugin:window|request_user_attention', {
+      label: this.label,
+      value: requestType_
+    })
+  }
+
+  /**
+   * Updates the window resizable flag.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setResizable(false);
+   * ```
+   *
+   * @param resizable
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setResizable(resizable: boolean): Promise<void> {
+    return invoke('plugin:window|set_resizable', {
+      label: this.label,
+      value: resizable
+    })
+  }
+
+  /**
+   * Sets whether the window's native maximize button is enabled or not.
+   * If resizable is set to false, this setting is ignored.
+   *
+   * #### Platform-specific
+   *
+   * - **macOS:** Disables the "zoom" button in the window titlebar, which is also used to enter fullscreen mode.
+   * - **Linux / iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setMaximizable(false);
+   * ```
+   *
+   * @param maximizable
+   * @returns A promise indicating the success or failure of the operation.
+   */
+  async setMaximizable(maximizable: boolean): Promise<void> {
+    return invoke('plugin:window|set_maximizable', {
+      label: this.label,
+      value: maximizable
+    })
+  }
+
+  /**
+   * Sets whether the window's native minimize button is enabled or not.
+   *
+   * #### Platform-specific
+   *
+   * - **Linux / iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setMinimizable(false);
+   * ```
+   *
+   * @param minimizable
+   * @returns A promise indicating the success or failure of the operation.
+   */
+  async setMinimizable(minimizable: boolean): Promise<void> {
+    return invoke('plugin:window|set_minimizable', {
+      label: this.label,
+      value: minimizable
+    })
+  }
+
+  /**
+   * Sets whether the window's native close button is enabled or not.
+   *
+   * #### Platform-specific
+   *
+   * - **Linux:** GTK+ will do its best to convince the window manager not to show a close button. Depending on the system, this function may not have any effect when called on a window that is already visible
+   * - **iOS / Android:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setClosable(false);
+   * ```
+   *
+   * @param closable
+   * @returns A promise indicating the success or failure of the operation.
+   */
+  async setClosable(closable: boolean): Promise<void> {
+    return invoke('plugin:window|set_closable', {
+      label: this.label,
+      value: closable
+    })
+  }
+
+  /**
+   * Sets the window title.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setTitle('Tauri');
+   * ```
+   *
+   * @param title The new title
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setTitle(title: string): Promise<void> {
+    return invoke('plugin:window|set_title', {
+      label: this.label,
+      value: title
+    })
+  }
+
+  /**
+   * Maximizes the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().maximize();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async maximize(): Promise<void> {
+    return invoke('plugin:window|maximize', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Unmaximizes the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().unmaximize();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async unmaximize(): Promise<void> {
+    return invoke('plugin:window|unmaximize', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Toggles the window maximized state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().toggleMaximize();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async toggleMaximize(): Promise<void> {
+    return invoke('plugin:window|toggle_maximize', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Minimizes the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().minimize();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async minimize(): Promise<void> {
+    return invoke('plugin:window|minimize', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Unminimizes the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().unminimize();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async unminimize(): Promise<void> {
+    return invoke('plugin:window|unminimize', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Sets the window visibility to true.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().show();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async show(): Promise<void> {
+    return invoke('plugin:window|show', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Sets the window visibility to false.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().hide();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async hide(): Promise<void> {
+    return invoke('plugin:window|hide', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Closes the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().close();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async close(): Promise<void> {
+    return invoke('plugin:window|close', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Whether the window should have borders and bars.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setDecorations(false);
+   * ```
+   *
+   * @param decorations Whether the window should have borders and bars.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setDecorations(decorations: boolean): Promise<void> {
+    return invoke('plugin:window|set_decorations', {
+      label: this.label,
+      value: decorations
+    })
+  }
+
+  /**
+   * Whether or not the window should have shadow.
+   *
+   * #### Platform-specific
+   *
+   * - **Windows:**
+   *   - `false` has no effect on decorated window, shadows are always ON.
+   *   - `true` will make ndecorated window have a 1px white border,
+   * and on Windows 11, it will have a rounded corners.
+   * - **Linux:** Unsupported.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setShadow(false);
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setShadow(enable: boolean): Promise<void> {
+    return invoke('plugin:window|set_shadow', {
+      label: this.label,
+      value: enable
+    })
+  }
+
+  /**
+   * Set window effects.
+   *
+   * @since 2.0
+   */
+  async setEffects(effects: Effects): Promise<void> {
+    return invoke('plugin:window|set_effects', {
+      label: this.label,
+      value: effects
+    })
+  }
+
+  /**
+   * Clear any applied effects if possible.
+   *
+   * @since 2.0
+   */
+  async clearEffects(): Promise<void> {
+    return invoke('plugin:window|set_effects', {
+      label: this.label,
+      value: null
+    })
+  }
+
+  /**
+   * Whether the window should always be on top of other windows.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setAlwaysOnTop(true);
+   * ```
+   *
+   * @param alwaysOnTop Whether the window should always be on top of other windows or not.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setAlwaysOnTop(alwaysOnTop: boolean): Promise<void> {
+    return invoke('plugin:window|set_always_on_top', {
+      label: this.label,
+      value: alwaysOnTop
+    })
+  }
+
+  /**
+   * Prevents the window contents from being captured by other apps.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setContentProtected(true);
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setContentProtected(protected_: boolean): Promise<void> {
+    return invoke('plugin:window|set_content_protected', {
+      label: this.label,
+      value: protected_
+    })
+  }
+
+  /**
+   * Resizes the window with a new inner size.
+   * @example
+   * ```typescript
+   * import { getCurrent, LogicalSize } from '@tauri-apps/api/window';
+   * await getCurrent().setSize(new LogicalSize(600, 500));
+   * ```
+   *
+   * @param size The logical or physical inner size.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setSize(size: LogicalSize | PhysicalSize): Promise<void> {
+    if (!size || (size.type !== 'Logical' && size.type !== 'Physical')) {
+      throw new Error(
+        'the `size` argument must be either a LogicalSize or a PhysicalSize instance'
+      )
+    }
+
+    return invoke('plugin:window|set_size', {
+      label: this.label,
+      value: {
+        type: size.type,
+        data: {
+          width: size.width,
+          height: size.height
+        }
+      }
+    })
+  }
+
+  /**
+   * Sets the window minimum inner size. If the `size` argument is not provided, the constraint is unset.
+   * @example
+   * ```typescript
+   * import { getCurrent, PhysicalSize } from '@tauri-apps/api/window';
+   * await getCurrent().setMinSize(new PhysicalSize(600, 500));
+   * ```
+   *
+   * @param size The logical or physical inner size, or `null` to unset the constraint.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setMinSize(
+    size: LogicalSize | PhysicalSize | null | undefined
+  ): Promise<void> {
+    if (size && size.type !== 'Logical' && size.type !== 'Physical') {
+      throw new Error(
+        'the `size` argument must be either a LogicalSize or a PhysicalSize instance'
+      )
+    }
+
+    return invoke('plugin:window|set_min_size', {
+      label: this.label,
+      value: size
+        ? {
+            type: size.type,
+            data: {
+              width: size.width,
+              height: size.height
+            }
+          }
+        : null
+    })
+  }
+
+  /**
+   * Sets the window maximum inner size. If the `size` argument is undefined, the constraint is unset.
+   * @example
+   * ```typescript
+   * import { getCurrent, LogicalSize } from '@tauri-apps/api/window';
+   * await getCurrent().setMaxSize(new LogicalSize(600, 500));
+   * ```
+   *
+   * @param size The logical or physical inner size, or `null` to unset the constraint.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setMaxSize(
+    size: LogicalSize | PhysicalSize | null | undefined
+  ): Promise<void> {
+    if (size && size.type !== 'Logical' && size.type !== 'Physical') {
+      throw new Error(
+        'the `size` argument must be either a LogicalSize or a PhysicalSize instance'
+      )
+    }
+
+    return invoke('plugin:window|set_max_size', {
+      label: this.label,
+      value: size
+        ? {
+            type: size.type,
+            data: {
+              width: size.width,
+              height: size.height
+            }
+          }
+        : null
+    })
+  }
+
+  /**
+   * Sets the window outer position.
+   * @example
+   * ```typescript
+   * import { getCurrent, LogicalPosition } from '@tauri-apps/api/window';
+   * await getCurrent().setPosition(new LogicalPosition(600, 500));
+   * ```
+   *
+   * @param position The new position, in logical or physical pixels.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setPosition(
+    position: LogicalPosition | PhysicalPosition
+  ): Promise<void> {
+    if (
+      !position ||
+      (position.type !== 'Logical' && position.type !== 'Physical')
+    ) {
+      throw new Error(
+        'the `position` argument must be either a LogicalPosition or a PhysicalPosition instance'
+      )
+    }
+
+    return invoke('plugin:window|set_position', {
+      label: this.label,
+      value: {
+        type: position.type,
+        data: {
+          x: position.x,
+          y: position.y
+        }
+      }
+    })
+  }
+
+  /**
+   * Sets the window fullscreen state.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setFullscreen(true);
+   * ```
+   *
+   * @param fullscreen Whether the window should go to fullscreen or not.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setFullscreen(fullscreen: boolean): Promise<void> {
+    return invoke('plugin:window|set_fullscreen', {
+      label: this.label,
+      value: fullscreen
+    })
+  }
+
+  /**
+   * Bring the window to front and focus.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setFocus();
+   * ```
+   *
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setFocus(): Promise<void> {
+    return invoke('plugin:window|set_focus', {
+      label: this.label
+    })
+  }
+
+  /**
+   * Sets the window icon.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setIcon('/tauri/awesome.png');
+   * ```
+   *
+   * Note that you need the `icon-ico` or `icon-png` Cargo features to use this API.
+   * To enable it, change your Cargo.toml file:
+   * ```toml
+   * [dependencies]
+   * tauri = { version = "...", features = ["...", "icon-png"] }
+   * ```
+   *
+   * @param icon Icon bytes or path to the icon file.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setIcon(icon: string | Uint8Array): Promise<void> {
+    return invoke('plugin:window|set_icon', {
+      label: this.label,
+      value: typeof icon === 'string' ? icon : Array.from(icon)
+    })
+  }
+
+  /**
+   * Whether the window icon should be hidden from the taskbar or not.
+   *
+   * #### Platform-specific
+   *
+   * - **macOS:** Unsupported.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setSkipTaskbar(true);
+   * ```
+   *
+   * @param skip true to hide window icon, false to show it.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setSkipTaskbar(skip: boolean): Promise<void> {
+    return invoke('plugin:window|set_skip_taskbar', {
+      label: this.label,
+      value: skip
+    })
+  }
+
+  /**
+   * Grabs the cursor, preventing it from leaving the window.
+   *
+   * There's no guarantee that the cursor will be hidden. You should
+   * hide it by yourself if you want so.
+   *
+   * #### Platform-specific
+   *
+   * - **Linux:** Unsupported.
+   * - **macOS:** This locks the cursor in a fixed location, which looks visually awkward.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setCursorGrab(true);
+   * ```
+   *
+   * @param grab `true` to grab the cursor icon, `false` to release it.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setCursorGrab(grab: boolean): Promise<void> {
+    return invoke('plugin:window|set_cursor_grab', {
+      label: this.label,
+      value: grab
+    })
+  }
+
+  /**
+   * Modifies the cursor's visibility.
+   *
+   * #### Platform-specific
+   *
+   * - **Windows:** The cursor is only hidden within the confines of the window.
+   * - **macOS:** The cursor is hidden as long as the window has input focus, even if the cursor is
+   *   outside of the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setCursorVisible(false);
+   * ```
+   *
+   * @param visible If `false`, this will hide the cursor. If `true`, this will show the cursor.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setCursorVisible(visible: boolean): Promise<void> {
+    return invoke('plugin:window|set_cursor_visible', {
+      label: this.label,
+      value: visible
+    })
+  }
+
+  /**
+   * Modifies the cursor icon of the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setCursorIcon('help');
+   * ```
+   *
+   * @param icon The new cursor icon.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setCursorIcon(icon: CursorIcon): Promise<void> {
+    return invoke('plugin:window|set_cursor_icon', {
+      label: this.label,
+      value: icon
+    })
+  }
+
+  /**
+   * Changes the position of the cursor in window coordinates.
+   * @example
+   * ```typescript
+   * import { getCurrent, LogicalPosition } from '@tauri-apps/api/window';
+   * await getCurrent().setCursorPosition(new LogicalPosition(600, 300));
+   * ```
+   *
+   * @param position The new cursor position.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setCursorPosition(
+    position: LogicalPosition | PhysicalPosition
+  ): Promise<void> {
+    if (
+      !position ||
+      (position.type !== 'Logical' && position.type !== 'Physical')
+    ) {
+      throw new Error(
+        'the `position` argument must be either a LogicalPosition or a PhysicalPosition instance'
+      )
+    }
+
+    return invoke('plugin:window|set_cursor_position', {
+      label: this.label,
+      value: {
+        type: position.type,
+        data: {
+          x: position.x,
+          y: position.y
+        }
+      }
+    })
+  }
+
+  /**
+   * Changes the cursor events behavior.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().setIgnoreCursorEvents(true);
+   * ```
+   *
+   * @param ignore `true` to ignore the cursor events; `false` to process them as usual.
+   * @returns A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async setIgnoreCursorEvents(ignore: boolean): Promise<void> {
+    return invoke('plugin:window|set_ignore_cursor_events', {
+      label: this.label,
+      value: ignore
+    })
+  }
+
+  /**
+   * Starts dragging the window.
+   * @example
+   * ```typescript
+   * import { getCurrent } from '@tauri-apps/api/window';
+   * await getCurrent().startDragging();
+   * ```
+   *
+   * @return A promise indicating the success or failure of the operation.
+   *
+   * @since 2.0.0
+   */
+  async startDragging(): Promise<void> {
+    return invoke('plugin:window|start_dragging', {
+      label: this.label
+    })
+  }
+
+  // Listeners
+
+  /**
+   * Listen to window resize.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onResized(({ payload: size }) => {
+   *  console.log('Window resized', size);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onResized(handler: EventCallback<PhysicalSize>): Promise<UnlistenFn> {
+    return this.listen<PhysicalSize>(TauriEvent.WINDOW_RESIZED, (e) => {
+      e.payload = mapPhysicalSize(e.payload)
+      handler(e)
+    })
+  }
+
+  /**
+   * Listen to window move.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onMoved(({ payload: position }) => {
+   *  console.log('Window moved', position);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onMoved(handler: EventCallback<PhysicalPosition>): Promise<UnlistenFn> {
+    return this.listen<PhysicalPosition>(TauriEvent.WINDOW_MOVED, (e) => {
+      e.payload = mapPhysicalPosition(e.payload)
+      handler(e)
+    })
+  }
+
+  /**
+   * Listen to window close requested. Emitted when the user requests to closes the window.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * import { confirm } from '@tauri-apps/api/dialog';
+   * const unlisten = await getCurrent().onCloseRequested(async (event) => {
+   *   const confirmed = await confirm('Are you sure?');
+   *   if (!confirmed) {
+   *     // user did not confirm closing the window; let's prevent it
+   *     event.preventDefault();
+   *   }
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  /* eslint-disable @typescript-eslint/promise-function-async */
+  async onCloseRequested(
+    handler: (event: CloseRequestedEvent) => void | Promise<void>
+  ): Promise<UnlistenFn> {
+    return this.listen<null>(TauriEvent.WINDOW_CLOSE_REQUESTED, (event) => {
+      const evt = new CloseRequestedEvent(event)
+      void Promise.resolve(handler(evt)).then(() => {
+        if (!evt.isPreventDefault()) {
+          return this.close()
+        }
+      })
+    })
+  }
+  /* eslint-enable */
+
+  /**
+   * Listen to window focus change.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onFocusChanged(({ payload: focused }) => {
+   *  console.log('Focus changed, window is focused? ' + focused);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onFocusChanged(handler: EventCallback<boolean>): Promise<UnlistenFn> {
+    const unlistenFocus = await this.listen<PhysicalPosition>(
+      TauriEvent.WINDOW_FOCUS,
+      (event) => {
+        handler({ ...event, payload: true })
+      }
+    )
+    const unlistenBlur = await this.listen<PhysicalPosition>(
+      TauriEvent.WINDOW_BLUR,
+      (event) => {
+        handler({ ...event, payload: false })
+      }
+    )
+    return () => {
+      unlistenFocus()
+      unlistenBlur()
+    }
+  }
+
+  /**
+   * Listen to window scale change. Emitted when the window's scale factor has changed.
+   * The following user actions can cause DPI changes:
+   * - Changing the display's resolution.
+   * - Changing the display's scale factor (e.g. in Control Panel on Windows).
+   * - Moving the window to a display with a different scale factor.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onScaleChanged(({ payload }) => {
+   *  console.log('Scale changed', payload.scaleFactor, payload.size);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onScaleChanged(
+    handler: EventCallback<ScaleFactorChanged>
+  ): Promise<UnlistenFn> {
+    return this.listen<ScaleFactorChanged>(
+      TauriEvent.WINDOW_SCALE_FACTOR_CHANGED,
+      handler
+    )
+  }
+
+  /**
+   * Listen to the window menu item click. The payload is the item id.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onMenuClicked(({ payload: menuId }) => {
+   *  console.log('Menu clicked: ' + menuId);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onMenuClicked(handler: EventCallback<string>): Promise<UnlistenFn> {
+    return this.listen<string>(TauriEvent.MENU, handler)
+  }
+
+  /**
+   * Listen to a file drop event.
+   * The listener is triggered when the user hovers the selected files on the window,
+   * drops the files or cancels the operation.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onFileDropEvent((event) => {
+   *  if (event.payload.type === 'hover') {
+   *    console.log('User hovering', event.payload.paths);
+   *  } else if (event.payload.type === 'drop') {
+   *    console.log('User dropped', event.payload.paths);
+   *  } else {
+   *    console.log('File drop cancelled');
+   *  }
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onFileDropEvent(
+    handler: EventCallback<FileDropEvent>
+  ): Promise<UnlistenFn> {
+    const unlistenFileDrop = await this.listen<string[]>(
+      TauriEvent.WINDOW_FILE_DROP,
+      (event) => {
+        handler({ ...event, payload: { type: 'drop', paths: event.payload } })
+      }
+    )
+
+    const unlistenFileHover = await this.listen<string[]>(
+      TauriEvent.WINDOW_FILE_DROP_HOVER,
+      (event) => {
+        handler({ ...event, payload: { type: 'hover', paths: event.payload } })
+      }
+    )
+
+    const unlistenCancel = await this.listen<null>(
+      TauriEvent.WINDOW_FILE_DROP_CANCELLED,
+      (event) => {
+        handler({ ...event, payload: { type: 'cancel' } })
+      }
+    )
+
+    return () => {
+      unlistenFileDrop()
+      unlistenFileHover()
+      unlistenCancel()
+    }
+  }
+
+  /**
+   * Listen to the system theme change.
+   *
+   * @example
+   * ```typescript
+   * import { getCurrent } from "@tauri-apps/api/window";
+   * const unlisten = await getCurrent().onThemeChanged(({ payload: theme }) => {
+   *  console.log('New theme: ' + theme);
+   * });
+   *
+   * // you need to call unlisten if your handler goes out of scope e.g. the component is unmounted
+   * unlisten();
+   * ```
+   *
+   * @returns A promise resolving to a function to unlisten to the event.
+   * Note that removing the listener is required if your listener goes out of scope e.g. the component is unmounted.
+   *
+   * @since 2.0.0
+   */
+  async onThemeChanged(handler: EventCallback<Theme>): Promise<UnlistenFn> {
+    return this.listen<Theme>(TauriEvent.WINDOW_THEME_CHANGED, handler)
+  }
+}
+
+/**
+ * an array RGBA colors. Each value has minimum of 0 and maximum of 255.
+ *
+ * @since 2.0
+ */
+type Color = [number, number, number, number]
+
+/**
+ * Platform-specific window effects
+ *
+ * @since 2.0
+ */
+enum Effect {
+  /**
+   * A default material appropriate for the view's effectiveAppearance.  **macOS 10.14-**
+   *
+   * @deprecated since macOS 10.14. You should instead choose an appropriate semantic material.
+   */
+  AppearanceBased = 'appearanceBased',
+  /**
+   *  **macOS 10.14-**
+   *
+   * @deprecated since macOS 10.14. Use a semantic material instead.
+   */
+  Light = 'light',
+  /**
+   *  **macOS 10.14-**
+   *
+   * @deprecated since macOS 10.14. Use a semantic material instead.
+   */
+  Dark = 'dark',
+  /**
+   *  **macOS 10.14-**
+   *
+   * @deprecated since macOS 10.14. Use a semantic material instead.
+   */
+  MediumLight = 'mediumLight',
+  /**
+   *  **macOS 10.14-**
+   *
+   * @deprecated since macOS 10.14. Use a semantic material instead.
+   */
+  UltraDark = 'ultraDark',
+  /**
+   *  **macOS 10.10+**
+   */
+  Titlebar = 'titlebar',
+  /**
+   *  **macOS 10.10+**
+   */
+  Selection = 'selection',
+  /**
+   *  **macOS 10.11+**
+   */
+  Menu = 'menu',
+  /**
+   *  **macOS 10.11+**
+   */
+  Popover = 'popover',
+  /**
+   *  **macOS 10.11+**
+   */
+  Sidebar = 'sidebar',
+  /**
+   *  **macOS 10.14+**
+   */
+  HeaderView = 'headerView',
+  /**
+   *  **macOS 10.14+**
+   */
+  Sheet = 'sheet',
+  /**
+   *  **macOS 10.14+**
+   */
+  WindowBackground = 'windowBackground',
+  /**
+   *  **macOS 10.14+**
+   */
+  HudWindow = 'hudWindow',
+  /**
+   *  **macOS 10.14+**
+   */
+  FullScreenUI = 'fullScreenUI',
+  /**
+   *  **macOS 10.14+**
+   */
+  Tooltip = 'tooltip',
+  /**
+   *  **macOS 10.14+**
+   */
+  ContentBackground = 'contentBackground',
+  /**
+   *  **macOS 10.14+**
+   */
+  UnderWindowBackground = 'underWindowBackground',
+  /**
+   *  **macOS 10.14+**
+   */
+  UnderPageBackground = 'underPageBackground',
+  /**
+   *  **Windows 11 Only**
+   */
+  Mica = 'mica',
+  /**
+   * **Windows 7/10/11(22H1) Only**
+   *
+   * ## Notes
+   *
+   * This effect has bad performance when resizing/dragging the window on Windows 11 build 22621.
+   */
+  Blur = 'blur',
+  /**
+   * **Windows 10/11**
+   *
+   * ## Notes
+   *
+   * This effect has bad performance when resizing/dragging the window on Windows 10 v1903+ and Windows 11 build 22000.
+   */
+  Acrylic = 'acrylic'
+}
+
+/**
+ * Window effect state **macOS only**
+ *
+ * @see https://developer.apple.com/documentation/appkit/nsvisualeffectview/state
+ *
+ * @since 2.0
+ */
+enum EffectState {
+  /**
+   *  Make window effect state follow the window's active state **macOS only**
+   */
+  FollowsWindowActiveState = 'followsWindowActiveState',
+  /**
+   *  Make window effect state always active **macOS only**
+   */
+  Active = 'active',
+  /**
+   *  Make window effect state always inactive **macOS only**
+   */
+  Inactive = 'inactive'
+}
+
+/** The window effects configuration object
+ *
+ * @since 2.0
+ */
+interface Effects {
+  /**
+   *  List of Window effects to apply to the Window.
+   * Conflicting effects will apply the first one and ignore the rest.
+   */
+  effects: Effect[]
+  /**
+   * Window effect state **macOS Only**
+   */
+  state?: EffectState
+  /**
+   * Window effect corner radius **macOS Only**
+   */
+  radius?: number
+  /**
+   *  Window effect color. Affects {@link Effects.Blur} and {@link Effects.Acrylic} only
+   * on Windows 10 v1903+. Doesn't have any effect on Windows 7 or Windows 11.
+   */
+  color?: Color
+}
+
+/**
+ * Configuration for the window to create.
+ *
+ * @since 2.0.0
+ */
+interface WindowOptions {
+  /**
+   * Remote URL or local file path to open.
+   *
+   * - URL such as `https://github.com/tauri-apps` is opened directly on a Tauri window.
+   * - data: URL such as `data:text/html,<html>...` is only supported with the `window-data-url` Cargo feature for the `tauri` dependency.
+   * - local file path or route such as `/path/to/page.html` or `/users` is appended to the application URL (the devServer URL on development, or `tauri://localhost/` and `https://tauri.localhost/` on production).
+   */
+  url?: string
+  /** Show window in the center of the screen.. */
+  center?: boolean
+  /** The initial vertical position. Only applies if `y` is also set. */
+  x?: number
+  /** The initial horizontal position. Only applies if `x` is also set. */
+  y?: number
+  /** The initial width. */
+  width?: number
+  /** The initial height. */
+  height?: number
+  /** The minimum width. Only applies if `minHeight` is also set. */
+  minWidth?: number
+  /** The minimum height. Only applies if `minWidth` is also set. */
+  minHeight?: number
+  /** The maximum width. Only applies if `maxHeight` is also set. */
+  maxWidth?: number
+  /** The maximum height. Only applies if `maxWidth` is also set. */
+  maxHeight?: number
+  /** Whether the window is resizable or not. */
+  resizable?: boolean
+  /** Window title. */
+  title?: string
+  /** Whether the window is in fullscreen mode or not. */
+  fullscreen?: boolean
+  /** Whether the window will be initially focused or not. */
+  focus?: boolean
+  /**
+   * Whether the window is transparent or not.
+   * Note that on `macOS` this requires the `macos-private-api` feature flag, enabled under `tauri.conf.json > tauri > macOSPrivateApi`.
+   * WARNING: Using private APIs on `macOS` prevents your application from being accepted to the `App Store`.
+   */
+  transparent?: boolean
+  /** Whether the window should be maximized upon creation or not. */
+  maximized?: boolean
+  /** Whether the window should be immediately visible upon creation or not. */
+  visible?: boolean
+  /** Whether the window should have borders and bars or not. */
+  decorations?: boolean
+  /** Whether the window should always be on top of other windows or not. */
+  alwaysOnTop?: boolean
+  /** Prevents the window contents from being captured by other apps. */
+  contentProtected?: boolean
+  /** Whether or not the window icon should be added to the taskbar. */
+  skipTaskbar?: boolean
+  /**
+   *  Whether or not the window has shadow.
+   *
+   * #### Platform-specific
+   *
+   * - **Windows:**
+   *   - `false` has no effect on decorated window, shadows are always ON.
+   *   - `true` will make ndecorated window have a 1px white border,
+   * and on Windows 11, it will have a rounded corners.
+   * - **Linux:** Unsupported.
+   *
+   * @since 2.0.0
+   */
+  shadow?: boolean
+  /**
+   * Whether the file drop is enabled or not on the webview. By default it is enabled.
+   *
+   * Disabling it is required to use drag and drop on the frontend on Windows.
+   */
+  fileDropEnabled?: boolean
+  /**
+   * The initial window theme. Defaults to the system theme.
+   *
+   * Only implemented on Windows and macOS 10.14+.
+   */
+  theme?: Theme
+  /**
+   * The style of the macOS title bar.
+   */
+  titleBarStyle?: TitleBarStyle
+  /**
+   * If `true`, sets the window title to be hidden on macOS.
+   */
+  hiddenTitle?: boolean
+  /**
+   * Whether clicking an inactive window also clicks through to the webview on macOS.
+   */
+  acceptFirstMouse?: boolean
+  /**
+   * Defines the window [tabbing identifier](https://developer.apple.com/documentation/appkit/nswindow/1644704-tabbingidentifier) on macOS.
+   *
+   * Windows with the same tabbing identifier will be grouped together.
+   * If the tabbing identifier is not set, automatic tabbing will be disabled.
+   */
+  tabbingIdentifier?: string
+  /**
+   * The user agent for the webview.
+   */
+  userAgent?: string
+  /**
+   * Whether or not the webview should be launched in incognito mode.
+   *
+   * #### Platform-specific
+   *
+   * - **Android:** Unsupported.
+   */
+  incognito?: boolean
+  /**
+   * Whether the window's native maximize button is enabled or not. Defaults to `true`.
+   */
+  maximizable?: boolean
+  /**
+   * Whether the window's native minimize button is enabled or not. Defaults to `true`.
+   */
+  minimizable?: boolean
+  /**
+   * Whether the window's native close button is enabled or not. Defaults to `true`.
+   */
+  closable?: boolean
+}
+
+function mapMonitor(m: Monitor | null): Monitor | null {
+  return m === null
+    ? null
+    : {
+        name: m.name,
+        scaleFactor: m.scaleFactor,
+        position: mapPhysicalPosition(m.position),
+        size: mapPhysicalSize(m.size)
+      }
+}
+
+function mapPhysicalPosition(m: PhysicalPosition): PhysicalPosition {
+  return new PhysicalPosition(m.x, m.y)
+}
+
+function mapPhysicalSize(m: PhysicalSize): PhysicalSize {
+  return new PhysicalSize(m.width, m.height)
+}
+
+/**
+ * Returns the monitor on which the window currently resides.
+ * Returns `null` if current monitor can't be detected.
+ * @example
+ * ```typescript
+ * import { currentMonitor } from '@tauri-apps/api/window';
+ * const monitor = currentMonitor();
+ * ```
+ *
+ * @since 2.0.0
+ */
+async function currentMonitor(): Promise<Monitor | null> {
+  return invoke<Monitor | null>('plugin:window|current_monitor').then(
+    mapMonitor
+  )
+}
+
+/**
+ * Returns the primary monitor of the system.
+ * Returns `null` if it can't identify any monitor as a primary one.
+ * @example
+ * ```typescript
+ * import { primaryMonitor } from '@tauri-apps/api/window';
+ * const monitor = primaryMonitor();
+ * ```
+ *
+ * @since 2.0.0
+ */
+async function primaryMonitor(): Promise<Monitor | null> {
+  return invoke<Monitor | null>('plugin:window|primary_monitor').then(
+    mapMonitor
+  )
+}
+
+/**
+ * Returns the list of all the monitors available on the system.
+ * @example
+ * ```typescript
+ * import { availableMonitors } from '@tauri-apps/api/window';
+ * const monitors = availableMonitors();
+ * ```
+ *
+ * @since 2.0.0
+ */
+async function availableMonitors(): Promise<Monitor[]> {
+  return invoke<Monitor[]>('plugin:window|available_monitors').then(
+    (ms) => ms.map(mapMonitor) as Monitor[]
+  )
+}
+
+export {
+  Window,
+  CloseRequestedEvent,
+  getCurrent,
+  getAll,
+  LogicalSize,
+  PhysicalSize,
+  LogicalPosition,
+  PhysicalPosition,
+  UserAttentionType,
+  Effect,
+  EffectState,
+  currentMonitor,
+  primaryMonitor,
+  availableMonitors
+}
+
+export type {
+  Theme,
+  TitleBarStyle,
+  ScaleFactorChanged,
+  FileDropEvent,
+  WindowOptions,
+  Color
+}

+ 2 - 1
tooling/api/typedoc.json

@@ -3,7 +3,8 @@
     "src/event.ts",
     "src/mocks.ts",
     "src/path.ts",
-    "src/tauri.ts"
+    "src/tauri.ts",
+    "src/window.ts"
   ],
   "githubPages": false,
   "readme": "none",

Some files were not shown because too many files changed in this diff