mirror of
https://github.com/tauri-apps/tauri.git
synced 2026-02-06 14:17:02 +00:00
Co-authored-by: Lucas Fernandes Nogueira <lucas@tauri.app> Co-authored-by: Fabian-Lars <fabianlars@fabianlars.de> Co-authored-by: Lucas Nogueira <lucas@tauri.studio>
This commit is contained in:
parent
92b50a3a39
commit
f93148eac0
5
.changes/api-tray-menu.md
Normal file
5
.changes/api-tray-menu.md
Normal file
@ -0,0 +1,5 @@
|
||||
---
|
||||
'@tauri-apps/api': 'minor:feat'
|
||||
---
|
||||
|
||||
Add `tray` and `menu` modules to create and manage tray icons and menus from Javascript.
|
||||
3
.github/workflows/lint-js.yml
vendored
3
.github/workflows/lint-js.yml
vendored
@ -49,6 +49,9 @@ jobs:
|
||||
- name: install deps via yarn
|
||||
working-directory: ./tooling/api/
|
||||
run: yarn
|
||||
- name: run ts:check
|
||||
working-directory: ./tooling/api/
|
||||
run: yarn ts:check
|
||||
- name: run lint
|
||||
working-directory: ./tooling/api/
|
||||
run: yarn lint
|
||||
|
||||
@ -11,7 +11,7 @@ if [ -z "$(git diff --name-only tooling/api)" ]; then
|
||||
else
|
||||
cd tooling/api
|
||||
yarn format
|
||||
yarn lint-fix
|
||||
yarn lint:fix
|
||||
cd ../..
|
||||
fi
|
||||
|
||||
|
||||
@ -13,6 +13,8 @@ SPDX-License-Identifier: Apache-2.0
|
||||
SPDX-License-Identifier: MIT`
|
||||
const bundlerLicense =
|
||||
'// Copyright 2016-2019 Cargo-Bundle developers <https://github.com/burtonageo/cargo-bundle>'
|
||||
const denoLicense =
|
||||
'// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.'
|
||||
|
||||
const extensions = ['.rs', '.js', '.ts', '.yml', '.swift', '.kt']
|
||||
const ignore = [
|
||||
@ -43,7 +45,8 @@ async function checkFile(file) {
|
||||
line.length === 0 ||
|
||||
line.startsWith('#!') ||
|
||||
line.startsWith('// swift-tools-version:') ||
|
||||
line === bundlerLicense
|
||||
line === bundlerLicense ||
|
||||
line === denoLicense
|
||||
) {
|
||||
continue
|
||||
}
|
||||
|
||||
@ -16,6 +16,7 @@ use proc_macro::TokenStream;
|
||||
use syn::{parse_macro_input, DeriveInput};
|
||||
|
||||
mod command;
|
||||
mod menu;
|
||||
mod mobile;
|
||||
mod runtime;
|
||||
|
||||
@ -89,3 +90,64 @@ pub fn default_runtime(attributes: TokenStream, input: TokenStream) -> TokenStre
|
||||
let input = parse_macro_input!(input as DeriveInput);
|
||||
runtime::default_runtime(attributes, input).into()
|
||||
}
|
||||
|
||||
/// Accepts a closure-like syntax to call arbitrary code on a menu item
|
||||
/// after matching against `kind` and retrieving it from `resources_table` using `rid`.
|
||||
///
|
||||
/// You can optionally pass a third parameter to select which item kinds
|
||||
/// to match against, by providing a `|` separated list of item kinds
|
||||
/// ```ignore
|
||||
/// do_menu_item!(|i| i.set_text(text), Check | Submenu);
|
||||
/// ```
|
||||
/// You could also provide a negated list
|
||||
/// ```ignore
|
||||
/// do_menu_item!(|i| i.set_text(text), !Check);
|
||||
/// do_menu_item!(|i| i.set_text(text), !Check | !Submenu);
|
||||
/// ```
|
||||
/// but you can't have mixed negations and positive kinds.
|
||||
/// ```ignore
|
||||
/// do_menu_item!(|i| i.set_text(text), !Check | Submeun);
|
||||
/// ```
|
||||
///
|
||||
/// #### Example
|
||||
///
|
||||
/// ```ignore
|
||||
/// let rid = 23;
|
||||
/// let kind = ItemKind::Check;
|
||||
/// let resources_table = app.manager.resources_table();
|
||||
/// do_menu_item!(|i| i.set_text(text))
|
||||
/// ```
|
||||
/// which will expand into:
|
||||
/// ```ignore
|
||||
/// let rid = 23;
|
||||
/// let kind = ItemKind::Check;
|
||||
/// let resources_table = app.manager.resources_table();
|
||||
/// match kind {
|
||||
/// ItemKind::Submenu => {
|
||||
/// let i = resources_table.get::<Submenu<R>>(rid)?;
|
||||
/// i.set_text(text)
|
||||
/// }
|
||||
/// ItemKind::MenuItem => {
|
||||
/// let i = resources_table.get::<MenuItem<R>>(rid)?;
|
||||
/// i.set_text(text)
|
||||
/// }
|
||||
/// ItemKind::Predefined => {
|
||||
/// let i = resources_table.get::<PredefinedMenuItem<R>>(rid)?;
|
||||
/// i.set_text(text)
|
||||
/// }
|
||||
/// ItemKind::Check => {
|
||||
/// let i = resources_table.get::<CheckMenuItem<R>>(rid)?;
|
||||
/// i.set_text(text)
|
||||
/// }
|
||||
/// ItemKind::Icon => {
|
||||
/// let i = resources_table.get::<IconMenuItem<R>>(rid)?;
|
||||
/// i.set_text(text)
|
||||
/// }
|
||||
/// _ => unreachable!(),
|
||||
/// }
|
||||
/// ```
|
||||
#[proc_macro]
|
||||
pub fn do_menu_item(input: TokenStream) -> TokenStream {
|
||||
let tokens = parse_macro_input!(input as menu::DoMenuItemInput);
|
||||
menu::do_menu_item(tokens).into()
|
||||
}
|
||||
|
||||
118
core/tauri-macros/src/menu.rs
Normal file
118
core/tauri-macros/src/menu.rs
Normal file
@ -0,0 +1,118 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use proc_macro2::{Ident, Span, TokenStream};
|
||||
use quote::quote;
|
||||
use syn::{
|
||||
parse::{Parse, ParseStream},
|
||||
punctuated::Punctuated,
|
||||
Expr, Token,
|
||||
};
|
||||
|
||||
pub struct DoMenuItemInput {
|
||||
resources_table: Ident,
|
||||
rid: Ident,
|
||||
kind: Ident,
|
||||
var: Ident,
|
||||
expr: Expr,
|
||||
kinds: Vec<NegatedIdent>,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
struct NegatedIdent(bool, Ident);
|
||||
|
||||
impl Parse for NegatedIdent {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let t = input.parse::<Token![!]>();
|
||||
let i: Ident = input.parse()?;
|
||||
Ok(NegatedIdent(t.is_ok(), i))
|
||||
}
|
||||
}
|
||||
|
||||
impl Parse for DoMenuItemInput {
|
||||
fn parse(input: ParseStream) -> syn::Result<Self> {
|
||||
let resources_table: Ident = input.parse()?;
|
||||
let _: Token![,] = input.parse()?;
|
||||
let rid: Ident = input.parse()?;
|
||||
let _: Token![,] = input.parse()?;
|
||||
let kind: Ident = input.parse()?;
|
||||
let _: Token![,] = input.parse()?;
|
||||
let _: Token![|] = input.parse()?;
|
||||
let var: Ident = input.parse()?;
|
||||
let _: Token![|] = input.parse()?;
|
||||
let expr: Expr = input.parse()?;
|
||||
let _: syn::Result<Token![,]> = input.parse();
|
||||
let kinds = Punctuated::<NegatedIdent, Token![|]>::parse_terminated(input)?;
|
||||
|
||||
Ok(Self {
|
||||
resources_table,
|
||||
rid,
|
||||
kind,
|
||||
var,
|
||||
expr,
|
||||
kinds: kinds.into_iter().collect(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn do_menu_item(input: DoMenuItemInput) -> TokenStream {
|
||||
let DoMenuItemInput {
|
||||
rid,
|
||||
resources_table,
|
||||
kind,
|
||||
expr,
|
||||
var,
|
||||
mut kinds,
|
||||
} = input;
|
||||
|
||||
let defaults = vec![
|
||||
NegatedIdent(false, Ident::new("Submenu", Span::call_site())),
|
||||
NegatedIdent(false, Ident::new("MenuItem", Span::call_site())),
|
||||
NegatedIdent(false, Ident::new("Predefined", Span::call_site())),
|
||||
NegatedIdent(false, Ident::new("Check", Span::call_site())),
|
||||
NegatedIdent(false, Ident::new("Icon", Span::call_site())),
|
||||
];
|
||||
|
||||
if kinds.is_empty() {
|
||||
kinds.extend(defaults.clone());
|
||||
}
|
||||
|
||||
let has_negated = kinds.iter().any(|n| n.0);
|
||||
|
||||
if has_negated {
|
||||
kinds.extend(defaults);
|
||||
kinds.sort_by(|a, b| a.1.cmp(&b.1));
|
||||
kinds.dedup_by(|a, b| a.1 == b.1);
|
||||
}
|
||||
|
||||
let (kinds, types): (Vec<Ident>, Vec<Ident>) = kinds
|
||||
.into_iter()
|
||||
.filter_map(|nident| {
|
||||
if nident.0 {
|
||||
None
|
||||
} else {
|
||||
match nident.1 {
|
||||
i if i == "MenuItem" => Some((i, Ident::new("MenuItem", Span::call_site()))),
|
||||
i if i == "Submenu" => Some((i, Ident::new("Submenu", Span::call_site()))),
|
||||
i if i == "Predefined" => Some((i, Ident::new("PredefinedMenuItem", Span::call_site()))),
|
||||
i if i == "Check" => Some((i, Ident::new("CheckMenuItem", Span::call_site()))),
|
||||
i if i == "Icon" => Some((i, Ident::new("IconMenuItem", Span::call_site()))),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
})
|
||||
.unzip();
|
||||
|
||||
quote! {
|
||||
match #kind {
|
||||
#(
|
||||
ItemKind::#kinds => {
|
||||
let #var = #resources_table.get::<#types<R>>(#rid)?;
|
||||
#expr
|
||||
}
|
||||
)*
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -73,8 +73,8 @@ ico = { version = "0.3.0", optional = true }
|
||||
http-range = { version = "0.1.5", optional = true }
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\", target_os = \"windows\", target_os = \"macos\"))".dependencies]
|
||||
muda = { version = "0.10", default-features = false }
|
||||
tray-icon = { version = "0.10", default-features = false, optional = true }
|
||||
muda = { version = "0.10", default-features = false, features = [ "serde" ] }
|
||||
tray-icon = { version = "0.10", default-features = false, features = [ "serde" ], optional = true }
|
||||
|
||||
[target."cfg(any(target_os = \"linux\", target_os = \"dragonfly\", target_os = \"freebsd\", target_os = \"openbsd\", target_os = \"netbsd\"))".dependencies]
|
||||
gtk = { version = "0.18", features = [ "v3_24" ] }
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -772,7 +772,8 @@ macro_rules! shared_app_impl {
|
||||
/// **You should always exit the tauri app immediately after this function returns and not use any tauri-related APIs.**
|
||||
pub fn cleanup_before_exit(&self) {
|
||||
#[cfg(all(desktop, feature = "tray-icon"))]
|
||||
self.manager.tray.icons.lock().unwrap().clear()
|
||||
self.manager.tray.icons.lock().unwrap().clear();
|
||||
self.manager.resources_table().clear();
|
||||
}
|
||||
}
|
||||
};
|
||||
@ -787,6 +788,11 @@ impl<R: Runtime> App<R> {
|
||||
self.handle.plugin(crate::event::plugin::init())?;
|
||||
self.handle.plugin(crate::window::plugin::init())?;
|
||||
self.handle.plugin(crate::app::plugin::init())?;
|
||||
self.handle.plugin(crate::resources::plugin::init())?;
|
||||
#[cfg(desktop)]
|
||||
self.handle.plugin(crate::menu::plugin::init())?;
|
||||
#[cfg(all(desktop, feature = "tray-icon"))]
|
||||
self.handle.plugin(crate::tray::plugin::init())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
|
||||
@ -130,6 +130,12 @@ pub enum Error {
|
||||
/// window not found.
|
||||
#[error("window not found")]
|
||||
WindowNotFound,
|
||||
/// The resource id is invalid.
|
||||
#[error("The resource id {0} is invalid.")]
|
||||
BadResourceId(crate::resources::ResourceId),
|
||||
/// The anyhow crate error.
|
||||
#[error(transparent)]
|
||||
Anyhow(#[from] anyhow::Error),
|
||||
}
|
||||
|
||||
/// `Result<T, ::tauri::Error>`
|
||||
|
||||
@ -4,13 +4,14 @@
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
str::FromStr,
|
||||
sync::{
|
||||
atomic::{AtomicU32, Ordering},
|
||||
Arc, Mutex,
|
||||
},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize, Serializer};
|
||||
use serde::{Deserialize, Deserializer, Serialize, Serializer};
|
||||
|
||||
use crate::{
|
||||
command,
|
||||
@ -50,6 +51,62 @@ impl Serialize for Channel {
|
||||
}
|
||||
}
|
||||
|
||||
/// The ID of a channel that was defined on the JavaScript layer.
|
||||
///
|
||||
/// Useful when expecting [`Channel`] as part of a JSON object instead of a top-level command argument.
|
||||
///
|
||||
/// # Examples
|
||||
///
|
||||
/// ```rust
|
||||
/// use tauri::{ipc::JavaScriptChannelId, Runtime, Window};
|
||||
///
|
||||
/// #[derive(serde::Deserialize)]
|
||||
/// #[serde(rename_all = "camelCase")]
|
||||
/// struct Button {
|
||||
/// label: String,
|
||||
/// on_click: JavaScriptChannelId,
|
||||
/// }
|
||||
///
|
||||
/// #[tauri::command]
|
||||
/// fn add_button<R: Runtime>(window: Window<R>, button: Button) {
|
||||
/// let channel = button.on_click.channel_on(window);
|
||||
/// channel.send("clicked").unwrap();
|
||||
/// }
|
||||
/// ```
|
||||
pub struct JavaScriptChannelId(CallbackFn);
|
||||
|
||||
impl FromStr for JavaScriptChannelId {
|
||||
type Err = &'static str;
|
||||
|
||||
fn from_str(s: &str) -> Result<Self, Self::Err> {
|
||||
s.split_once(IPC_PAYLOAD_PREFIX)
|
||||
.ok_or("invalid channel string")
|
||||
.and_then(|(_prefix, id)| id.parse().map_err(|_| "invalid channel ID"))
|
||||
.map(|id| Self(CallbackFn(id)))
|
||||
}
|
||||
}
|
||||
|
||||
impl JavaScriptChannelId {
|
||||
/// Gets a [`Channel`] for this channel ID on the given [`Window`].
|
||||
pub fn channel_on<R: Runtime>(&self, window: Window<R>) -> Channel {
|
||||
Channel::from_callback_fn(window, self.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'de> Deserialize<'de> for JavaScriptChannelId {
|
||||
fn deserialize<D>(deserializer: D) -> std::result::Result<Self, D::Error>
|
||||
where
|
||||
D: Deserializer<'de>,
|
||||
{
|
||||
let value: String = Deserialize::deserialize(deserializer)?;
|
||||
Self::from_str(&value).map_err(|_| {
|
||||
serde::de::Error::custom(format!(
|
||||
"invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
|
||||
))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl Channel {
|
||||
/// Creates a new channel with the given message handler.
|
||||
pub fn new<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
|
||||
@ -58,7 +115,7 @@ impl Channel {
|
||||
Self::new_with_id(CHANNEL_COUNTER.fetch_add(1, Ordering::Relaxed), on_message)
|
||||
}
|
||||
|
||||
pub(crate) fn new_with_id<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
|
||||
fn new_with_id<F: Fn(InvokeBody) -> crate::Result<()> + Send + Sync + 'static>(
|
||||
id: u32,
|
||||
on_message: F,
|
||||
) -> Self {
|
||||
@ -74,7 +131,7 @@ impl Channel {
|
||||
channel
|
||||
}
|
||||
|
||||
pub(crate) fn from_ipc<R: Runtime>(window: Window<R>, callback: CallbackFn) -> Self {
|
||||
pub(crate) fn from_callback_fn<R: Runtime>(window: Window<R>, callback: CallbackFn) -> Self {
|
||||
Channel::new_with_id(callback.0, move |body| {
|
||||
let data_id = CHANNEL_DATA_COUNTER.fetch_add(1, Ordering::Relaxed);
|
||||
window
|
||||
@ -90,23 +147,12 @@ impl Channel {
|
||||
})
|
||||
}
|
||||
|
||||
pub(crate) fn load_from_ipc<R: Runtime>(
|
||||
window: Window<R>,
|
||||
value: impl AsRef<str>,
|
||||
) -> Option<Self> {
|
||||
value
|
||||
.as_ref()
|
||||
.split_once(IPC_PAYLOAD_PREFIX)
|
||||
.and_then(|(_prefix, id)| id.parse().ok())
|
||||
.map(|callback_id| Self::from_ipc(window, CallbackFn(callback_id)))
|
||||
}
|
||||
|
||||
/// The channel identifier.
|
||||
pub fn id(&self) -> u32 {
|
||||
self.id
|
||||
}
|
||||
|
||||
/// Sends the given data through the channel.
|
||||
/// Sends the given data through the channel.
|
||||
pub fn send<T: IpcResponse>(&self, data: T) -> crate::Result<()> {
|
||||
let body = data.body()?;
|
||||
(self.on_message)(body)
|
||||
@ -121,11 +167,13 @@ impl<'de, R: Runtime> CommandArg<'de, R> for Channel {
|
||||
let window = command.message.window();
|
||||
let value: String =
|
||||
Deserialize::deserialize(command).map_err(|e| crate::Error::InvalidArgs(name, arg, e))?;
|
||||
Channel::load_from_ipc(window, &value).ok_or_else(|| {
|
||||
InvokeError::from_anyhow(anyhow::anyhow!(
|
||||
JavaScriptChannelId::from_str(&value)
|
||||
.map(|id| id.channel_on(window))
|
||||
.map_err(|_| {
|
||||
InvokeError::from_anyhow(anyhow::anyhow!(
|
||||
"invalid channel value `{value}`, expected a string in the `{IPC_PAYLOAD_PREFIX}ID` format"
|
||||
))
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -25,7 +25,7 @@ pub(crate) mod channel;
|
||||
pub(crate) mod format_callback;
|
||||
pub(crate) mod protocol;
|
||||
|
||||
pub use channel::Channel;
|
||||
pub use channel::{Channel, JavaScriptChannelId};
|
||||
|
||||
/// A closure that is run every time Tauri receives a message it doesn't explicitly handle.
|
||||
pub type InvokeHandler<R> = dyn Fn(Invoke<R>) -> bool + Send + Sync + 'static;
|
||||
|
||||
@ -245,7 +245,7 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
|
||||
if !(cfg!(target_os = "macos") || cfg!(target_os = "ios"))
|
||||
&& matches!(v, JsonValue::Object(_) | JsonValue::Array(_))
|
||||
{
|
||||
let _ = Channel::from_ipc(window, callback).send(v);
|
||||
let _ = Channel::from_callback_fn(window, callback).send(v);
|
||||
} else {
|
||||
responder_eval(
|
||||
&window,
|
||||
@ -262,7 +262,8 @@ fn handle_ipc_message<R: Runtime>(message: String, manager: &AppManager<R>, labe
|
||||
error,
|
||||
);
|
||||
} else {
|
||||
let _ = Channel::from_ipc(window, callback).send(InvokeBody::Raw(v.clone()));
|
||||
let _ =
|
||||
Channel::from_callback_fn(window, callback).send(InvokeBody::Raw(v.clone()));
|
||||
}
|
||||
}
|
||||
InvokeResponse::Err(e) => responder_eval(
|
||||
|
||||
@ -82,6 +82,7 @@ mod manager;
|
||||
mod pattern;
|
||||
pub mod plugin;
|
||||
pub(crate) mod protocol;
|
||||
mod resources;
|
||||
mod vibrancy;
|
||||
pub mod window;
|
||||
use tauri_runtime as runtime;
|
||||
@ -160,7 +161,7 @@ pub use tauri_runtime_wry::wry;
|
||||
/// A task to run on the main thread.
|
||||
pub type SyncTask = Box<dyn FnOnce() + Send>;
|
||||
|
||||
use serde::Serialize;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
fmt::{self, Debug},
|
||||
@ -903,6 +904,40 @@ mod tests {
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
pub(crate) 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,
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(unused)]
|
||||
macro_rules! run_main_thread {
|
||||
($self:ident, $ex:expr) => {{
|
||||
|
||||
@ -6,7 +6,7 @@ use std::{
|
||||
borrow::Cow,
|
||||
collections::HashMap,
|
||||
fmt,
|
||||
sync::{Arc, Mutex},
|
||||
sync::{Arc, Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
use serde::Serialize;
|
||||
@ -20,7 +20,6 @@ use tauri_utils::{
|
||||
html::{SCRIPT_NONCE_TOKEN, STYLE_NONCE_TOKEN},
|
||||
};
|
||||
|
||||
use crate::event::EmitArgs;
|
||||
use crate::{
|
||||
app::{AppHandle, GlobalWindowEventListener, OnPageLoad},
|
||||
event::{assert_event_name_is_valid, Event, EventId, Listeners},
|
||||
@ -33,6 +32,7 @@ use crate::{
|
||||
},
|
||||
Context, Pattern, Runtime, StateManager, Window,
|
||||
};
|
||||
use crate::{event::EmitArgs, resources::ResourceTable};
|
||||
|
||||
#[cfg(desktop)]
|
||||
mod menu;
|
||||
@ -196,6 +196,9 @@ pub struct AppManager<R: Runtime> {
|
||||
|
||||
/// Application pattern.
|
||||
pub pattern: Arc<Pattern>,
|
||||
|
||||
/// Application Resources Table
|
||||
pub(crate) resources_table: Arc<Mutex<ResourceTable>>,
|
||||
}
|
||||
|
||||
impl<R: Runtime> fmt::Debug for AppManager<R> {
|
||||
@ -274,6 +277,7 @@ impl<R: Runtime> AppManager<R> {
|
||||
app_icon: context.app_icon,
|
||||
package_info: context.package_info,
|
||||
pattern: Arc::new(context.pattern),
|
||||
resources_table: Arc::default(),
|
||||
}
|
||||
}
|
||||
|
||||
@ -535,6 +539,14 @@ impl<R: Runtime> AppManager<R> {
|
||||
pub fn windows(&self) -> HashMap<String, Window<R>> {
|
||||
self.window.windows_lock().clone()
|
||||
}
|
||||
|
||||
/// Resources table managed by the application.
|
||||
pub(crate) fn resources_table(&self) -> MutexGuard<'_, ResourceTable> {
|
||||
self
|
||||
.resources_table
|
||||
.lock()
|
||||
.expect("poisoned window manager")
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(desktop)]
|
||||
|
||||
@ -2,9 +2,11 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{menu::MenuId, run_main_thread, AppHandle, Manager, Runtime};
|
||||
use crate::{menu::MenuId, resources::Resource, run_main_thread, AppHandle, Manager, Runtime};
|
||||
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`]
|
||||
/// and usually contains a text and a check mark or a similar toggle
|
||||
/// that corresponds to a checked and unchecked states.
|
||||
///
|
||||
/// [`Menu`]: super::Menu
|
||||
/// [`Submenu`]: super::Submenu
|
||||
@ -31,7 +33,7 @@ unsafe impl<R: Runtime> Sync for CheckMenuItem<R> {}
|
||||
unsafe impl<R: Runtime> Send for CheckMenuItem<R> {}
|
||||
|
||||
impl<R: Runtime> super::sealed::IsMenuItemBase for CheckMenuItem<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@ -146,3 +148,5 @@ impl<R: Runtime> CheckMenuItem<R> {
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_checked(checked))
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for CheckMenuItem<R> {}
|
||||
|
||||
@ -3,9 +3,12 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::NativeIcon;
|
||||
use crate::{menu::MenuId, run_main_thread, AppHandle, Icon, Manager, Runtime};
|
||||
use crate::{
|
||||
menu::MenuId, resources::Resource, run_main_thread, AppHandle, Icon, Manager, Runtime,
|
||||
};
|
||||
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`]
|
||||
/// and usually contains an icon and a text.
|
||||
///
|
||||
/// [`Menu`]: super::Menu
|
||||
/// [`Submenu`]: super::Submenu
|
||||
@ -32,7 +35,7 @@ unsafe impl<R: Runtime> Sync for IconMenuItem<R> {}
|
||||
unsafe impl<R: Runtime> Send for IconMenuItem<R> {}
|
||||
|
||||
impl<R: Runtime> super::sealed::IsMenuItemBase for IconMenuItem<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@ -214,3 +217,5 @@ impl<R: Runtime> IconMenuItem<R> {
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for IconMenuItem<R> {}
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
|
||||
use super::sealed::ContextMenuBase;
|
||||
use super::{AboutMetadata, IsMenuItem, MenuItemKind, PredefinedMenuItem, Submenu};
|
||||
use crate::resources::Resource;
|
||||
use crate::Window;
|
||||
use crate::{run_main_thread, AppHandle, Manager, Position, Runtime};
|
||||
use muda::ContextMenu;
|
||||
@ -84,12 +85,12 @@ impl<R: Runtime> ContextMenuBase for Menu<R> {
|
||||
}
|
||||
})
|
||||
}
|
||||
fn inner(&self) -> &dyn muda::ContextMenu {
|
||||
fn inner_context(&self) -> &dyn muda::ContextMenu {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
fn inner_owned(&self) -> Box<dyn muda::ContextMenu> {
|
||||
Box::new(self.clone().inner)
|
||||
fn inner_context_owned(&self) -> Box<dyn muda::ContextMenu> {
|
||||
Box::new(self.inner.clone())
|
||||
}
|
||||
}
|
||||
|
||||
@ -261,8 +262,10 @@ impl<R: Runtime> Menu<R> {
|
||||
/// [`Submenu`]: super::Submenu
|
||||
pub fn append(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_.inner.append(kind.inner().inner()))?
|
||||
.map_err(Into::into)
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.append(kind.inner().inner_muda()))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Add menu items to the end of this menu. It calls [`Menu::append`] in a loop internally.
|
||||
@ -291,7 +294,7 @@ impl<R: Runtime> Menu<R> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.prepend(kind.inner().inner()))?
|
||||
.prepend(kind.inner().inner_muda()))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -317,7 +320,7 @@ impl<R: Runtime> Menu<R> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.insert(kind.inner().inner(), position))?
|
||||
.insert(kind.inner().inner_muda(), position))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
@ -339,8 +342,18 @@ impl<R: Runtime> Menu<R> {
|
||||
/// Remove a menu item from this menu.
|
||||
pub fn remove(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_.inner.remove(kind.inner().inner()))?
|
||||
.map_err(Into::into)
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.remove(kind.inner().inner_muda()))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Remove the menu item at the specified position from this menu and returns it.
|
||||
pub fn remove_at(&self, position: usize) -> crate::Result<Option<MenuItemKind<R>>> {
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.remove_at(position)
|
||||
.map(|i| MenuItemKind::from_muda(self_.app_handle.clone(), i)))
|
||||
}
|
||||
|
||||
/// Retrieves the menu item matching the given identifier.
|
||||
@ -358,40 +371,27 @@ impl<R: Runtime> Menu<R> {
|
||||
|
||||
/// Returns a list of menu items that has been added to this menu.
|
||||
pub fn items(&self) -> crate::Result<Vec<MenuItemKind<R>>> {
|
||||
let handle = self.app_handle.clone();
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.items()
|
||||
.into_iter()
|
||||
.map(|i| match i {
|
||||
muda::MenuItemKind::MenuItem(i) => super::MenuItemKind::MenuItem(super::MenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Submenu(i) => super::MenuItemKind::Submenu(super::Submenu {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Predefined(i) => {
|
||||
super::MenuItemKind::Predefined(super::PredefinedMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
})
|
||||
}
|
||||
muda::MenuItemKind::Check(i) => super::MenuItemKind::Check(super::CheckMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Icon(i) => super::MenuItemKind::Icon(super::IconMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
})
|
||||
.map(|i| MenuItemKind::from_muda(self_.app_handle.clone(), i))
|
||||
.collect::<Vec<_>>())
|
||||
}
|
||||
|
||||
/// Set this menu as the application menu.
|
||||
///
|
||||
/// This is an alias for [`AppHandle::set_menu`].
|
||||
pub fn set_as_app_menu(&self) -> crate::Result<Option<Menu<R>>> {
|
||||
self.app_handle.set_menu(self.clone())
|
||||
}
|
||||
|
||||
/// Set this menu as the window menu.
|
||||
///
|
||||
/// This is an alias for [`Window::set_menu`].
|
||||
pub fn set_as_window_menu(&self, window: &Window<R>) -> crate::Result<Option<Menu<R>>> {
|
||||
window.set_menu(self.clone())
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for Menu<R> {}
|
||||
|
||||
@ -4,7 +4,7 @@
|
||||
|
||||
#![cfg(desktop)]
|
||||
|
||||
//! Menu types and utility functions
|
||||
//! Menu types and utilities.
|
||||
|
||||
// TODO(muda-migration): figure out js events
|
||||
|
||||
@ -14,6 +14,7 @@ mod icon;
|
||||
#[allow(clippy::module_inception)]
|
||||
mod menu;
|
||||
mod normal;
|
||||
pub(crate) mod plugin;
|
||||
mod predefined;
|
||||
mod submenu;
|
||||
pub use builders::*;
|
||||
@ -22,13 +23,14 @@ pub use icon::IconMenuItem;
|
||||
pub use menu::{Menu, HELP_SUBMENU_ID, WINDOW_SUBMENU_ID};
|
||||
pub use normal::MenuItem;
|
||||
pub use predefined::PredefinedMenuItem;
|
||||
use serde::{Deserialize, Serialize};
|
||||
pub use submenu::Submenu;
|
||||
|
||||
use crate::{Icon, Runtime};
|
||||
use crate::{AppHandle, Icon, Runtime};
|
||||
pub use muda::MenuId;
|
||||
|
||||
/// Describes a menu event emitted when a menu item is activated
|
||||
#[derive(Debug, Clone)]
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
pub struct MenuEvent {
|
||||
/// Id of the menu item which triggered this event
|
||||
pub id: MenuId,
|
||||
@ -233,7 +235,7 @@ impl From<AboutMetadata> for muda::AboutMetadata {
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
/// - **Windows / Linux**: Unsupported.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Deserialize)]
|
||||
pub enum NativeIcon {
|
||||
/// An add item template image.
|
||||
Add,
|
||||
@ -449,6 +451,36 @@ impl<R: Runtime> MenuItemKind<R> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn from_muda(app_handle: AppHandle<R>, i: muda::MenuItemKind) -> Self {
|
||||
match i {
|
||||
muda::MenuItemKind::MenuItem(i) => Self::MenuItem(MenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle,
|
||||
}),
|
||||
muda::MenuItemKind::Submenu(i) => Self::Submenu(Submenu {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle,
|
||||
}),
|
||||
muda::MenuItemKind::Predefined(i) => Self::Predefined(PredefinedMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle,
|
||||
}),
|
||||
muda::MenuItemKind::Check(i) => Self::Check(CheckMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle,
|
||||
}),
|
||||
muda::MenuItemKind::Icon(i) => Self::Icon(IconMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
/// Casts this item to a [`MenuItem`], and returns `None` if it wasn't.
|
||||
pub fn as_menuitem(&self) -> Option<&MenuItem<R>> {
|
||||
match self {
|
||||
@ -543,8 +575,8 @@ impl<R: Runtime> Clone for MenuItemKind<R> {
|
||||
}
|
||||
|
||||
impl<R: Runtime> sealed::IsMenuItemBase for MenuItemKind<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
self.inner().inner()
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
self.inner().inner_muda()
|
||||
}
|
||||
}
|
||||
|
||||
@ -593,12 +625,12 @@ pub trait ContextMenu: sealed::ContextMenuBase + Send + Sync {
|
||||
pub(crate) mod sealed {
|
||||
|
||||
pub trait IsMenuItemBase {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem;
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem;
|
||||
}
|
||||
|
||||
pub trait ContextMenuBase {
|
||||
fn inner(&self) -> &dyn muda::ContextMenu;
|
||||
fn inner_owned(&self) -> Box<dyn muda::ContextMenu>;
|
||||
fn inner_context(&self) -> &dyn muda::ContextMenu;
|
||||
fn inner_context_owned(&self) -> Box<dyn muda::ContextMenu>;
|
||||
fn popup_inner<R: crate::Runtime, P: Into<crate::Position>>(
|
||||
&self,
|
||||
window: crate::Window<R>,
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{menu::MenuId, run_main_thread, AppHandle, Manager, Runtime};
|
||||
use crate::{menu::MenuId, resources::Resource, run_main_thread, AppHandle, Manager, Runtime};
|
||||
|
||||
/// A menu item inside a [`Menu`] or [`Submenu`] and contains only text.
|
||||
///
|
||||
@ -31,7 +31,7 @@ unsafe impl<R: Runtime> Sync for MenuItem<R> {}
|
||||
unsafe impl<R: Runtime> Send for MenuItem<R> {}
|
||||
|
||||
impl<R: Runtime> super::sealed::IsMenuItemBase for MenuItem<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@ -132,3 +132,5 @@ impl<R: Runtime> MenuItem<R> {
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_accelerator(accel))?.map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for MenuItem<R> {}
|
||||
|
||||
876
core/tauri/src/menu/plugin.rs
Normal file
876
core/tauri/src/menu/plugin.rs
Normal file
@ -0,0 +1,876 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::{
|
||||
collections::HashMap,
|
||||
sync::{Mutex, MutexGuard},
|
||||
};
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use tauri_runtime::window::dpi::Position;
|
||||
|
||||
use super::{sealed::ContextMenuBase, *};
|
||||
use crate::{
|
||||
command,
|
||||
ipc::{channel::JavaScriptChannelId, Channel},
|
||||
plugin::{Builder, TauriPlugin},
|
||||
resources::{ResourceId, ResourceTable},
|
||||
AppHandle, IconDto, Manager, RunEvent, Runtime, State, Window,
|
||||
};
|
||||
use tauri_macros::do_menu_item;
|
||||
|
||||
#[derive(Deserialize, Serialize)]
|
||||
pub(crate) enum ItemKind {
|
||||
Menu,
|
||||
MenuItem,
|
||||
Predefined,
|
||||
Submenu,
|
||||
Check,
|
||||
Icon,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub(crate) struct AboutMetadata {
|
||||
pub name: Option<String>,
|
||||
pub version: Option<String>,
|
||||
pub short_version: Option<String>,
|
||||
pub authors: Option<Vec<String>>,
|
||||
pub comments: Option<String>,
|
||||
pub copyright: Option<String>,
|
||||
pub license: Option<String>,
|
||||
pub website: Option<String>,
|
||||
pub website_label: Option<String>,
|
||||
pub credits: Option<String>,
|
||||
pub icon: Option<IconDto>,
|
||||
}
|
||||
|
||||
impl From<AboutMetadata> for super::AboutMetadata {
|
||||
fn from(value: AboutMetadata) -> Self {
|
||||
Self {
|
||||
name: value.name,
|
||||
version: value.version,
|
||||
short_version: value.short_version,
|
||||
authors: value.authors,
|
||||
comments: value.comments,
|
||||
copyright: value.copyright,
|
||||
license: value.license,
|
||||
website: value.website,
|
||||
website_label: value.website_label,
|
||||
credits: value.credits,
|
||||
icon: value.icon.map(Into::into),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::large_enum_variant)]
|
||||
#[derive(Deserialize)]
|
||||
enum Predefined {
|
||||
Separator,
|
||||
Copy,
|
||||
Cut,
|
||||
Paste,
|
||||
SelectAll,
|
||||
Undo,
|
||||
Redo,
|
||||
Minimize,
|
||||
Maximize,
|
||||
Fullscreen,
|
||||
Hide,
|
||||
HideOthers,
|
||||
ShowAll,
|
||||
CloseWindow,
|
||||
Quit,
|
||||
About(Option<AboutMetadata>),
|
||||
Services,
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct SubmenuPayload {
|
||||
id: Option<MenuId>,
|
||||
text: String,
|
||||
enabled: Option<bool>,
|
||||
items: Vec<MenuItemPayloadKind>,
|
||||
}
|
||||
|
||||
impl SubmenuPayload {
|
||||
pub fn create_item<R: Runtime>(
|
||||
self,
|
||||
window: &Window<R>,
|
||||
resources_table: &MutexGuard<'_, ResourceTable>,
|
||||
) -> crate::Result<Submenu<R>> {
|
||||
let mut builder = if let Some(id) = self.id {
|
||||
SubmenuBuilder::with_id(window, id, self.text)
|
||||
} else {
|
||||
SubmenuBuilder::new(window, self.text)
|
||||
};
|
||||
if let Some(enabled) = self.enabled {
|
||||
builder = builder.enabled(enabled);
|
||||
}
|
||||
for item in self.items {
|
||||
builder = item.with_item(window, resources_table, |i| Ok(builder.item(i)))?;
|
||||
}
|
||||
|
||||
builder.build()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct CheckMenuItemPayload {
|
||||
handler: Option<JavaScriptChannelId>,
|
||||
id: Option<MenuId>,
|
||||
text: String,
|
||||
checked: bool,
|
||||
enabled: Option<bool>,
|
||||
accelerator: Option<String>,
|
||||
}
|
||||
|
||||
impl CheckMenuItemPayload {
|
||||
pub fn create_item<R: Runtime>(self, window: &Window<R>) -> CheckMenuItem<R> {
|
||||
let mut builder = if let Some(id) = self.id {
|
||||
CheckMenuItemBuilder::with_id(id, self.text)
|
||||
} else {
|
||||
CheckMenuItemBuilder::new(self.text)
|
||||
};
|
||||
if let Some(accelerator) = self.accelerator {
|
||||
builder = builder.accelerator(accelerator);
|
||||
}
|
||||
if let Some(enabled) = self.enabled {
|
||||
builder = builder.enabled(enabled);
|
||||
}
|
||||
|
||||
let item = builder.checked(self.checked).build(window);
|
||||
|
||||
if let Some(handler) = self.handler {
|
||||
let handler = handler.channel_on(window.clone());
|
||||
window
|
||||
.state::<MenuChannels>()
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(item.id().clone(), handler);
|
||||
}
|
||||
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum Icon {
|
||||
Native(NativeIcon),
|
||||
Icon(IconDto),
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct IconMenuItemPayload {
|
||||
handler: Option<JavaScriptChannelId>,
|
||||
id: Option<MenuId>,
|
||||
text: String,
|
||||
icon: Icon,
|
||||
enabled: Option<bool>,
|
||||
accelerator: Option<String>,
|
||||
}
|
||||
|
||||
impl IconMenuItemPayload {
|
||||
pub fn create_item<R: Runtime>(self, window: &Window<R>) -> IconMenuItem<R> {
|
||||
let mut builder = if let Some(id) = self.id {
|
||||
IconMenuItemBuilder::with_id(id, self.text)
|
||||
} else {
|
||||
IconMenuItemBuilder::new(self.text)
|
||||
};
|
||||
if let Some(accelerator) = self.accelerator {
|
||||
builder = builder.accelerator(accelerator);
|
||||
}
|
||||
if let Some(enabled) = self.enabled {
|
||||
builder = builder.enabled(enabled);
|
||||
}
|
||||
builder = match self.icon {
|
||||
Icon::Native(native_icon) => builder.native_icon(native_icon),
|
||||
Icon::Icon(icon) => builder.icon(icon.into()),
|
||||
};
|
||||
|
||||
let item = builder.build(window);
|
||||
|
||||
if let Some(handler) = self.handler {
|
||||
let handler = handler.channel_on(window.clone());
|
||||
window
|
||||
.state::<MenuChannels>()
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(item.id().clone(), handler);
|
||||
}
|
||||
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct MenuItemPayload {
|
||||
handler: Option<JavaScriptChannelId>,
|
||||
id: Option<MenuId>,
|
||||
text: String,
|
||||
enabled: Option<bool>,
|
||||
accelerator: Option<String>,
|
||||
}
|
||||
|
||||
impl MenuItemPayload {
|
||||
pub fn create_item<R: Runtime>(self, window: &Window<R>) -> MenuItem<R> {
|
||||
let mut builder = if let Some(id) = self.id {
|
||||
MenuItemBuilder::with_id(id, self.text)
|
||||
} else {
|
||||
MenuItemBuilder::new(self.text)
|
||||
};
|
||||
if let Some(accelerator) = self.accelerator {
|
||||
builder = builder.accelerator(accelerator);
|
||||
}
|
||||
if let Some(enabled) = self.enabled {
|
||||
builder = builder.enabled(enabled);
|
||||
}
|
||||
|
||||
let item = builder.build(window);
|
||||
|
||||
if let Some(handler) = self.handler {
|
||||
let handler = handler.channel_on(window.clone());
|
||||
window
|
||||
.state::<MenuChannels>()
|
||||
.0
|
||||
.lock()
|
||||
.unwrap()
|
||||
.insert(item.id().clone(), handler);
|
||||
}
|
||||
|
||||
item
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
struct PredefinedMenuItemPayload {
|
||||
item: Predefined,
|
||||
text: Option<String>,
|
||||
}
|
||||
|
||||
impl PredefinedMenuItemPayload {
|
||||
pub fn create_item<R: Runtime>(self, window: &Window<R>) -> PredefinedMenuItem<R> {
|
||||
match self.item {
|
||||
Predefined::Separator => PredefinedMenuItem::separator(window),
|
||||
Predefined::Copy => PredefinedMenuItem::copy(window, self.text.as_deref()),
|
||||
Predefined::Cut => PredefinedMenuItem::cut(window, self.text.as_deref()),
|
||||
Predefined::Paste => PredefinedMenuItem::paste(window, self.text.as_deref()),
|
||||
Predefined::SelectAll => PredefinedMenuItem::select_all(window, self.text.as_deref()),
|
||||
Predefined::Undo => PredefinedMenuItem::undo(window, self.text.as_deref()),
|
||||
Predefined::Redo => PredefinedMenuItem::redo(window, self.text.as_deref()),
|
||||
Predefined::Minimize => PredefinedMenuItem::minimize(window, self.text.as_deref()),
|
||||
Predefined::Maximize => PredefinedMenuItem::maximize(window, self.text.as_deref()),
|
||||
Predefined::Fullscreen => PredefinedMenuItem::fullscreen(window, self.text.as_deref()),
|
||||
Predefined::Hide => PredefinedMenuItem::hide(window, self.text.as_deref()),
|
||||
Predefined::HideOthers => PredefinedMenuItem::hide_others(window, self.text.as_deref()),
|
||||
Predefined::ShowAll => PredefinedMenuItem::show_all(window, self.text.as_deref()),
|
||||
Predefined::CloseWindow => PredefinedMenuItem::close_window(window, self.text.as_deref()),
|
||||
Predefined::Quit => PredefinedMenuItem::quit(window, self.text.as_deref()),
|
||||
Predefined::About(metadata) => {
|
||||
PredefinedMenuItem::about(window, self.text.as_deref(), metadata.map(Into::into))
|
||||
}
|
||||
Predefined::Services => PredefinedMenuItem::services(window, self.text.as_deref()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(untagged)]
|
||||
enum MenuItemPayloadKind {
|
||||
ExistingItem((ResourceId, ItemKind)),
|
||||
Predefined(PredefinedMenuItemPayload),
|
||||
Check(CheckMenuItemPayload),
|
||||
Submenu(SubmenuPayload),
|
||||
Icon(IconMenuItemPayload),
|
||||
MenuItem(MenuItemPayload),
|
||||
}
|
||||
|
||||
impl MenuItemPayloadKind {
|
||||
pub fn with_item<T, R: Runtime, F: FnOnce(&dyn IsMenuItem<R>) -> crate::Result<T>>(
|
||||
self,
|
||||
window: &Window<R>,
|
||||
resources_table: &MutexGuard<'_, ResourceTable>,
|
||||
f: F,
|
||||
) -> crate::Result<T> {
|
||||
match self {
|
||||
Self::ExistingItem((rid, kind)) => {
|
||||
do_menu_item!(resources_table, rid, kind, |i| f(&*i))
|
||||
}
|
||||
Self::Submenu(i) => f(&i.create_item(window, resources_table)?),
|
||||
Self::Predefined(i) => f(&i.create_item(window)),
|
||||
Self::Check(i) => f(&i.create_item(window)),
|
||||
Self::Icon(i) => f(&i.create_item(window)),
|
||||
Self::MenuItem(i) => f(&i.create_item(window)),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Default)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct NewOptions {
|
||||
id: Option<MenuId>,
|
||||
text: Option<String>,
|
||||
enabled: Option<bool>,
|
||||
checked: Option<bool>,
|
||||
accelerator: Option<String>,
|
||||
#[serde(rename = "item")]
|
||||
predefined_item: Option<Predefined>,
|
||||
icon: Option<Icon>,
|
||||
items: Option<Vec<MenuItemPayloadKind>>,
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn new<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
window: Window<R>,
|
||||
kind: ItemKind,
|
||||
options: Option<NewOptions>,
|
||||
channels: State<'_, MenuChannels>,
|
||||
handler: Channel,
|
||||
) -> crate::Result<(ResourceId, MenuId)> {
|
||||
let options = options.unwrap_or_default();
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
|
||||
let (rid, id) = match kind {
|
||||
ItemKind::Menu => {
|
||||
let mut builder = MenuBuilder::new(&app);
|
||||
if let Some(id) = options.id {
|
||||
builder = builder.id(id);
|
||||
}
|
||||
if let Some(items) = options.items {
|
||||
for item in items {
|
||||
builder = item.with_item(&window, &resources_table, |i| Ok(builder.item(i)))?;
|
||||
}
|
||||
}
|
||||
let menu = builder.build()?;
|
||||
let id = menu.id().clone();
|
||||
let rid = resources_table.add(menu);
|
||||
|
||||
(rid, id)
|
||||
}
|
||||
|
||||
ItemKind::Submenu => {
|
||||
let submenu = SubmenuPayload {
|
||||
id: options.id,
|
||||
text: options.text.unwrap_or_default(),
|
||||
enabled: options.enabled,
|
||||
items: options.items.unwrap_or_default(),
|
||||
}
|
||||
.create_item(&window, &resources_table)?;
|
||||
let id = submenu.id().clone();
|
||||
let rid = resources_table.add(submenu);
|
||||
|
||||
(rid, id)
|
||||
}
|
||||
|
||||
ItemKind::MenuItem => {
|
||||
let item = MenuItemPayload {
|
||||
// handler managed in this function instead
|
||||
handler: None,
|
||||
id: options.id,
|
||||
text: options.text.unwrap_or_default(),
|
||||
enabled: options.enabled,
|
||||
accelerator: options.accelerator,
|
||||
}
|
||||
.create_item(&window);
|
||||
let id = item.id().clone();
|
||||
let rid = resources_table.add(item);
|
||||
(rid, id)
|
||||
}
|
||||
|
||||
ItemKind::Predefined => {
|
||||
let item = PredefinedMenuItemPayload {
|
||||
item: options.predefined_item.unwrap(),
|
||||
text: options.text,
|
||||
}
|
||||
.create_item(&window);
|
||||
let id = item.id().clone();
|
||||
let rid = resources_table.add(item);
|
||||
(rid, id)
|
||||
}
|
||||
|
||||
ItemKind::Check => {
|
||||
let item = CheckMenuItemPayload {
|
||||
// handler managed in this function instead
|
||||
handler: None,
|
||||
id: options.id,
|
||||
text: options.text.unwrap_or_default(),
|
||||
checked: options.checked.unwrap_or_default(),
|
||||
enabled: options.enabled,
|
||||
accelerator: options.accelerator,
|
||||
}
|
||||
.create_item(&window);
|
||||
let id = item.id().clone();
|
||||
let rid = resources_table.add(item);
|
||||
(rid, id)
|
||||
}
|
||||
|
||||
ItemKind::Icon => {
|
||||
let item = IconMenuItemPayload {
|
||||
// handler managed in this function instead
|
||||
handler: None,
|
||||
id: options.id,
|
||||
text: options.text.unwrap_or_default(),
|
||||
icon: options.icon.unwrap_or(Icon::Native(NativeIcon::User)),
|
||||
enabled: options.enabled,
|
||||
accelerator: options.accelerator,
|
||||
}
|
||||
.create_item(&window);
|
||||
let id = item.id().clone();
|
||||
let rid = resources_table.add(item);
|
||||
(rid, id)
|
||||
}
|
||||
};
|
||||
|
||||
channels.0.lock().unwrap().insert(id.clone(), handler);
|
||||
|
||||
Ok((rid, id))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn append<R: Runtime>(
|
||||
window: Window<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
items: Vec<MenuItemPayloadKind>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = window.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| menu.append(i))?;
|
||||
}
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| submenu.append(i))?;
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn prepend<R: Runtime>(
|
||||
window: Window<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
items: Vec<MenuItemPayloadKind>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = window.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| menu.prepend(i))?;
|
||||
}
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| submenu.prepend(i))?;
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn insert<R: Runtime>(
|
||||
window: Window<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
items: Vec<MenuItemPayloadKind>,
|
||||
mut position: usize,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = window.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| menu.insert(i, position))?;
|
||||
position += 1
|
||||
}
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
for item in items {
|
||||
item.with_item(&window, &resources_table, |i| submenu.insert(i, position))?;
|
||||
position += 1
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn remove<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
menu_rid: ResourceId,
|
||||
menu_kind: ItemKind,
|
||||
item: (ResourceId, ItemKind),
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let (rid, kind) = item;
|
||||
match menu_kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(menu_rid)?;
|
||||
do_menu_item!(resources_table, rid, kind, |i| menu.remove(&*i))?;
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(menu_rid)?;
|
||||
do_menu_item!(resources_table, rid, kind, |i| submenu.remove(&*i))?;
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
macro_rules! make_item_resource {
|
||||
($resources_table:ident, $item:ident) => {{
|
||||
let id = $item.id().clone();
|
||||
let (rid, kind) = match $item {
|
||||
MenuItemKind::MenuItem(i) => ($resources_table.add(i), ItemKind::MenuItem),
|
||||
MenuItemKind::Submenu(i) => ($resources_table.add(i), ItemKind::Submenu),
|
||||
MenuItemKind::Predefined(i) => ($resources_table.add(i), ItemKind::Predefined),
|
||||
MenuItemKind::Check(i) => ($resources_table.add(i), ItemKind::Check),
|
||||
MenuItemKind::Icon(i) => ($resources_table.add(i), ItemKind::Icon),
|
||||
};
|
||||
(rid, id, kind)
|
||||
}};
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn remove_at<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
position: usize,
|
||||
) -> crate::Result<Option<(ResourceId, MenuId, ItemKind)>> {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
if let Some(item) = menu.remove_at(position)? {
|
||||
return Ok(Some(make_item_resource!(resources_table, item)));
|
||||
}
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
if let Some(item) = submenu.remove_at(position)? {
|
||||
return Ok(Some(make_item_resource!(resources_table, item)));
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn items<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
) -> crate::Result<Vec<(ResourceId, MenuId, ItemKind)>> {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
let items = match kind {
|
||||
ItemKind::Menu => resources_table.get::<Menu<R>>(rid)?.items()?,
|
||||
ItemKind::Submenu => resources_table.get::<Submenu<R>>(rid)?.items()?,
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(
|
||||
items
|
||||
.into_iter()
|
||||
.map(|i| make_item_resource!(resources_table, i))
|
||||
.collect::<Vec<_>>(),
|
||||
)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn get<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
id: MenuId,
|
||||
) -> crate::Result<Option<(ResourceId, MenuId, ItemKind)>> {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
if let Some(item) = menu.get(&id) {
|
||||
return Ok(Some(make_item_resource!(resources_table, item)));
|
||||
}
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
if let Some(item) = submenu.get(&id) {
|
||||
return Ok(Some(make_item_resource!(resources_table, item)));
|
||||
}
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
async fn popup<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
current_window: Window<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
window: Option<String>,
|
||||
at: Option<Position>,
|
||||
) -> crate::Result<()> {
|
||||
let window = window
|
||||
.map(|w| app.get_window(&w))
|
||||
.unwrap_or(Some(current_window));
|
||||
|
||||
if let Some(window) = window {
|
||||
let resources_table = app.manager.resources_table();
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
menu.popup_inner(window, at)?;
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
submenu.popup_inner(window, at)?;
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn default<R: Runtime>(app: AppHandle<R>) -> crate::Result<(ResourceId, MenuId)> {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
let menu = Menu::default(&app)?;
|
||||
let id = menu.id().clone();
|
||||
let rid = resources_table.add(menu);
|
||||
Ok((rid, id))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
async fn set_as_app_menu<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
) -> crate::Result<Option<(ResourceId, MenuId)>> {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
if let Some(menu) = menu.set_as_app_menu()? {
|
||||
let id = menu.id().clone();
|
||||
let rid = resources_table.add(menu);
|
||||
return Ok(Some((rid, id)));
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
async fn set_as_window_menu<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
current_window: Window<R>,
|
||||
rid: ResourceId,
|
||||
window: Option<String>,
|
||||
) -> crate::Result<Option<(ResourceId, MenuId)>> {
|
||||
let window = window
|
||||
.map(|w| app.get_window(&w))
|
||||
.unwrap_or(Some(current_window));
|
||||
|
||||
if let Some(window) = window {
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
if let Some(menu) = menu.set_as_window_menu(&window)? {
|
||||
let id = menu.id().clone();
|
||||
let rid = resources_table.add(menu);
|
||||
return Ok(Some((rid, id)));
|
||||
}
|
||||
}
|
||||
Ok(None)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn text<R: Runtime>(app: AppHandle<R>, rid: ResourceId, kind: ItemKind) -> crate::Result<String> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
do_menu_item!(resources_table, rid, kind, |i| i.text())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_text<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
text: String,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
do_menu_item!(resources_table, rid, kind, |i| i.set_text(text))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn is_enabled<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
) -> crate::Result<bool> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
do_menu_item!(resources_table, rid, kind, |i| i.is_enabled(), !Predefined)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_enabled<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
enabled: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
do_menu_item!(
|
||||
resources_table,
|
||||
rid,
|
||||
kind,
|
||||
|i| i.set_enabled(enabled),
|
||||
!Predefined
|
||||
)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_accelerator<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
kind: ItemKind,
|
||||
accelerator: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
do_menu_item!(
|
||||
resources_table,
|
||||
rid,
|
||||
kind,
|
||||
|i| i.set_accelerator(accelerator),
|
||||
!Predefined | !Submenu
|
||||
)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_as_windows_menu_for_nsapp<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
) -> crate::Result<()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let resources_table = app.manager.resources_table();
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
submenu.set_as_help_menu_for_nsapp()?;
|
||||
}
|
||||
|
||||
let _ = rid;
|
||||
let _ = app;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_as_help_menu_for_nsapp<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
{
|
||||
let resources_table = app.manager.resources_table();
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
submenu.set_as_help_menu_for_nsapp()?;
|
||||
}
|
||||
|
||||
let _ = rid;
|
||||
let _ = app;
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn is_checked<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<bool> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let check_item = resources_table.get::<CheckMenuItem<R>>(rid)?;
|
||||
check_item.is_checked()
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_checked<R: Runtime>(app: AppHandle<R>, rid: ResourceId, checked: bool) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let check_item = resources_table.get::<CheckMenuItem<R>>(rid)?;
|
||||
check_item.set_checked(checked)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_icon<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
icon: Option<Icon>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let icon_item = resources_table.get::<IconMenuItem<R>>(rid)?;
|
||||
match icon {
|
||||
Some(Icon::Native(icon)) => icon_item.set_native_icon(Some(icon)),
|
||||
Some(Icon::Icon(icon)) => icon_item.set_icon(Some(icon.into())),
|
||||
None => {
|
||||
icon_item.set_icon(None)?;
|
||||
icon_item.set_native_icon(None)?;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct MenuChannels(Mutex<HashMap<MenuId, Channel>>);
|
||||
|
||||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("menu")
|
||||
.setup(|app, _api| {
|
||||
app.manage(MenuChannels(Mutex::default()));
|
||||
Ok(())
|
||||
})
|
||||
.on_event(|app, e| {
|
||||
if let RunEvent::MenuEvent(e) = e {
|
||||
if let Some(channel) = app.state::<MenuChannels>().0.lock().unwrap().get(&e.id) {
|
||||
let _ = channel.send(&e.id);
|
||||
}
|
||||
}
|
||||
})
|
||||
.invoke_handler(crate::generate_handler![
|
||||
new,
|
||||
append,
|
||||
prepend,
|
||||
insert,
|
||||
remove,
|
||||
remove_at,
|
||||
items,
|
||||
get,
|
||||
popup,
|
||||
default,
|
||||
set_as_app_menu,
|
||||
set_as_window_menu,
|
||||
text,
|
||||
set_text,
|
||||
is_enabled,
|
||||
set_enabled,
|
||||
set_accelerator,
|
||||
set_as_windows_menu_for_nsapp,
|
||||
set_as_help_menu_for_nsapp,
|
||||
is_checked,
|
||||
set_checked,
|
||||
set_icon,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::AboutMetadata;
|
||||
use crate::{menu::MenuId, run_main_thread, AppHandle, Manager, Runtime};
|
||||
use crate::{menu::MenuId, resources::Resource, run_main_thread, AppHandle, Manager, Runtime};
|
||||
|
||||
/// A predefined (native) menu item which has a predfined behavior by the OS or by this crate.
|
||||
pub struct PredefinedMenuItem<R: Runtime> {
|
||||
@ -29,7 +29,7 @@ unsafe impl<R: Runtime> Sync for PredefinedMenuItem<R> {}
|
||||
unsafe impl<R: Runtime> Send for PredefinedMenuItem<R> {}
|
||||
|
||||
impl<R: Runtime> super::sealed::IsMenuItemBase for PredefinedMenuItem<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@ -285,3 +285,5 @@ impl<R: Runtime> PredefinedMenuItem<R> {
|
||||
&self.app_handle
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for PredefinedMenuItem<R> {}
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use super::{sealed::ContextMenuBase, IsMenuItem, MenuItemKind};
|
||||
use crate::{run_main_thread, AppHandle, Manager, Position, Runtime, Window};
|
||||
use crate::{resources::Resource, run_main_thread, AppHandle, Manager, Position, Runtime, Window};
|
||||
use muda::{ContextMenu, MenuId};
|
||||
|
||||
/// A type that is a submenu inside a [`Menu`] or [`Submenu`]
|
||||
@ -33,7 +33,7 @@ impl<R: Runtime> Clone for Submenu<R> {
|
||||
}
|
||||
|
||||
impl<R: Runtime> super::sealed::IsMenuItemBase for Submenu<R> {
|
||||
fn inner(&self) -> &dyn muda::IsMenuItem {
|
||||
fn inner_muda(&self) -> &dyn muda::IsMenuItem {
|
||||
&self.inner
|
||||
}
|
||||
}
|
||||
@ -95,11 +95,11 @@ impl<R: Runtime> ContextMenuBase for Submenu<R> {
|
||||
})
|
||||
}
|
||||
|
||||
fn inner(&self) -> &dyn muda::ContextMenu {
|
||||
fn inner_context(&self) -> &dyn muda::ContextMenu {
|
||||
&self.inner
|
||||
}
|
||||
|
||||
fn inner_owned(&self) -> Box<dyn muda::ContextMenu> {
|
||||
fn inner_context_owned(&self) -> Box<dyn muda::ContextMenu> {
|
||||
Box::new(self.clone().inner)
|
||||
}
|
||||
}
|
||||
@ -173,8 +173,10 @@ impl<R: Runtime> Submenu<R> {
|
||||
/// Add a menu item to the end of this submenu.
|
||||
pub fn append(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_.inner.append(kind.inner().inner()))?
|
||||
.map_err(Into::into)
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.append(kind.inner().inner_muda()))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Add menu items to the end of this submenu. It calls [`Submenu::append`] in a loop internally.
|
||||
@ -190,7 +192,7 @@ impl<R: Runtime> Submenu<R> {
|
||||
pub fn prepend(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| {
|
||||
self_.inner.prepend(kind.inner().inner())
|
||||
self_.inner.prepend(kind.inner().inner_muda())
|
||||
})?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -204,7 +206,7 @@ impl<R: Runtime> Submenu<R> {
|
||||
pub fn insert(&self, item: &dyn IsMenuItem<R>, position: usize) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| {
|
||||
self_.inner.insert(kind.inner().inner(), position)
|
||||
self_.inner.insert(kind.inner().inner_muda(), position)
|
||||
})?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
@ -221,47 +223,41 @@ impl<R: Runtime> Submenu<R> {
|
||||
/// Remove a menu item from this submenu.
|
||||
pub fn remove(&self, item: &dyn IsMenuItem<R>) -> crate::Result<()> {
|
||||
let kind = item.kind();
|
||||
run_main_thread!(self, |self_: Self| self_.inner.remove(kind.inner().inner()))?
|
||||
.map_err(Into::into)
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.remove(kind.inner().inner_muda()))?
|
||||
.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Remove the menu item at the specified position from this submenu and returns it.
|
||||
pub fn remove_at(&self, position: usize) -> crate::Result<Option<MenuItemKind<R>>> {
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.remove_at(position)
|
||||
.map(|i| MenuItemKind::from_muda(self_.app_handle.clone(), i)))
|
||||
}
|
||||
|
||||
/// Retrieves the menu item matching the given identifier.
|
||||
pub fn get<'a, I>(&self, id: &'a I) -> Option<MenuItemKind<R>>
|
||||
where
|
||||
I: ?Sized,
|
||||
MenuId: PartialEq<&'a I>,
|
||||
{
|
||||
self
|
||||
.items()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.find(|i| i.id() == &id)
|
||||
}
|
||||
|
||||
/// Returns a list of menu items that has been added to this submenu.
|
||||
pub fn items(&self) -> crate::Result<Vec<MenuItemKind<R>>> {
|
||||
let handle = self.app_handle.clone();
|
||||
run_main_thread!(self, |self_: Self| {
|
||||
self_
|
||||
.inner
|
||||
.items()
|
||||
.into_iter()
|
||||
.map(|i| match i {
|
||||
muda::MenuItemKind::MenuItem(i) => super::MenuItemKind::MenuItem(super::MenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Submenu(i) => super::MenuItemKind::Submenu(super::Submenu {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Predefined(i) => {
|
||||
super::MenuItemKind::Predefined(super::PredefinedMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
})
|
||||
}
|
||||
muda::MenuItemKind::Check(i) => super::MenuItemKind::Check(super::CheckMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
muda::MenuItemKind::Icon(i) => super::MenuItemKind::Icon(super::IconMenuItem {
|
||||
id: i.id().clone(),
|
||||
inner: i,
|
||||
app_handle: handle.clone(),
|
||||
}),
|
||||
})
|
||||
.map(|i| MenuItemKind::from_muda(self_.app_handle.clone(), i))
|
||||
.collect::<Vec<_>>()
|
||||
})
|
||||
}
|
||||
@ -307,22 +303,11 @@ impl<R: Runtime> Submenu<R> {
|
||||
///
|
||||
/// If no menu is set as the Help menu, macOS will automatically use any menu
|
||||
/// which has a title matching the localized word "Help".
|
||||
#[cfg(target_os = "macos")]
|
||||
pub fn set_as_help_menu_for_nsapp(&self) -> crate::Result<()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_as_help_menu_for_nsapp())?;
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Retrieves the menu item matching the given identifier.
|
||||
pub fn get<'a, I>(&self, id: &'a I) -> Option<MenuItemKind<R>>
|
||||
where
|
||||
I: ?Sized,
|
||||
MenuId: PartialEq<&'a I>,
|
||||
{
|
||||
self
|
||||
.items()
|
||||
.unwrap_or_default()
|
||||
.into_iter()
|
||||
.find(|i| i.id() == &id)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for Submenu<R> {}
|
||||
|
||||
143
core/tauri/src/resources/mod.rs
Normal file
143
core/tauri/src/resources/mod.rs
Normal file
@ -0,0 +1,143 @@
|
||||
// Copyright 2018-2023 the Deno authors. All rights reserved. MIT license.
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
// a modified version of https://github.com/denoland/deno/blob/0ae83847f498a2886ae32172e50fd5bdbab2f524/core/resources.rs#L220
|
||||
|
||||
pub(crate) mod plugin;
|
||||
|
||||
use crate::error::Error;
|
||||
use std::{
|
||||
any::{type_name, Any, TypeId},
|
||||
borrow::Cow,
|
||||
collections::BTreeMap,
|
||||
sync::Arc,
|
||||
};
|
||||
|
||||
/// Resources are Rust objects that are stored in [ResourceTable] and managed by tauri.
|
||||
/// They are identified in JS by a numeric ID (the resource ID, or rid).
|
||||
/// Resources can be created in commands. Resources can also be retrieved in commands by
|
||||
/// their rid. Resources are thread-safe.
|
||||
///
|
||||
/// Resources are reference counted in Rust. This means that they can be
|
||||
/// cloned and passed around. When the last reference is dropped, the resource
|
||||
/// is automatically closed. As long as the resource exists in the resource
|
||||
/// table, the reference count is at least 1.
|
||||
pub(crate) trait Resource: Any + 'static + Send + Sync {
|
||||
/// Returns a string representation of the resource. The default implementation
|
||||
/// returns the Rust type name, but specific resource types may override this
|
||||
/// trait method.
|
||||
fn name(&self) -> Cow<'_, str> {
|
||||
type_name::<Self>().into()
|
||||
}
|
||||
|
||||
/// Resources may implement the `close()` trait method if they need to do
|
||||
/// resource specific clean-ups, such as cancelling pending futures, after a
|
||||
/// resource has been removed from the resource table.
|
||||
fn close(self: Arc<Self>) {}
|
||||
}
|
||||
|
||||
impl dyn Resource {
|
||||
#[inline(always)]
|
||||
fn is<T: Resource>(&self) -> bool {
|
||||
self.type_id() == TypeId::of::<T>()
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
pub(crate) fn downcast_arc<'a, T: Resource>(self: &'a Arc<Self>) -> Option<&'a Arc<T>> {
|
||||
if self.is::<T>() {
|
||||
// A resource is stored as `Arc<T>` in a BTreeMap
|
||||
// and is safe to cast to `Arc<T>` because of the runtime
|
||||
// check done in `self.is::<T>()`
|
||||
let ptr = self as *const Arc<_> as *const Arc<T>;
|
||||
Some(unsafe { &*ptr })
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A `ResourceId` is an integer value referencing a resource. It could be
|
||||
/// considered to be the tauri equivalent of a `file descriptor` in POSIX like
|
||||
/// operating systems. Elsewhere in the code base it is commonly abbreviated
|
||||
/// to `rid`.
|
||||
pub(crate) type ResourceId = u32;
|
||||
|
||||
/// Map-like data structure storing Tauri's resources (equivalent to file
|
||||
/// descriptors).
|
||||
///
|
||||
/// Provides basic methods for element access. A resource can be of any type.
|
||||
/// Different types of resources can be stored in the same map, and provided
|
||||
/// with a name for description.
|
||||
///
|
||||
/// Each resource is identified through a _resource ID (rid)_, which acts as
|
||||
/// the key in the map.
|
||||
#[derive(Default)]
|
||||
pub(crate) struct ResourceTable {
|
||||
pub(crate) index: BTreeMap<ResourceId, Arc<dyn Resource>>,
|
||||
pub(crate) next_rid: ResourceId,
|
||||
}
|
||||
|
||||
impl ResourceTable {
|
||||
/// Inserts resource into the resource table, which takes ownership of it.
|
||||
///
|
||||
/// The resource type is erased at runtime and must be statically known
|
||||
/// when retrieving it through `get()`.
|
||||
///
|
||||
/// Returns a unique resource ID, which acts as a key for this resource.
|
||||
pub(crate) fn add<T: Resource>(&mut self, resource: T) -> ResourceId {
|
||||
self.add_arc(Arc::new(resource))
|
||||
}
|
||||
|
||||
/// Inserts a `Arc`-wrapped resource into the resource table.
|
||||
///
|
||||
/// The resource type is erased at runtime and must be statically known
|
||||
/// when retrieving it through `get()`.
|
||||
///
|
||||
/// Returns a unique resource ID, which acts as a key for this resource.
|
||||
pub(crate) fn add_arc<T: Resource>(&mut self, resource: Arc<T>) -> ResourceId {
|
||||
let resource = resource as Arc<dyn Resource>;
|
||||
self.add_arc_dyn(resource)
|
||||
}
|
||||
|
||||
pub(crate) fn add_arc_dyn(&mut self, resource: Arc<dyn Resource>) -> ResourceId {
|
||||
let rid = self.next_rid;
|
||||
let removed_resource = self.index.insert(rid, resource);
|
||||
// TODO: add replace method if needed
|
||||
assert!(removed_resource.is_none());
|
||||
self.next_rid += 1;
|
||||
rid
|
||||
}
|
||||
|
||||
/// Returns a reference counted pointer to the resource of type `T` with the
|
||||
/// given `rid`. If `rid` is not present or has a type different than `T`,
|
||||
/// this function returns [`Error::BadResourceId`].
|
||||
pub(crate) fn get<T: Resource>(&self, rid: ResourceId) -> Result<Arc<T>, Error> {
|
||||
self
|
||||
.index
|
||||
.get(&rid)
|
||||
.and_then(|rc| rc.downcast_arc::<T>())
|
||||
.map(Clone::clone)
|
||||
.ok_or_else(|| Error::BadResourceId(rid))
|
||||
}
|
||||
|
||||
/// Removes the resource with the given `rid` from the resource table. If the
|
||||
/// only reference to this resource existed in the resource table, this will
|
||||
/// cause the resource to be dropped. However, since resources are reference
|
||||
/// counted, therefore pending ops are not automatically cancelled. A resource
|
||||
/// may implement the `close()` method to perform clean-ups such as canceling
|
||||
/// ops.
|
||||
pub(crate) fn close(&mut self, rid: ResourceId) -> Result<(), Error> {
|
||||
self
|
||||
.index
|
||||
.remove(&rid)
|
||||
.ok_or_else(|| Error::BadResourceId(rid))
|
||||
.map(|resource| resource.close())
|
||||
}
|
||||
|
||||
/// Removes and frees all resources stored.
|
||||
pub(crate) fn clear(&mut self) {
|
||||
self.index.clear()
|
||||
}
|
||||
}
|
||||
22
core/tauri/src/resources/plugin.rs
Normal file
22
core/tauri/src/resources/plugin.rs
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use crate::{
|
||||
command,
|
||||
plugin::{Builder, TauriPlugin},
|
||||
AppHandle, Runtime,
|
||||
};
|
||||
|
||||
use super::ResourceId;
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn close<R: Runtime>(app: AppHandle<R>, rid: ResourceId) -> crate::Result<()> {
|
||||
app.manager.resources_table().close(rid)
|
||||
}
|
||||
|
||||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("resources")
|
||||
.invoke_handler(crate::generate_handler![close])
|
||||
.build()
|
||||
}
|
||||
@ -4,19 +4,21 @@
|
||||
|
||||
#![cfg(all(desktop, feature = "tray-icon"))]
|
||||
|
||||
//! Tray icon types and utility functions
|
||||
//! Tray icon types and utilities.
|
||||
|
||||
pub(crate) mod plugin;
|
||||
|
||||
use crate::app::{GlobalMenuEventListener, GlobalTrayIconEventListener};
|
||||
use crate::menu::ContextMenu;
|
||||
use crate::menu::MenuEvent;
|
||||
use crate::resources::Resource;
|
||||
use crate::{run_main_thread, AppHandle, Icon, Manager, Runtime};
|
||||
use serde::Serialize;
|
||||
use std::path::Path;
|
||||
pub use tray_icon::TrayIconId;
|
||||
|
||||
// TODO(muda-migration): figure out js events
|
||||
|
||||
/// Describes a rectangle including position (x - y axis) and size.
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default)]
|
||||
#[derive(Debug, PartialEq, Clone, Copy, Default, Serialize)]
|
||||
pub struct Rectangle {
|
||||
/// The x-coordinate of the upper-left corner of the rectangle.
|
||||
pub left: f64,
|
||||
@ -29,7 +31,7 @@ pub struct Rectangle {
|
||||
}
|
||||
|
||||
/// Describes the click type that triggered this tray icon event.
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug)]
|
||||
#[derive(Clone, Copy, PartialEq, Eq, Debug, Serialize)]
|
||||
pub enum ClickType {
|
||||
/// Left mouse click.
|
||||
Left,
|
||||
@ -51,7 +53,8 @@ impl Default for ClickType {
|
||||
///
|
||||
/// - **Linux**: Unsupported. The event is not emmited even though the icon is shown,
|
||||
/// the icon will still show a context menu on right click.
|
||||
#[derive(Debug, Clone, Default)]
|
||||
#[derive(Debug, Clone, Default, Serialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
pub struct TrayIconEvent {
|
||||
/// Id of the tray icon which triggered this event.
|
||||
pub id: TrayIconId,
|
||||
@ -146,7 +149,7 @@ impl<R: Runtime> TrayIconBuilder<R> {
|
||||
///
|
||||
/// - **Linux**: once a menu is set, it cannot be removed or replaced but you can change its content.
|
||||
pub fn menu<M: ContextMenu>(mut self, menu: &M) -> Self {
|
||||
self.inner = self.inner.with_menu(menu.inner_owned());
|
||||
self.inner = self.inner.with_menu(menu.inner_context_owned());
|
||||
self
|
||||
}
|
||||
|
||||
@ -360,13 +363,13 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
&self.id
|
||||
}
|
||||
|
||||
/// Set new tray icon. If `None` is provided, it will remove the icon.
|
||||
/// Sets a new tray icon. If `None` is provided, it will remove the icon.
|
||||
pub fn set_icon(&self, icon: Option<Icon>) -> crate::Result<()> {
|
||||
let icon = icon.and_then(|i| i.try_into().ok());
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_icon(icon))?.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Set new tray menu.
|
||||
/// Sets a new tray menu.
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
@ -374,7 +377,7 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
pub fn set_menu<M: ContextMenu + 'static>(&self, menu: Option<M>) -> crate::Result<()> {
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
.inner
|
||||
.set_menu(menu.map(|m| m.inner_owned())))
|
||||
.set_menu(menu.map(|m| m.inner_context_owned())))
|
||||
}
|
||||
|
||||
/// Sets the tooltip for this tray icon.
|
||||
@ -387,7 +390,7 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_tooltip(s))?.map_err(Into::into)
|
||||
}
|
||||
|
||||
/// Sets the tooltip for this tray icon.
|
||||
/// Sets the title for this tray icon.
|
||||
///
|
||||
/// ## Platform-specific:
|
||||
///
|
||||
@ -402,7 +405,7 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_title(s))
|
||||
}
|
||||
|
||||
/// Show or hide this tray icon
|
||||
/// Show or hide this tray icon.
|
||||
pub fn set_visible(&self, visible: bool) -> crate::Result<()> {
|
||||
run_main_thread!(self, |self_: Self| self_.inner.set_visible(visible))?.map_err(Into::into)
|
||||
}
|
||||
@ -419,7 +422,7 @@ impl<R: Runtime> TrayIcon<R> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
/// Set the current icon as a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc). **macOS only**.
|
||||
/// Sets the current icon as a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc). **macOS only**.
|
||||
pub fn set_icon_as_template(&self, #[allow(unused)] is_template: bool) -> crate::Result<()> {
|
||||
#[cfg(target_os = "macos")]
|
||||
run_main_thread!(self, |self_: Self| self_
|
||||
@ -446,3 +449,9 @@ impl TryFrom<Icon> for tray_icon::Icon {
|
||||
tray_icon::Icon::from_rgba(value.rgba, value.width, value.height).map_err(Into::into)
|
||||
}
|
||||
}
|
||||
|
||||
impl<R: Runtime> Resource for TrayIcon<R> {
|
||||
fn close(self: std::sync::Arc<Self>) {
|
||||
self.app_handle.remove_tray_by_id(&self.id);
|
||||
}
|
||||
}
|
||||
204
core/tauri/src/tray/plugin.rs
Normal file
204
core/tauri/src/tray/plugin.rs
Normal file
@ -0,0 +1,204 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
use std::path::PathBuf;
|
||||
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
command,
|
||||
ipc::Channel,
|
||||
menu::{plugin::ItemKind, Menu, Submenu},
|
||||
plugin::{Builder, TauriPlugin},
|
||||
resources::ResourceId,
|
||||
tray::TrayIconBuilder,
|
||||
AppHandle, IconDto, Runtime,
|
||||
};
|
||||
|
||||
use super::TrayIcon;
|
||||
|
||||
#[derive(Deserialize)]
|
||||
#[serde(rename_all = "camelCase")]
|
||||
struct TrayIconOptions {
|
||||
id: Option<String>,
|
||||
menu: Option<(ResourceId, ItemKind)>,
|
||||
icon: Option<IconDto>,
|
||||
tooltip: Option<String>,
|
||||
title: Option<String>,
|
||||
temp_dir_path: Option<PathBuf>,
|
||||
icon_as_template: Option<bool>,
|
||||
menu_on_left_click: Option<bool>,
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn new<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
options: TrayIconOptions,
|
||||
handler: Channel,
|
||||
) -> crate::Result<(ResourceId, String)> {
|
||||
let mut builder = if let Some(id) = options.id {
|
||||
TrayIconBuilder::<R>::with_id(id)
|
||||
} else {
|
||||
TrayIconBuilder::<R>::new()
|
||||
};
|
||||
|
||||
builder = builder.on_tray_icon_event(move |_tray, e| {
|
||||
let _ = handler.send(e);
|
||||
});
|
||||
|
||||
let mut resources_table = app.manager.resources_table();
|
||||
|
||||
if let Some((rid, kind)) = options.menu {
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
builder = builder.menu(&*menu);
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
builder = builder.menu(&*submenu);
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
}
|
||||
if let Some(icon) = options.icon {
|
||||
builder = builder.icon(icon.into());
|
||||
}
|
||||
if let Some(tooltip) = options.tooltip {
|
||||
builder = builder.tooltip(tooltip);
|
||||
}
|
||||
if let Some(title) = options.title {
|
||||
builder = builder.title(title);
|
||||
}
|
||||
if let Some(temp_dir_path) = options.temp_dir_path {
|
||||
builder = builder.temp_dir_path(temp_dir_path);
|
||||
}
|
||||
if let Some(icon_as_template) = options.icon_as_template {
|
||||
builder = builder.icon_as_template(icon_as_template);
|
||||
}
|
||||
if let Some(menu_on_left_click) = options.menu_on_left_click {
|
||||
builder = builder.menu_on_left_click(menu_on_left_click);
|
||||
}
|
||||
|
||||
let tray = builder.build(&app)?;
|
||||
let id = tray.id().as_ref().to_string();
|
||||
let rid = resources_table.add(tray);
|
||||
|
||||
Ok((rid, id))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_icon<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
icon: Option<IconDto>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_icon(icon.map(Into::into))
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_menu<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
menu: Option<(ResourceId, ItemKind)>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
if let Some((rid, kind)) = menu {
|
||||
match kind {
|
||||
ItemKind::Menu => {
|
||||
let menu = resources_table.get::<Menu<R>>(rid)?;
|
||||
tray.set_menu(Some((*menu).clone()))?;
|
||||
}
|
||||
ItemKind::Submenu => {
|
||||
let submenu = resources_table.get::<Submenu<R>>(rid)?;
|
||||
tray.set_menu(Some((*submenu).clone()))?;
|
||||
}
|
||||
_ => return Err(anyhow::anyhow!("unexpected menu item kind").into()),
|
||||
};
|
||||
} else {
|
||||
tray.set_menu(None::<Menu<R>>)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_tooltip<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
tooltip: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_tooltip(tooltip)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_title<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
title: Option<String>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_title(title)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_visible<R: Runtime>(app: AppHandle<R>, rid: ResourceId, visible: bool) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_visible(visible)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_temp_dir_path<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
path: Option<PathBuf>,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_temp_dir_path(path)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_icon_as_template<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
as_template: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_icon_as_template(as_template)
|
||||
}
|
||||
|
||||
#[command(root = "crate")]
|
||||
fn set_show_menu_on_left_click<R: Runtime>(
|
||||
app: AppHandle<R>,
|
||||
rid: ResourceId,
|
||||
on_left: bool,
|
||||
) -> crate::Result<()> {
|
||||
let resources_table = app.manager.resources_table();
|
||||
let tray = resources_table.get::<TrayIcon<R>>(rid)?;
|
||||
tray.set_show_menu_on_left_click(on_left)
|
||||
}
|
||||
|
||||
pub(crate) fn init<R: Runtime>() -> TauriPlugin<R> {
|
||||
Builder::new("tray")
|
||||
.invoke_handler(crate::generate_handler![
|
||||
new,
|
||||
set_icon,
|
||||
set_menu,
|
||||
set_tooltip,
|
||||
set_title,
|
||||
set_visible,
|
||||
set_temp_dir_path,
|
||||
set_icon_as_template,
|
||||
set_show_menu_on_left_click,
|
||||
])
|
||||
.build()
|
||||
}
|
||||
@ -1411,12 +1411,13 @@ impl<R: Runtime> Window<R> {
|
||||
/// - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
|
||||
/// window, if you need to remove it, use [`AppHandle::remove_menu`] instead.
|
||||
pub fn remove_menu(&self) -> crate::Result<Option<Menu<R>>> {
|
||||
let current_menu = self.menu_lock().as_ref().map(|m| m.menu.clone());
|
||||
let prev_menu = self.menu_lock().take().map(|m| m.menu);
|
||||
|
||||
// remove from the window
|
||||
#[cfg_attr(target_os = "macos", allow(unused_variables))]
|
||||
if let Some(menu) = current_menu {
|
||||
if let Some(menu) = &prev_menu {
|
||||
let window = self.clone();
|
||||
let menu = menu.clone();
|
||||
self.run_on_main_thread(move || {
|
||||
#[cfg(windows)]
|
||||
if let Ok(hwnd) = window.hwnd() {
|
||||
@ -1435,8 +1436,6 @@ impl<R: Runtime> Window<R> {
|
||||
})?;
|
||||
}
|
||||
|
||||
let prev_menu = self.menu_lock().take().map(|m| m.menu);
|
||||
|
||||
self
|
||||
.manager
|
||||
.remove_menu_from_stash_by_id(prev_menu.as_ref().map(|m| m.id()));
|
||||
@ -2287,12 +2286,13 @@ impl<R: Runtime> Window<R> {
|
||||
handled = true;
|
||||
|
||||
fn load_channels<R: Runtime>(payload: &serde_json::Value, window: &Window<R>) {
|
||||
use std::str::FromStr;
|
||||
|
||||
if let serde_json::Value::Object(map) = payload {
|
||||
for v in map.values() {
|
||||
if let serde_json::Value::String(s) = v {
|
||||
if s.starts_with(crate::ipc::channel::IPC_PAYLOAD_PREFIX) {
|
||||
crate::ipc::Channel::load_from_ipc(window.clone(), s);
|
||||
}
|
||||
crate::ipc::JavaScriptChannelId::from_str(s)
|
||||
.map(|id| id.channel_on(window.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
# API example
|
||||
|
||||
This example demonstrates Tauri's API capabilities using the `@tauri-apps/api` package. It's used as the main validation app, serving as the testbed of our development process.
|
||||
In the future, this app will be used on Tauri's integration tests.
|
||||
|
||||
@ -7,19 +8,22 @@ In the future, this app will be used on Tauri's integration tests.
|
||||
## Running the example
|
||||
|
||||
- Compile Tauri
|
||||
go to root of the Tauri repo and run:
|
||||
Linux / Mac:
|
||||
go to root of the Tauri repo and run:
|
||||
Linux / Mac:
|
||||
|
||||
```
|
||||
# choose to install node cli (1)
|
||||
bash .scripts/setup.sh
|
||||
```
|
||||
|
||||
Windows:
|
||||
|
||||
```
|
||||
./.scripts/setup.ps1
|
||||
```
|
||||
|
||||
- Install dependencies (Run inside of this folder `examples/api/`)
|
||||
|
||||
```bash
|
||||
# with yarn
|
||||
$ yarn
|
||||
@ -28,6 +32,7 @@ $ npm install
|
||||
```
|
||||
|
||||
- Run the app in development mode (Run inside of this folder `examples/api/`)
|
||||
|
||||
```bash
|
||||
# with yarn
|
||||
$ yarn tauri dev
|
||||
@ -36,6 +41,7 @@ $ npm run tauri dev
|
||||
```
|
||||
|
||||
- Build an run the release app (Run inside of this folder `examples/api/`)
|
||||
|
||||
```bash
|
||||
$ yarn tauri build
|
||||
$ ./src-tauri/target/release/app
|
||||
|
||||
2
examples/api/dist/assets/index.css
vendored
2
examples/api/dist/assets/index.css
vendored
File diff suppressed because one or more lines are too long
82
examples/api/dist/assets/index.js
vendored
82
examples/api/dist/assets/index.js
vendored
File diff suppressed because one or more lines are too long
2
examples/api/dist/index.html
vendored
2
examples/api/dist/index.html
vendored
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + Vite App</title>
|
||||
<title>API Example App</title>
|
||||
<script type="module" crossorigin src="/assets/index.js"></script>
|
||||
<link rel="stylesheet" href="/assets/index.css">
|
||||
</head>
|
||||
|
||||
@ -3,7 +3,7 @@
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>Svelte + Vite App</title>
|
||||
<title>API Example App</title>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
{
|
||||
"name": "svelte-app",
|
||||
"name": "api",
|
||||
"private": true,
|
||||
"version": "1.0.0",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite --clearScreen false",
|
||||
"dev": "vite",
|
||||
"build": "vite build",
|
||||
"serve": "vite preview",
|
||||
"preview": "vite preview",
|
||||
"tauri": "node ../../tooling/cli/node/tauri.js"
|
||||
},
|
||||
"dependencies": {
|
||||
|
||||
2
examples/api/src-tauri/Cargo.lock
generated
2
examples/api/src-tauri/Cargo.lock
generated
@ -2091,6 +2091,7 @@ dependencies = [
|
||||
"objc",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
@ -3884,6 +3885,7 @@ dependencies = [
|
||||
"objc",
|
||||
"once_cell",
|
||||
"png",
|
||||
"serde",
|
||||
"thiserror",
|
||||
"windows-sys 0.48.0",
|
||||
]
|
||||
|
||||
@ -2,11 +2,6 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
#![cfg_attr(
|
||||
all(not(debug_assertions), target_os = "windows"),
|
||||
windows_subsystem = "windows"
|
||||
)]
|
||||
|
||||
mod cmd;
|
||||
#[cfg(desktop)]
|
||||
mod tray;
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
"$schema": "../../../core/tauri-config-schema/schema.json",
|
||||
"build": {
|
||||
"distDir": "../dist",
|
||||
"devPath": "http://localhost:5173",
|
||||
"devPath": "http://localhost:1420",
|
||||
"beforeDevCommand": "yarn dev",
|
||||
"beforeBuildCommand": "yarn build",
|
||||
"withGlobalTauri": true
|
||||
@ -26,11 +26,7 @@
|
||||
"name": "theme",
|
||||
"takesValue": true,
|
||||
"description": "App theme",
|
||||
"possibleValues": [
|
||||
"light",
|
||||
"dark",
|
||||
"system"
|
||||
]
|
||||
"possibleValues": ["light", "dark", "system"]
|
||||
},
|
||||
{
|
||||
"short": "v",
|
||||
@ -93,9 +89,7 @@
|
||||
"csp": {
|
||||
"default-src": "'self' customprotocol: asset:",
|
||||
"connect-src": "ipc: http://ipc.localhost",
|
||||
"font-src": [
|
||||
"https://fonts.gstatic.com"
|
||||
],
|
||||
"font-src": ["https://fonts.gstatic.com"],
|
||||
"img-src": "'self' asset: http://asset.localhost blob: data:",
|
||||
"style-src": "'unsafe-inline' 'self' https://fonts.googleapis.com"
|
||||
},
|
||||
@ -103,15 +97,10 @@
|
||||
"assetProtocol": {
|
||||
"enable": true,
|
||||
"scope": {
|
||||
"allow": [
|
||||
"$APPDATA/db/**",
|
||||
"$RESOURCE/**"
|
||||
],
|
||||
"deny": [
|
||||
"$APPDATA/db/*.stronghold"
|
||||
]
|
||||
"allow": ["$APPDATA/db/**", "$RESOURCE/**"],
|
||||
"deny": ["$APPDATA/db/*.stronghold"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,8 @@
|
||||
import Window from './views/Window.svelte'
|
||||
import WebRTC from './views/WebRTC.svelte'
|
||||
import App from './views/App.svelte'
|
||||
|
||||
import Menu from './views/Menu.svelte'
|
||||
import Tray from './views/Tray.svelte'
|
||||
|
||||
document.addEventListener('keydown', (event) => {
|
||||
if (event.ctrlKey && event.key === 'b') {
|
||||
@ -40,6 +41,16 @@
|
||||
component: Window,
|
||||
icon: 'i-codicon-window'
|
||||
},
|
||||
{
|
||||
label: 'Menu',
|
||||
component: Menu,
|
||||
icon: 'i-ph-list'
|
||||
},
|
||||
{
|
||||
label: 'Tray',
|
||||
component: Tray,
|
||||
icon: 'i-ph-tray'
|
||||
},
|
||||
{
|
||||
label: 'WebRTC',
|
||||
component: WebRTC,
|
||||
@ -299,16 +310,21 @@
|
||||
class="select-none h-15rem grid grid-rows-[2px_2rem_1fr] gap-1 overflow-hidden"
|
||||
>
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
on:mousedown={startResizingConsole}
|
||||
class="bg-black/20 h-2px cursor-ns-resize"
|
||||
/>
|
||||
<div class="flex justify-between items-center px-2">
|
||||
<p class="font-semibold">Console</p>
|
||||
<div
|
||||
role="button"
|
||||
tabindex="0"
|
||||
class="cursor-pointer h-85% rd-1 p-1 flex justify-center items-center
|
||||
hover:bg-hoverOverlay dark:hover:bg-darkHoverOverlay
|
||||
active:bg-hoverOverlay/25 dark:active:bg-darkHoverOverlay/25
|
||||
"
|
||||
on:keypress={(e) => e.key === "Enter"? clear() : {} }
|
||||
on:click={clear}
|
||||
>
|
||||
<div class="i-codicon-clear-all" />
|
||||
|
||||
51
examples/api/src/components/MenuBuilder.svelte
Normal file
51
examples/api/src/components/MenuBuilder.svelte
Normal file
@ -0,0 +1,51 @@
|
||||
<script>
|
||||
import { CheckMenuItem } from '@tauri-apps/api/menu'
|
||||
import MenuItemBuilder from './MenuItemBuilder.svelte'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
export let items = []
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
function addItem(event) {
|
||||
items = [
|
||||
...items,
|
||||
{ item: event.detail.item, options: event.detail.options }
|
||||
]
|
||||
}
|
||||
|
||||
function onItemClick(event) {
|
||||
dispatch('itemClick', event.detail)
|
||||
}
|
||||
|
||||
function itemIcon(item) {
|
||||
if (item.options.icon) {
|
||||
return 'i-ph-images-square'
|
||||
}
|
||||
if (item.item instanceof CheckMenuItem) {
|
||||
return item.options.checked ? 'i-ph-check-duotone' : 'i-ph-square-duotone'
|
||||
}
|
||||
if (item.options.item) {
|
||||
return 'i-ph-globe-stand'
|
||||
}
|
||||
return 'i-ph-chat-teardrop-text'
|
||||
}
|
||||
|
||||
function itemToString(item) {
|
||||
// icon || check|normal || predefined
|
||||
return item.options.icon || item.options.text || item.options.item
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col children:grow gap-2">
|
||||
<MenuItemBuilder on:new={addItem} on:itemClick={onItemClick} />
|
||||
|
||||
<div>
|
||||
{#each items as item}
|
||||
<div class="flex flex-row gap-1">
|
||||
<div class={itemIcon(item)} />
|
||||
<p>{itemToString(item)}</p>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
</div>
|
||||
158
examples/api/src/components/MenuItemBuilder.svelte
Normal file
158
examples/api/src/components/MenuItemBuilder.svelte
Normal file
@ -0,0 +1,158 @@
|
||||
<script>
|
||||
import {
|
||||
IconMenuItem,
|
||||
CheckMenuItem,
|
||||
PredefinedMenuItem,
|
||||
MenuItem
|
||||
} from '@tauri-apps/api/menu'
|
||||
import { createEventDispatcher } from 'svelte'
|
||||
|
||||
const dispatch = createEventDispatcher()
|
||||
|
||||
let kind = 'Normal'
|
||||
let text = ''
|
||||
let icon = ''
|
||||
let predefinedItem = ''
|
||||
let checked = true
|
||||
|
||||
const itemKinds = ['Normal', 'Icon', 'Check', 'Predefined']
|
||||
const predefinedOptions = [
|
||||
'Separator',
|
||||
'Copy',
|
||||
'Cut',
|
||||
'Paste',
|
||||
'SelectAll',
|
||||
'Undo',
|
||||
'Redo',
|
||||
'Minimize',
|
||||
'Maximize',
|
||||
'Fullscreen',
|
||||
'Hide',
|
||||
'HideOthers',
|
||||
'ShowAll',
|
||||
'CloseWindow',
|
||||
'Quit',
|
||||
'Services'
|
||||
]
|
||||
|
||||
function onKindChange(event) {
|
||||
kind = event.currentTarget.value
|
||||
}
|
||||
|
||||
function onPredefinedChange(event) {
|
||||
predefinedItem = event.currentTarget.value
|
||||
}
|
||||
|
||||
async function create() {
|
||||
let options = null
|
||||
let item = null
|
||||
|
||||
const t = text
|
||||
|
||||
switch (kind) {
|
||||
case 'Normal':
|
||||
options = {
|
||||
text,
|
||||
action: (id) => dispatch('itemClick', { id, text: t })
|
||||
}
|
||||
item = await MenuItem.new(options)
|
||||
break
|
||||
case 'Icon':
|
||||
options = {
|
||||
text,
|
||||
icon,
|
||||
action: (id) => dispatch('itemClick', { id, text: t })
|
||||
}
|
||||
item = await IconMenuItem.new(options)
|
||||
break
|
||||
case 'Check':
|
||||
options = {
|
||||
text,
|
||||
checked,
|
||||
action: (id) => dispatch('itemClick', { id, text: t })
|
||||
}
|
||||
item = await CheckMenuItem.new(options)
|
||||
break
|
||||
case 'Predefined':
|
||||
options = {
|
||||
item: predefinedItem
|
||||
}
|
||||
item = await PredefinedMenuItem.new(options)
|
||||
break
|
||||
}
|
||||
dispatch('new', { item, options })
|
||||
|
||||
text = ''
|
||||
predefinedItem = ''
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-row gap-2 flex-grow-0">
|
||||
<div class="flex flex-col">
|
||||
{#each itemKinds as itemKind}
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
id="{itemKind}Input"
|
||||
checked={kind === itemKind}
|
||||
on:change={onKindChange}
|
||||
type="radio"
|
||||
name="kind"
|
||||
bind:value={itemKind}
|
||||
/>
|
||||
<label for="{itemKind}Input">{itemKind}</label>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
|
||||
<div class="bg-gray/30 dark:bg-white/5 w-1px flex-shrink-0" />
|
||||
|
||||
<div class="flex flex-col gap-2">
|
||||
{#if kind == 'Normal' || kind == 'Icon' || kind == 'Check'}
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
placeholder="Text"
|
||||
bind:value={text}
|
||||
/>
|
||||
{/if}
|
||||
{#if kind == 'Icon'}
|
||||
<input
|
||||
class="input"
|
||||
type="icon"
|
||||
placeholder="Icon"
|
||||
bind:value={icon}
|
||||
/>
|
||||
{:else if kind == 'Check'}
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
id="checkItemCheckedInput"
|
||||
type="checkbox"
|
||||
bind:checked={checked}
|
||||
/>
|
||||
<label for="checkItemCheckedInput">Enabled</label>
|
||||
</div>
|
||||
{:else if kind == 'Predefined'}
|
||||
<div class="flex gap-2 flex-wrap">
|
||||
{#each predefinedOptions as predefinedOption}
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
id="{predefinedOption}Input"
|
||||
checked={kind === predefinedOption}
|
||||
on:change={onPredefinedChange}
|
||||
type="radio"
|
||||
name="predefinedKind"
|
||||
bind:value={predefinedOption}
|
||||
/>
|
||||
<label for="{predefinedOption}Input">{predefinedOption}</label>
|
||||
</div>
|
||||
{/each}
|
||||
</div>
|
||||
{/if}
|
||||
</div>
|
||||
|
||||
<div class="grow"></div>
|
||||
|
||||
<div class="flex flex-col">
|
||||
<button class="btn" on:click={create}>Create</button>
|
||||
</div>
|
||||
</div>
|
||||
47
examples/api/src/views/Menu.svelte
Normal file
47
examples/api/src/views/Menu.svelte
Normal file
@ -0,0 +1,47 @@
|
||||
<script>
|
||||
import { Menu, Submenu } from '@tauri-apps/api/menu'
|
||||
import MenuBuilder from '../components/MenuBuilder.svelte'
|
||||
|
||||
export let onMessage
|
||||
let items = []
|
||||
let menu = null
|
||||
let submenu = null
|
||||
let menuItemCount = 0
|
||||
|
||||
const macOS = navigator.userAgent.includes('Macintosh')
|
||||
|
||||
async function createSubmenu() {
|
||||
submenu = await Submenu.new({
|
||||
text: 'app',
|
||||
items: items.map((i) => i.item)
|
||||
})
|
||||
}
|
||||
|
||||
async function create() {
|
||||
await createSubmenu()
|
||||
menuItemCount = items.length
|
||||
menu = await Menu.new({
|
||||
items: [submenu]
|
||||
})
|
||||
await (macOS ? menu.setAsAppMenu() : menu.setAsWindowMenu())
|
||||
}
|
||||
|
||||
async function popup() {
|
||||
if (!submenu || menuItemCount !== items.length) {
|
||||
await createSubmenu()
|
||||
}
|
||||
// we can't popup the same menu because it's the app menu (it crashes on macOS)
|
||||
const m = await Menu.new({ items: [submenu] })
|
||||
m.popup()
|
||||
}
|
||||
|
||||
function onItemClick(event) {
|
||||
onMessage(`Item ${event.detail.text} clicked`)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div>
|
||||
<MenuBuilder bind:items on:itemClick={onItemClick} />
|
||||
<button class="btn" on:click={create}>Create menu</button>
|
||||
<button class="btn" on:click={popup}>Popup</button>
|
||||
</div>
|
||||
79
examples/api/src/views/Tray.svelte
Normal file
79
examples/api/src/views/Tray.svelte
Normal file
@ -0,0 +1,79 @@
|
||||
<script>
|
||||
import { TrayIcon } from '@tauri-apps/api/tray'
|
||||
import MenuBuilder from '../components/MenuBuilder.svelte'
|
||||
import { Menu } from '@tauri-apps/api/menu'
|
||||
|
||||
export let onMessage
|
||||
|
||||
let icon = null
|
||||
let tooltip = null
|
||||
let title = null
|
||||
let iconAsTemplate = false
|
||||
let menuOnLeftClick = true
|
||||
let menuItems = []
|
||||
|
||||
function onItemClick(event) {
|
||||
onMessage(`Item ${event.detail.text} clicked`)
|
||||
}
|
||||
|
||||
async function create() {
|
||||
TrayIcon.new({
|
||||
icon,
|
||||
tooltip,
|
||||
title,
|
||||
iconAsTemplate,
|
||||
menuOnLeftClick,
|
||||
menu: await Menu.new({
|
||||
items: menuItems.map((i) => i.item)
|
||||
}),
|
||||
action: (event) => onMessage(event)
|
||||
}).catch(onMessage)
|
||||
}
|
||||
</script>
|
||||
|
||||
<div class="flex flex-col children:grow gap-2">
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="Title"
|
||||
bind:value={title}
|
||||
/>
|
||||
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="Tooltip"
|
||||
bind:value={tooltip}
|
||||
/>
|
||||
|
||||
<label>
|
||||
Menu on left click
|
||||
<input type="checkbox" bind:checked={menuOnLeftClick} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex gap-1">
|
||||
<input
|
||||
class="input grow"
|
||||
type="text"
|
||||
placeholder="Icon path"
|
||||
bind:value={icon}
|
||||
/>
|
||||
|
||||
<label>
|
||||
Icon as template
|
||||
<input type="checkbox" bind:checked={iconAsTemplate} />
|
||||
</label>
|
||||
</div>
|
||||
|
||||
<div class="flex children:grow">
|
||||
<MenuBuilder bind:items={menuItems} on:itemClick={onItemClick} />
|
||||
</div>
|
||||
|
||||
<div class="flex">
|
||||
<button class="btn" on:click={create} title="Creates the tray icon"
|
||||
>Create tray</button
|
||||
>
|
||||
</div>
|
||||
</div>
|
||||
@ -42,7 +42,7 @@
|
||||
})
|
||||
|
||||
onDestroy(() => {
|
||||
window.stream.getTracks().forEach(function (track) {
|
||||
window.stream?.getTracks().forEach(function (track) {
|
||||
track.stop()
|
||||
})
|
||||
})
|
||||
|
||||
@ -10,7 +10,6 @@
|
||||
ProgressBarStatus,
|
||||
Window
|
||||
} from '@tauri-apps/api/window'
|
||||
import { invoke } from '@tauri-apps/api/primitives'
|
||||
|
||||
const appWindow = getCurrent()
|
||||
|
||||
@ -309,9 +308,9 @@
|
||||
{#if windowMap[selectedWindow]}
|
||||
<br />
|
||||
<div class="flex gap-1 items-center">
|
||||
<label> Icon path </label>
|
||||
<label for="windowIconPath"> Icon path </label>
|
||||
<form class="flex gap-1 grow" on:submit|preventDefault={setTitle_}>
|
||||
<input class="input grow" bind:value={windowIconPath} />
|
||||
<input id="windowIconPath" class="input grow" bind:value={windowIconPath} />
|
||||
<button class="btn" type="submit"> Change window icon </button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
6
examples/api/src/vite-env.d.ts
vendored
Normal file
6
examples/api/src/vite-env.d.ts
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
/// <reference types="svelte" />
|
||||
/// <reference types="vite/client" />
|
||||
@ -21,15 +21,20 @@ export default defineConfig({
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
// Vite optons tailored for Tauri development and only applied in `tauri dev` or `tauri build`
|
||||
// prevent vite from obscuring rust errors
|
||||
clearScreen: false,
|
||||
// tauri expects a fixed port, fail if that port is not available
|
||||
server: {
|
||||
host: mobile ? '0.0.0.0' : false,
|
||||
port: 5173,
|
||||
port: 1420,
|
||||
strictPort: true,
|
||||
hmr: mobile
|
||||
? {
|
||||
protocol: 'ws',
|
||||
host: internalIpV4Sync(),
|
||||
port: 5183
|
||||
port: 1421
|
||||
}
|
||||
: undefined,
|
||||
fs: {
|
||||
|
||||
@ -11,18 +11,23 @@
|
||||
"@jridgewell/trace-mapping" "^0.3.9"
|
||||
|
||||
"@antfu/install-pkg@^0.1.0":
|
||||
version "0.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-0.1.0.tgz#8d8c61820cbc32e5c37d82d515485ad3ee9bd052"
|
||||
integrity sha512-VaIJd3d1o7irZfK1U0nvBsHMyjkuyMP3HKYVV53z8DKyulkHKmjhhtccXO51WSPeeSHIeoJEoNOKavYpS7jkZw==
|
||||
version "0.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@antfu/install-pkg/-/install-pkg-0.1.1.tgz#157bb04f0de8100b9e4c01734db1a6c77e98bbb5"
|
||||
integrity sha512-LyB/8+bSfa0DFGC06zpCEfs89/XoWZwws5ygEa5D+Xsm3OfI+aXQ86VgVG7Acyef+rSZ5HE7J8rrxzrQeM3PjQ==
|
||||
dependencies:
|
||||
execa "^5.1.1"
|
||||
find-up "^5.0.0"
|
||||
|
||||
"@antfu/utils@^0.5.0", "@antfu/utils@^0.5.1":
|
||||
"@antfu/utils@^0.5.0":
|
||||
version "0.5.2"
|
||||
resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.5.2.tgz#8c2d931ff927be0ebe740169874a3d4004ab414b"
|
||||
integrity sha512-CQkeV+oJxUazwjlHD0/3ZD08QWKuGQkhnrKo3e6ly5pd48VUpXbb77q0xMU4+vc2CkJnDS02Eq/M9ugyX20XZA==
|
||||
|
||||
"@antfu/utils@^0.7.5":
|
||||
version "0.7.6"
|
||||
resolved "https://registry.yarnpkg.com/@antfu/utils/-/utils-0.7.6.tgz#30a046419b9e1ecd276e53d41ab68fb6c558c04d"
|
||||
integrity sha512-pvFiLP2BeOKA/ZOS6jxx4XhKzdVLHDhGlFEaZ2flWWYf2xOqVniqpk38I04DFRyz+L0ASggl7SkItTc+ZLju4w==
|
||||
|
||||
"@esbuild/android-arm64@0.18.20":
|
||||
version "0.18.20"
|
||||
resolved "https://registry.yarnpkg.com/@esbuild/android-arm64/-/android-arm64-0.18.20.tgz#984b4f9c8d0377443cc2dfcef266d02244593622"
|
||||
@ -139,20 +144,25 @@
|
||||
integrity sha512-JUFJad5lv7jxj926GPgymrWQxxjPYuJNiNjNMzqT+HiuP6Vl3dk5xzG+8sTX96np0ZAluvaMzPsjhHZ5rNuNQQ==
|
||||
|
||||
"@iconify-json/codicon@^1.1.10":
|
||||
version "1.1.10"
|
||||
resolved "https://registry.yarnpkg.com/@iconify-json/codicon/-/codicon-1.1.10.tgz#22fee909be51afebfbcc6cd57209064b5363f202"
|
||||
integrity sha512-xx3nX5k4UeDQnpX9D1T6L1RCEwyLtqu3Lqk9plYK+SoBSQ/kR73bPy9WbYyDVOw2MDw50JCSpZZYlBC718k7Sg==
|
||||
version "1.1.28"
|
||||
resolved "https://registry.yarnpkg.com/@iconify-json/codicon/-/codicon-1.1.28.tgz#b753f619a902e382dad6575fa8075aff8fb1d9e3"
|
||||
integrity sha512-a4rcQ/Rh65QJbifXeb5eAqiXZmgen6oX0jM/BV/64Hh69EREHYVnjndTSdQq+rywCxG6ipiMJBrfBnjX4+20Sg==
|
||||
dependencies:
|
||||
"@iconify/types" "^1.1.0"
|
||||
"@iconify/types" "*"
|
||||
|
||||
"@iconify-json/ph@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@iconify-json/ph/-/ph-1.1.1.tgz#17b3dee91a47055bac93d65c32b9ee33c654d56f"
|
||||
integrity sha512-sIHTY+c1F8x29BM49IqoccJ3T8mvVXPcrE4WOpJ3GsBaip2YqFJRYU60rw64UL6GEI13vWSD7NsZKq8ytTO87g==
|
||||
version "1.1.6"
|
||||
resolved "https://registry.yarnpkg.com/@iconify-json/ph/-/ph-1.1.6.tgz#bbb7a3589199d91d8298bc084c4e893c8821ad8e"
|
||||
integrity sha512-dexzEndlXQX/sbQhnEpA94Pby6JCGV2tZToSGcPPQpbilDGyk5VMd0ymusYoocRAn6+qLpGRvMoz5XFKGqP+VA==
|
||||
dependencies:
|
||||
"@iconify/types" "^1.0.12"
|
||||
"@iconify/types" "*"
|
||||
|
||||
"@iconify/types@^1.0.12", "@iconify/types@^1.1.0":
|
||||
"@iconify/types@*":
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@iconify/types/-/types-2.0.0.tgz#ab0e9ea681d6c8a1214f30cd741fe3a20cc57f57"
|
||||
integrity sha512-+wluvCrRhXrhyOmRDJ3q8mux9JkKy5SJ/v8ol2tu4FVjyYvtEzkc/3pK15ET6RKg4b4w4BmTk1+gsCUhf21Ykg==
|
||||
|
||||
"@iconify/types@^1.1.0":
|
||||
version "1.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@iconify/types/-/types-1.1.0.tgz#dc15fc988b1b3fd558dd140a24ede7e0aac11280"
|
||||
integrity sha512-Jh0llaK2LRXQoYsorIH8maClebsnzTcve+7U3rQUSnC11X4jtPnFuyatqFLvMxZ8MLG8dB4zfHsbPfuvxluONw==
|
||||
@ -256,7 +266,7 @@
|
||||
vitefu "^0.2.4"
|
||||
|
||||
"@tauri-apps/api@../../tooling/api/dist":
|
||||
version "2.0.0-alpha.8"
|
||||
version "2.0.0-alpha.9"
|
||||
|
||||
"@types/estree@*", "@types/estree@^1.0.0", "@types/estree@^1.0.1":
|
||||
version "1.0.2"
|
||||
@ -418,9 +428,9 @@ acorn@^8.10.0, acorn@^8.9.0:
|
||||
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
||||
|
||||
anymatch@~3.1.2:
|
||||
version "3.1.2"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.2.tgz#c0557c096af32f106198f4f4e2a383537e378716"
|
||||
integrity sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==
|
||||
version "3.1.3"
|
||||
resolved "https://registry.yarnpkg.com/anymatch/-/anymatch-3.1.3.tgz#790c58b19ba1720a84205b57c618d5ad8524973e"
|
||||
integrity sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==
|
||||
dependencies:
|
||||
normalize-path "^3.0.0"
|
||||
picomatch "^2.0.4"
|
||||
@ -452,9 +462,9 @@ braces@^3.0.2, braces@~3.0.2:
|
||||
fill-range "^7.0.1"
|
||||
|
||||
cac@^6.7.12:
|
||||
version "6.7.12"
|
||||
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.12.tgz#6fb5ea2ff50bd01490dbda497f4ae75a99415193"
|
||||
integrity sha512-rM7E2ygtMkJqD9c7WnFU6fruFcN3xe4FM5yUmgxhZzIKJk4uHl9U/fhwdajGFQbQuv43FAUo1Fe8gX/oIKDeSA==
|
||||
version "6.7.14"
|
||||
resolved "https://registry.yarnpkg.com/cac/-/cac-6.7.14.tgz#804e1e6f506ee363cb0e3ccbb09cad5dd9870959"
|
||||
integrity sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==
|
||||
|
||||
chokidar@^3.5.3:
|
||||
version "3.5.3"
|
||||
@ -483,9 +493,9 @@ code-red@^1.0.3:
|
||||
periscopic "^3.1.0"
|
||||
|
||||
colorette@^2.0.16:
|
||||
version "2.0.19"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.19.tgz#cdf044f47ad41a0f4b56b3a0d5b4e6e1a2d5a798"
|
||||
integrity sha512-3tlv/dIP7FWvj3BsbHrGLJ6l/oKh1O3TcgBqMn+yyCagOxc23fyzDS6HypQbgxWbkpDnf52p1LuR4eWDQ/K9WQ==
|
||||
version "2.0.20"
|
||||
resolved "https://registry.yarnpkg.com/colorette/-/colorette-2.0.20.tgz#9eb793e6833067f7235902fcd3b09917a000a95a"
|
||||
integrity sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==
|
||||
|
||||
consola@^2.15.3:
|
||||
version "2.15.3"
|
||||
@ -501,15 +511,7 @@ cross-spawn@^7.0.3:
|
||||
shebang-command "^2.0.0"
|
||||
which "^2.0.1"
|
||||
|
||||
css-tree@^2.1.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.1.0.tgz#170e27ccf94e7c5facb183765c25898be843d1d2"
|
||||
integrity sha512-PcysZRzToBbrpoUrZ9qfblRIRf8zbEAkU0AIpQFtgkFK0vSbzOmBCvdSAx2Zg7Xx5wiYJKUKk0NMP7kxevie/A==
|
||||
dependencies:
|
||||
mdn-data "2.0.27"
|
||||
source-map-js "^1.0.1"
|
||||
|
||||
css-tree@^2.3.1:
|
||||
css-tree@^2.1.0, css-tree@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/css-tree/-/css-tree-2.3.1.tgz#10264ce1e5442e8572fc82fbe490644ff54b5c20"
|
||||
integrity sha512-6Fv1DV/TYw//QF5IzQdqsNDjx/wc8TrMBZsqjL9eW01tWb7R7k/mq+/VXfJCl7SoD5emsJop9cOByJZfs8hYIw==
|
||||
@ -536,20 +538,20 @@ default-gateway@^6.0.3:
|
||||
dependencies:
|
||||
execa "^5.0.0"
|
||||
|
||||
defu@^6.0.0:
|
||||
version "6.0.0"
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-6.0.0.tgz#b397a6709a2f3202747a3d9daf9446e41ad0c5fc"
|
||||
integrity sha512-t2MZGLf1V2rV4VBZbWIaXKdX/mUcYW0n2znQZoADBkGGxYL8EWqCuCZBmJPJ/Yy9fofJkyuuSuo5GSwo0XdEgw==
|
||||
defu@^6.1.2:
|
||||
version "6.1.2"
|
||||
resolved "https://registry.yarnpkg.com/defu/-/defu-6.1.2.tgz#1217cba167410a1765ba93893c6dbac9ed9d9e5c"
|
||||
integrity sha512-+uO4+qr7msjNNWKYPHqN/3+Dx3NFkmIzayk2L1MyZQlvgZb/J1A0fo410dpKrN2SnqFjt8n4JL8fDJE0wIgjFQ==
|
||||
|
||||
dequal@^2.0.3:
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/dequal/-/dequal-2.0.3.tgz#2644214f1997d39ed0ee0ece72335490a7ac67be"
|
||||
integrity sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==
|
||||
|
||||
destr@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/destr/-/destr-1.1.1.tgz#910457d10a2f2f247add4ca4fdb4a03adcc49079"
|
||||
integrity sha512-QqkneF8LrYmwATMdnuD2MLI3GHQIcBnG6qFC2q9bSH430VTCDAVjcspPmUaKhPGtAtPAftIUFqY1obQYQuwmbg==
|
||||
destr@^1.2.0:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/destr/-/destr-1.2.2.tgz#7ba9befcafb645a50e76b260449c63927b51e22f"
|
||||
integrity sha512-lrbCJwD9saUQrqUfXvl6qoM+QN3W7tLV5pAOs+OqOmopCCz/JkE05MHedJR1xfk4IAnZuJXPVuN5+7jNA2ZCiA==
|
||||
|
||||
duplexer@^0.1.2:
|
||||
version "0.1.2"
|
||||
@ -612,9 +614,9 @@ execa@^5.0.0, execa@^5.1.1:
|
||||
strip-final-newline "^2.0.0"
|
||||
|
||||
fast-glob@^3.2.11:
|
||||
version "3.2.11"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.2.11.tgz#a1172ad95ceb8a16e20caa5c5e56480e5129c1d9"
|
||||
integrity sha512-xrO3+1bxSo3ZVHAnqzyuewYT6aMFHRAd4Kcs92MAonjwQZLsK9d0SF1IyQ3k5PoirxTW0Oe/RqFgMQ6TcNE5Ew==
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
|
||||
integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
@ -623,9 +625,9 @@ fast-glob@^3.2.11:
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fastq@^1.6.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.13.0.tgz#616760f88a7526bdfc596b7cab8c18938c36b98c"
|
||||
integrity sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==
|
||||
version "1.15.0"
|
||||
resolved "https://registry.yarnpkg.com/fastq/-/fastq-1.15.0.tgz#d04d07c6a2a68fe4599fea8d2e103a937fae6b3a"
|
||||
integrity sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==
|
||||
dependencies:
|
||||
reusify "^1.0.4"
|
||||
|
||||
@ -645,9 +647,9 @@ find-up@^5.0.0:
|
||||
path-exists "^4.0.0"
|
||||
|
||||
fsevents@~2.3.2:
|
||||
version "2.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.2.tgz#8a526f78b8fdf4623b709e0b975c52c24c02fd1a"
|
||||
integrity sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==
|
||||
version "2.3.3"
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
get-stream@^6.0.0:
|
||||
version "6.0.1"
|
||||
@ -689,9 +691,9 @@ ip-regex@^4.0.0:
|
||||
integrity sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==
|
||||
|
||||
ipaddr.js@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.0.1.tgz#eca256a7a877e917aeb368b0a7497ddf42ef81c0"
|
||||
integrity sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ipaddr.js/-/ipaddr.js-2.1.0.tgz#2119bc447ff8c257753b196fc5f1ce08a4cdf39f"
|
||||
integrity sha512-LlbxQ7xKzfBusov6UMi4MFpEg0m+mAm9xyNGEduwXMEDuf4WfzB/RZwMVYEd7IKGvh4IUkEXYxtAVu9T3OelJQ==
|
||||
|
||||
is-binary-path@~2.1.0:
|
||||
version "2.1.0"
|
||||
@ -741,10 +743,15 @@ isexe@^2.0.0:
|
||||
resolved "https://registry.yarnpkg.com/isexe/-/isexe-2.0.0.tgz#e8fbf374dc556ff8947a10dcb0572d633f2cfa10"
|
||||
integrity sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==
|
||||
|
||||
jiti@^1.13.0:
|
||||
version "1.14.0"
|
||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.14.0.tgz#5350fff532a4d891ca4bcd700c47c1f40e6ee326"
|
||||
integrity sha512-4IwstlaKQc9vCTC+qUXLM1hajy2ImiL9KnLvVYiaHOtS/v3wRjhLlGl121AmgDgx/O43uKmxownJghS5XMya2A==
|
||||
jiti@^1.19.1:
|
||||
version "1.19.3"
|
||||
resolved "https://registry.yarnpkg.com/jiti/-/jiti-1.19.3.tgz#ef554f76465b3c2b222dc077834a71f0d4a37569"
|
||||
integrity sha512-5eEbBDQT/jF1xg6l36P+mWGGoH9Spuy0PCdSr2dtWRDGC6ph/w9ZCL4lmESW8f8F7MwT3XKescfP0wnZWAKL9w==
|
||||
|
||||
jsonc-parser@^3.2.0:
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/jsonc-parser/-/jsonc-parser-3.2.0.tgz#31ff3f4c2b9793f89c67212627c51c6394f88e76"
|
||||
integrity sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==
|
||||
|
||||
kleur@^4.1.5:
|
||||
version "4.1.5"
|
||||
@ -752,14 +759,14 @@ kleur@^4.1.5:
|
||||
integrity sha512-o+NO+8WrRiQEE4/7nwRJhN1HWpVmJm511pBHUxPLtp0BUISzlBplORYSmTclCnJvQq2tKu/sgl3xVpkc7ZWuQQ==
|
||||
|
||||
kolorist@^1.5.1:
|
||||
version "1.5.1"
|
||||
resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.5.1.tgz#c3d66dc4fabde4f6b7faa6efda84c00491f9e52b"
|
||||
integrity sha512-lxpCM3HTvquGxKGzHeknB/sUjuVoUElLlfYnXZT73K8geR9jQbroGlSCFBax9/0mpGoD3kzcMLnOlGQPJJNyqQ==
|
||||
version "1.8.0"
|
||||
resolved "https://registry.yarnpkg.com/kolorist/-/kolorist-1.8.0.tgz#edddbbbc7894bc13302cdf740af6374d4a04743c"
|
||||
integrity sha512-Y+60/zizpJ3HRH8DCss+q95yr6145JXZo46OTpFvDZWLfRCE4qChOyk1b26nMaNpfHHgxagk9dXT5OP0Tfe+dQ==
|
||||
|
||||
local-pkg@^0.4.1:
|
||||
version "0.4.1"
|
||||
resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.1.tgz#e7b0d7aa0b9c498a1110a5ac5b00ba66ef38cfff"
|
||||
integrity sha512-lL87ytIGP2FU5PWwNDo0w3WhIo2gopIAxPg9RxDYF7m4rr5ahuZxP22xnJHIvaLTe4Z9P6uKKY2UHiwyB4pcrw==
|
||||
version "0.4.3"
|
||||
resolved "https://registry.yarnpkg.com/local-pkg/-/local-pkg-0.4.3.tgz#0ff361ab3ae7f1c19113d9bb97b98b905dbc4963"
|
||||
integrity sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==
|
||||
|
||||
locate-character@^3.0.0:
|
||||
version "3.0.0"
|
||||
@ -774,9 +781,9 @@ locate-path@^6.0.0:
|
||||
p-locate "^5.0.0"
|
||||
|
||||
magic-string@^0.26.2:
|
||||
version "0.26.2"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.2.tgz#5331700e4158cd6befda738bb6b0c7b93c0d4432"
|
||||
integrity sha512-NzzlXpclt5zAbmo6h6jNc8zl2gNRGHvmsZW4IvZhTC4W7k4OlLP+S5YLussa/r3ixNT66KOQfNORlXHSOy/X4A==
|
||||
version "0.26.7"
|
||||
resolved "https://registry.yarnpkg.com/magic-string/-/magic-string-0.26.7.tgz#caf7daf61b34e9982f8228c4527474dac8981d6f"
|
||||
integrity sha512-hX9XH3ziStPoPhJxLq1syWuZMxbDvGNbVchfrdCtanC7D13888bMFow61x8axrx+GfHLtVeAx2kxL7tTGRl+Ow==
|
||||
dependencies:
|
||||
sourcemap-codec "^1.4.8"
|
||||
|
||||
@ -787,11 +794,6 @@ magic-string@^0.30.0, magic-string@^0.30.3:
|
||||
dependencies:
|
||||
"@jridgewell/sourcemap-codec" "^1.4.15"
|
||||
|
||||
mdn-data@2.0.27:
|
||||
version "2.0.27"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.27.tgz#1710baa7b0db8176d3b3d565ccb7915fc69525ab"
|
||||
integrity sha512-kwqO0I0jtWr25KcfLm9pia8vLZ8qoAKhWZuZMbneJq3jjBD3gl5nZs8l8Tu3ZBlBAHVQtDur9rdDGyvtfVraHQ==
|
||||
|
||||
mdn-data@2.0.30:
|
||||
version "2.0.30"
|
||||
resolved "https://registry.yarnpkg.com/mdn-data/-/mdn-data-2.0.30.tgz#ce4df6f80af6cfbe218ecd5c552ba13c4dfa08cc"
|
||||
@ -820,6 +822,16 @@ mimic-fn@^2.1.0:
|
||||
resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
|
||||
integrity sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==
|
||||
|
||||
mlly@^1.2.0, mlly@^1.4.0:
|
||||
version "1.4.1"
|
||||
resolved "https://registry.yarnpkg.com/mlly/-/mlly-1.4.1.tgz#7ab9cbb040bf8bd8205a0c341ce9acc3ae0c3a74"
|
||||
integrity sha512-SCDs78Q2o09jiZiE2WziwVBEqXQ02XkGdUy45cbJf+BpYRIjArXRJ1Wbowxkb+NaM9DWvS3UC9GiO/6eqvQ/pg==
|
||||
dependencies:
|
||||
acorn "^8.10.0"
|
||||
pathe "^1.1.1"
|
||||
pkg-types "^1.0.3"
|
||||
ufo "^1.3.0"
|
||||
|
||||
mrmime@^1.0.0:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/mrmime/-/mrmime-1.0.1.tgz#5f90c825fad4bdd41dc914eff5d1a8cfdaf24f27"
|
||||
@ -835,10 +847,10 @@ nanoid@^3.3.6:
|
||||
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.6.tgz#443380c856d6e9f9824267d960b4236ad583ea4c"
|
||||
integrity sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==
|
||||
|
||||
node-fetch-native@^0.1.3:
|
||||
version "0.1.4"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-0.1.4.tgz#09b06754f9e298bac23848050da2352125634f89"
|
||||
integrity sha512-10EKpOCQPXwZVFh3U1ptOMWBgKTbsN7Vvo6WVKt5pw4hp8zbv6ZVBZPlXw+5M6Tyi1oc1iD4/sNPd71KYA16tQ==
|
||||
node-fetch-native@^0.1.8:
|
||||
version "0.1.8"
|
||||
resolved "https://registry.yarnpkg.com/node-fetch-native/-/node-fetch-native-0.1.8.tgz#19e2eaf6d86ac14e711ebd2612f40517c3468f2a"
|
||||
integrity sha512-ZNaury9r0NxaT2oL65GvdGDy+5PlSaHTovT6JV5tOW07k1TQmgC0olZETa4C9KZg0+6zBr99ctTYa3Utqj9P/Q==
|
||||
|
||||
normalize-path@^3.0.0, normalize-path@~3.0.0:
|
||||
version "3.0.0"
|
||||
@ -853,14 +865,14 @@ npm-run-path@^4.0.1:
|
||||
path-key "^3.0.0"
|
||||
|
||||
ohmyfetch@^0.4.18:
|
||||
version "0.4.18"
|
||||
resolved "https://registry.yarnpkg.com/ohmyfetch/-/ohmyfetch-0.4.18.tgz#2952e04bd52662d0618d3d2f344db0250c3eeac2"
|
||||
integrity sha512-MslzNrQzBLtZHmiZBI8QMOcMpdNFlK61OJ34nFNFynZ4v+4BonfCQ7VIN4EGXvGGq5zhDzgdJoY3o9S1l2T7KQ==
|
||||
version "0.4.21"
|
||||
resolved "https://registry.yarnpkg.com/ohmyfetch/-/ohmyfetch-0.4.21.tgz#6850db751fc7bbf08153aa8b11ff1ef45fcfd963"
|
||||
integrity sha512-VG7f/JRvqvBOYvL0tHyEIEG7XHWm7OqIfAs6/HqwWwDfjiJ1g0huIpe5sFEmyb+7hpFa1EGNH2aERWR72tlClw==
|
||||
dependencies:
|
||||
destr "^1.1.1"
|
||||
node-fetch-native "^0.1.3"
|
||||
ufo "^0.8.4"
|
||||
undici "^5.2.0"
|
||||
destr "^1.2.0"
|
||||
node-fetch-native "^0.1.8"
|
||||
ufo "^0.8.6"
|
||||
undici "^5.12.0"
|
||||
|
||||
onetime@^5.1.2:
|
||||
version "5.1.2"
|
||||
@ -913,9 +925,14 @@ path-key@^3.0.0, path-key@^3.1.0:
|
||||
integrity sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==
|
||||
|
||||
pathe@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.3.0.tgz#fd95bc16208263fa6dc1c78c07b3907a528de6eb"
|
||||
integrity sha512-3vUjp552BJzCw9vqKsO5sttHkbYqqsZtH0x1PNtItgqx8BXEXzoY1SYRKcL6BTyVh4lGJGLj0tM42elUDMvcYA==
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-0.3.9.tgz#4baff768f37f03e3d9341502865fb93116f65191"
|
||||
integrity sha512-6Y6s0vT112P3jD8dGfuS6r+lpa0qqNrLyHPOwvXMnyNTQaYiwgau2DP3aNDsR13xqtGj7rrPo+jFUATpU6/s+g==
|
||||
|
||||
pathe@^1.1.0, pathe@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/pathe/-/pathe-1.1.1.tgz#1dd31d382b974ba69809adc9a7a347e65d84829a"
|
||||
integrity sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==
|
||||
|
||||
perfect-debounce@^0.1.3:
|
||||
version "0.1.3"
|
||||
@ -936,15 +953,19 @@ picocolors@^1.0.0:
|
||||
resolved "https://registry.yarnpkg.com/picocolors/-/picocolors-1.0.0.tgz#cb5bdc74ff3f51892236eaf79d68bc44564ab81c"
|
||||
integrity sha512-1fygroTLlHu66zi26VoTDv8yRgm0Fccecssto+MhsZ0D/DGW2sm8E8AjW7NU5VVTRt5GxbeZ5qBuJr+HyLYkjQ==
|
||||
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.3.1:
|
||||
picomatch@^2.0.4, picomatch@^2.2.1, picomatch@^2.2.2, picomatch@^2.3.1:
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.3.1.tgz#3ba3833733646d9d3e4995946c1365a67fb07a42"
|
||||
integrity sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==
|
||||
|
||||
picomatch@^2.2.2:
|
||||
version "2.2.2"
|
||||
resolved "https://registry.yarnpkg.com/picomatch/-/picomatch-2.2.2.tgz#21f333e9b6b8eaff02468f5146ea406d345f4dad"
|
||||
integrity sha512-q0M/9eZHzmr0AulXyPwNfZjtwZ/RBZlbN3K3CErVrk50T2ASYI7Bye0EvekFY3IP1Nt2DHu0re+V2ZHIpMkuWg==
|
||||
pkg-types@^1.0.3:
|
||||
version "1.0.3"
|
||||
resolved "https://registry.yarnpkg.com/pkg-types/-/pkg-types-1.0.3.tgz#988b42ab19254c01614d13f4f65a2cfc7880f868"
|
||||
integrity sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==
|
||||
dependencies:
|
||||
jsonc-parser "^3.2.0"
|
||||
mlly "^1.2.0"
|
||||
pathe "^1.1.0"
|
||||
|
||||
postcss@^8.4.27:
|
||||
version "8.4.31"
|
||||
@ -1004,20 +1025,15 @@ signal-exit@^3.0.3:
|
||||
integrity sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==
|
||||
|
||||
sirv@^2.0.2:
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.2.tgz#128b9a628d77568139cff85703ad5497c46a4760"
|
||||
integrity sha512-4Qog6aE29nIjAOKe/wowFTxOdmbEZKb+3tsLljaBRzJwtqto0BChD2zzH0LhgCSXiI+V7X+Y45v14wBZQ1TK3w==
|
||||
version "2.0.3"
|
||||
resolved "https://registry.yarnpkg.com/sirv/-/sirv-2.0.3.tgz#ca5868b87205a74bef62a469ed0296abceccd446"
|
||||
integrity sha512-O9jm9BsID1P+0HOi81VpXPoDxYP374pkOLzACAoyUQ/3OUVndNpsz6wMnY2z+yOxzbllCKZrM+9QrWsv4THnyA==
|
||||
dependencies:
|
||||
"@polka/url" "^1.0.0-next.20"
|
||||
mrmime "^1.0.0"
|
||||
totalist "^3.0.0"
|
||||
|
||||
source-map-js@^1.0.1:
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.1.tgz#a1741c131e3c77d048252adfa24e23b908670caf"
|
||||
integrity sha512-4+TN2b3tqOCd/kaGRJ/sTYA0tR0mdXx26ipdolxcwtJVqEnqNYvlCAt1q3ypy4QMlYus+Zh34RNtYLoq2oQ4IA==
|
||||
|
||||
source-map-js@^1.0.2:
|
||||
source-map-js@^1.0.1, source-map-js@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/source-map-js/-/source-map-js-1.0.2.tgz#adbc361d9c62df380125e7f161f71c826f1e490c"
|
||||
integrity sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==
|
||||
@ -1064,28 +1080,34 @@ to-regex-range@^5.0.1:
|
||||
is-number "^7.0.0"
|
||||
|
||||
totalist@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.0.tgz#4ef9c58c5f095255cdc3ff2a0a55091c57a3a1bd"
|
||||
integrity sha512-eM+pCBxXO/njtF7vdFsHuqb+ElbxqtI4r5EAvk6grfAFyJ6IvWlSkfZ5T9ozC6xWw3Fj1fGoSmrl0gUs46JVIw==
|
||||
version "3.0.1"
|
||||
resolved "https://registry.yarnpkg.com/totalist/-/totalist-3.0.1.tgz#ba3a3d600c915b1a97872348f79c127475f6acf8"
|
||||
integrity sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==
|
||||
|
||||
ufo@^0.8.4:
|
||||
version "0.8.4"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-0.8.4.tgz#23e9ed82398d2116dcb378e8fba5ced8eca2ee40"
|
||||
integrity sha512-/+BmBDe8GvlB2nIflWasLLAInjYG0bC9HRnfEpNi4sw77J2AJNnEVnTDReVrehoh825+Q/evF3THXTAweyam2g==
|
||||
ufo@^0.8.6:
|
||||
version "0.8.6"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-0.8.6.tgz#c0ec89bc0e0c9fa59a683680feb0f28b55ec323b"
|
||||
integrity sha512-fk6CmUgwKCfX79EzcDQQpSCMxrHstvbLswFChHS0Vump+kFkw7nJBfTZoC1j0bOGoY9I7R3n2DGek5ajbcYnOw==
|
||||
|
||||
ufo@^1.3.0:
|
||||
version "1.3.0"
|
||||
resolved "https://registry.yarnpkg.com/ufo/-/ufo-1.3.0.tgz#c92f8ac209daff607c57bbd75029e190930a0019"
|
||||
integrity sha512-bRn3CsoojyNStCZe0BG0Mt4Nr/4KF+rhFlnNXybgqt5pXHNFRlqinSoQaTrGyzE4X8aHplSb+TorH+COin9Yxw==
|
||||
|
||||
unconfig@^0.3.4:
|
||||
version "0.3.4"
|
||||
resolved "https://registry.yarnpkg.com/unconfig/-/unconfig-0.3.4.tgz#f0c85584a088a434dde2215d8a3b272427d6056c"
|
||||
integrity sha512-cf39F1brwQuLSuMLTYXOdWJH0O1CJee6a4QW1nYtO7SoBUfVvQCvEel6ssTNXtPfi17kop1ADmVtmC49NlFkIQ==
|
||||
version "0.3.10"
|
||||
resolved "https://registry.yarnpkg.com/unconfig/-/unconfig-0.3.10.tgz#2439cfc4303c8e12f7333d7cb7286917a3eb9b63"
|
||||
integrity sha512-tj317lhIq2iZF/NXrJnU1t2UaGUKKz1eL1sK2t63Oq66V9BxqvZV12m55fp/fpQJ+DDmVlLgo7cnLVOZkhlO/A==
|
||||
dependencies:
|
||||
"@antfu/utils" "^0.5.1"
|
||||
defu "^6.0.0"
|
||||
jiti "^1.13.0"
|
||||
"@antfu/utils" "^0.7.5"
|
||||
defu "^6.1.2"
|
||||
jiti "^1.19.1"
|
||||
mlly "^1.4.0"
|
||||
|
||||
undici@^5.2.0:
|
||||
version "5.26.3"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.26.3.tgz#ab3527b3d5bb25b12f898dfd22165d472dd71b79"
|
||||
integrity sha512-H7n2zmKEWgOllKkIUkLvFmsJQj062lSm3uA4EYApG8gLuiOM0/go9bIoC3HVaSnfg4xunowDE2i9p8drkXuvDw==
|
||||
undici@^5.12.0:
|
||||
version "5.26.5"
|
||||
resolved "https://registry.yarnpkg.com/undici/-/undici-5.26.5.tgz#f6dc8c565e3cad8c4475b187f51a13e505092838"
|
||||
integrity sha512-cSb4bPFd5qgR7qr2jYAi0hlX9n5YKK2ONKkLFkxl+v/9BvC0sOpZjBHDBSXc5lWAf5ty9oZdRXytBIHzgUcerw==
|
||||
dependencies:
|
||||
"@fastify/busboy" "^2.0.0"
|
||||
|
||||
|
||||
@ -9,7 +9,6 @@
|
||||
"parser": "@typescript-eslint/parser",
|
||||
|
||||
"extends": [
|
||||
"standard-with-typescript",
|
||||
"plugin:@typescript-eslint/recommended-requiring-type-checking",
|
||||
// TODO: make this work with typescript
|
||||
// "plugin:node/recommended"
|
||||
|
||||
@ -20,10 +20,10 @@
|
||||
"homepage": "https://github.com/tauri-apps/tauri#readme",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"build": "yarn rollup --config rollup.config.ts --configPlugin typescript",
|
||||
"build": "rollup -c --configPlugin typescript",
|
||||
"npm-pack": "yarn build && cd ./dist && npm pack",
|
||||
"npm-publish": "yarn build && cd ./dist && yarn publish --access public --loglevel silly --tag next",
|
||||
"check": "tsc",
|
||||
"ts:check": "tsc -noEmit",
|
||||
"lint": "eslint --ext ts \"./src/**/*.ts\"",
|
||||
"lint:fix": "eslint --fix --ext ts \"./src/**/*.ts\"",
|
||||
"format": "prettier --write . --config ../../.prettierrc --ignore-path .gitignore --ignore-path ../../.prettierignore",
|
||||
@ -37,7 +37,6 @@
|
||||
"@typescript-eslint/parser": "6.10.0",
|
||||
"eslint": "8.53.0",
|
||||
"eslint-config-prettier": "9.0.0",
|
||||
"eslint-config-standard-with-typescript": "39.1.1",
|
||||
"eslint-plugin-import": "2.29.0",
|
||||
"eslint-plugin-n": "16.3.0",
|
||||
"eslint-plugin-node": "11.1.0",
|
||||
|
||||
@ -2,7 +2,7 @@
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { defineConfig, Plugin } from 'rollup'
|
||||
import { defineConfig, Plugin, RollupLog } from 'rollup'
|
||||
import typescript from '@rollup/plugin-typescript'
|
||||
import terser from '@rollup/plugin-terser'
|
||||
import fg from 'fast-glob'
|
||||
@ -31,6 +31,7 @@ export default defineConfig([
|
||||
format: 'esm',
|
||||
dir: './dist',
|
||||
preserveModules: true,
|
||||
preserveModulesRoot: 'src',
|
||||
entryFileNames: (chunkInfo) => {
|
||||
if (chunkInfo.name.includes('node_modules')) {
|
||||
return chunkInfo.name.replace('node_modules', 'external') + '.js'
|
||||
@ -43,6 +44,7 @@ export default defineConfig([
|
||||
format: 'cjs',
|
||||
dir: './dist',
|
||||
preserveModules: true,
|
||||
preserveModulesRoot: 'src',
|
||||
entryFileNames: (chunkInfo) => {
|
||||
if (chunkInfo.name.includes('node_modules')) {
|
||||
return chunkInfo.name.replace('node_modules', 'external') + '.cjs'
|
||||
@ -55,11 +57,12 @@ export default defineConfig([
|
||||
plugins: [
|
||||
typescript({
|
||||
declaration: true,
|
||||
declarationDir: './dist',
|
||||
declarationDir: './dist/types',
|
||||
rootDir: 'src'
|
||||
}),
|
||||
makeFlatPackageInDist()
|
||||
]
|
||||
],
|
||||
onwarn
|
||||
},
|
||||
|
||||
{
|
||||
@ -67,13 +70,19 @@ export default defineConfig([
|
||||
output: {
|
||||
format: 'iife',
|
||||
name: '__TAURI_IIFE__',
|
||||
file: '../../core/tauri/scripts/bundle.global.js',
|
||||
footer: 'window.__TAURI__ = __TAURI_IIFE__'
|
||||
footer: 'window.__TAURI__ = __TAURI_IIFE__',
|
||||
file: '../../core/tauri/scripts/bundle.global.js'
|
||||
},
|
||||
plugins: [typescript(), terser()]
|
||||
plugins: [typescript(), terser()],
|
||||
onwarn
|
||||
}
|
||||
])
|
||||
|
||||
function onwarn(warning: RollupLog) {
|
||||
// deny warnings by default
|
||||
throw Object.assign(new Error(), warning)
|
||||
}
|
||||
|
||||
function makeFlatPackageInDist(): Plugin {
|
||||
return {
|
||||
name: 'makeFlatPackageInDist',
|
||||
@ -88,13 +97,17 @@ function makeFlatPackageInDist(): Plugin {
|
||||
exports: Object.assign(
|
||||
{},
|
||||
...mods.map((mod) => {
|
||||
let temp: Record<string, { import: string; require: string }> = {}
|
||||
let temp: Record<
|
||||
string,
|
||||
{ types: string; import: string; require: string }
|
||||
> = {}
|
||||
let key = `./${mod}`
|
||||
if (mod === 'index') {
|
||||
key = '.'
|
||||
}
|
||||
|
||||
temp[key] = {
|
||||
types: `./types/${mod}.d.ts`,
|
||||
import: `./${mod}.js`,
|
||||
require: `./${mod}.cjs`
|
||||
}
|
||||
|
||||
@ -55,8 +55,7 @@ enum TauriEvent {
|
||||
WINDOW_THEME_CHANGED = 'tauri://theme-changed',
|
||||
WINDOW_FILE_DROP = 'tauri://file-drop',
|
||||
WINDOW_FILE_DROP_HOVER = 'tauri://file-drop-hover',
|
||||
WINDOW_FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled',
|
||||
MENU = 'tauri://menu'
|
||||
WINDOW_FILE_DROP_CANCELLED = 'tauri://file-drop-cancelled'
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -19,5 +19,7 @@ import * as primitives from './primitives'
|
||||
import * as window from './window'
|
||||
import * as path from './path'
|
||||
import * as dpi from './dpi'
|
||||
import * as tray from './tray'
|
||||
import * as menu from './menu'
|
||||
|
||||
export { app, dpi, event, path, primitives, window }
|
||||
export { app, dpi, event, path, primitives, window, tray, menu }
|
||||
|
||||
34
tooling/api/src/internal/index.ts
Normal file
34
tooling/api/src/internal/index.ts
Normal file
@ -0,0 +1,34 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { invoke } from '../primitives'
|
||||
|
||||
/**
|
||||
* A rust-backed resource.
|
||||
*
|
||||
* The resource lives in the main process and does not exist
|
||||
* in the Javascript world, and thus will not be cleaned up automatiacally
|
||||
* except on application exit. If you want to clean it up early, call {@linkcode Resource.close}
|
||||
*/
|
||||
export class Resource {
|
||||
readonly #rid: number
|
||||
|
||||
get rid(): number {
|
||||
return this.#rid
|
||||
}
|
||||
|
||||
constructor(rid: number) {
|
||||
this.#rid = rid
|
||||
}
|
||||
|
||||
/**
|
||||
* Destroys and cleans up this resource from memory.
|
||||
* **You should not call any method on this object anymore and should drop any reference to it.**
|
||||
*/
|
||||
async close(): Promise<void> {
|
||||
return invoke('plugin:resources|close', {
|
||||
rid: this.rid
|
||||
})
|
||||
}
|
||||
}
|
||||
17
tooling/api/src/menu.ts
Normal file
17
tooling/api/src/menu.ts
Normal file
@ -0,0 +1,17 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
export * from './menu/submenu'
|
||||
export * from './menu/menuItem'
|
||||
export * from './menu/menu'
|
||||
export * from './menu/checkMenuItem'
|
||||
export * from './menu/iconMenuItem'
|
||||
export * from './menu/predefinedMenuItem'
|
||||
|
||||
/**
|
||||
* Menu types and utilities.
|
||||
*
|
||||
* This package is also accessible with `window.__TAURI__.menu` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
|
||||
* @module
|
||||
*/
|
||||
114
tooling/api/src/menu/base.ts
Normal file
114
tooling/api/src/menu/base.ts
Normal file
@ -0,0 +1,114 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { Resource } from '../internal'
|
||||
import { Channel, invoke } from '../primitives'
|
||||
import { CheckMenuItemOptions } from './checkMenuItem'
|
||||
import { IconMenuItemOptions } from './iconMenuItem'
|
||||
import { MenuItemOptions } from './menuItem'
|
||||
import { PredefinedMenuItemOptions } from './predefinedMenuItem'
|
||||
import { SubmenuOptions } from './submenu'
|
||||
|
||||
export type ItemKind =
|
||||
| 'MenuItem'
|
||||
| 'Predefined'
|
||||
| 'Check'
|
||||
| 'Icon'
|
||||
| 'Submenu'
|
||||
| 'Menu'
|
||||
|
||||
function injectChannel(
|
||||
i:
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
):
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| (CheckMenuItemOptions & { handler?: Channel<string> }) {
|
||||
if ('items' in i) {
|
||||
i.items = i.items?.map((item) =>
|
||||
'rid' in item ? item : injectChannel(item)
|
||||
)
|
||||
} else if ('action' in i && i.action) {
|
||||
const handler = new Channel<string>()
|
||||
handler.onmessage = i.action
|
||||
delete i.action
|
||||
return { ...i, handler }
|
||||
}
|
||||
return i
|
||||
}
|
||||
|
||||
export async function newMenu(
|
||||
kind: ItemKind,
|
||||
opts?: unknown
|
||||
): Promise<[number, string]> {
|
||||
const handler = new Channel<string>()
|
||||
let items: null | Array<
|
||||
| [number, string]
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
> = null
|
||||
if (opts && typeof opts === 'object') {
|
||||
if ('action' in opts && opts.action) {
|
||||
handler.onmessage = opts.action as () => void
|
||||
delete opts.action
|
||||
}
|
||||
|
||||
if ('items' in opts && opts.items) {
|
||||
items = (
|
||||
opts.items as Array<
|
||||
| { rid: number; kind: string }
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>
|
||||
).map((i) => {
|
||||
if ('rid' in i) {
|
||||
return [i.rid, i.kind]
|
||||
}
|
||||
return injectChannel(i)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
return invoke('plugin:menu|new', {
|
||||
kind,
|
||||
options: opts ? { ...opts, items } : undefined,
|
||||
handler
|
||||
})
|
||||
}
|
||||
|
||||
export class MenuItemBase extends Resource {
|
||||
/** @ignore */
|
||||
readonly #id: string
|
||||
/** @ignore */
|
||||
readonly #kind: ItemKind
|
||||
|
||||
/** The id of this item. */
|
||||
get id(): string {
|
||||
return this.#id
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
get kind(): string {
|
||||
return this.#kind
|
||||
}
|
||||
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string, kind: ItemKind) {
|
||||
super(rid)
|
||||
this.#id = id
|
||||
this.#kind = kind
|
||||
}
|
||||
}
|
||||
82
tooling/api/src/menu/checkMenuItem.ts
Normal file
82
tooling/api/src/menu/checkMenuItem.ts
Normal file
@ -0,0 +1,82 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { MenuItemBase, newMenu } from './base'
|
||||
import { invoke } from '../primitives'
|
||||
import { type MenuItemOptions } from '../menu'
|
||||
|
||||
/** Options for creating a new check menu item. */
|
||||
export interface CheckMenuItemOptions extends MenuItemOptions {
|
||||
/** Whether the new check menu item is enabled or not. */
|
||||
checked?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* A check menu item inside a {@linkcode Menu} or {@linkcode Submenu}
|
||||
* and usually contains a text and a check mark or a similar toggle
|
||||
* that corresponds to a checked and unchecked states.
|
||||
*/
|
||||
export class CheckMenuItem extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'Check')
|
||||
}
|
||||
|
||||
/** Create a new check menu item. */
|
||||
static async new(opts: CheckMenuItemOptions): Promise<CheckMenuItem> {
|
||||
return newMenu('Check', opts).then(
|
||||
([rid, id]) => new CheckMenuItem(rid, id)
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns the text of this check menu item. */
|
||||
async text(): Promise<string> {
|
||||
return invoke('plugin:menu|text', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets the text for this check menu item. */
|
||||
async setText(text: string): Promise<void> {
|
||||
return invoke('plugin:menu|set_text', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
/** Returns whether this check menu item is enabled or not. */
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return invoke('plugin:menu|is_enabled', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets whether this check menu item is enabled or not. */
|
||||
async setEnabled(enabled: boolean): Promise<void> {
|
||||
return invoke('plugin:menu|set_enabled', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
enabled
|
||||
})
|
||||
}
|
||||
|
||||
/** Sets the accelerator for this check menu item. */
|
||||
async setAccelerator(accelerator: string | null): Promise<void> {
|
||||
return invoke('plugin:menu|set_accelerator', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
accelerator
|
||||
})
|
||||
}
|
||||
|
||||
/** Returns whether this check menu item is checked or not. */
|
||||
async isChecked(): Promise<boolean> {
|
||||
return invoke('plugin:menu|is_checked', { rid: this.rid })
|
||||
}
|
||||
|
||||
/** Sets whether this check menu item is checked or not. */
|
||||
async setChecked(checked: boolean): Promise<void> {
|
||||
return invoke('plugin:menu|set_checked', {
|
||||
rid: this.rid,
|
||||
checked
|
||||
})
|
||||
}
|
||||
}
|
||||
195
tooling/api/src/menu/iconMenuItem.ts
Normal file
195
tooling/api/src/menu/iconMenuItem.ts
Normal file
@ -0,0 +1,195 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { MenuItemBase, newMenu } from './base'
|
||||
import { type MenuItemOptions } from '../menu'
|
||||
import { invoke } from '../primitives'
|
||||
|
||||
/**
|
||||
* A native Icon to be used for the menu item
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Windows / Linux**: Unsupported.
|
||||
*/
|
||||
export enum NativeIcon {
|
||||
/** An add item template image. */
|
||||
Add = 'Add',
|
||||
/** Advanced preferences toolbar icon for the preferences window. */
|
||||
Advanced = 'Advanced',
|
||||
/** A Bluetooth template image. */
|
||||
Bluetooth = 'Bluetooth',
|
||||
/** Bookmarks image suitable for a template. */
|
||||
Bookmarks = 'Bookmarks',
|
||||
/** A caution image. */
|
||||
Caution = 'Caution',
|
||||
/** A color panel toolbar icon. */
|
||||
ColorPanel = 'ColorPanel',
|
||||
/** A column view mode template image. */
|
||||
ColumnView = 'ColumnView',
|
||||
/** A computer icon. */
|
||||
Computer = 'Computer',
|
||||
/** An enter full-screen mode template image. */
|
||||
EnterFullScreen = 'EnterFullScreen',
|
||||
/** Permissions for all users. */
|
||||
Everyone = 'Everyone',
|
||||
/** An exit full-screen mode template image. */
|
||||
ExitFullScreen = 'ExitFullScreen',
|
||||
/** A cover flow view mode template image. */
|
||||
FlowView = 'FlowView',
|
||||
/** A folder image. */
|
||||
Folder = 'Folder',
|
||||
/** A burnable folder icon. */
|
||||
FolderBurnable = 'FolderBurnable',
|
||||
/** A smart folder icon. */
|
||||
FolderSmart = 'FolderSmart',
|
||||
/** A link template image. */
|
||||
FollowLinkFreestanding = 'FollowLinkFreestanding',
|
||||
/** A font panel toolbar icon. */
|
||||
FontPanel = 'FontPanel',
|
||||
/** A `go back` template image. */
|
||||
GoLeft = 'GoLeft',
|
||||
/** A `go forward` template image. */
|
||||
GoRight = 'GoRight',
|
||||
/** Home image suitable for a template. */
|
||||
Home = 'Home',
|
||||
/** An iChat Theater template image. */
|
||||
IChatTheater = 'IChatTheater',
|
||||
/** An icon view mode template image. */
|
||||
IconView = 'IconView',
|
||||
/** An information toolbar icon. */
|
||||
Info = 'Info',
|
||||
/** A template image used to denote invalid data. */
|
||||
InvalidDataFreestanding = 'InvalidDataFreestanding',
|
||||
/** A generic left-facing triangle template image. */
|
||||
LeftFacingTriangle = 'LeftFacingTriangle',
|
||||
/** A list view mode template image. */
|
||||
ListView = 'ListView',
|
||||
/** A locked padlock template image. */
|
||||
LockLocked = 'LockLocked',
|
||||
/** An unlocked padlock template image. */
|
||||
LockUnlocked = 'LockUnlocked',
|
||||
/** A horizontal dash, for use in menus. */
|
||||
MenuMixedState = 'MenuMixedState',
|
||||
/** A check mark template image, for use in menus. */
|
||||
MenuOnState = 'MenuOnState',
|
||||
/** A MobileMe icon. */
|
||||
MobileMe = 'MobileMe',
|
||||
/** A drag image for multiple items. */
|
||||
MultipleDocuments = 'MultipleDocuments',
|
||||
/** A network icon. */
|
||||
Network = 'Network',
|
||||
/** A path button template image. */
|
||||
Path = 'Path',
|
||||
/** General preferences toolbar icon for the preferences window. */
|
||||
PreferencesGeneral = 'PreferencesGeneral',
|
||||
/** A Quick Look template image. */
|
||||
QuickLook = 'QuickLook',
|
||||
/** A refresh template image. */
|
||||
RefreshFreestanding = 'RefreshFreestanding',
|
||||
/** A refresh template image. */
|
||||
Refresh = 'Refresh',
|
||||
/** A remove item template image. */
|
||||
Remove = 'Remove',
|
||||
/** A reveal contents template image. */
|
||||
RevealFreestanding = 'RevealFreestanding',
|
||||
/** A generic right-facing triangle template image. */
|
||||
RightFacingTriangle = 'RightFacingTriangle',
|
||||
/** A share view template image. */
|
||||
Share = 'Share',
|
||||
/** A slideshow template image. */
|
||||
Slideshow = 'Slideshow',
|
||||
/** A badge for a `smart` item. */
|
||||
SmartBadge = 'SmartBadge',
|
||||
/** Small green indicator, similar to iChat’s available image. */
|
||||
StatusAvailable = 'StatusAvailable',
|
||||
/** Small clear indicator. */
|
||||
StatusNone = 'StatusNone',
|
||||
/** Small yellow indicator, similar to iChat’s idle image. */
|
||||
StatusPartiallyAvailable = 'StatusPartiallyAvailable',
|
||||
/** Small red indicator, similar to iChat’s unavailable image. */
|
||||
StatusUnavailable = 'StatusUnavailable',
|
||||
/** A stop progress template image. */
|
||||
StopProgressFreestanding = 'StopProgressFreestanding',
|
||||
/** A stop progress button template image. */
|
||||
StopProgress = 'StopProgress',
|
||||
/** An image of the empty trash can. */
|
||||
TrashEmpty = 'TrashEmpty',
|
||||
/** An image of the full trash can. */
|
||||
TrashFull = 'TrashFull',
|
||||
/** Permissions for a single user. */
|
||||
User = 'User',
|
||||
/** User account toolbar icon for the preferences window. */
|
||||
UserAccounts = 'UserAccounts',
|
||||
/** Permissions for a group of users. */
|
||||
UserGroup = 'UserGroup',
|
||||
/** Permissions for guests. */
|
||||
UserGuest = 'UserGuest'
|
||||
}
|
||||
|
||||
/** Options for creating a new icon menu item. */
|
||||
export interface IconMenuItemOptions extends MenuItemOptions {
|
||||
/**
|
||||
* Icon to be used for the new icon menu item.
|
||||
*/
|
||||
icon?: NativeIcon | string | Uint8Array
|
||||
}
|
||||
|
||||
/**
|
||||
* An icon menu item inside a {@linkcode Menu} or {@linkcode Submenu}
|
||||
* and usually contains an icon and a text.
|
||||
*/
|
||||
export class IconMenuItem extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'Icon')
|
||||
}
|
||||
|
||||
/** Create a new icon menu item. */
|
||||
static async new(opts: IconMenuItemOptions): Promise<IconMenuItem> {
|
||||
return newMenu('Icon', opts).then(([rid, id]) => new IconMenuItem(rid, id))
|
||||
}
|
||||
|
||||
/** Returns the text of this icon menu item. */
|
||||
async text(): Promise<string> {
|
||||
return invoke('plugin:menu|text', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets the text for this icon menu item. */
|
||||
async setText(text: string): Promise<void> {
|
||||
return invoke('plugin:menu|set_text', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
/** Returns whether this icon menu item is enabled or not. */
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return invoke('plugin:menu|is_enabled', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets whether this icon menu item is enabled or not. */
|
||||
async setEnabled(enabled: boolean): Promise<void> {
|
||||
return invoke('plugin:menu|set_enabled', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
enabled
|
||||
})
|
||||
}
|
||||
|
||||
/** Sets the accelerator for this icon menu item. */
|
||||
async setAccelerator(accelerator: string | null): Promise<void> {
|
||||
return invoke('plugin:menu|set_accelerator', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
accelerator
|
||||
})
|
||||
}
|
||||
|
||||
/** Sets an icon for this icon menu item */
|
||||
async setIcon(icon: NativeIcon | string | Uint8Array | null): Promise<void> {
|
||||
return invoke('plugin:menu|set_icon', { rid: this.rid, icon })
|
||||
}
|
||||
}
|
||||
287
tooling/api/src/menu/menu.ts
Normal file
287
tooling/api/src/menu/menu.ts
Normal file
@ -0,0 +1,287 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import {
|
||||
MenuItemOptions,
|
||||
SubmenuOptions,
|
||||
IconMenuItemOptions,
|
||||
PredefinedMenuItemOptions,
|
||||
CheckMenuItemOptions
|
||||
} from '../menu'
|
||||
import { MenuItem } from './menuItem'
|
||||
import { CheckMenuItem } from './checkMenuItem'
|
||||
import { IconMenuItem } from './iconMenuItem'
|
||||
import { PredefinedMenuItem } from './predefinedMenuItem'
|
||||
import { Submenu } from './submenu'
|
||||
import { type LogicalPosition, PhysicalPosition } from '../dpi'
|
||||
import { type Window } from '../window'
|
||||
import { invoke } from '../primitives'
|
||||
import { type ItemKind, MenuItemBase, newMenu } from './base'
|
||||
|
||||
function itemFromKind([rid, id, kind]: [number, string, ItemKind]):
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem {
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
switch (kind) {
|
||||
case 'Submenu':
|
||||
// @ts-expect-error constructor is protected for external usage only
|
||||
return new Submenu(rid, id)
|
||||
case 'Predefined':
|
||||
// @ts-expect-error constructor is protected for external usage only
|
||||
return new PredefinedMenuItem(rid, id)
|
||||
case 'Check':
|
||||
// @ts-expect-error constructor is protected for external usage only
|
||||
return new CheckMenuItem(rid, id)
|
||||
case 'Icon':
|
||||
// @ts-expect-error constructor is protected for external usage only
|
||||
return new IconMenuItem(rid, id)
|
||||
case 'MenuItem':
|
||||
default:
|
||||
// @ts-expect-error constructor is protected for external usage only
|
||||
return new MenuItem(rid, id)
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-return */
|
||||
}
|
||||
|
||||
/** Options for creating a new menu. */
|
||||
export interface MenuOptions {
|
||||
/** Specify an id to use for the new menu. */
|
||||
id?: string
|
||||
/** List of items to add to the new menu. */
|
||||
items?: Array<
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>
|
||||
}
|
||||
|
||||
/** A type that is either a menu bar on the window
|
||||
* on Windows and Linux or as a global menu in the menubar on macOS.
|
||||
*/
|
||||
export class Menu extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'Menu')
|
||||
}
|
||||
|
||||
/** Create a new menu. */
|
||||
static async new(opts?: MenuOptions): Promise<Menu> {
|
||||
return newMenu('Menu', opts).then(([rid, id]) => new Menu(rid, id))
|
||||
}
|
||||
|
||||
/** Create a default menu. */
|
||||
static async default(): Promise<Menu> {
|
||||
return invoke<[number, string]>('plugin:menu|default').then(
|
||||
([rid, id]) => new Menu(rid, id)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the end of this menu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async append<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[]): Promise<void> {
|
||||
return invoke('plugin:menu|append', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the beginning of this menu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async prepend<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[]): Promise<void> {
|
||||
return invoke('plugin:menu|prepend', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the specified position in this menu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async insert<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[], position: number): Promise<void> {
|
||||
return invoke('plugin:menu|insert', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
),
|
||||
position
|
||||
})
|
||||
}
|
||||
|
||||
/** Remove a menu item from this menu. */
|
||||
async remove(
|
||||
item: Submenu | MenuItem | PredefinedMenuItem | CheckMenuItem | IconMenuItem
|
||||
): Promise<void> {
|
||||
return invoke('plugin:menu|remove', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
item: [item.rid, item.kind]
|
||||
})
|
||||
}
|
||||
|
||||
/** Remove a menu item from this menu at the specified position. */
|
||||
async removeAt(
|
||||
position: number
|
||||
): Promise<
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| null
|
||||
> {
|
||||
return invoke<[number, string, ItemKind]>('plugin:menu|remove_at', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
position
|
||||
}).then(itemFromKind)
|
||||
}
|
||||
|
||||
/** Returns a list of menu items that has been added to this menu. */
|
||||
async items(): Promise<
|
||||
Array<
|
||||
Submenu | MenuItem | PredefinedMenuItem | CheckMenuItem | IconMenuItem
|
||||
>
|
||||
> {
|
||||
return invoke<Array<[number, string, ItemKind]>>('plugin:menu|items', {
|
||||
rid: this.rid,
|
||||
kind: this.kind
|
||||
}).then((i) => i.map(itemFromKind))
|
||||
}
|
||||
|
||||
/** Retrieves the menu item matching the given identifier. */
|
||||
async get(
|
||||
id: string
|
||||
): Promise<
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| null
|
||||
> {
|
||||
return invoke<[number, string, ItemKind] | null>('plugin:menu|get', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
id
|
||||
}).then((r) => (r ? itemFromKind(r) : null))
|
||||
}
|
||||
|
||||
/**
|
||||
* Popup this menu as a context menu on the specified window.
|
||||
*
|
||||
* If the position, is provided, it is relative to the window's top-left corner.
|
||||
*/
|
||||
async popup(
|
||||
at?: PhysicalPosition | LogicalPosition,
|
||||
window?: Window
|
||||
): Promise<void> {
|
||||
let atValue = null
|
||||
if (at) {
|
||||
atValue = {
|
||||
type: at instanceof PhysicalPosition ? 'Physical' : 'Logical',
|
||||
data: at
|
||||
}
|
||||
}
|
||||
return invoke('plugin:menu|popup', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
window: window?.label ?? null,
|
||||
at: atValue
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the app-wide menu and returns the previous one.
|
||||
*
|
||||
* If a window was not created with an explicit menu or had one set explicitly,
|
||||
* this menu will be assigned to it.
|
||||
*/
|
||||
async setAsAppMenu(): Promise<Menu | null> {
|
||||
return invoke<[number, string] | null>('plugin:menu|set_as_app_menu', {
|
||||
rid: this.rid
|
||||
}).then((r) => (r ? new Menu(r[0], r[1]) : null))
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the window menu and returns the previous one.
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **macOS:** Unsupported. The menu on macOS is app-wide and not specific to one
|
||||
* window, if you need to set it, use {@linkcode Menu.setAsAppMenu} instead.
|
||||
*/
|
||||
async setAsWindowMenu(window?: Window): Promise<Menu | null> {
|
||||
return invoke<[number, string] | null>('plugin:menu|set_as_window_menu', {
|
||||
rid: this.rid,
|
||||
window: window?.label ?? null
|
||||
}).then((r) => (r ? new Menu(r[0], r[1]) : null))
|
||||
}
|
||||
}
|
||||
70
tooling/api/src/menu/menuItem.ts
Normal file
70
tooling/api/src/menu/menuItem.ts
Normal file
@ -0,0 +1,70 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { MenuItemBase, newMenu } from './base'
|
||||
import { invoke } from '../primitives'
|
||||
|
||||
/** Options for creating a new menu item. */
|
||||
export interface MenuItemOptions {
|
||||
/** Specify an id to use for the new menu item. */
|
||||
id?: string
|
||||
/** The text of the new menu item. */
|
||||
text: string
|
||||
/** Whether the new menu item is enabled or not. */
|
||||
enabled?: boolean
|
||||
/** Specify an accelerator for the new menu item. */
|
||||
accelerator?: string
|
||||
/** Specify a handler to be called when this menu item is activated. */
|
||||
action?: (id: string) => void
|
||||
}
|
||||
|
||||
/** A menu item inside a {@linkcode Menu} or {@linkcode Submenu} and contains only text. */
|
||||
export class MenuItem extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'MenuItem')
|
||||
}
|
||||
|
||||
/** Create a new menu item. */
|
||||
static async new(opts: MenuItemOptions): Promise<MenuItem> {
|
||||
return newMenu('MenuItem', opts).then(([rid, id]) => new MenuItem(rid, id))
|
||||
}
|
||||
|
||||
/** Returns the text of this menu item. */
|
||||
async text(): Promise<string> {
|
||||
return invoke('plugin:menu|text', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets the text for this menu item. */
|
||||
async setText(text: string): Promise<void> {
|
||||
return invoke('plugin:menu|set_text', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
/** Returns whether this menu item is enabled or not. */
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return invoke('plugin:menu|is_enabled', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets whether this menu item is enabled or not. */
|
||||
async setEnabled(enabled: boolean): Promise<void> {
|
||||
return invoke('plugin:menu|set_enabled', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
enabled
|
||||
})
|
||||
}
|
||||
|
||||
/** Sets the accelerator for this menu item. */
|
||||
async setAccelerator(accelerator: string | null): Promise<void> {
|
||||
return invoke('plugin:menu|set_accelerator', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
accelerator
|
||||
})
|
||||
}
|
||||
}
|
||||
138
tooling/api/src/menu/predefinedMenuItem.ts
Normal file
138
tooling/api/src/menu/predefinedMenuItem.ts
Normal file
@ -0,0 +1,138 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import { MenuItemBase, newMenu } from './base'
|
||||
import { invoke } from '../primitives'
|
||||
|
||||
/** A metadata for the about predefined menu item. */
|
||||
export interface AboutMetadata {
|
||||
/** Sets the application name. */
|
||||
name?: string
|
||||
/** The application version. */
|
||||
version?: string
|
||||
/**
|
||||
* The short version, e.g. "1.0".
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **Windows / Linux:** Appended to the end of `version` in parentheses.
|
||||
*/
|
||||
shortVersion?: string
|
||||
/**
|
||||
* The authors of the application.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** Unsupported.
|
||||
*/
|
||||
authors?: string[]
|
||||
/**
|
||||
* Application comments.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** Unsupported.
|
||||
*/
|
||||
comments?: string
|
||||
/** The copyright of the application. */
|
||||
copyright?: string
|
||||
/**
|
||||
* The license of the application.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** Unsupported.
|
||||
*/
|
||||
license?: string
|
||||
/**
|
||||
* The application website.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** Unsupported.
|
||||
*/
|
||||
website?: string
|
||||
/**
|
||||
* The website label.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **macOS:** Unsupported.
|
||||
*/
|
||||
websiteLabel?: string
|
||||
/**
|
||||
* The credits.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **Windows / Linux:** Unsupported.
|
||||
*/
|
||||
credits?: string
|
||||
/**
|
||||
* The application icon.
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **Windows:** Unsupported.
|
||||
*/
|
||||
icon?: string | Uint8Array
|
||||
}
|
||||
|
||||
/** Options for creating a new predefined menu item. */
|
||||
export interface PredefinedMenuItemOptions {
|
||||
/** The text of the new predefined menu item. */
|
||||
text?: string
|
||||
/** The predefined item type */
|
||||
item:
|
||||
| 'Separator'
|
||||
| 'Copy'
|
||||
| 'Cut'
|
||||
| 'Paste'
|
||||
| 'SelectAll'
|
||||
| 'Undo'
|
||||
| 'Redo'
|
||||
| 'Minimize'
|
||||
| 'Maximize'
|
||||
| 'Fullscreen'
|
||||
| 'Hide'
|
||||
| 'HideOthers'
|
||||
| 'ShowAll'
|
||||
| 'CloseWindow'
|
||||
| 'Quit'
|
||||
| 'Services'
|
||||
| {
|
||||
About: AboutMetadata | null
|
||||
}
|
||||
}
|
||||
|
||||
/** A predefined (native) menu item which has a predfined behavior by the OS or by tauri. */
|
||||
export class PredefinedMenuItem extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'Predefined')
|
||||
}
|
||||
|
||||
/** Create a new predefined menu item. */
|
||||
static async new(
|
||||
opts?: PredefinedMenuItemOptions
|
||||
): Promise<PredefinedMenuItem> {
|
||||
return newMenu('Predefined', opts).then(
|
||||
([rid, id]) => new PredefinedMenuItem(rid, id)
|
||||
)
|
||||
}
|
||||
|
||||
/** Returns the text of this predefined menu item. */
|
||||
async text(): Promise<string> {
|
||||
return invoke('plugin:menu|text', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets the text for this predefined menu item. */
|
||||
async setText(text: string): Promise<void> {
|
||||
return invoke('plugin:menu|set_text', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
text
|
||||
})
|
||||
}
|
||||
}
|
||||
294
tooling/api/src/menu/submenu.ts
Normal file
294
tooling/api/src/menu/submenu.ts
Normal file
@ -0,0 +1,294 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import {
|
||||
IconMenuItemOptions,
|
||||
PredefinedMenuItemOptions,
|
||||
CheckMenuItemOptions
|
||||
} from '../menu'
|
||||
import { MenuItem, type MenuItemOptions } from './menuItem'
|
||||
import { CheckMenuItem } from './checkMenuItem'
|
||||
import { IconMenuItem } from './iconMenuItem'
|
||||
import { PredefinedMenuItem } from './predefinedMenuItem'
|
||||
import { invoke } from '../primitives'
|
||||
import { type LogicalPosition, PhysicalPosition, type Window } from '../window'
|
||||
import { type ItemKind, MenuItemBase, newMenu } from './base'
|
||||
import { type MenuOptions } from './menu'
|
||||
|
||||
function itemFromKind([rid, id, kind]: [number, string, ItemKind]):
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem {
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-return */
|
||||
switch (kind) {
|
||||
case 'Submenu':
|
||||
// @ts-expect-error constructor is protected for external usage only, safe for us to use
|
||||
return new Submenu(rid, id)
|
||||
case 'Predefined':
|
||||
// @ts-expect-error constructor is protected for external usage only, safe for us to use
|
||||
return new PredefinedMenuItem(rid, id)
|
||||
case 'Check':
|
||||
// @ts-expect-error constructor is protected for external usage only, safe for us to use
|
||||
return new CheckMenuItem(rid, id)
|
||||
case 'Icon':
|
||||
// @ts-expect-error constructor is protected for external usage only, safe for us to use
|
||||
return new IconMenuItem(rid, id)
|
||||
case 'MenuItem':
|
||||
default:
|
||||
// @ts-expect-error constructor is protected for external usage only, safe for us to use
|
||||
return new MenuItem(rid, id)
|
||||
}
|
||||
/* eslint-enable @typescript-eslint/no-unsafe-return */
|
||||
}
|
||||
|
||||
export type SubmenuOptions = Omit<MenuItemOptions, 'accelerator' | 'action'> &
|
||||
MenuOptions
|
||||
|
||||
/** A type that is a submenu inside a {@linkcode Menu} or {@linkcode Submenu}. */
|
||||
export class Submenu extends MenuItemBase {
|
||||
/** @ignore */
|
||||
protected constructor(rid: number, id: string) {
|
||||
super(rid, id, 'Submenu')
|
||||
}
|
||||
|
||||
/** Create a new submenu. */
|
||||
static async new(opts: SubmenuOptions): Promise<Submenu> {
|
||||
return newMenu('Submenu', opts).then(([rid, id]) => new Submenu(rid, id))
|
||||
}
|
||||
|
||||
/** Returns the text of this submenu. */
|
||||
async text(): Promise<string> {
|
||||
return invoke('plugin:menu|text', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets the text for this submenu. */
|
||||
async setText(text: string): Promise<void> {
|
||||
return invoke('plugin:menu|set_text', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
text
|
||||
})
|
||||
}
|
||||
|
||||
/** Returns whether this submenu is enabled or not. */
|
||||
async isEnabled(): Promise<boolean> {
|
||||
return invoke('plugin:menu|is_enabled', { rid: this.rid, kind: this.kind })
|
||||
}
|
||||
|
||||
/** Sets whether this submenu is enabled or not. */
|
||||
async setEnabled(enabled: boolean): Promise<void> {
|
||||
return invoke('plugin:menu|set_enabled', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
enabled
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the end of this submenu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async append<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[]): Promise<void> {
|
||||
return invoke('plugin:menu|append', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the beginning of this submenu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async prepend<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[]): Promise<void> {
|
||||
return invoke('plugin:menu|prepend', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a menu item to the specified position in this submenu.
|
||||
*
|
||||
* ## Platform-spcific:
|
||||
*
|
||||
* - **macOS:** Only {@linkcode Submenu}s can be added to a {@linkcode Menu}.
|
||||
*/
|
||||
async insert<
|
||||
T extends
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| MenuItemOptions
|
||||
| SubmenuOptions
|
||||
| IconMenuItemOptions
|
||||
| PredefinedMenuItemOptions
|
||||
| CheckMenuItemOptions
|
||||
>(items: T | T[], position: number): Promise<void> {
|
||||
return invoke('plugin:menu|insert', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
items: (Array.isArray(items) ? items : [items]).map((i) =>
|
||||
'rid' in i ? [i.rid, i.kind] : i
|
||||
),
|
||||
position
|
||||
})
|
||||
}
|
||||
|
||||
/** Remove a menu item from this submenu. */
|
||||
async remove(
|
||||
item: Submenu | MenuItem | PredefinedMenuItem | CheckMenuItem | IconMenuItem
|
||||
): Promise<void> {
|
||||
return invoke('plugin:menu|remove', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
item: [item.rid, item.kind]
|
||||
})
|
||||
}
|
||||
|
||||
/** Remove a menu item from this submenu at the specified position. */
|
||||
async removeAt(
|
||||
position: number
|
||||
): Promise<
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| null
|
||||
> {
|
||||
return invoke<[number, string, ItemKind]>('plugin:menu|remove_at', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
position
|
||||
}).then(itemFromKind)
|
||||
}
|
||||
|
||||
/** Returns a list of menu items that has been added to this submenu. */
|
||||
async items(): Promise<
|
||||
Array<
|
||||
Submenu | MenuItem | PredefinedMenuItem | CheckMenuItem | IconMenuItem
|
||||
>
|
||||
> {
|
||||
return invoke<Array<[number, string, ItemKind]>>('plugin:menu|items', {
|
||||
rid: this.rid,
|
||||
kind: this.kind
|
||||
}).then((i) => i.map(itemFromKind))
|
||||
}
|
||||
|
||||
/** Retrieves the menu item matching the given identifier. */
|
||||
async get(
|
||||
id: string
|
||||
): Promise<
|
||||
| Submenu
|
||||
| MenuItem
|
||||
| PredefinedMenuItem
|
||||
| CheckMenuItem
|
||||
| IconMenuItem
|
||||
| null
|
||||
> {
|
||||
return invoke<[number, string, ItemKind] | null>('plugin:menu|get', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
id
|
||||
}).then((r) => (r ? itemFromKind(r) : null))
|
||||
}
|
||||
|
||||
/**
|
||||
* Popup this submenu as a context menu on the specified window.
|
||||
*
|
||||
* If the position, is provided, it is relative to the window's top-left corner.
|
||||
*/
|
||||
async popup(
|
||||
at?: PhysicalPosition | LogicalPosition,
|
||||
window?: Window
|
||||
): Promise<void> {
|
||||
let atValue = null
|
||||
if (at) {
|
||||
atValue = {
|
||||
type: at instanceof PhysicalPosition ? 'Physical' : 'Logical',
|
||||
data: at
|
||||
}
|
||||
}
|
||||
return invoke('plugin:menu|popup', {
|
||||
rid: this.rid,
|
||||
kind: this.kind,
|
||||
window: window?.label ?? null,
|
||||
at: atValue
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this submenu as the Window menu for the application on macOS.
|
||||
*
|
||||
* This will cause macOS to automatically add window-switching items and
|
||||
* certain other items to the menu.
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Windows / Linux**: Unsupported.
|
||||
*/
|
||||
async setAsWindowsMenuForNSApp(): Promise<void> {
|
||||
return invoke('plugin:menu|set_as_windows_menu_for_nsapp', {
|
||||
rid: this.rid
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Set this submenu as the Help menu for the application on macOS.
|
||||
*
|
||||
* This will cause macOS to automatically add a search box to the menu.
|
||||
*
|
||||
* If no menu is set as the Help menu, macOS will automatically use any menu
|
||||
* which has a title matching the localized word "Help".
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Windows / Linux**: Unsupported.
|
||||
*/
|
||||
async setAsHelpMenuForNSApp(): Promise<void> {
|
||||
return invoke('plugin:menu|set_as_help_menu_for_nsapp', {
|
||||
rid: this.rid
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -533,7 +533,7 @@ async function appLogDir(): Promise<string> {
|
||||
*
|
||||
* @since 2.0.0
|
||||
*/
|
||||
async function tempDir(path: string): Promise<string> {
|
||||
async function tempDir(): Promise<string> {
|
||||
return invoke('plugin:path|resolve_directory', {
|
||||
directory: BaseDirectory.Temp
|
||||
})
|
||||
|
||||
235
tooling/api/src/tray.ts
Normal file
235
tooling/api/src/tray.ts
Normal file
@ -0,0 +1,235 @@
|
||||
// Copyright 2019-2023 Tauri Programme within The Commons Conservancy
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
// SPDX-License-Identifier: MIT
|
||||
|
||||
import type { Menu, Submenu } from './menu'
|
||||
import { Resource } from './internal'
|
||||
import { Channel, invoke } from './primitives'
|
||||
|
||||
/**
|
||||
* Describes a tray event emitted when a tray icon is clicked
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Linux**: Unsupported. The event is not emmited even though the icon is shown,
|
||||
* the icon will still show a context menu on right click.
|
||||
*/
|
||||
export interface TrayIconEvent {
|
||||
/** Id of the tray icon which triggered this event. */
|
||||
id: string
|
||||
/** Physical X Position of the click the triggered this event. */
|
||||
x: number
|
||||
/** Physical Y Position of the click the triggered this event. */
|
||||
y: number
|
||||
/** Position and size of the tray icon. */
|
||||
iconRect: {
|
||||
/** The x-coordinate of the upper-left corner of the rectangle. */
|
||||
left: number
|
||||
/** The y-coordinate of the upper-left corner of the rectangle. */
|
||||
top: number
|
||||
/** The x-coordinate of the lower-right corner of the rectangle. */
|
||||
right: number
|
||||
/** The y-coordinate of the lower-right corner of the rectangle. */
|
||||
bottom: number
|
||||
}
|
||||
/** The click type that triggered this event. */
|
||||
clickType: 'Left' | 'Right' | 'Double'
|
||||
}
|
||||
|
||||
/**
|
||||
* Tray icon types and utilities.
|
||||
*
|
||||
* This package is also accessible with `window.__TAURI__.tray` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
|
||||
* @module
|
||||
*/
|
||||
|
||||
/** {@link TrayIcon.new|`TrayIcon`} creation options */
|
||||
export interface TrayIconOptions {
|
||||
/** The tray icon id. If undefined, a random one will be assigend */
|
||||
id?: string
|
||||
/** The tray icon menu */
|
||||
menu?: Menu | Submenu
|
||||
/**
|
||||
* The tray icon which could be icon bytes or path to the icon file.
|
||||
*
|
||||
* 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"] }
|
||||
* ```
|
||||
*/
|
||||
icon?: string | Uint8Array | number[]
|
||||
/** The tray icon tooltip */
|
||||
tooltip?: string
|
||||
/**
|
||||
* The tray title
|
||||
*
|
||||
* #### Platform-specific
|
||||
*
|
||||
* - **Linux:** The title will not be shown unless there is an icon
|
||||
* as well. The title is useful for numerical and other frequently
|
||||
* updated information. In general, it shouldn't be shown unless a
|
||||
* user requests it as it can take up a significant amount of space
|
||||
* on the user's panel. This may not be shown in all visualizations.
|
||||
* - **Windows:** Unsupported.
|
||||
*/
|
||||
title?: string
|
||||
/**
|
||||
* The tray icon temp dir path. **Linux only**.
|
||||
*
|
||||
* On Linux, we need to write the icon to the disk and usually it will
|
||||
* be `$XDG_RUNTIME_DIR/tray-icon` or `$TEMP/tray-icon`.
|
||||
*/
|
||||
tempDirPath?: string
|
||||
/**
|
||||
* Use the icon as a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc). **macOS only**.
|
||||
*/
|
||||
iconAsTemplate?: boolean
|
||||
/** Whether to show the tray menu on left click or not, default is `true`. **macOS only**. */
|
||||
menuOnLeftClick?: boolean
|
||||
/** A handler for an event on the tray icon. */
|
||||
action?: (event: TrayIconEvent) => void
|
||||
}
|
||||
|
||||
/**
|
||||
* Tray icon class and associated methods. This type constructor is private,
|
||||
* instead, you should use the static method {@linkcode TrayIcon.new}.
|
||||
*
|
||||
* #### Warning
|
||||
*
|
||||
* Unlike Rust, javascript does not have any way to run cleanup code
|
||||
* when an object is being removed by garbage collection, but this tray icon
|
||||
* will be cleaned up when the tauri app exists, however if you want to cleanup
|
||||
* this object early, you need to call {@linkcode TrayIcon.close}.
|
||||
*
|
||||
* @example
|
||||
* ```ts
|
||||
* import { TrayIcon } from '@tauri-apps/api/tray';
|
||||
* const tray = await TrayIcon.new({ tooltip: 'awesome tray tooltip' });
|
||||
* tray.set_tooltip('new tooltip');
|
||||
* ```
|
||||
*/
|
||||
export class TrayIcon extends Resource {
|
||||
/** The id associated with this tray icon. */
|
||||
public id: string
|
||||
|
||||
private constructor(rid: number, id: string) {
|
||||
super(rid)
|
||||
this.id = id
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a new {@linkcode TrayIcon}
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Linux:** Sometimes the icon won't be visible unless a menu is set.
|
||||
* Setting an empty {@linkcode Menu} is enough.
|
||||
*/
|
||||
static async new(options?: TrayIconOptions): Promise<TrayIcon> {
|
||||
if (options?.menu) {
|
||||
// @ts-expect-error we only need the rid and kind
|
||||
options.menu = [options.menu.rid, options.menu.kind]
|
||||
}
|
||||
if (options?.icon) {
|
||||
options.icon =
|
||||
typeof options.icon === 'string'
|
||||
? options.icon
|
||||
: Array.from(options.icon)
|
||||
}
|
||||
|
||||
const handler = new Channel<TrayIconEvent>()
|
||||
if (options?.action) {
|
||||
handler.onmessage = options.action
|
||||
delete options.action
|
||||
}
|
||||
|
||||
return invoke<[number, string]>('plugin:tray|new', {
|
||||
options: options ?? {},
|
||||
handler
|
||||
}).then(([rid, id]) => new TrayIcon(rid, id))
|
||||
}
|
||||
|
||||
/** Sets a new tray icon. If `null` is provided, it will remove the icon. */
|
||||
async setIcon(icon: string | Uint8Array | null): Promise<void> {
|
||||
let trayIcon = null
|
||||
if (icon) {
|
||||
trayIcon = typeof icon === 'string' ? icon : Array.from(icon)
|
||||
}
|
||||
return invoke('plugin:tray|set_icon', { rid: this.rid, icon: trayIcon })
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a new tray menu.
|
||||
*
|
||||
* #### Platform-specific:
|
||||
*
|
||||
* - **Linux**: once a menu is set it cannot be removed so `null` has no effect
|
||||
*/
|
||||
async setMenu(menu: Menu | Submenu | null): Promise<void> {
|
||||
if (menu) {
|
||||
// @ts-expect-error we only need the rid and kind
|
||||
menu = [menu.rid, menu.kind]
|
||||
}
|
||||
return invoke('plugin:tray|set_menu', { rid: this.rid, menu })
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip for this tray icon.
|
||||
*
|
||||
* ## Platform-specific:
|
||||
*
|
||||
* - **Linux:** Unsupported
|
||||
*/
|
||||
async setTooltip(tooltip: string | null): Promise<void> {
|
||||
return invoke('plugin:tray|set_tooltip', { rid: this.rid, tooltip })
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tooltip for this tray icon.
|
||||
*
|
||||
* ## Platform-specific:
|
||||
*
|
||||
* - **Linux:** The title will not be shown unless there is an icon
|
||||
* as well. The title is useful for numerical and other frequently
|
||||
* updated information. In general, it shouldn't be shown unless a
|
||||
* user requests it as it can take up a significant amount of space
|
||||
* on the user's panel. This may not be shown in all visualizations.
|
||||
* - **Windows:** Unsupported
|
||||
*/
|
||||
async setTitle(title: string | null): Promise<void> {
|
||||
return invoke('plugin:tray|set_title', { rid: this.rid, title })
|
||||
}
|
||||
|
||||
/** Show or hide this tray icon. */
|
||||
async setVisible(visible: boolean): Promise<void> {
|
||||
return invoke('plugin:tray|set_visible', { rid: this.rid, visible })
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the tray icon temp dir path. **Linux only**.
|
||||
*
|
||||
* On Linux, we need to write the icon to the disk and usually it will
|
||||
* be `$XDG_RUNTIME_DIR/tray-icon` or `$TEMP/tray-icon`.
|
||||
*/
|
||||
async setTempDirPath(path: string | null): Promise<void> {
|
||||
return invoke('plugin:tray|set_temp_dir_path', { rid: this.rid, path })
|
||||
}
|
||||
|
||||
/** Sets the current icon as a [template](https://developer.apple.com/documentation/appkit/nsimage/1520017-template?language=objc). **macOS only** */
|
||||
async setIconAsTemplate(asTemplate: boolean): Promise<void> {
|
||||
return invoke('plugin:tray|set_icon_as_template', {
|
||||
rid: this.rid,
|
||||
asTemplate
|
||||
})
|
||||
}
|
||||
|
||||
/** Disable or enable showing the tray menu on left click. **macOS only**. */
|
||||
async setMenuOnLeftClick(onLeft: boolean): Promise<void> {
|
||||
return invoke('plugin:tray|set_show_menu_on_left_click', {
|
||||
rid: this.rid,
|
||||
onLeft
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -1686,27 +1686,6 @@ class Window {
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
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,
|
||||
|
||||
@ -15,9 +15,9 @@
|
||||
eslint-visitor-keys "^3.3.0"
|
||||
|
||||
"@eslint-community/regexpp@^4.5.1", "@eslint-community/regexpp@^4.6.0", "@eslint-community/regexpp@^4.6.1":
|
||||
version "4.9.1"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.9.1.tgz#449dfa81a57a1d755b09aa58d826c1262e4283b4"
|
||||
integrity sha512-Y27x+MBLjXa+0JWDhykM3+JE+il3kHKAEqabfEWq3SDhZjLYb6/BHL/JKFnH3fe207JaXkyDo685Oc2Glt6ifA==
|
||||
version "4.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@eslint-community/regexpp/-/regexpp-4.10.0.tgz#548f6de556857c8bb73bbee70c35dc82a2e74d63"
|
||||
integrity sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==
|
||||
|
||||
"@eslint/eslintrc@^2.1.3":
|
||||
version "2.1.3"
|
||||
@ -91,9 +91,9 @@
|
||||
integrity sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==
|
||||
|
||||
"@jridgewell/trace-mapping@^0.3.9":
|
||||
version "0.3.19"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.19.tgz#f8a3249862f91be48d3127c3cfe992f79b4b8811"
|
||||
integrity sha512-kf37QtfW+Hwx/buWGMPcR60iF9ziHa6r/CZJIHbmcm4+0qrXiVdxegAH0F6yddEVQ7zdkjcGCgCzUu+BcbhQxw==
|
||||
version "0.3.20"
|
||||
resolved "https://registry.yarnpkg.com/@jridgewell/trace-mapping/-/trace-mapping-0.3.20.tgz#72e45707cf240fa6b081d0366f8265b0cd10197f"
|
||||
integrity sha512-R8LcPeWZol2zR8mmH3JeKQ6QRCFb7XgUhV9ZlGhHLGyg4wpPiPZNQOOWhFZhxKw8u//yTbNGI42Bx/3paXEQ+Q==
|
||||
dependencies:
|
||||
"@jridgewell/resolve-uri" "^3.1.0"
|
||||
"@jridgewell/sourcemap-codec" "^1.4.14"
|
||||
@ -206,14 +206,14 @@
|
||||
integrity sha512-dMvGV8p92GQ8jhNlGIKpyhVZPzJlT258pPrM5q2F8lKcc9Iv9BbfdnhX1OfinYWnb9ms5zLw6MlaMnqLfUkKnQ==
|
||||
|
||||
"@types/estree@^1.0.0":
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.2.tgz#ff02bc3dc8317cd668dfec247b750ba1f1d62453"
|
||||
integrity sha512-VeiPZ9MMwXjO32/Xu7+OwflfmeoRwkE/qzndw42gGtgJwZopBnzy2gD//NN1+go1mADzkDcqf/KnFRSjTJ8xJA==
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/estree/-/estree-1.0.5.tgz#a6ce3e556e00fd9895dd872dd172ad0d4bd687f4"
|
||||
integrity sha512-/kYRxGDLWzHOB7q+wtSUQlFrtcdUccpfy+X+9iMBpHK8QLLhx2wIPYuS5DYtR9Wa/YlZAbIovy7qVdB1Aq6Lyw==
|
||||
|
||||
"@types/json-schema@^7.0.12":
|
||||
version "7.0.14"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.14.tgz#74a97a5573980802f32c8e47b663530ab3b6b7d1"
|
||||
integrity sha512-U3PUjAudAdJBeC2pgN8uTIKgxrb4nlDF3SF0++EldXQvQBGkpFZMSnwQiIoDU77tv45VgNkl/L4ouD+rEomujw==
|
||||
version "7.0.15"
|
||||
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.15.tgz#596a1747233694d50f6ad8a7869fcb6f56cf5841"
|
||||
integrity sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==
|
||||
|
||||
"@types/json5@^0.0.29":
|
||||
version "0.0.29"
|
||||
@ -228,9 +228,9 @@
|
||||
undici-types "~5.26.4"
|
||||
|
||||
"@types/semver@^7.5.0":
|
||||
version "7.5.4"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.4.tgz#0a41252ad431c473158b22f9bfb9a63df7541cff"
|
||||
integrity sha512-MMzuxN3GdFwskAnb6fz0orFvhfqi752yjaXylr0Rp4oDg5H0Zn1IuyRhDVvYOwAXoJirx2xuS16I3WjxnAIHiQ==
|
||||
version "7.5.5"
|
||||
resolved "https://registry.yarnpkg.com/@types/semver/-/semver-7.5.5.tgz#deed5ab7019756c9c90ea86139106b0346223f35"
|
||||
integrity sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==
|
||||
|
||||
"@typescript-eslint/eslint-plugin@6.10.0":
|
||||
version "6.10.0"
|
||||
@ -260,17 +260,6 @@
|
||||
"@typescript-eslint/visitor-keys" "6.10.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/parser@^6.4.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/parser/-/parser-6.9.0.tgz#2b402cadeadd3f211c25820e5433413347b27391"
|
||||
integrity sha512-GZmjMh4AJ/5gaH4XF2eXA8tMnHWP+Pm1mjQR2QN4Iz+j/zO04b9TOvJYOX2sCNIQHtRStKTxRY1FX7LhpJT4Gw==
|
||||
dependencies:
|
||||
"@typescript-eslint/scope-manager" "6.9.0"
|
||||
"@typescript-eslint/types" "6.9.0"
|
||||
"@typescript-eslint/typescript-estree" "6.9.0"
|
||||
"@typescript-eslint/visitor-keys" "6.9.0"
|
||||
debug "^4.3.4"
|
||||
|
||||
"@typescript-eslint/scope-manager@6.10.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz#b0276118b13d16f72809e3cecc86a72c93708540"
|
||||
@ -279,14 +268,6 @@
|
||||
"@typescript-eslint/types" "6.10.0"
|
||||
"@typescript-eslint/visitor-keys" "6.10.0"
|
||||
|
||||
"@typescript-eslint/scope-manager@6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/scope-manager/-/scope-manager-6.9.0.tgz#2626e9a7fe0e004c3e25f3b986c75f584431134e"
|
||||
integrity sha512-1R8A9Mc39n4pCCz9o79qRO31HGNDvC7UhPhv26TovDsWPBDx+Sg3rOZdCELIA3ZmNoWAuxaMOT7aWtGRSYkQxw==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.9.0"
|
||||
"@typescript-eslint/visitor-keys" "6.9.0"
|
||||
|
||||
"@typescript-eslint/type-utils@6.10.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz#1007faede067c78bdbcef2e8abb31437e163e2e1"
|
||||
@ -302,11 +283,6 @@
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.10.0.tgz#f4f0a84aeb2ac546f21a66c6e0da92420e921367"
|
||||
integrity sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==
|
||||
|
||||
"@typescript-eslint/types@6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/types/-/types-6.9.0.tgz#86a0cbe7ac46c0761429f928467ff3d92f841098"
|
||||
integrity sha512-+KB0lbkpxBkBSiVCuQvduqMJy+I1FyDbdwSpM3IoBS7APl4Bu15lStPjgBIdykdRqQNYqYNMa8Kuidax6phaEw==
|
||||
|
||||
"@typescript-eslint/typescript-estree@6.10.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz#667381eed6f723a1a8ad7590a31f312e31e07697"
|
||||
@ -320,19 +296,6 @@
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/typescript-estree@6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/typescript-estree/-/typescript-estree-6.9.0.tgz#d0601b245be873d8fe49f3737f93f8662c8693d4"
|
||||
integrity sha512-NJM2BnJFZBEAbCfBP00zONKXvMqihZCrmwCaik0UhLr0vAgb6oguXxLX1k00oQyD+vZZ+CJn3kocvv2yxm4awQ==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.9.0"
|
||||
"@typescript-eslint/visitor-keys" "6.9.0"
|
||||
debug "^4.3.4"
|
||||
globby "^11.1.0"
|
||||
is-glob "^4.0.3"
|
||||
semver "^7.5.4"
|
||||
ts-api-utils "^1.0.1"
|
||||
|
||||
"@typescript-eslint/utils@6.10.0":
|
||||
version "6.10.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/utils/-/utils-6.10.0.tgz#4d76062d94413c30e402c9b0df8c14aef8d77336"
|
||||
@ -354,14 +317,6 @@
|
||||
"@typescript-eslint/types" "6.10.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@typescript-eslint/visitor-keys@6.9.0":
|
||||
version "6.9.0"
|
||||
resolved "https://registry.yarnpkg.com/@typescript-eslint/visitor-keys/-/visitor-keys-6.9.0.tgz#cc69421c10c4ac997ed34f453027245988164e80"
|
||||
integrity sha512-dGtAfqjV6RFOtIP8I0B4ZTBRrlTT8NHHlZZSchQx3qReaoDeXhYM++M4So2AgFK9ZB0emRPA6JI1HkafzA2Ibg==
|
||||
dependencies:
|
||||
"@typescript-eslint/types" "6.9.0"
|
||||
eslint-visitor-keys "^3.4.1"
|
||||
|
||||
"@ungap/structured-clone@^1.2.0":
|
||||
version "1.2.0"
|
||||
resolved "https://registry.yarnpkg.com/@ungap/structured-clone/-/structured-clone-1.2.0.tgz#756641adb587851b5ccb3e095daf27ae581c8406"
|
||||
@ -373,9 +328,9 @@ acorn-jsx@^5.3.2:
|
||||
integrity sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==
|
||||
|
||||
acorn@^8.8.2, acorn@^8.9.0:
|
||||
version "8.10.0"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.10.0.tgz#8be5b3907a67221a81ab23c7889c4c5526b62ec5"
|
||||
integrity sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==
|
||||
version "8.11.2"
|
||||
resolved "https://registry.yarnpkg.com/acorn/-/acorn-8.11.2.tgz#ca0d78b51895be5390a5903c5b3bdcdaf78ae40b"
|
||||
integrity sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==
|
||||
|
||||
ajv@^6.12.4:
|
||||
version "6.12.6"
|
||||
@ -509,13 +464,14 @@ builtins@^5.0.1:
|
||||
dependencies:
|
||||
semver "^7.0.0"
|
||||
|
||||
call-bind@^1.0.0, call-bind@^1.0.2:
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.2.tgz#b1d4e89e688119c3c9a903ad30abb2f6a919be3c"
|
||||
integrity sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==
|
||||
call-bind@^1.0.0, call-bind@^1.0.2, call-bind@^1.0.4, call-bind@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.5.tgz#6fa2b7845ce0ea49bf4d8b9ef64727a2c2e2e513"
|
||||
integrity sha512-C3nQxfFZxFRVoJoGKKI8y3MOEo129NQ+FgQ08iye+Mk4zNZZGdjfs06bVTr+DBSlA66Q2VEcMki/cUCP4SercQ==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
get-intrinsic "^1.0.2"
|
||||
function-bind "^1.1.2"
|
||||
get-intrinsic "^1.2.1"
|
||||
set-function-length "^1.1.1"
|
||||
|
||||
callsites@^3.0.0:
|
||||
version "3.1.0"
|
||||
@ -580,7 +536,7 @@ deep-is@^0.1.3:
|
||||
resolved "https://registry.yarnpkg.com/deep-is/-/deep-is-0.1.4.tgz#a6f2dce612fadd2ef1f519b73551f17e85199831"
|
||||
integrity sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==
|
||||
|
||||
define-data-property@^1.0.1:
|
||||
define-data-property@^1.0.1, define-data-property@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.1.tgz#c35f7cd0ab09883480d12ac5cb213715587800b3"
|
||||
integrity sha512-E7uGkTzkk1d0ByLeSc6ZsFS79Axg+m1P/VsgYsxHgiuc3tFSj+MjMIwe90FC4lOAZzNBdY7kkO2P2wKdsQ1vgQ==
|
||||
@ -620,25 +576,25 @@ doctrine@^3.0.0:
|
||||
esutils "^2.0.2"
|
||||
|
||||
es-abstract@^1.22.1:
|
||||
version "1.22.2"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.2.tgz#90f7282d91d0ad577f505e423e52d4c1d93c1b8a"
|
||||
integrity sha512-YoxfFcDmhjOgWPWsV13+2RNjq1F6UQnfs+8TftwNqtzlmFzEXvlUwdrNrYeaizfjQzRMxkZ6ElWMOJIFKdVqwA==
|
||||
version "1.22.3"
|
||||
resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.3.tgz#48e79f5573198de6dee3589195727f4f74bc4f32"
|
||||
integrity sha512-eiiY8HQeYfYH2Con2berK+To6GrK2RxbPawDkGq4UiCQQfZHb6wX9qQqkbpPqaxQFcl8d9QzZqo0tGE0VcrdwA==
|
||||
dependencies:
|
||||
array-buffer-byte-length "^1.0.0"
|
||||
arraybuffer.prototype.slice "^1.0.2"
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
call-bind "^1.0.5"
|
||||
es-set-tostringtag "^2.0.1"
|
||||
es-to-primitive "^1.2.1"
|
||||
function.prototype.name "^1.1.6"
|
||||
get-intrinsic "^1.2.1"
|
||||
get-intrinsic "^1.2.2"
|
||||
get-symbol-description "^1.0.0"
|
||||
globalthis "^1.0.3"
|
||||
gopd "^1.0.1"
|
||||
has "^1.0.3"
|
||||
has-property-descriptors "^1.0.0"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
internal-slot "^1.0.5"
|
||||
is-array-buffer "^3.0.2"
|
||||
is-callable "^1.2.7"
|
||||
@ -648,7 +604,7 @@ es-abstract@^1.22.1:
|
||||
is-string "^1.0.7"
|
||||
is-typed-array "^1.1.12"
|
||||
is-weakref "^1.0.2"
|
||||
object-inspect "^1.12.3"
|
||||
object-inspect "^1.13.1"
|
||||
object-keys "^1.1.1"
|
||||
object.assign "^4.1.4"
|
||||
regexp.prototype.flags "^1.5.1"
|
||||
@ -662,23 +618,23 @@ es-abstract@^1.22.1:
|
||||
typed-array-byte-offset "^1.0.0"
|
||||
typed-array-length "^1.0.4"
|
||||
unbox-primitive "^1.0.2"
|
||||
which-typed-array "^1.1.11"
|
||||
which-typed-array "^1.1.13"
|
||||
|
||||
es-set-tostringtag@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8"
|
||||
integrity sha512-g3OMbtlwY3QewlqAiMLI47KywjWZoEytKr8pf6iTC8uJq5bIAH52Z9pnQ8pVL6whrCto53JZDuUIsifGeLorTg==
|
||||
version "2.0.2"
|
||||
resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.2.tgz#11f7cc9f63376930a5f20be4915834f4bc74f9c9"
|
||||
integrity sha512-BuDyupZt65P9D2D2vA/zqcI3G5xRsklm5N3xCwuiy+/vKy8i0ifdsQP1sLgO4tZDSCaQUSnmC48khknGMV3D2Q==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.3"
|
||||
has "^1.0.3"
|
||||
get-intrinsic "^1.2.2"
|
||||
has-tostringtag "^1.0.0"
|
||||
hasown "^2.0.0"
|
||||
|
||||
es-shim-unscopables@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241"
|
||||
integrity sha512-Jm6GPcCdC30eMLbZ2x8z2WuRwAws3zTBBKuusffYVUrNj/GVSUAZ+xKMaUpfNDR5IbyNA5LJbaecoUVbmUcB1w==
|
||||
version "1.0.2"
|
||||
resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.2.tgz#1f6942e71ecc7835ed1c8a83006d8771a63a3763"
|
||||
integrity sha512-J3yBRXCzDu4ULnQwxyToo/OjdMx6akgVC7K6few0a7F/0wLtmKKN7I73AH5T2836UuXRqN7Qg+IIUw/+YJksRw==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
es-to-primitive@^1.2.1:
|
||||
version "1.2.1"
|
||||
@ -699,19 +655,6 @@ eslint-config-prettier@9.0.0:
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-9.0.0.tgz#eb25485946dd0c66cd216a46232dc05451518d1f"
|
||||
integrity sha512-IcJsTkJae2S35pRsRAwoCE+925rJJStOdkKnLVgtE+tEpqU0EVVM7OqrwxqgptKdX29NUwC82I5pXsGFIgSevw==
|
||||
|
||||
eslint-config-standard-with-typescript@39.1.1:
|
||||
version "39.1.1"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard-with-typescript/-/eslint-config-standard-with-typescript-39.1.1.tgz#d682bd1fc8f1ee996940f85c9b0a833d7cfa5fee"
|
||||
integrity sha512-t6B5Ep8E4I18uuoYeYxINyqcXb2UbC0SOOTxRtBSt2JUs+EzeXbfe2oaiPs71AIdnoWhXDO2fYOHz8df3kV84A==
|
||||
dependencies:
|
||||
"@typescript-eslint/parser" "^6.4.0"
|
||||
eslint-config-standard "17.1.0"
|
||||
|
||||
eslint-config-standard@17.1.0:
|
||||
version "17.1.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-17.1.0.tgz#40ffb8595d47a6b242e07cbfd49dc211ed128975"
|
||||
integrity sha512-IwHwmaBNtDK4zDHQukFDW5u/aTb8+meQWZvNFWkiGmbWjD6bqyuSSBxxXKkCftCUzc1zwCH2m/baCNDLGmuO5Q==
|
||||
|
||||
eslint-import-resolver-node@^0.3.9:
|
||||
version "0.3.9"
|
||||
resolved "https://registry.yarnpkg.com/eslint-import-resolver-node/-/eslint-import-resolver-node-0.3.9.tgz#d4eaac52b8a2e7c3cd1903eb00f7e053356118ac"
|
||||
@ -729,9 +672,9 @@ eslint-module-utils@^2.8.0:
|
||||
debug "^3.2.7"
|
||||
|
||||
eslint-plugin-es-x@^7.1.0:
|
||||
version "7.2.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.2.0.tgz#5779d742ad31f8fd780b9481331481e142b72311"
|
||||
integrity sha512-9dvv5CcvNjSJPqnS5uZkqb3xmbeqRLnvXKK7iI5+oK/yTusyc46zbBZKENGsOfojm/mKfszyZb+wNqNPAPeGXA==
|
||||
version "7.3.0"
|
||||
resolved "https://registry.yarnpkg.com/eslint-plugin-es-x/-/eslint-plugin-es-x-7.3.0.tgz#c699280ad35cd315720c3cccf0fe503092c08788"
|
||||
integrity sha512-W9zIs+k00I/I13+Bdkl/zG1MEO07G97XjUSQuH117w620SJ6bHtLUmoMvkGA2oYnI/gNdr+G7BONLyYnFaLLEQ==
|
||||
dependencies:
|
||||
"@eslint-community/eslint-utils" "^4.1.2"
|
||||
"@eslint-community/regexpp" "^4.6.0"
|
||||
@ -918,7 +861,7 @@ fast-deep-equal@^3.1.1, fast-deep-equal@^3.1.3:
|
||||
resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz#3a7d56b559d6cbc3eb512325244e619a65c6c525"
|
||||
integrity sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==
|
||||
|
||||
fast-glob@3.3.2:
|
||||
fast-glob@3.3.2, fast-glob@^3.2.9:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.2.tgz#a904501e57cfdd2ffcded45e99a54fef55e46129"
|
||||
integrity sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==
|
||||
@ -929,17 +872,6 @@ fast-glob@3.3.2:
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-glob@^3.2.9:
|
||||
version "3.3.1"
|
||||
resolved "https://registry.yarnpkg.com/fast-glob/-/fast-glob-3.3.1.tgz#784b4e897340f3dbbef17413b3f11acf03c874c4"
|
||||
integrity sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==
|
||||
dependencies:
|
||||
"@nodelib/fs.stat" "^2.0.2"
|
||||
"@nodelib/fs.walk" "^1.2.3"
|
||||
glob-parent "^5.1.2"
|
||||
merge2 "^1.3.0"
|
||||
micromatch "^4.0.4"
|
||||
|
||||
fast-json-stable-stringify@^2.0.0:
|
||||
version "2.1.0"
|
||||
resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633"
|
||||
@ -980,9 +912,9 @@ find-up@^5.0.0:
|
||||
path-exists "^4.0.0"
|
||||
|
||||
flat-cache@^3.0.4:
|
||||
version "3.1.1"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.1.1.tgz#a02a15fdec25a8f844ff7cc658f03dd99eb4609b"
|
||||
integrity sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==
|
||||
version "3.2.0"
|
||||
resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-3.2.0.tgz#2c0c2d5040c99b1632771a9d105725c0115363ee"
|
||||
integrity sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==
|
||||
dependencies:
|
||||
flatted "^3.2.9"
|
||||
keyv "^4.5.3"
|
||||
@ -1010,7 +942,7 @@ fsevents@~2.3.2:
|
||||
resolved "https://registry.yarnpkg.com/fsevents/-/fsevents-2.3.3.tgz#cac6407785d03675a2a5e1a5305c697b347d90d6"
|
||||
integrity sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==
|
||||
|
||||
function-bind@^1.1.1, function-bind@^1.1.2:
|
||||
function-bind@^1.1.2:
|
||||
version "1.1.2"
|
||||
resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c"
|
||||
integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==
|
||||
@ -1030,15 +962,15 @@ functions-have-names@^1.2.3:
|
||||
resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834"
|
||||
integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==
|
||||
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1:
|
||||
version "1.2.1"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.1.tgz#d295644fed4505fc9cde952c37ee12b477a83d82"
|
||||
integrity sha512-2DcsyfABl+gVHEfCOaTrWgyt+tb6MSEGmKq+kI5HwLbIYgjgmMcV8KQ41uaKz1xxUcn9tJtgFbQUEVcEbd0FYw==
|
||||
get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@^1.2.0, get-intrinsic@^1.2.1, get-intrinsic@^1.2.2:
|
||||
version "1.2.2"
|
||||
resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.2.tgz#281b7622971123e1ef4b3c90fd7539306da93f3b"
|
||||
integrity sha512-0gSo4ml/0j98Y3lngkFEot/zhiCeWsbYIlZ+uZOVgzLyLaUw7wxUL+nCTP0XJvJg1AXulJRI3UJi8GsbDuxdGA==
|
||||
dependencies:
|
||||
function-bind "^1.1.1"
|
||||
has "^1.0.3"
|
||||
function-bind "^1.1.2"
|
||||
has-proto "^1.0.1"
|
||||
has-symbols "^1.0.3"
|
||||
hasown "^2.0.0"
|
||||
|
||||
get-symbol-description@^1.0.0:
|
||||
version "1.0.0"
|
||||
@ -1130,11 +1062,11 @@ has-flag@^4.0.0:
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
|
||||
has-property-descriptors@^1.0.0:
|
||||
version "1.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.0.tgz#610708600606d36961ed04c196193b6a607fa861"
|
||||
integrity sha512-62DVLZGoiEBDHQyqG4w9xCuZ7eJEwNmJRWw2VY84Oedb7WFcA27fiEVe8oUQx9hAUJ4ekurquucTGwsyO1XGdQ==
|
||||
version "1.0.1"
|
||||
resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.1.tgz#52ba30b6c5ec87fd89fa574bc1c39125c6f65340"
|
||||
integrity sha512-VsX8eaIewvas0xnvinAe9bw4WfIeODpGYikiWYLH+dma0Jw6KHYqWiWfhQlgOVK8D6PvjubK5Uc4P0iIhIcNVg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.1.1"
|
||||
get-intrinsic "^1.2.2"
|
||||
|
||||
has-proto@^1.0.1:
|
||||
version "1.0.1"
|
||||
@ -1153,11 +1085,6 @@ has-tostringtag@^1.0.0:
|
||||
dependencies:
|
||||
has-symbols "^1.0.2"
|
||||
|
||||
has@^1.0.3:
|
||||
version "1.0.4"
|
||||
resolved "https://registry.yarnpkg.com/has/-/has-1.0.4.tgz#2eb2860e000011dae4f1406a86fe80e530fb2ec6"
|
||||
integrity sha512-qdSAmqLF6209RFj4VVItywPMbm3vWylknmB3nvNiUIs72xAimcM8nVYxYr7ncvZq5qzk9MKIZR8ijqD/1QuYjQ==
|
||||
|
||||
hasown@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.0.tgz#f4c513d454a57b7c7e1650778de226b11700546c"
|
||||
@ -1197,12 +1124,12 @@ inherits@2:
|
||||
integrity sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==
|
||||
|
||||
internal-slot@^1.0.5:
|
||||
version "1.0.5"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.5.tgz#f2a2ee21f668f8627a4667f309dc0f4fb6674986"
|
||||
integrity sha512-Y+R5hJrzs52QCG2laLn4udYVnxsfny9CpOhNhUvk/SSSVyF6T27FzRbF0sroPidSu3X8oEAkOn2K804mjpt6UQ==
|
||||
version "1.0.6"
|
||||
resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.6.tgz#37e756098c4911c5e912b8edbf71ed3aa116f930"
|
||||
integrity sha512-Xj6dv+PsbtwyPpEflsejS+oIZxmMlV44zAhG479uYu89MsjcYOhCFnNyKrkJrihbsiasQyY0afoCl/9BLR65bg==
|
||||
dependencies:
|
||||
get-intrinsic "^1.2.0"
|
||||
has "^1.0.3"
|
||||
get-intrinsic "^1.2.2"
|
||||
hasown "^2.0.0"
|
||||
side-channel "^1.0.4"
|
||||
|
||||
is-array-buffer@^3.0.1, is-array-buffer@^3.0.2:
|
||||
@ -1234,14 +1161,7 @@ is-callable@^1.1.3, is-callable@^1.1.4, is-callable@^1.2.7:
|
||||
resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.2.7.tgz#3bc2a85ea742d9e36205dcacdd72ca1fdc51b055"
|
||||
integrity sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==
|
||||
|
||||
is-core-module@^2.12.1, is-core-module@^2.13.0:
|
||||
version "2.13.0"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.0.tgz#bb52aa6e2cbd49a30c2ba68c42bf3435ba6072db"
|
||||
integrity sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==
|
||||
dependencies:
|
||||
has "^1.0.3"
|
||||
|
||||
is-core-module@^2.13.1:
|
||||
is-core-module@^2.12.1, is-core-module@^2.13.0, is-core-module@^2.13.1:
|
||||
version "2.13.1"
|
||||
resolved "https://registry.yarnpkg.com/is-core-module/-/is-core-module-2.13.1.tgz#ad0d7532c6fea9da1ebdc82742d74525c6273384"
|
||||
integrity sha512-hHrIjvZsftOsvKSn2TRYl63zvxsgE0K+0mYMoH6gD4omR5IWB2KynivBQczo3+wF1cCkjzvptnI9Q0sPU66ilw==
|
||||
@ -1445,10 +1365,10 @@ natural-compare@^1.4.0:
|
||||
resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7"
|
||||
integrity sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==
|
||||
|
||||
object-inspect@^1.12.3, object-inspect@^1.9.0:
|
||||
version "1.13.0"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.0.tgz#42695d3879e1cd5bda6df5062164d80c996e23e2"
|
||||
integrity sha512-HQ4J+ic8hKrgIt3mqk6cVOVrW2ozL4KdvHlqpBv9vDYWx9ysAgENAdvy4FoGF+KFdhR7nQTNm5J0ctAeOwn+3g==
|
||||
object-inspect@^1.13.1, object-inspect@^1.9.0:
|
||||
version "1.13.1"
|
||||
resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2"
|
||||
integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ==
|
||||
|
||||
object-keys@^1.1.1:
|
||||
version "1.1.1"
|
||||
@ -1574,9 +1494,9 @@ prettier@3.0.3:
|
||||
integrity sha512-L/4pUDMxcNa8R/EthV08Zt42WBO4h1rarVtK0K+QJG0X187OLo7l699jWw0GKuwzkPQ//jMFA/8Xm6Fh3J/DAg==
|
||||
|
||||
punycode@^2.1.0:
|
||||
version "2.3.0"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.0.tgz#f67fa67c94da8f4d0cfff981aee4118064199b8f"
|
||||
integrity sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==
|
||||
version "2.3.1"
|
||||
resolved "https://registry.yarnpkg.com/punycode/-/punycode-2.3.1.tgz#027422e2faec0b25e1549c3e1bd8309b9133b6e5"
|
||||
integrity sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==
|
||||
|
||||
queue-microtask@^1.2.2:
|
||||
version "1.2.3"
|
||||
@ -1716,6 +1636,16 @@ serialize-javascript@^6.0.1:
|
||||
dependencies:
|
||||
randombytes "^2.1.0"
|
||||
|
||||
set-function-length@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.1.1.tgz#4bc39fafb0307224a33e106a7d35ca1218d659ed"
|
||||
integrity sha512-VoaqjbBJKiWtg4yRcKBQ7g7wnGnLV3M8oLvVWwOk2PdYY6PEFegR1vezXR0tw6fZGF9csVakIRjrJiy2veSBFQ==
|
||||
dependencies:
|
||||
define-data-property "^1.1.1"
|
||||
get-intrinsic "^1.2.1"
|
||||
gopd "^1.0.1"
|
||||
has-property-descriptors "^1.0.0"
|
||||
|
||||
set-function-name@^2.0.0:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.1.tgz#12ce38b7954310b9f61faa12701620a0c882793a"
|
||||
@ -1826,9 +1756,9 @@ supports-preserve-symlinks-flag@^1.0.0:
|
||||
integrity sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==
|
||||
|
||||
terser@^5.17.4:
|
||||
version "5.22.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.22.0.tgz#4f18103f84c5c9437aafb7a14918273310a8a49d"
|
||||
integrity sha512-hHZVLgRA2z4NWcN6aS5rQDc+7Dcy58HOf2zbYwmFcQ+ua3h6eEFf5lIDKTzbWwlazPyOZsFQO8V80/IjVNExEw==
|
||||
version "5.24.0"
|
||||
resolved "https://registry.yarnpkg.com/terser/-/terser-5.24.0.tgz#4ae50302977bca4831ccc7b4fef63a3c04228364"
|
||||
integrity sha512-ZpGR4Hy3+wBEzVEnHvstMvqpD/nABNelQn/z2r0fjVWGQsN3bpOLzQlqDxmb4CDZnXq5lpjnQ+mHQLAOpfM5iw==
|
||||
dependencies:
|
||||
"@jridgewell/source-map" "^0.3.3"
|
||||
acorn "^8.8.2"
|
||||
@ -1956,13 +1886,13 @@ which-boxed-primitive@^1.0.2:
|
||||
is-string "^1.0.5"
|
||||
is-symbol "^1.0.3"
|
||||
|
||||
which-typed-array@^1.1.11:
|
||||
version "1.1.11"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.11.tgz#99d691f23c72aab6768680805a271b69761ed61a"
|
||||
integrity sha512-qe9UWWpkeG5yzZ0tNYxDmd7vo58HDBc39mZ0xWWpolAGADdFOzkfamWLDxkOWcvHQKVmdTyQdLD4NOfjLWTKew==
|
||||
which-typed-array@^1.1.11, which-typed-array@^1.1.13:
|
||||
version "1.1.13"
|
||||
resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.13.tgz#870cd5be06ddb616f504e7b039c4c24898184d36"
|
||||
integrity sha512-P5Nra0qjSncduVPEAr7xhoF5guty49ArDTwzJ/yNuPIbZppyRxFQsRCWrocxIY+CnMVG+qfbU2FmDKyvSGClow==
|
||||
dependencies:
|
||||
available-typed-arrays "^1.0.5"
|
||||
call-bind "^1.0.2"
|
||||
call-bind "^1.0.4"
|
||||
for-each "^0.3.3"
|
||||
gopd "^1.0.1"
|
||||
has-tostringtag "^1.0.0"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user