diff --git a/package-lock.json b/package-lock.json index e2f56b8..65349b7 100644 --- a/package-lock.json +++ b/package-lock.json @@ -28,6 +28,7 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.8", "@tauri-apps/api": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.4.2", "@tauri-apps/plugin-shell": "^2.3.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", @@ -3070,6 +3071,15 @@ "node": ">= 10" } }, + "node_modules/@tauri-apps/plugin-dialog": { + "version": "2.4.2", + "resolved": "https://registry.npmmirror.com/@tauri-apps/plugin-dialog/-/plugin-dialog-2.4.2.tgz", + "integrity": "sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==", + "license": "MIT OR Apache-2.0", + "dependencies": { + "@tauri-apps/api": "^2.8.0" + } + }, "node_modules/@tauri-apps/plugin-shell": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/@tauri-apps/plugin-shell/-/plugin-shell-2.3.3.tgz", diff --git a/package.json b/package.json index 6167d8e..d4344a6 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "@radix-ui/react-toast": "^1.2.15", "@radix-ui/react-tooltip": "^1.2.8", "@tauri-apps/api": "^2.0.0", + "@tauri-apps/plugin-dialog": "^2.4.2", "@tauri-apps/plugin-shell": "^2.3.3", "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", diff --git a/src-tauri/Cargo.lock b/src-tauri/Cargo.lock index 3886398..095ed19 100644 --- a/src-tauri/Cargo.lock +++ b/src-tauri/Cargo.lock @@ -59,6 +59,27 @@ version = "1.0.100" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +[[package]] +name = "ashpd" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" +dependencies = [ + "enumflags2", + "futures-channel", + "futures-util", + "rand 0.9.2", + "raw-window-handle", + "serde", + "serde_repr", + "tokio", + "url", + "wayland-backend", + "wayland-client", + "wayland-protocols", + "zbus", +] + [[package]] name = "async-broadcast" version = "0.7.2" @@ -781,6 +802,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" dependencies = [ "bitflags 2.10.0", + "block2 0.6.2", + "libc", "objc2 0.6.3", ] @@ -795,6 +818,15 @@ dependencies = [ "syn 2.0.109", ] +[[package]] +name = "dlib" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" +dependencies = [ + "libloading", +] + [[package]] name = "dlopen2" version = "0.8.0" @@ -818,6 +850,12 @@ dependencies = [ "syn 2.0.109", ] +[[package]] +name = "downcast-rs" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" + [[package]] name = "dpi" version = "0.1.2" @@ -873,6 +911,7 @@ dependencies = [ "sha2", "tauri", "tauri-build", + "tauri-plugin-dialog", "tauri-plugin-shell", "tauri-plugin-single-instance", "tempfile", @@ -3080,7 +3119,7 @@ checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" dependencies = [ "base64 0.22.1", "indexmap 2.12.0", - "quick-xml", + "quick-xml 0.38.3", "serde", "time", ] @@ -3210,6 +3249,15 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "quick-xml" +version = "0.37.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" +dependencies = [ + "memchr", +] + [[package]] name = "quick-xml" version = "0.38.3" @@ -3259,6 +3307,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -3279,6 +3337,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -3297,6 +3365,15 @@ dependencies = [ "getrandom 0.2.16", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -3433,6 +3510,31 @@ dependencies = [ "web-sys", ] +[[package]] +name = "rfd" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" +dependencies = [ + "ashpd", + "block2 0.6.2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2 0.6.3", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.59.0", +] + [[package]] name = "ring" version = "0.17.14" @@ -3606,6 +3708,12 @@ dependencies = [ "syn 2.0.109", ] +[[package]] +name = "scoped-tls" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" + [[package]] name = "scopeguard" version = "1.2.0" @@ -4343,6 +4451,46 @@ dependencies = [ "walkdir", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "313f8138692ddc4a2127c4c9607d616a46f5c042e77b3722450866da0aad2f19" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.17", + "url", +] + +[[package]] +name = "tauri-plugin-fs" +version = "2.4.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "47df422695255ecbe7bac7012440eddaeefd026656171eac9559f5243d3230d9" +dependencies = [ + "anyhow", + "dunce", + "glob", + "percent-encoding", + "schemars 0.8.22", + "serde", + "serde_json", + "serde_repr", + "tauri", + "tauri-plugin", + "tauri-utils", + "thiserror 2.0.17", + "toml 0.9.8", + "url", +] + [[package]] name = "tauri-plugin-shell" version = "2.3.3" @@ -4607,6 +4755,7 @@ dependencies = [ "signal-hook-registry", "socket2", "tokio-macros", + "tracing", "windows-sys 0.61.2", ] @@ -5213,6 +5362,66 @@ dependencies = [ "web-sys", ] +[[package]] +name = "wayland-backend" +version = "0.3.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" +dependencies = [ + "cc", + "downcast-rs", + "rustix", + "scoped-tls", + "smallvec", + "wayland-sys", +] + +[[package]] +name = "wayland-client" +version = "0.31.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" +dependencies = [ + "bitflags 2.10.0", + "rustix", + "wayland-backend", + "wayland-scanner", +] + +[[package]] +name = "wayland-protocols" +version = "0.32.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" +dependencies = [ + "bitflags 2.10.0", + "wayland-backend", + "wayland-client", + "wayland-scanner", +] + +[[package]] +name = "wayland-scanner" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" +dependencies = [ + "proc-macro2", + "quick-xml 0.37.5", + "quote", +] + +[[package]] +name = "wayland-sys" +version = "0.31.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" +dependencies = [ + "dlib", + "log", + "pkg-config", +] + [[package]] name = "web-sys" version = "0.3.82" @@ -5967,6 +6176,7 @@ dependencies = [ "ordered-stream", "serde", "serde_repr", + "tokio", "tracing", "uds_windows", "uuid", @@ -6093,6 +6303,7 @@ dependencies = [ "endi", "enumflags2", "serde", + "url", "winnow 0.7.13", "zvariant_derive", "zvariant_utils", diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index d6851fb..7e56506 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -17,6 +17,7 @@ tauri-build = { version = "2", features = [] } tauri = { version = "2", features = ["tray-icon"] } tauri-plugin-shell = "2" tauri-plugin-single-instance = "2" +tauri-plugin-dialog = "2" serde = { version = "1", features = ["derive"] } serde_json = "1" dirs = "6" diff --git a/src-tauri/capabilities/default.json b/src-tauri/capabilities/default.json index aeaba5b..12718c1 100644 --- a/src-tauri/capabilities/default.json +++ b/src-tauri/capabilities/default.json @@ -3,5 +3,5 @@ "identifier": "default", "description": "Default permissions for the application", "windows": ["main"], - "permissions": ["core:default", "shell:allow-open"] + "permissions": ["core:default", "shell:allow-open", "dialog:allow-open"] } diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json index 86cdb1f..35f90a7 100644 --- a/src-tauri/gen/schemas/acl-manifests.json +++ b/src-tauri/gen/schemas/acl-manifests.json @@ -1 +1 @@ -{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}} \ No newline at end of file +{"core":{"default_permission":{"identifier":"default","description":"Default core plugins set.","permissions":["core:path:default","core:event:default","core:window:default","core:webview:default","core:app:default","core:image:default","core:resources:default","core:menu:default","core:tray:default"]},"permissions":{},"permission_sets":{},"global_scope_schema":null},"core:app":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-version","allow-name","allow-tauri-version","allow-identifier","allow-bundle-type","allow-register-listener","allow-remove-listener"]},"permissions":{"allow-app-hide":{"identifier":"allow-app-hide","description":"Enables the app_hide command without any pre-configured scope.","commands":{"allow":["app_hide"],"deny":[]}},"allow-app-show":{"identifier":"allow-app-show","description":"Enables the app_show command without any pre-configured scope.","commands":{"allow":["app_show"],"deny":[]}},"allow-bundle-type":{"identifier":"allow-bundle-type","description":"Enables the bundle_type command without any pre-configured scope.","commands":{"allow":["bundle_type"],"deny":[]}},"allow-default-window-icon":{"identifier":"allow-default-window-icon","description":"Enables the default_window_icon command without any pre-configured scope.","commands":{"allow":["default_window_icon"],"deny":[]}},"allow-fetch-data-store-identifiers":{"identifier":"allow-fetch-data-store-identifiers","description":"Enables the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":["fetch_data_store_identifiers"],"deny":[]}},"allow-identifier":{"identifier":"allow-identifier","description":"Enables the identifier command without any pre-configured scope.","commands":{"allow":["identifier"],"deny":[]}},"allow-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"deny":[]}},"allow-register-listener":{"identifier":"allow-register-listener","description":"Enables the register_listener command without any pre-configured scope.","commands":{"allow":["register_listener"],"deny":[]}},"allow-remove-data-store":{"identifier":"allow-remove-data-store","description":"Enables the remove_data_store command without any pre-configured scope.","commands":{"allow":["remove_data_store"],"deny":[]}},"allow-remove-listener":{"identifier":"allow-remove-listener","description":"Enables the remove_listener command without any pre-configured scope.","commands":{"allow":["remove_listener"],"deny":[]}},"allow-set-app-theme":{"identifier":"allow-set-app-theme","description":"Enables the set_app_theme command without any pre-configured scope.","commands":{"allow":["set_app_theme"],"deny":[]}},"allow-set-dock-visibility":{"identifier":"allow-set-dock-visibility","description":"Enables the set_dock_visibility command without any pre-configured scope.","commands":{"allow":["set_dock_visibility"],"deny":[]}},"allow-tauri-version":{"identifier":"allow-tauri-version","description":"Enables the tauri_version command without any pre-configured scope.","commands":{"allow":["tauri_version"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-app-hide":{"identifier":"deny-app-hide","description":"Denies the app_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["app_hide"]}},"deny-app-show":{"identifier":"deny-app-show","description":"Denies the app_show command without any pre-configured scope.","commands":{"allow":[],"deny":["app_show"]}},"deny-bundle-type":{"identifier":"deny-bundle-type","description":"Denies the bundle_type command without any pre-configured scope.","commands":{"allow":[],"deny":["bundle_type"]}},"deny-default-window-icon":{"identifier":"deny-default-window-icon","description":"Denies the default_window_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["default_window_icon"]}},"deny-fetch-data-store-identifiers":{"identifier":"deny-fetch-data-store-identifiers","description":"Denies the fetch_data_store_identifiers command without any pre-configured scope.","commands":{"allow":[],"deny":["fetch_data_store_identifiers"]}},"deny-identifier":{"identifier":"deny-identifier","description":"Denies the identifier command without any pre-configured scope.","commands":{"allow":[],"deny":["identifier"]}},"deny-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"deny-register-listener":{"identifier":"deny-register-listener","description":"Denies the register_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["register_listener"]}},"deny-remove-data-store":{"identifier":"deny-remove-data-store","description":"Denies the remove_data_store command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_data_store"]}},"deny-remove-listener":{"identifier":"deny-remove-listener","description":"Denies the remove_listener command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_listener"]}},"deny-set-app-theme":{"identifier":"deny-set-app-theme","description":"Denies the set_app_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_app_theme"]}},"deny-set-dock-visibility":{"identifier":"deny-set-dock-visibility","description":"Denies the set_dock_visibility command without any pre-configured scope.","commands":{"allow":[],"deny":["set_dock_visibility"]}},"deny-tauri-version":{"identifier":"deny-tauri-version","description":"Denies the tauri_version command without any pre-configured scope.","commands":{"allow":[],"deny":["tauri_version"]}},"deny-version":{"identifier":"deny-version","description":"Denies the version command without any pre-configured scope.","commands":{"allow":[],"deny":["version"]}}},"permission_sets":{},"global_scope_schema":null},"core:event":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-listen","allow-unlisten","allow-emit","allow-emit-to"]},"permissions":{"allow-emit":{"identifier":"allow-emit","description":"Enables the emit command without any pre-configured scope.","commands":{"allow":["emit"],"deny":[]}},"allow-emit-to":{"identifier":"allow-emit-to","description":"Enables the emit_to command without any pre-configured scope.","commands":{"allow":["emit_to"],"deny":[]}},"allow-listen":{"identifier":"allow-listen","description":"Enables the listen command without any pre-configured scope.","commands":{"allow":["listen"],"deny":[]}},"allow-unlisten":{"identifier":"allow-unlisten","description":"Enables the unlisten command without any pre-configured scope.","commands":{"allow":["unlisten"],"deny":[]}},"deny-emit":{"identifier":"deny-emit","description":"Denies the emit command without any pre-configured scope.","commands":{"allow":[],"deny":["emit"]}},"deny-emit-to":{"identifier":"deny-emit-to","description":"Denies the emit_to command without any pre-configured scope.","commands":{"allow":[],"deny":["emit_to"]}},"deny-listen":{"identifier":"deny-listen","description":"Denies the listen command without any pre-configured scope.","commands":{"allow":[],"deny":["listen"]}},"deny-unlisten":{"identifier":"deny-unlisten","description":"Denies the unlisten command without any pre-configured scope.","commands":{"allow":[],"deny":["unlisten"]}}},"permission_sets":{},"global_scope_schema":null},"core:image":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-from-bytes","allow-from-path","allow-rgba","allow-size"]},"permissions":{"allow-from-bytes":{"identifier":"allow-from-bytes","description":"Enables the from_bytes command without any pre-configured scope.","commands":{"allow":["from_bytes"],"deny":[]}},"allow-from-path":{"identifier":"allow-from-path","description":"Enables the from_path command without any pre-configured scope.","commands":{"allow":["from_path"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-rgba":{"identifier":"allow-rgba","description":"Enables the rgba command without any pre-configured scope.","commands":{"allow":["rgba"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"deny-from-bytes":{"identifier":"deny-from-bytes","description":"Denies the from_bytes command without any pre-configured scope.","commands":{"allow":[],"deny":["from_bytes"]}},"deny-from-path":{"identifier":"deny-from-path","description":"Denies the from_path command without any pre-configured scope.","commands":{"allow":[],"deny":["from_path"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-rgba":{"identifier":"deny-rgba","description":"Denies the rgba command without any pre-configured scope.","commands":{"allow":[],"deny":["rgba"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}}},"permission_sets":{},"global_scope_schema":null},"core:menu":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-append","allow-prepend","allow-insert","allow-remove","allow-remove-at","allow-items","allow-get","allow-popup","allow-create-default","allow-set-as-app-menu","allow-set-as-window-menu","allow-text","allow-set-text","allow-is-enabled","allow-set-enabled","allow-set-accelerator","allow-set-as-windows-menu-for-nsapp","allow-set-as-help-menu-for-nsapp","allow-is-checked","allow-set-checked","allow-set-icon"]},"permissions":{"allow-append":{"identifier":"allow-append","description":"Enables the append command without any pre-configured scope.","commands":{"allow":["append"],"deny":[]}},"allow-create-default":{"identifier":"allow-create-default","description":"Enables the create_default command without any pre-configured scope.","commands":{"allow":["create_default"],"deny":[]}},"allow-get":{"identifier":"allow-get","description":"Enables the get command without any pre-configured scope.","commands":{"allow":["get"],"deny":[]}},"allow-insert":{"identifier":"allow-insert","description":"Enables the insert command without any pre-configured scope.","commands":{"allow":["insert"],"deny":[]}},"allow-is-checked":{"identifier":"allow-is-checked","description":"Enables the is_checked command without any pre-configured scope.","commands":{"allow":["is_checked"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-items":{"identifier":"allow-items","description":"Enables the items command without any pre-configured scope.","commands":{"allow":["items"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-popup":{"identifier":"allow-popup","description":"Enables the popup command without any pre-configured scope.","commands":{"allow":["popup"],"deny":[]}},"allow-prepend":{"identifier":"allow-prepend","description":"Enables the prepend command without any pre-configured scope.","commands":{"allow":["prepend"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-remove-at":{"identifier":"allow-remove-at","description":"Enables the remove_at command without any pre-configured scope.","commands":{"allow":["remove_at"],"deny":[]}},"allow-set-accelerator":{"identifier":"allow-set-accelerator","description":"Enables the set_accelerator command without any pre-configured scope.","commands":{"allow":["set_accelerator"],"deny":[]}},"allow-set-as-app-menu":{"identifier":"allow-set-as-app-menu","description":"Enables the set_as_app_menu command without any pre-configured scope.","commands":{"allow":["set_as_app_menu"],"deny":[]}},"allow-set-as-help-menu-for-nsapp":{"identifier":"allow-set-as-help-menu-for-nsapp","description":"Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_help_menu_for_nsapp"],"deny":[]}},"allow-set-as-window-menu":{"identifier":"allow-set-as-window-menu","description":"Enables the set_as_window_menu command without any pre-configured scope.","commands":{"allow":["set_as_window_menu"],"deny":[]}},"allow-set-as-windows-menu-for-nsapp":{"identifier":"allow-set-as-windows-menu-for-nsapp","description":"Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":["set_as_windows_menu_for_nsapp"],"deny":[]}},"allow-set-checked":{"identifier":"allow-set-checked","description":"Enables the set_checked command without any pre-configured scope.","commands":{"allow":["set_checked"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-text":{"identifier":"allow-set-text","description":"Enables the set_text command without any pre-configured scope.","commands":{"allow":["set_text"],"deny":[]}},"allow-text":{"identifier":"allow-text","description":"Enables the text command without any pre-configured scope.","commands":{"allow":["text"],"deny":[]}},"deny-append":{"identifier":"deny-append","description":"Denies the append command without any pre-configured scope.","commands":{"allow":[],"deny":["append"]}},"deny-create-default":{"identifier":"deny-create-default","description":"Denies the create_default command without any pre-configured scope.","commands":{"allow":[],"deny":["create_default"]}},"deny-get":{"identifier":"deny-get","description":"Denies the get command without any pre-configured scope.","commands":{"allow":[],"deny":["get"]}},"deny-insert":{"identifier":"deny-insert","description":"Denies the insert command without any pre-configured scope.","commands":{"allow":[],"deny":["insert"]}},"deny-is-checked":{"identifier":"deny-is-checked","description":"Denies the is_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["is_checked"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-items":{"identifier":"deny-items","description":"Denies the items command without any pre-configured scope.","commands":{"allow":[],"deny":["items"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-popup":{"identifier":"deny-popup","description":"Denies the popup command without any pre-configured scope.","commands":{"allow":[],"deny":["popup"]}},"deny-prepend":{"identifier":"deny-prepend","description":"Denies the prepend command without any pre-configured scope.","commands":{"allow":[],"deny":["prepend"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-remove-at":{"identifier":"deny-remove-at","description":"Denies the remove_at command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_at"]}},"deny-set-accelerator":{"identifier":"deny-set-accelerator","description":"Denies the set_accelerator command without any pre-configured scope.","commands":{"allow":[],"deny":["set_accelerator"]}},"deny-set-as-app-menu":{"identifier":"deny-set-as-app-menu","description":"Denies the set_as_app_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_app_menu"]}},"deny-set-as-help-menu-for-nsapp":{"identifier":"deny-set-as-help-menu-for-nsapp","description":"Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_help_menu_for_nsapp"]}},"deny-set-as-window-menu":{"identifier":"deny-set-as-window-menu","description":"Denies the set_as_window_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_window_menu"]}},"deny-set-as-windows-menu-for-nsapp":{"identifier":"deny-set-as-windows-menu-for-nsapp","description":"Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.","commands":{"allow":[],"deny":["set_as_windows_menu_for_nsapp"]}},"deny-set-checked":{"identifier":"deny-set-checked","description":"Denies the set_checked command without any pre-configured scope.","commands":{"allow":[],"deny":["set_checked"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-text":{"identifier":"deny-set-text","description":"Denies the set_text command without any pre-configured scope.","commands":{"allow":[],"deny":["set_text"]}},"deny-text":{"identifier":"deny-text","description":"Denies the text command without any pre-configured scope.","commands":{"allow":[],"deny":["text"]}}},"permission_sets":{},"global_scope_schema":null},"core:path":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-resolve-directory","allow-resolve","allow-normalize","allow-join","allow-dirname","allow-extname","allow-basename","allow-is-absolute"]},"permissions":{"allow-basename":{"identifier":"allow-basename","description":"Enables the basename command without any pre-configured scope.","commands":{"allow":["basename"],"deny":[]}},"allow-dirname":{"identifier":"allow-dirname","description":"Enables the dirname command without any pre-configured scope.","commands":{"allow":["dirname"],"deny":[]}},"allow-extname":{"identifier":"allow-extname","description":"Enables the extname command without any pre-configured scope.","commands":{"allow":["extname"],"deny":[]}},"allow-is-absolute":{"identifier":"allow-is-absolute","description":"Enables the is_absolute command without any pre-configured scope.","commands":{"allow":["is_absolute"],"deny":[]}},"allow-join":{"identifier":"allow-join","description":"Enables the join command without any pre-configured scope.","commands":{"allow":["join"],"deny":[]}},"allow-normalize":{"identifier":"allow-normalize","description":"Enables the normalize command without any pre-configured scope.","commands":{"allow":["normalize"],"deny":[]}},"allow-resolve":{"identifier":"allow-resolve","description":"Enables the resolve command without any pre-configured scope.","commands":{"allow":["resolve"],"deny":[]}},"allow-resolve-directory":{"identifier":"allow-resolve-directory","description":"Enables the resolve_directory command without any pre-configured scope.","commands":{"allow":["resolve_directory"],"deny":[]}},"deny-basename":{"identifier":"deny-basename","description":"Denies the basename command without any pre-configured scope.","commands":{"allow":[],"deny":["basename"]}},"deny-dirname":{"identifier":"deny-dirname","description":"Denies the dirname command without any pre-configured scope.","commands":{"allow":[],"deny":["dirname"]}},"deny-extname":{"identifier":"deny-extname","description":"Denies the extname command without any pre-configured scope.","commands":{"allow":[],"deny":["extname"]}},"deny-is-absolute":{"identifier":"deny-is-absolute","description":"Denies the is_absolute command without any pre-configured scope.","commands":{"allow":[],"deny":["is_absolute"]}},"deny-join":{"identifier":"deny-join","description":"Denies the join command without any pre-configured scope.","commands":{"allow":[],"deny":["join"]}},"deny-normalize":{"identifier":"deny-normalize","description":"Denies the normalize command without any pre-configured scope.","commands":{"allow":[],"deny":["normalize"]}},"deny-resolve":{"identifier":"deny-resolve","description":"Denies the resolve command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve"]}},"deny-resolve-directory":{"identifier":"deny-resolve-directory","description":"Denies the resolve_directory command without any pre-configured scope.","commands":{"allow":[],"deny":["resolve_directory"]}}},"permission_sets":{},"global_scope_schema":null},"core:resources":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-close"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}}},"permission_sets":{},"global_scope_schema":null},"core:tray":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin, which enables all commands.","permissions":["allow-new","allow-get-by-id","allow-remove-by-id","allow-set-icon","allow-set-menu","allow-set-tooltip","allow-set-title","allow-set-visible","allow-set-temp-dir-path","allow-set-icon-as-template","allow-set-show-menu-on-left-click"]},"permissions":{"allow-get-by-id":{"identifier":"allow-get-by-id","description":"Enables the get_by_id command without any pre-configured scope.","commands":{"allow":["get_by_id"],"deny":[]}},"allow-new":{"identifier":"allow-new","description":"Enables the new command without any pre-configured scope.","commands":{"allow":["new"],"deny":[]}},"allow-remove-by-id":{"identifier":"allow-remove-by-id","description":"Enables the remove_by_id command without any pre-configured scope.","commands":{"allow":["remove_by_id"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-icon-as-template":{"identifier":"allow-set-icon-as-template","description":"Enables the set_icon_as_template command without any pre-configured scope.","commands":{"allow":["set_icon_as_template"],"deny":[]}},"allow-set-menu":{"identifier":"allow-set-menu","description":"Enables the set_menu command without any pre-configured scope.","commands":{"allow":["set_menu"],"deny":[]}},"allow-set-show-menu-on-left-click":{"identifier":"allow-set-show-menu-on-left-click","description":"Enables the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":["set_show_menu_on_left_click"],"deny":[]}},"allow-set-temp-dir-path":{"identifier":"allow-set-temp-dir-path","description":"Enables the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":["set_temp_dir_path"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-tooltip":{"identifier":"allow-set-tooltip","description":"Enables the set_tooltip command without any pre-configured scope.","commands":{"allow":["set_tooltip"],"deny":[]}},"allow-set-visible":{"identifier":"allow-set-visible","description":"Enables the set_visible command without any pre-configured scope.","commands":{"allow":["set_visible"],"deny":[]}},"deny-get-by-id":{"identifier":"deny-get-by-id","description":"Denies the get_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["get_by_id"]}},"deny-new":{"identifier":"deny-new","description":"Denies the new command without any pre-configured scope.","commands":{"allow":[],"deny":["new"]}},"deny-remove-by-id":{"identifier":"deny-remove-by-id","description":"Denies the remove_by_id command without any pre-configured scope.","commands":{"allow":[],"deny":["remove_by_id"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-icon-as-template":{"identifier":"deny-set-icon-as-template","description":"Denies the set_icon_as_template command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon_as_template"]}},"deny-set-menu":{"identifier":"deny-set-menu","description":"Denies the set_menu command without any pre-configured scope.","commands":{"allow":[],"deny":["set_menu"]}},"deny-set-show-menu-on-left-click":{"identifier":"deny-set-show-menu-on-left-click","description":"Denies the set_show_menu_on_left_click command without any pre-configured scope.","commands":{"allow":[],"deny":["set_show_menu_on_left_click"]}},"deny-set-temp-dir-path":{"identifier":"deny-set-temp-dir-path","description":"Denies the set_temp_dir_path command without any pre-configured scope.","commands":{"allow":[],"deny":["set_temp_dir_path"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-tooltip":{"identifier":"deny-set-tooltip","description":"Denies the set_tooltip command without any pre-configured scope.","commands":{"allow":[],"deny":["set_tooltip"]}},"deny-set-visible":{"identifier":"deny-set-visible","description":"Denies the set_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible"]}}},"permission_sets":{},"global_scope_schema":null},"core:webview":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-webviews","allow-webview-position","allow-webview-size","allow-internal-toggle-devtools"]},"permissions":{"allow-clear-all-browsing-data":{"identifier":"allow-clear-all-browsing-data","description":"Enables the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":["clear_all_browsing_data"],"deny":[]}},"allow-create-webview":{"identifier":"allow-create-webview","description":"Enables the create_webview command without any pre-configured scope.","commands":{"allow":["create_webview"],"deny":[]}},"allow-create-webview-window":{"identifier":"allow-create-webview-window","description":"Enables the create_webview_window command without any pre-configured scope.","commands":{"allow":["create_webview_window"],"deny":[]}},"allow-get-all-webviews":{"identifier":"allow-get-all-webviews","description":"Enables the get_all_webviews command without any pre-configured scope.","commands":{"allow":["get_all_webviews"],"deny":[]}},"allow-internal-toggle-devtools":{"identifier":"allow-internal-toggle-devtools","description":"Enables the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":["internal_toggle_devtools"],"deny":[]}},"allow-print":{"identifier":"allow-print","description":"Enables the print command without any pre-configured scope.","commands":{"allow":["print"],"deny":[]}},"allow-reparent":{"identifier":"allow-reparent","description":"Enables the reparent command without any pre-configured scope.","commands":{"allow":["reparent"],"deny":[]}},"allow-set-webview-auto-resize":{"identifier":"allow-set-webview-auto-resize","description":"Enables the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":["set_webview_auto_resize"],"deny":[]}},"allow-set-webview-background-color":{"identifier":"allow-set-webview-background-color","description":"Enables the set_webview_background_color command without any pre-configured scope.","commands":{"allow":["set_webview_background_color"],"deny":[]}},"allow-set-webview-focus":{"identifier":"allow-set-webview-focus","description":"Enables the set_webview_focus command without any pre-configured scope.","commands":{"allow":["set_webview_focus"],"deny":[]}},"allow-set-webview-position":{"identifier":"allow-set-webview-position","description":"Enables the set_webview_position command without any pre-configured scope.","commands":{"allow":["set_webview_position"],"deny":[]}},"allow-set-webview-size":{"identifier":"allow-set-webview-size","description":"Enables the set_webview_size command without any pre-configured scope.","commands":{"allow":["set_webview_size"],"deny":[]}},"allow-set-webview-zoom":{"identifier":"allow-set-webview-zoom","description":"Enables the set_webview_zoom command without any pre-configured scope.","commands":{"allow":["set_webview_zoom"],"deny":[]}},"allow-webview-close":{"identifier":"allow-webview-close","description":"Enables the webview_close command without any pre-configured scope.","commands":{"allow":["webview_close"],"deny":[]}},"allow-webview-hide":{"identifier":"allow-webview-hide","description":"Enables the webview_hide command without any pre-configured scope.","commands":{"allow":["webview_hide"],"deny":[]}},"allow-webview-position":{"identifier":"allow-webview-position","description":"Enables the webview_position command without any pre-configured scope.","commands":{"allow":["webview_position"],"deny":[]}},"allow-webview-show":{"identifier":"allow-webview-show","description":"Enables the webview_show command without any pre-configured scope.","commands":{"allow":["webview_show"],"deny":[]}},"allow-webview-size":{"identifier":"allow-webview-size","description":"Enables the webview_size command without any pre-configured scope.","commands":{"allow":["webview_size"],"deny":[]}},"deny-clear-all-browsing-data":{"identifier":"deny-clear-all-browsing-data","description":"Denies the clear_all_browsing_data command without any pre-configured scope.","commands":{"allow":[],"deny":["clear_all_browsing_data"]}},"deny-create-webview":{"identifier":"deny-create-webview","description":"Denies the create_webview command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview"]}},"deny-create-webview-window":{"identifier":"deny-create-webview-window","description":"Denies the create_webview_window command without any pre-configured scope.","commands":{"allow":[],"deny":["create_webview_window"]}},"deny-get-all-webviews":{"identifier":"deny-get-all-webviews","description":"Denies the get_all_webviews command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_webviews"]}},"deny-internal-toggle-devtools":{"identifier":"deny-internal-toggle-devtools","description":"Denies the internal_toggle_devtools command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_devtools"]}},"deny-print":{"identifier":"deny-print","description":"Denies the print command without any pre-configured scope.","commands":{"allow":[],"deny":["print"]}},"deny-reparent":{"identifier":"deny-reparent","description":"Denies the reparent command without any pre-configured scope.","commands":{"allow":[],"deny":["reparent"]}},"deny-set-webview-auto-resize":{"identifier":"deny-set-webview-auto-resize","description":"Denies the set_webview_auto_resize command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_auto_resize"]}},"deny-set-webview-background-color":{"identifier":"deny-set-webview-background-color","description":"Denies the set_webview_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_background_color"]}},"deny-set-webview-focus":{"identifier":"deny-set-webview-focus","description":"Denies the set_webview_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_focus"]}},"deny-set-webview-position":{"identifier":"deny-set-webview-position","description":"Denies the set_webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_position"]}},"deny-set-webview-size":{"identifier":"deny-set-webview-size","description":"Denies the set_webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_size"]}},"deny-set-webview-zoom":{"identifier":"deny-set-webview-zoom","description":"Denies the set_webview_zoom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_webview_zoom"]}},"deny-webview-close":{"identifier":"deny-webview-close","description":"Denies the webview_close command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_close"]}},"deny-webview-hide":{"identifier":"deny-webview-hide","description":"Denies the webview_hide command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_hide"]}},"deny-webview-position":{"identifier":"deny-webview-position","description":"Denies the webview_position command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_position"]}},"deny-webview-show":{"identifier":"deny-webview-show","description":"Denies the webview_show command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_show"]}},"deny-webview-size":{"identifier":"deny-webview-size","description":"Denies the webview_size command without any pre-configured scope.","commands":{"allow":[],"deny":["webview_size"]}}},"permission_sets":{},"global_scope_schema":null},"core:window":{"default_permission":{"identifier":"default","description":"Default permissions for the plugin.","permissions":["allow-get-all-windows","allow-scale-factor","allow-inner-position","allow-outer-position","allow-inner-size","allow-outer-size","allow-is-fullscreen","allow-is-minimized","allow-is-maximized","allow-is-focused","allow-is-decorated","allow-is-resizable","allow-is-maximizable","allow-is-minimizable","allow-is-closable","allow-is-visible","allow-is-enabled","allow-title","allow-current-monitor","allow-primary-monitor","allow-monitor-from-point","allow-available-monitors","allow-cursor-position","allow-theme","allow-is-always-on-top","allow-internal-toggle-maximize"]},"permissions":{"allow-available-monitors":{"identifier":"allow-available-monitors","description":"Enables the available_monitors command without any pre-configured scope.","commands":{"allow":["available_monitors"],"deny":[]}},"allow-center":{"identifier":"allow-center","description":"Enables the center command without any pre-configured scope.","commands":{"allow":["center"],"deny":[]}},"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-current-monitor":{"identifier":"allow-current-monitor","description":"Enables the current_monitor command without any pre-configured scope.","commands":{"allow":["current_monitor"],"deny":[]}},"allow-cursor-position":{"identifier":"allow-cursor-position","description":"Enables the cursor_position command without any pre-configured scope.","commands":{"allow":["cursor_position"],"deny":[]}},"allow-destroy":{"identifier":"allow-destroy","description":"Enables the destroy command without any pre-configured scope.","commands":{"allow":["destroy"],"deny":[]}},"allow-get-all-windows":{"identifier":"allow-get-all-windows","description":"Enables the get_all_windows command without any pre-configured scope.","commands":{"allow":["get_all_windows"],"deny":[]}},"allow-hide":{"identifier":"allow-hide","description":"Enables the hide command without any pre-configured scope.","commands":{"allow":["hide"],"deny":[]}},"allow-inner-position":{"identifier":"allow-inner-position","description":"Enables the inner_position command without any pre-configured scope.","commands":{"allow":["inner_position"],"deny":[]}},"allow-inner-size":{"identifier":"allow-inner-size","description":"Enables the inner_size command without any pre-configured scope.","commands":{"allow":["inner_size"],"deny":[]}},"allow-internal-toggle-maximize":{"identifier":"allow-internal-toggle-maximize","description":"Enables the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":["internal_toggle_maximize"],"deny":[]}},"allow-is-always-on-top":{"identifier":"allow-is-always-on-top","description":"Enables the is_always_on_top command without any pre-configured scope.","commands":{"allow":["is_always_on_top"],"deny":[]}},"allow-is-closable":{"identifier":"allow-is-closable","description":"Enables the is_closable command without any pre-configured scope.","commands":{"allow":["is_closable"],"deny":[]}},"allow-is-decorated":{"identifier":"allow-is-decorated","description":"Enables the is_decorated command without any pre-configured scope.","commands":{"allow":["is_decorated"],"deny":[]}},"allow-is-enabled":{"identifier":"allow-is-enabled","description":"Enables the is_enabled command without any pre-configured scope.","commands":{"allow":["is_enabled"],"deny":[]}},"allow-is-focused":{"identifier":"allow-is-focused","description":"Enables the is_focused command without any pre-configured scope.","commands":{"allow":["is_focused"],"deny":[]}},"allow-is-fullscreen":{"identifier":"allow-is-fullscreen","description":"Enables the is_fullscreen command without any pre-configured scope.","commands":{"allow":["is_fullscreen"],"deny":[]}},"allow-is-maximizable":{"identifier":"allow-is-maximizable","description":"Enables the is_maximizable command without any pre-configured scope.","commands":{"allow":["is_maximizable"],"deny":[]}},"allow-is-maximized":{"identifier":"allow-is-maximized","description":"Enables the is_maximized command without any pre-configured scope.","commands":{"allow":["is_maximized"],"deny":[]}},"allow-is-minimizable":{"identifier":"allow-is-minimizable","description":"Enables the is_minimizable command without any pre-configured scope.","commands":{"allow":["is_minimizable"],"deny":[]}},"allow-is-minimized":{"identifier":"allow-is-minimized","description":"Enables the is_minimized command without any pre-configured scope.","commands":{"allow":["is_minimized"],"deny":[]}},"allow-is-resizable":{"identifier":"allow-is-resizable","description":"Enables the is_resizable command without any pre-configured scope.","commands":{"allow":["is_resizable"],"deny":[]}},"allow-is-visible":{"identifier":"allow-is-visible","description":"Enables the is_visible command without any pre-configured scope.","commands":{"allow":["is_visible"],"deny":[]}},"allow-maximize":{"identifier":"allow-maximize","description":"Enables the maximize command without any pre-configured scope.","commands":{"allow":["maximize"],"deny":[]}},"allow-minimize":{"identifier":"allow-minimize","description":"Enables the minimize command without any pre-configured scope.","commands":{"allow":["minimize"],"deny":[]}},"allow-monitor-from-point":{"identifier":"allow-monitor-from-point","description":"Enables the monitor_from_point command without any pre-configured scope.","commands":{"allow":["monitor_from_point"],"deny":[]}},"allow-outer-position":{"identifier":"allow-outer-position","description":"Enables the outer_position command without any pre-configured scope.","commands":{"allow":["outer_position"],"deny":[]}},"allow-outer-size":{"identifier":"allow-outer-size","description":"Enables the outer_size command without any pre-configured scope.","commands":{"allow":["outer_size"],"deny":[]}},"allow-primary-monitor":{"identifier":"allow-primary-monitor","description":"Enables the primary_monitor command without any pre-configured scope.","commands":{"allow":["primary_monitor"],"deny":[]}},"allow-request-user-attention":{"identifier":"allow-request-user-attention","description":"Enables the request_user_attention command without any pre-configured scope.","commands":{"allow":["request_user_attention"],"deny":[]}},"allow-scale-factor":{"identifier":"allow-scale-factor","description":"Enables the scale_factor command without any pre-configured scope.","commands":{"allow":["scale_factor"],"deny":[]}},"allow-set-always-on-bottom":{"identifier":"allow-set-always-on-bottom","description":"Enables the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":["set_always_on_bottom"],"deny":[]}},"allow-set-always-on-top":{"identifier":"allow-set-always-on-top","description":"Enables the set_always_on_top command without any pre-configured scope.","commands":{"allow":["set_always_on_top"],"deny":[]}},"allow-set-background-color":{"identifier":"allow-set-background-color","description":"Enables the set_background_color command without any pre-configured scope.","commands":{"allow":["set_background_color"],"deny":[]}},"allow-set-badge-count":{"identifier":"allow-set-badge-count","description":"Enables the set_badge_count command without any pre-configured scope.","commands":{"allow":["set_badge_count"],"deny":[]}},"allow-set-badge-label":{"identifier":"allow-set-badge-label","description":"Enables the set_badge_label command without any pre-configured scope.","commands":{"allow":["set_badge_label"],"deny":[]}},"allow-set-closable":{"identifier":"allow-set-closable","description":"Enables the set_closable command without any pre-configured scope.","commands":{"allow":["set_closable"],"deny":[]}},"allow-set-content-protected":{"identifier":"allow-set-content-protected","description":"Enables the set_content_protected command without any pre-configured scope.","commands":{"allow":["set_content_protected"],"deny":[]}},"allow-set-cursor-grab":{"identifier":"allow-set-cursor-grab","description":"Enables the set_cursor_grab command without any pre-configured scope.","commands":{"allow":["set_cursor_grab"],"deny":[]}},"allow-set-cursor-icon":{"identifier":"allow-set-cursor-icon","description":"Enables the set_cursor_icon command without any pre-configured scope.","commands":{"allow":["set_cursor_icon"],"deny":[]}},"allow-set-cursor-position":{"identifier":"allow-set-cursor-position","description":"Enables the set_cursor_position command without any pre-configured scope.","commands":{"allow":["set_cursor_position"],"deny":[]}},"allow-set-cursor-visible":{"identifier":"allow-set-cursor-visible","description":"Enables the set_cursor_visible command without any pre-configured scope.","commands":{"allow":["set_cursor_visible"],"deny":[]}},"allow-set-decorations":{"identifier":"allow-set-decorations","description":"Enables the set_decorations command without any pre-configured scope.","commands":{"allow":["set_decorations"],"deny":[]}},"allow-set-effects":{"identifier":"allow-set-effects","description":"Enables the set_effects command without any pre-configured scope.","commands":{"allow":["set_effects"],"deny":[]}},"allow-set-enabled":{"identifier":"allow-set-enabled","description":"Enables the set_enabled command without any pre-configured scope.","commands":{"allow":["set_enabled"],"deny":[]}},"allow-set-focus":{"identifier":"allow-set-focus","description":"Enables the set_focus command without any pre-configured scope.","commands":{"allow":["set_focus"],"deny":[]}},"allow-set-focusable":{"identifier":"allow-set-focusable","description":"Enables the set_focusable command without any pre-configured scope.","commands":{"allow":["set_focusable"],"deny":[]}},"allow-set-fullscreen":{"identifier":"allow-set-fullscreen","description":"Enables the set_fullscreen command without any pre-configured scope.","commands":{"allow":["set_fullscreen"],"deny":[]}},"allow-set-icon":{"identifier":"allow-set-icon","description":"Enables the set_icon command without any pre-configured scope.","commands":{"allow":["set_icon"],"deny":[]}},"allow-set-ignore-cursor-events":{"identifier":"allow-set-ignore-cursor-events","description":"Enables the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":["set_ignore_cursor_events"],"deny":[]}},"allow-set-max-size":{"identifier":"allow-set-max-size","description":"Enables the set_max_size command without any pre-configured scope.","commands":{"allow":["set_max_size"],"deny":[]}},"allow-set-maximizable":{"identifier":"allow-set-maximizable","description":"Enables the set_maximizable command without any pre-configured scope.","commands":{"allow":["set_maximizable"],"deny":[]}},"allow-set-min-size":{"identifier":"allow-set-min-size","description":"Enables the set_min_size command without any pre-configured scope.","commands":{"allow":["set_min_size"],"deny":[]}},"allow-set-minimizable":{"identifier":"allow-set-minimizable","description":"Enables the set_minimizable command without any pre-configured scope.","commands":{"allow":["set_minimizable"],"deny":[]}},"allow-set-overlay-icon":{"identifier":"allow-set-overlay-icon","description":"Enables the set_overlay_icon command without any pre-configured scope.","commands":{"allow":["set_overlay_icon"],"deny":[]}},"allow-set-position":{"identifier":"allow-set-position","description":"Enables the set_position command without any pre-configured scope.","commands":{"allow":["set_position"],"deny":[]}},"allow-set-progress-bar":{"identifier":"allow-set-progress-bar","description":"Enables the set_progress_bar command without any pre-configured scope.","commands":{"allow":["set_progress_bar"],"deny":[]}},"allow-set-resizable":{"identifier":"allow-set-resizable","description":"Enables the set_resizable command without any pre-configured scope.","commands":{"allow":["set_resizable"],"deny":[]}},"allow-set-shadow":{"identifier":"allow-set-shadow","description":"Enables the set_shadow command without any pre-configured scope.","commands":{"allow":["set_shadow"],"deny":[]}},"allow-set-simple-fullscreen":{"identifier":"allow-set-simple-fullscreen","description":"Enables the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":["set_simple_fullscreen"],"deny":[]}},"allow-set-size":{"identifier":"allow-set-size","description":"Enables the set_size command without any pre-configured scope.","commands":{"allow":["set_size"],"deny":[]}},"allow-set-size-constraints":{"identifier":"allow-set-size-constraints","description":"Enables the set_size_constraints command without any pre-configured scope.","commands":{"allow":["set_size_constraints"],"deny":[]}},"allow-set-skip-taskbar":{"identifier":"allow-set-skip-taskbar","description":"Enables the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":["set_skip_taskbar"],"deny":[]}},"allow-set-theme":{"identifier":"allow-set-theme","description":"Enables the set_theme command without any pre-configured scope.","commands":{"allow":["set_theme"],"deny":[]}},"allow-set-title":{"identifier":"allow-set-title","description":"Enables the set_title command without any pre-configured scope.","commands":{"allow":["set_title"],"deny":[]}},"allow-set-title-bar-style":{"identifier":"allow-set-title-bar-style","description":"Enables the set_title_bar_style command without any pre-configured scope.","commands":{"allow":["set_title_bar_style"],"deny":[]}},"allow-set-visible-on-all-workspaces":{"identifier":"allow-set-visible-on-all-workspaces","description":"Enables the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":["set_visible_on_all_workspaces"],"deny":[]}},"allow-show":{"identifier":"allow-show","description":"Enables the show command without any pre-configured scope.","commands":{"allow":["show"],"deny":[]}},"allow-start-dragging":{"identifier":"allow-start-dragging","description":"Enables the start_dragging command without any pre-configured scope.","commands":{"allow":["start_dragging"],"deny":[]}},"allow-start-resize-dragging":{"identifier":"allow-start-resize-dragging","description":"Enables the start_resize_dragging command without any pre-configured scope.","commands":{"allow":["start_resize_dragging"],"deny":[]}},"allow-theme":{"identifier":"allow-theme","description":"Enables the theme command without any pre-configured scope.","commands":{"allow":["theme"],"deny":[]}},"allow-title":{"identifier":"allow-title","description":"Enables the title command without any pre-configured scope.","commands":{"allow":["title"],"deny":[]}},"allow-toggle-maximize":{"identifier":"allow-toggle-maximize","description":"Enables the toggle_maximize command without any pre-configured scope.","commands":{"allow":["toggle_maximize"],"deny":[]}},"allow-unmaximize":{"identifier":"allow-unmaximize","description":"Enables the unmaximize command without any pre-configured scope.","commands":{"allow":["unmaximize"],"deny":[]}},"allow-unminimize":{"identifier":"allow-unminimize","description":"Enables the unminimize command without any pre-configured scope.","commands":{"allow":["unminimize"],"deny":[]}},"deny-available-monitors":{"identifier":"deny-available-monitors","description":"Denies the available_monitors command without any pre-configured scope.","commands":{"allow":[],"deny":["available_monitors"]}},"deny-center":{"identifier":"deny-center","description":"Denies the center command without any pre-configured scope.","commands":{"allow":[],"deny":["center"]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-current-monitor":{"identifier":"deny-current-monitor","description":"Denies the current_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["current_monitor"]}},"deny-cursor-position":{"identifier":"deny-cursor-position","description":"Denies the cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["cursor_position"]}},"deny-destroy":{"identifier":"deny-destroy","description":"Denies the destroy command without any pre-configured scope.","commands":{"allow":[],"deny":["destroy"]}},"deny-get-all-windows":{"identifier":"deny-get-all-windows","description":"Denies the get_all_windows command without any pre-configured scope.","commands":{"allow":[],"deny":["get_all_windows"]}},"deny-hide":{"identifier":"deny-hide","description":"Denies the hide command without any pre-configured scope.","commands":{"allow":[],"deny":["hide"]}},"deny-inner-position":{"identifier":"deny-inner-position","description":"Denies the inner_position command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_position"]}},"deny-inner-size":{"identifier":"deny-inner-size","description":"Denies the inner_size command without any pre-configured scope.","commands":{"allow":[],"deny":["inner_size"]}},"deny-internal-toggle-maximize":{"identifier":"deny-internal-toggle-maximize","description":"Denies the internal_toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["internal_toggle_maximize"]}},"deny-is-always-on-top":{"identifier":"deny-is-always-on-top","description":"Denies the is_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["is_always_on_top"]}},"deny-is-closable":{"identifier":"deny-is-closable","description":"Denies the is_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_closable"]}},"deny-is-decorated":{"identifier":"deny-is-decorated","description":"Denies the is_decorated command without any pre-configured scope.","commands":{"allow":[],"deny":["is_decorated"]}},"deny-is-enabled":{"identifier":"deny-is-enabled","description":"Denies the is_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["is_enabled"]}},"deny-is-focused":{"identifier":"deny-is-focused","description":"Denies the is_focused command without any pre-configured scope.","commands":{"allow":[],"deny":["is_focused"]}},"deny-is-fullscreen":{"identifier":"deny-is-fullscreen","description":"Denies the is_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["is_fullscreen"]}},"deny-is-maximizable":{"identifier":"deny-is-maximizable","description":"Denies the is_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximizable"]}},"deny-is-maximized":{"identifier":"deny-is-maximized","description":"Denies the is_maximized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_maximized"]}},"deny-is-minimizable":{"identifier":"deny-is-minimizable","description":"Denies the is_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimizable"]}},"deny-is-minimized":{"identifier":"deny-is-minimized","description":"Denies the is_minimized command without any pre-configured scope.","commands":{"allow":[],"deny":["is_minimized"]}},"deny-is-resizable":{"identifier":"deny-is-resizable","description":"Denies the is_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["is_resizable"]}},"deny-is-visible":{"identifier":"deny-is-visible","description":"Denies the is_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["is_visible"]}},"deny-maximize":{"identifier":"deny-maximize","description":"Denies the maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["maximize"]}},"deny-minimize":{"identifier":"deny-minimize","description":"Denies the minimize command without any pre-configured scope.","commands":{"allow":[],"deny":["minimize"]}},"deny-monitor-from-point":{"identifier":"deny-monitor-from-point","description":"Denies the monitor_from_point command without any pre-configured scope.","commands":{"allow":[],"deny":["monitor_from_point"]}},"deny-outer-position":{"identifier":"deny-outer-position","description":"Denies the outer_position command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_position"]}},"deny-outer-size":{"identifier":"deny-outer-size","description":"Denies the outer_size command without any pre-configured scope.","commands":{"allow":[],"deny":["outer_size"]}},"deny-primary-monitor":{"identifier":"deny-primary-monitor","description":"Denies the primary_monitor command without any pre-configured scope.","commands":{"allow":[],"deny":["primary_monitor"]}},"deny-request-user-attention":{"identifier":"deny-request-user-attention","description":"Denies the request_user_attention command without any pre-configured scope.","commands":{"allow":[],"deny":["request_user_attention"]}},"deny-scale-factor":{"identifier":"deny-scale-factor","description":"Denies the scale_factor command without any pre-configured scope.","commands":{"allow":[],"deny":["scale_factor"]}},"deny-set-always-on-bottom":{"identifier":"deny-set-always-on-bottom","description":"Denies the set_always_on_bottom command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_bottom"]}},"deny-set-always-on-top":{"identifier":"deny-set-always-on-top","description":"Denies the set_always_on_top command without any pre-configured scope.","commands":{"allow":[],"deny":["set_always_on_top"]}},"deny-set-background-color":{"identifier":"deny-set-background-color","description":"Denies the set_background_color command without any pre-configured scope.","commands":{"allow":[],"deny":["set_background_color"]}},"deny-set-badge-count":{"identifier":"deny-set-badge-count","description":"Denies the set_badge_count command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_count"]}},"deny-set-badge-label":{"identifier":"deny-set-badge-label","description":"Denies the set_badge_label command without any pre-configured scope.","commands":{"allow":[],"deny":["set_badge_label"]}},"deny-set-closable":{"identifier":"deny-set-closable","description":"Denies the set_closable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_closable"]}},"deny-set-content-protected":{"identifier":"deny-set-content-protected","description":"Denies the set_content_protected command without any pre-configured scope.","commands":{"allow":[],"deny":["set_content_protected"]}},"deny-set-cursor-grab":{"identifier":"deny-set-cursor-grab","description":"Denies the set_cursor_grab command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_grab"]}},"deny-set-cursor-icon":{"identifier":"deny-set-cursor-icon","description":"Denies the set_cursor_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_icon"]}},"deny-set-cursor-position":{"identifier":"deny-set-cursor-position","description":"Denies the set_cursor_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_position"]}},"deny-set-cursor-visible":{"identifier":"deny-set-cursor-visible","description":"Denies the set_cursor_visible command without any pre-configured scope.","commands":{"allow":[],"deny":["set_cursor_visible"]}},"deny-set-decorations":{"identifier":"deny-set-decorations","description":"Denies the set_decorations command without any pre-configured scope.","commands":{"allow":[],"deny":["set_decorations"]}},"deny-set-effects":{"identifier":"deny-set-effects","description":"Denies the set_effects command without any pre-configured scope.","commands":{"allow":[],"deny":["set_effects"]}},"deny-set-enabled":{"identifier":"deny-set-enabled","description":"Denies the set_enabled command without any pre-configured scope.","commands":{"allow":[],"deny":["set_enabled"]}},"deny-set-focus":{"identifier":"deny-set-focus","description":"Denies the set_focus command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focus"]}},"deny-set-focusable":{"identifier":"deny-set-focusable","description":"Denies the set_focusable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_focusable"]}},"deny-set-fullscreen":{"identifier":"deny-set-fullscreen","description":"Denies the set_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_fullscreen"]}},"deny-set-icon":{"identifier":"deny-set-icon","description":"Denies the set_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_icon"]}},"deny-set-ignore-cursor-events":{"identifier":"deny-set-ignore-cursor-events","description":"Denies the set_ignore_cursor_events command without any pre-configured scope.","commands":{"allow":[],"deny":["set_ignore_cursor_events"]}},"deny-set-max-size":{"identifier":"deny-set-max-size","description":"Denies the set_max_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_max_size"]}},"deny-set-maximizable":{"identifier":"deny-set-maximizable","description":"Denies the set_maximizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_maximizable"]}},"deny-set-min-size":{"identifier":"deny-set-min-size","description":"Denies the set_min_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_min_size"]}},"deny-set-minimizable":{"identifier":"deny-set-minimizable","description":"Denies the set_minimizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_minimizable"]}},"deny-set-overlay-icon":{"identifier":"deny-set-overlay-icon","description":"Denies the set_overlay_icon command without any pre-configured scope.","commands":{"allow":[],"deny":["set_overlay_icon"]}},"deny-set-position":{"identifier":"deny-set-position","description":"Denies the set_position command without any pre-configured scope.","commands":{"allow":[],"deny":["set_position"]}},"deny-set-progress-bar":{"identifier":"deny-set-progress-bar","description":"Denies the set_progress_bar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_progress_bar"]}},"deny-set-resizable":{"identifier":"deny-set-resizable","description":"Denies the set_resizable command without any pre-configured scope.","commands":{"allow":[],"deny":["set_resizable"]}},"deny-set-shadow":{"identifier":"deny-set-shadow","description":"Denies the set_shadow command without any pre-configured scope.","commands":{"allow":[],"deny":["set_shadow"]}},"deny-set-simple-fullscreen":{"identifier":"deny-set-simple-fullscreen","description":"Denies the set_simple_fullscreen command without any pre-configured scope.","commands":{"allow":[],"deny":["set_simple_fullscreen"]}},"deny-set-size":{"identifier":"deny-set-size","description":"Denies the set_size command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size"]}},"deny-set-size-constraints":{"identifier":"deny-set-size-constraints","description":"Denies the set_size_constraints command without any pre-configured scope.","commands":{"allow":[],"deny":["set_size_constraints"]}},"deny-set-skip-taskbar":{"identifier":"deny-set-skip-taskbar","description":"Denies the set_skip_taskbar command without any pre-configured scope.","commands":{"allow":[],"deny":["set_skip_taskbar"]}},"deny-set-theme":{"identifier":"deny-set-theme","description":"Denies the set_theme command without any pre-configured scope.","commands":{"allow":[],"deny":["set_theme"]}},"deny-set-title":{"identifier":"deny-set-title","description":"Denies the set_title command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title"]}},"deny-set-title-bar-style":{"identifier":"deny-set-title-bar-style","description":"Denies the set_title_bar_style command without any pre-configured scope.","commands":{"allow":[],"deny":["set_title_bar_style"]}},"deny-set-visible-on-all-workspaces":{"identifier":"deny-set-visible-on-all-workspaces","description":"Denies the set_visible_on_all_workspaces command without any pre-configured scope.","commands":{"allow":[],"deny":["set_visible_on_all_workspaces"]}},"deny-show":{"identifier":"deny-show","description":"Denies the show command without any pre-configured scope.","commands":{"allow":[],"deny":["show"]}},"deny-start-dragging":{"identifier":"deny-start-dragging","description":"Denies the start_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_dragging"]}},"deny-start-resize-dragging":{"identifier":"deny-start-resize-dragging","description":"Denies the start_resize_dragging command without any pre-configured scope.","commands":{"allow":[],"deny":["start_resize_dragging"]}},"deny-theme":{"identifier":"deny-theme","description":"Denies the theme command without any pre-configured scope.","commands":{"allow":[],"deny":["theme"]}},"deny-title":{"identifier":"deny-title","description":"Denies the title command without any pre-configured scope.","commands":{"allow":[],"deny":["title"]}},"deny-toggle-maximize":{"identifier":"deny-toggle-maximize","description":"Denies the toggle_maximize command without any pre-configured scope.","commands":{"allow":[],"deny":["toggle_maximize"]}},"deny-unmaximize":{"identifier":"deny-unmaximize","description":"Denies the unmaximize command without any pre-configured scope.","commands":{"allow":[],"deny":["unmaximize"]}},"deny-unminimize":{"identifier":"deny-unminimize","description":"Denies the unminimize command without any pre-configured scope.","commands":{"allow":[],"deny":["unminimize"]}}},"permission_sets":{},"global_scope_schema":null},"dialog":{"default_permission":{"identifier":"default","description":"This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n","permissions":["allow-ask","allow-confirm","allow-message","allow-save","allow-open"]},"permissions":{"allow-ask":{"identifier":"allow-ask","description":"Enables the ask command without any pre-configured scope.","commands":{"allow":["ask"],"deny":[]}},"allow-confirm":{"identifier":"allow-confirm","description":"Enables the confirm command without any pre-configured scope.","commands":{"allow":["confirm"],"deny":[]}},"allow-message":{"identifier":"allow-message","description":"Enables the message command without any pre-configured scope.","commands":{"allow":["message"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-save":{"identifier":"allow-save","description":"Enables the save command without any pre-configured scope.","commands":{"allow":["save"],"deny":[]}},"deny-ask":{"identifier":"deny-ask","description":"Denies the ask command without any pre-configured scope.","commands":{"allow":[],"deny":["ask"]}},"deny-confirm":{"identifier":"deny-confirm","description":"Denies the confirm command without any pre-configured scope.","commands":{"allow":[],"deny":["confirm"]}},"deny-message":{"identifier":"deny-message","description":"Denies the message command without any pre-configured scope.","commands":{"allow":[],"deny":["message"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-save":{"identifier":"deny-save","description":"Denies the save command without any pre-configured scope.","commands":{"allow":[],"deny":["save"]}}},"permission_sets":{},"global_scope_schema":null},"shell":{"default_permission":{"identifier":"default","description":"This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n","permissions":["allow-open"]},"permissions":{"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-kill":{"identifier":"allow-kill","description":"Enables the kill command without any pre-configured scope.","commands":{"allow":["kill"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-spawn":{"identifier":"allow-spawn","description":"Enables the spawn command without any pre-configured scope.","commands":{"allow":["spawn"],"deny":[]}},"allow-stdin-write":{"identifier":"allow-stdin-write","description":"Enables the stdin_write command without any pre-configured scope.","commands":{"allow":["stdin_write"],"deny":[]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-kill":{"identifier":"deny-kill","description":"Denies the kill command without any pre-configured scope.","commands":{"allow":[],"deny":["kill"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-spawn":{"identifier":"deny-spawn","description":"Denies the spawn command without any pre-configured scope.","commands":{"allow":[],"deny":["spawn"]}},"deny-stdin-write":{"identifier":"deny-stdin-write","description":"Denies the stdin_write command without any pre-configured scope.","commands":{"allow":[],"deny":["stdin_write"]}}},"permission_sets":{},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"cmd":{"description":"The command name. It can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"}},"required":["cmd","name"],"type":"object"},{"additionalProperties":false,"properties":{"args":{"allOf":[{"$ref":"#/definitions/ShellScopeEntryAllowedArgs"}],"description":"The allowed arguments for the command execution."},"name":{"description":"The name for this allowed shell command configuration.\n\nThis name will be used inside of the webview API to call this command along with any specified arguments.","type":"string"},"sidecar":{"description":"If this command is a sidecar command.","type":"boolean"}},"required":["name","sidecar"],"type":"object"}],"definitions":{"ShellScopeEntryAllowedArg":{"anyOf":[{"description":"A non-configurable argument that is passed to the command in the order it was specified.","type":"string"},{"additionalProperties":false,"description":"A variable that is set while calling the command from the webview API.","properties":{"raw":{"default":false,"description":"Marks the validator as a raw regex, meaning the plugin should not make any modification at runtime.\n\nThis means the regex will not match on the entire string by default, which might be exploited if your regex allow unexpected input to be considered valid. When using this option, make sure your regex is correct.","type":"boolean"},"validator":{"description":"[regex] validator to require passed values to conform to an expected input.\n\nThis will require the argument value passed to this variable to match the `validator` regex before it will be executed.\n\nThe regex string is by default surrounded by `^...$` to match the full string. For example the `https?://\\w+` regex would be registered as `^https?://\\w+$`.\n\n[regex]: ","type":"string"}},"required":["validator"],"type":"object"}],"description":"A command argument allowed to be executed by the webview API."},"ShellScopeEntryAllowedArgs":{"anyOf":[{"description":"Use a simple boolean to allow all or disable all arguments to this command configuration.","type":"boolean"},{"description":"A specific set of [`ShellScopeEntryAllowedArg`] that are valid to call for the command configuration.","items":{"$ref":"#/definitions/ShellScopeEntryAllowedArg"},"type":"array"}],"description":"A set of command arguments allowed to be executed by the webview API.\n\nA value of `true` will allow any arguments to be passed to the command. `false` will disable all arguments. A list of [`ShellScopeEntryAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration."}},"description":"Shell scope entry.","title":"ShellScopeEntry"}}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/capabilities.json b/src-tauri/gen/schemas/capabilities.json index 569ea0a..62f66a6 100644 --- a/src-tauri/gen/schemas/capabilities.json +++ b/src-tauri/gen/schemas/capabilities.json @@ -1 +1 @@ -{"default":{"identifier":"default","description":"Default permissions for the application","local":true,"windows":["main"],"permissions":["core:default","shell:allow-open"]}} \ No newline at end of file +{"default":{"identifier":"default","description":"Default permissions for the application","local":true,"windows":["main"],"permissions":["core:default","shell:allow-open","dialog:allow-open"]}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json index f827fe1..fcf88e0 100644 --- a/src-tauri/gen/schemas/desktop-schema.json +++ b/src-tauri/gen/schemas/desktop-schema.json @@ -2354,6 +2354,72 @@ "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope." + }, + { + "description": "Enables the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope." + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope." + }, + { + "description": "Denies the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope." + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, { "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", diff --git a/src-tauri/gen/schemas/windows-schema.json b/src-tauri/gen/schemas/windows-schema.json index f827fe1..fcf88e0 100644 --- a/src-tauri/gen/schemas/windows-schema.json +++ b/src-tauri/gen/schemas/windows-schema.json @@ -2354,6 +2354,72 @@ "const": "core:window:deny-unminimize", "markdownDescription": "Denies the unminimize command without any pre-configured scope." }, + { + "description": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`", + "type": "string", + "const": "dialog:default", + "markdownDescription": "This permission set configures the types of dialogs\navailable from the dialog plugin.\n\n#### Granted Permissions\n\nAll dialog types are enabled.\n\n\n\n#### This default permission set includes:\n\n- `allow-ask`\n- `allow-confirm`\n- `allow-message`\n- `allow-save`\n- `allow-open`" + }, + { + "description": "Enables the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-ask", + "markdownDescription": "Enables the ask command without any pre-configured scope." + }, + { + "description": "Enables the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-confirm", + "markdownDescription": "Enables the confirm command without any pre-configured scope." + }, + { + "description": "Enables the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-message", + "markdownDescription": "Enables the message command without any pre-configured scope." + }, + { + "description": "Enables the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-open", + "markdownDescription": "Enables the open command without any pre-configured scope." + }, + { + "description": "Enables the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:allow-save", + "markdownDescription": "Enables the save command without any pre-configured scope." + }, + { + "description": "Denies the ask command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-ask", + "markdownDescription": "Denies the ask command without any pre-configured scope." + }, + { + "description": "Denies the confirm command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-confirm", + "markdownDescription": "Denies the confirm command without any pre-configured scope." + }, + { + "description": "Denies the message command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-message", + "markdownDescription": "Denies the message command without any pre-configured scope." + }, + { + "description": "Denies the open command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-open", + "markdownDescription": "Denies the open command without any pre-configured scope." + }, + { + "description": "Denies the save command without any pre-configured scope.", + "type": "string", + "const": "dialog:deny-save", + "markdownDescription": "Denies the save command without any pre-configured scope." + }, { "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality with a reasonable\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n\n#### This default permission set includes:\n\n- `allow-open`", "type": "string", diff --git a/src-tauri/src/commands/tool_commands.rs b/src-tauri/src/commands/tool_commands.rs index 7e71399..852a883 100644 --- a/src-tauri/src/commands/tool_commands.rs +++ b/src-tauri/src/commands/tool_commands.rs @@ -28,23 +28,96 @@ pub async fn check_installations( .map_err(|e| format!("检查工具状态失败: {}", e)) } -/// 刷新工具状态(重新检测并更新数据库) +/// 刷新工具状态(仅从数据库读取,不重新检测) /// -/// 工作流程: -/// 1. 重新检测所有本地工具(并行,约 1.3s) -/// 2. 更新数据库(upsert,删除已卸载的工具) -/// 3. 返回最新的 ToolStatus -/// -/// 用途:用户点击"刷新"按钮、安装/卸载工具后 +/// 修改说明:不再自动检测所有工具,仅返回数据库中已有的工具状态 +/// 如需添加新工具或验证已有工具,请使用: +/// - 添加新工具:工具管理页面 → 添加实例 +/// - 验证单个工具:使用 detect_single_tool 命令 #[tauri::command] pub async fn refresh_tool_status( registry_state: tauri::State<'_, ToolRegistryState>, ) -> Result, String> { let registry = registry_state.registry.lock().await; registry - .refresh_and_get_local_status() + .get_local_tool_status() .await - .map_err(|e| format!("刷新工具状态失败: {}", e)) + .map_err(|e| format!("获取工具状态失败: {}", e)) +} + +/// 扫描工具路径的安装器 +/// +/// 工作流程: +/// 1. 从工具路径提取目录 +/// 2. 在同级目录扫描安装器(npm、brew 等) +/// 3. 在上级目录扫描安装器 +/// 4. 返回候选列表(按优先级排序) +/// +/// 返回:安装器候选列表 +#[tauri::command] +pub async fn scan_installer_for_tool_path( + tool_path: String, +) -> Result, String> { + use ::duckcoding::utils::scan_installer_paths; + + Ok(scan_installer_paths(&tool_path)) +} + +/// 扫描所有工具候选(用于自动扫描) +/// +/// 工作流程: +/// 1. 使用硬编码路径列表查找所有工具实例 +/// 2. 对每个找到的工具:获取版本、检测安装方法、扫描安装器 +/// 3. 返回候选列表供用户选择 +/// +/// 返回:工具候选列表 +#[tauri::command] +pub async fn scan_all_tool_candidates( + tool_id: String, +) -> Result, String> { + use ::duckcoding::utils::{scan_installer_paths, scan_tool_executables, ToolCandidate}; + use std::process::Command; + + // 1. 扫描所有工具路径 + let tool_paths = scan_tool_executables(&tool_id); + let mut candidates = Vec::new(); + + // 2. 对每个工具路径:获取版本和安装器 + for tool_path in tool_paths { + // 获取版本 + let version_cmd = format!("{} --version", tool_path); + + #[cfg(target_os = "windows")] + let output = Command::new("cmd").arg("/C").arg(&version_cmd).output(); + + #[cfg(not(target_os = "windows"))] + let output = Command::new("sh").arg("-c").arg(&version_cmd).output(); + + let version = match output { + Ok(out) if out.status.success() => { + let raw = String::from_utf8_lossy(&out.stdout).trim().to_string(); + parse_version_string(&raw) + } + _ => continue, // 版本获取失败,跳过此候选 + }; + + // 扫描安装器 + let installer_candidates = scan_installer_paths(&tool_path); + let installer_path = installer_candidates.first().map(|c| c.path.clone()); + let install_method = installer_candidates + .first() + .map(|c| c.installer_type.clone()) + .unwrap_or(InstallMethod::Official); + + candidates.push(ToolCandidate { + tool_path: tool_path.clone(), + installer_path, + install_method, + version, + }); + } + + Ok(candidates) } /// 检测 Node.js 和 npm 环境 @@ -197,6 +270,213 @@ pub async fn check_update(tool: String) -> Result { } } +/// 解析版本号字符串,处理特殊格式 +/// +/// 支持格式: +/// - "2.0.61" -> "2.0.61" +/// - "2.0.61 (Claude Code)" -> "2.0.61" +/// - "codex-cli 0.65.0" -> "0.65.0" +/// - "v1.2.3" -> "1.2.3" +fn parse_version_string(raw: &str) -> String { + let trimmed = raw.trim(); + + // 1. 处理括号格式:2.0.61 (Claude Code) -> 2.0.61 + if let Some(idx) = trimmed.find('(') { + return trimmed[..idx].trim().to_string(); + } + + // 2. 处理空格分隔格式:codex-cli 0.65.0 -> 0.65.0 + let parts: Vec<&str> = trimmed.split_whitespace().collect(); + if parts.len() > 1 { + // 查找第一个以数字开头的部分 + for part in parts { + if part.chars().next().is_some_and(|c| c.is_numeric()) { + return part.trim_start_matches('v').to_string(); + } + } + } + + // 3. 移除 'v' 前缀:v1.2.3 -> 1.2.3 + trimmed.trim_start_matches('v').to_string() +} + +/// 检查工具更新(基于实例ID,使用配置的路径) +/// +/// 工作流程: +/// 1. 从数据库获取实例信息 +/// 2. 使用 install_path 执行 --version 获取当前版本 +/// 3. 检查远程最新版本 +/// +/// 返回:更新信息 +#[tauri::command] +pub async fn check_update_for_instance( + instance_id: String, + _registry_state: tauri::State<'_, ToolRegistryState>, +) -> Result { + use ::duckcoding::models::ToolType; + use ::duckcoding::services::tool::ToolInstanceDB; + use std::process::Command; + + // 1. 从数据库获取实例信息 + let db = ToolInstanceDB::new().map_err(|e| format!("初始化数据库失败: {}", e))?; + let all_instances = db + .get_all_instances() + .map_err(|e| format!("读取数据库失败: {}", e))?; + + let instance = all_instances + .iter() + .find(|inst| inst.instance_id == instance_id && inst.tool_type == ToolType::Local) + .ok_or_else(|| format!("未找到实例: {}", instance_id))?; + + // 2. 使用 install_path 执行 --version 获取当前版本 + let current_version = if let Some(path) = &instance.install_path { + let version_cmd = format!("{} --version", path); + tracing::info!("实例 {} 版本更新命令: {:?}", instance_id, version_cmd); + + #[cfg(target_os = "windows")] + let output = Command::new("cmd").arg("/C").arg(&version_cmd).output(); + + #[cfg(not(target_os = "windows"))] + let output = Command::new("sh").arg("-c").arg(&version_cmd).output(); + + match output { + Ok(out) if out.status.success() => { + let raw_version = String::from_utf8_lossy(&out.stdout).trim().to_string(); + Some(parse_version_string(&raw_version)) + } + Ok(_) => { + return Err(format!("版本号获取错误:无法执行命令 {}", version_cmd)); + } + Err(e) => { + return Err(format!("版本号获取错误:执行失败 - {}", e)); + } + } + } else { + // 没有路径,使用数据库中的版本 + instance.version.clone() + }; + + // 3. 检查远程最新版本 + let tool_id = &instance.base_id; + let update_result = check_update(tool_id.clone()).await?; + + // 4. 如果当前版本有变化,更新数据库 + if current_version != instance.version { + let mut updated_instance = instance.clone(); + updated_instance.version = current_version.clone(); + updated_instance.updated_at = chrono::Utc::now().timestamp(); + + if let Err(e) = db.update_instance(&updated_instance) { + tracing::warn!("更新实例 {} 版本失败: {}", instance_id, e); + } else { + tracing::info!( + "实例 {} 版本已同步更新: {:?} -> {:?}", + instance_id, + instance.version, + current_version + ); + } + } + + // 5. 返回结果,使用路径检测的版本号 + Ok(UpdateResult { + success: update_result.success, + message: update_result.message, + has_update: update_result.has_update, + current_version, + latest_version: update_result.latest_version, + mirror_version: update_result.mirror_version, + mirror_is_stale: update_result.mirror_is_stale, + tool_id: Some(tool_id.clone()), + }) +} + +/// 刷新数据库中所有工具的版本号(使用配置的路径检测) +/// +/// 工作流程: +/// 1. 读取数据库中所有本地工具实例 +/// 2. 对每个有路径的实例,执行 --version 获取最新版本号 +/// 3. 更新数据库中的版本号 +/// +/// 返回:更新后的工具状态列表 +#[tauri::command] +pub async fn refresh_all_tool_versions( + _registry_state: tauri::State<'_, ToolRegistryState>, +) -> Result, String> { + use ::duckcoding::models::ToolType; + use ::duckcoding::services::tool::ToolInstanceDB; + use std::process::Command; + + let db = ToolInstanceDB::new().map_err(|e| format!("初始化数据库失败: {}", e))?; + let all_instances = db + .get_all_instances() + .map_err(|e| format!("读取数据库失败: {}", e))?; + + let mut statuses = Vec::new(); + + for instance in all_instances + .iter() + .filter(|i| i.tool_type == ToolType::Local) + { + // 使用 install_path 检测版本 + let new_version = if let Some(path) = &instance.install_path { + let version_cmd = format!("{} --version", path); + tracing::info!("工具 {} 版本检查: {:?}", instance.tool_name, version_cmd); + + #[cfg(target_os = "windows")] + let output = Command::new("cmd").arg("/C").arg(&version_cmd).output(); + + #[cfg(not(target_os = "windows"))] + let output = Command::new("sh").arg("-c").arg(&version_cmd).output(); + + match output { + Ok(out) if out.status.success() => { + let raw_version = String::from_utf8_lossy(&out.stdout).trim().to_string(); + Some(parse_version_string(&raw_version)) + } + _ => { + // 版本获取失败,保持原版本 + tracing::warn!("工具 {} 版本检测失败1,保持原版本", instance.tool_name); + instance.version.clone() + } + } + } else { + tracing::warn!("工具 {} 版本检测失败2,保持原版本", instance.tool_name); + instance.version.clone() + }; + + tracing::info!("工具 {} 新版本号: {:?}", instance.tool_name, new_version); + + // 如果版本号有变化,更新数据库 + if new_version != instance.version { + let mut updated_instance = instance.clone(); + updated_instance.version = new_version.clone(); + updated_instance.updated_at = chrono::Utc::now().timestamp(); + + if let Err(e) = db.update_instance(&updated_instance) { + tracing::warn!("更新实例 {} 失败: {}", instance.instance_id, e); + } else { + tracing::info!( + "工具 {} 版本已更新: {:?} -> {:?}", + instance.tool_name, + instance.version, + new_version + ); + } + } + + // 添加到返回列表 + statuses.push(crate::commands::types::ToolStatus { + id: instance.base_id.clone(), + name: instance.tool_name.clone(), + installed: instance.installed, + version: new_version, + }); + } + + Ok(statuses) +} + /// 批量检查所有工具更新 #[tauri::command] pub async fn check_all_updates() -> Result, String> { @@ -226,7 +506,149 @@ pub async fn check_all_updates() -> Result, String> { Ok(results) } -/// 更新指定工具 +/// 更新工具实例(使用配置的安装器路径) +/// +/// 工作流程: +/// 1. 从数据库读取实例信息 +/// 2. 使用 installer_path 和 install_method 执行更新 +/// 3. 更新数据库中的版本号 +/// +/// 返回:更新结果 +#[tauri::command] +pub async fn update_tool_instance( + instance_id: String, + force: Option, +) -> Result { + use ::duckcoding::models::{InstallMethod, Tool, ToolType}; + use ::duckcoding::services::tool::ToolInstanceDB; + use std::process::Command; + use tokio::time::{timeout, Duration}; + + let force = force.unwrap_or(false); + + // 1. 从数据库读取实例信息 + let db = ToolInstanceDB::new().map_err(|e| format!("初始化数据库失败: {}", e))?; + let all_instances = db + .get_all_instances() + .map_err(|e| format!("读取数据库失败: {}", e))?; + + let instance = all_instances + .iter() + .find(|inst| inst.instance_id == instance_id && inst.tool_type == ToolType::Local) + .ok_or_else(|| format!("未找到实例: {}", instance_id))?; + + // 2. 检查是否有安装器路径和安装方法 + let installer_path = instance.installer_path.as_ref().ok_or_else(|| { + "该实例未配置安装器路径,无法执行快捷更新。请手动更新或重新添加实例。".to_string() + })?; + + let install_method = instance + .install_method + .as_ref() + .ok_or_else(|| "该实例未配置安装方法,无法执行快捷更新".to_string())?; + + // 3. 根据安装方法构建更新命令 + let tool_obj = Tool::by_id(&instance.base_id).ok_or_else(|| "未知工具".to_string())?; + + let update_cmd = match install_method { + InstallMethod::Npm => { + let package_name = &tool_obj.npm_package; + if force { + format!("{} install -g {} --force", installer_path, package_name) + } else { + format!("{} update -g {}", installer_path, package_name) + } + } + InstallMethod::Brew => { + let tool_id = &instance.base_id; + format!("{} upgrade {}", installer_path, tool_id) + } + InstallMethod::Official => { + return Err("官方安装方式暂不支持快捷更新,请手动重新安装".to_string()); + } + InstallMethod::Other => { + return Err("「其他」类型不支持 APP 内快捷更新,请手动更新".to_string()); + } + }; + + // 4. 执行更新命令(120秒超时) + tracing::info!("使用安装器 {} 执行更新: {}", installer_path, update_cmd); + + let update_future = async { + #[cfg(target_os = "windows")] + let output = Command::new("cmd").arg("/C").arg(&update_cmd).output(); + + #[cfg(not(target_os = "windows"))] + let output = Command::new("sh").arg("-c").arg(&update_cmd).output(); + + output + }; + + let update_result = timeout(Duration::from_secs(120), update_future).await; + + match update_result { + Ok(Ok(output)) if output.status.success() => { + // 5. 更新成功,获取新版本 + let version_cmd = format!("{} --version", instance.install_path.as_ref().unwrap()); + + #[cfg(target_os = "windows")] + let version_output = Command::new("cmd").arg("/C").arg(&version_cmd).output(); + + #[cfg(not(target_os = "windows"))] + let version_output = Command::new("sh").arg("-c").arg(&version_cmd).output(); + + let new_version = match version_output { + Ok(out) if out.status.success() => { + let raw = String::from_utf8_lossy(&out.stdout).trim().to_string(); + Some(parse_version_string(&raw)) + } + _ => None, + }; + + // 6. 更新数据库中的版本号 + if let Some(ref version) = new_version { + let mut updated_instance = instance.clone(); + updated_instance.version = Some(version.clone()); + updated_instance.updated_at = chrono::Utc::now().timestamp(); + + if let Err(e) = db.update_instance(&updated_instance) { + tracing::warn!("更新数据库版本失败: {}", e); + } + } + + Ok(UpdateResult { + success: true, + message: "✅ 更新成功!".to_string(), + has_update: false, + current_version: new_version.clone(), + latest_version: new_version, + mirror_version: None, + mirror_is_stale: None, + tool_id: Some(instance.base_id.clone()), + }) + } + Ok(Ok(output)) => { + // 命令执行失败 + let stderr = String::from_utf8_lossy(&output.stderr); + let stdout = String::from_utf8_lossy(&output.stdout); + Err(format!( + "更新失败\n\nstderr: {}\nstdout: {}", + stderr, stdout + )) + } + Ok(Err(e)) => Err(format!("执行命令失败: {}", e)), + Err(_) => Err("更新超时(120秒)".to_string()), + } +} + +/// 更新工具(旧版本,已废弃) +/// +/// ⚠️ 此命令已废弃,请使用 update_tool_instance +/// +/// 原因: +/// - 不使用数据库中的 installer_path 配置 +/// - 每次重新检测安装方法,可能不准确 +/// - 无法支持同一工具的多个实例 #[tauri::command] pub async fn update_tool(tool: String, force: Option) -> Result { // 应用代理配置(如果已配置) @@ -300,3 +722,280 @@ pub async fn update_tool(tool: String, force: Option) -> Result Result { + use std::path::PathBuf; + use std::process::Command; + + let path_buf = PathBuf::from(&path); + + // 检查文件是否存在 + if !path_buf.exists() { + return Err(format!("路径不存在: {}", path)); + } + + // 检查是否是文件 + if !path_buf.is_file() { + return Err(format!("路径不是文件: {}", path)); + } + + // 执行 --version 命令 + let version_cmd = format!("{} --version", path); + + #[cfg(target_os = "windows")] + let output = Command::new("cmd") + .arg("/C") + .arg(&version_cmd) + .output() + .map_err(|e| format!("执行命令失败: {}", e))?; + + #[cfg(not(target_os = "windows"))] + let output = Command::new("sh") + .arg("-c") + .arg(&version_cmd) + .output() + .map_err(|e| format!("执行命令失败: {}", e))?; + + if !output.status.success() { + return Err(format!("命令执行失败,退出码: {}", output.status)); + } + + // 解析版本号 + let version_str = String::from_utf8_lossy(&output.stdout).trim().to_string(); + if version_str.is_empty() { + return Err("无法获取版本信息".to_string()); + } + + // 简单验证:版本号应该包含数字 + if !version_str.chars().any(|c| c.is_numeric()) { + return Err(format!("无效的版本信息: {}", version_str)); + } + + Ok(version_str) +} + +/// 手动添加工具实例(保存用户指定的路径) +/// +/// 工作流程: +/// 1. 验证工具路径有效性 +/// 2. 验证安装器路径有效性(非 Other 类型时) +/// 3. 检查路径是否已被其他工具使用(防止重复) +/// 4. 创建 ToolInstance +/// 5. 保存到数据库 +/// +/// 返回:工具状态信息 +#[tauri::command] +pub async fn add_manual_tool_instance( + tool_id: String, + path: String, + install_method: String, // "npm" | "brew" | "official" | "other" + installer_path: Option, + _registry_state: tauri::State<'_, ToolRegistryState>, +) -> Result { + use ::duckcoding::models::{InstallMethod, ToolInstance, ToolType}; + use ::duckcoding::services::tool::ToolInstanceDB; + use std::path::PathBuf; + + // 1. 验证工具路径 + let version = validate_tool_path(tool_id.clone(), path.clone()).await?; + + // 2. 解析安装方法 + let parsed_method = match install_method.as_str() { + "npm" => InstallMethod::Npm, + "brew" => InstallMethod::Brew, + "official" => InstallMethod::Official, + "other" => InstallMethod::Other, + _ => return Err(format!("未知的安装方法: {}", install_method)), + }; + + // 3. 验证安装器路径(非 Other 类型时需要) + if parsed_method != InstallMethod::Other { + if let Some(ref installer) = installer_path { + let installer_buf = PathBuf::from(installer); + if !installer_buf.exists() { + return Err(format!("安装器路径不存在: {}", installer)); + } + if !installer_buf.is_file() { + return Err(format!("安装器路径不是文件: {}", installer)); + } + } else { + return Err("非「其他」类型必须提供安装器路径".to_string()); + } + } + + // 4. 检查路径是否已存在 + let db = ToolInstanceDB::new().map_err(|e| format!("初始化数据库失败: {}", e))?; + let all_instances = db + .get_all_instances() + .map_err(|e| format!("读取数据库失败: {}", e))?; + + // 路径冲突检查 + if let Some(existing) = all_instances + .iter() + .find(|inst| inst.install_path.as_ref() == Some(&path) && inst.tool_type == ToolType::Local) + { + return Err(format!( + "路径冲突:该路径已被 {} 使用,无法重复添加", + existing.tool_name + )); + } + + // 5. 创建工具显示名称 + let tool_name = match tool_id.as_str() { + "claude-code" => "Claude Code", + "codex" => "CodeX", + "gemini-cli" => "Gemini CLI", + _ => &tool_id, + }; + + // 6. 创建 ToolInstance(使用时间戳确保唯一性) + let now = chrono::Utc::now().timestamp(); + let instance_id = format!("{}-local-{}", tool_id, now); + let instance = ToolInstance { + instance_id: instance_id.clone(), + base_id: tool_id.clone(), + tool_name: tool_name.to_string(), + tool_type: ToolType::Local, + install_method: Some(parsed_method), + installed: true, + version: Some(version.clone()), + install_path: Some(path.clone()), + installer_path, // 用户提供的安装器路径 + wsl_distro: None, + ssh_config: None, + is_builtin: false, + created_at: now, + updated_at: now, + }; + + // 7. 保存到数据库 + db.add_instance(&instance) + .map_err(|e| format!("保存到数据库失败: {}", e))?; + + // 8. 返回 ToolStatus 格式 + Ok(crate::commands::types::ToolStatus { + id: tool_id.clone(), + name: tool_name.to_string(), + installed: true, + version: Some(version), + }) +} + +/// 检测单个工具但不保存(仅用于预览) +/// +/// 工作流程: +/// 1. 简化版检测:直接调用命令检查工具是否存在 +/// 2. 返回检测结果(不保存到数据库) +/// +/// 返回:工具状态信息 +#[tauri::command] +pub async fn detect_tool_without_save( + tool_id: String, + _registry_state: tauri::State<'_, ToolRegistryState>, +) -> Result { + use ::duckcoding::utils::CommandExecutor; + + let command_executor = CommandExecutor::new(); + + // 根据工具ID确定检测命令和名称 + let (check_cmd, tool_name) = match tool_id.as_str() { + "claude-code" => ("claude", "Claude Code"), + "codex" => ("codex", "CodeX"), + "gemini-cli" => ("gemini", "Gemini CLI"), + _ => return Err(format!("未知工具ID: {}", tool_id)), + }; + + // 检测工具是否存在 + let installed = command_executor.command_exists_async(check_cmd).await; + + let version = if installed { + // 获取版本 + let version_cmd = format!("{} --version", check_cmd); + let result = command_executor.execute_async(&version_cmd).await; + if result.success { + let version_str = result.stdout.trim().to_string(); + if !version_str.is_empty() { + Some(parse_version_string(&version_str)) + } else { + None + } + } else { + None + } + } else { + None + }; + + Ok(crate::commands::types::ToolStatus { + id: tool_id.clone(), + name: tool_name.to_string(), + installed, + version, + }) +} + +/// 检测单个工具并保存到数据库 +/// +/// 工作流程: +/// 1. 先查询数据库中是否已有该工具的实例 +/// 2. 如果已有且已安装,直接返回(除非 force_redetect = true) +/// 3. 如果没有或需要重新检测,执行单工具检测(会先删除旧实例) +/// +/// 返回:工具实例信息 +#[tauri::command] +pub async fn detect_single_tool( + tool_id: String, + force_redetect: Option, + registry_state: tauri::State<'_, ToolRegistryState>, +) -> Result { + use ::duckcoding::models::ToolType; + use ::duckcoding::services::tool::ToolInstanceDB; + + let force = force_redetect.unwrap_or(false); + + if !force { + // 1. 先查询数据库中是否已有该工具的本地实例 + let db = ToolInstanceDB::new().map_err(|e| format!("初始化数据库失败: {}", e))?; + let all_instances = db + .get_all_instances() + .map_err(|e| format!("读取数据库失败: {}", e))?; + + // 查找该工具的本地实例 + if let Some(existing) = all_instances.iter().find(|inst| { + inst.base_id == tool_id && inst.tool_type == ToolType::Local && inst.installed + }) { + // 如果已有实例且已安装,直接返回 + tracing::info!("工具 {} 已在数据库中,直接返回", existing.tool_name); + return Ok(crate::commands::types::ToolStatus { + id: tool_id.clone(), + name: existing.tool_name.clone(), + installed: true, + version: existing.version.clone(), + }); + } + } + + // 2. 执行单工具检测(会删除旧实例避免重复) + let registry = registry_state.registry.lock().await; + let instance = registry + .detect_and_persist_single_tool(&tool_id) + .await + .map_err(|e| format!("检测失败: {}", e))?; + + // 3. 返回 ToolStatus 格式 + Ok(crate::commands::types::ToolStatus { + id: tool_id.clone(), + name: instance.tool_name.clone(), + installed: instance.installed, + version: instance.version.clone(), + }) +} diff --git a/src-tauri/src/commands/tool_management.rs b/src-tauri/src/commands/tool_management.rs index d459bdb..afe18f6 100644 --- a/src-tauri/src/commands/tool_management.rs +++ b/src-tauri/src/commands/tool_management.rs @@ -10,30 +10,6 @@ pub struct ToolRegistryState { pub registry: Arc>, } -/// 检查数据库中是否已有本地工具数据(用于判断是否需要执行首次检测) -#[tauri::command] -pub async fn has_tools_in_database( - state: tauri::State<'_, ToolRegistryState>, -) -> Result { - let registry = state.registry.lock().await; - registry - .has_local_tools_in_db() - .await - .map_err(|e| format!("检查数据库失败: {}", e)) -} - -/// 检测本地工具并保存到数据库(用于新手引导) -#[tauri::command] -pub async fn detect_and_save_tools( - state: tauri::State<'_, ToolRegistryState>, -) -> Result, String> { - let registry = state.registry.lock().await; - registry - .detect_and_persist_local_tools() - .await - .map_err(|e| format!("检测工具失败: {}", e)) -} - /// 获取所有工具实例(按工具ID分组)- 只从数据库读取 #[tauri::command] pub async fn get_tool_instances( @@ -46,16 +22,19 @@ pub async fn get_tool_instances( .map_err(|e| format!("获取工具实例失败: {}", e)) } -/// 刷新工具实例状态(重新检测并更新数据库) +/// 刷新工具实例状态(仅从数据库读取,不重新检测) +/// +/// 修改说明:不再自动检测所有工具,仅返回数据库中已有的工具实例 +/// 如需添加新工具,请使用工具管理页面的「添加实例」功能 #[tauri::command] pub async fn refresh_tool_instances( state: tauri::State<'_, ToolRegistryState>, ) -> Result>, String> { let registry = state.registry.lock().await; registry - .refresh_all() + .get_all_grouped() .await - .map_err(|e| format!("刷新工具实例失败: {}", e)) + .map_err(|e| format!("获取工具实例失败: {}", e)) } /// 列出所有可用的WSL发行版 diff --git a/src-tauri/src/main.rs b/src-tauri/src/main.rs index bd2c709..4c96f8a 100644 --- a/src-tauri/src/main.rs +++ b/src-tauri/src/main.rs @@ -413,7 +413,8 @@ fn main() { Ok(()) }) - .plugin(tauri_plugin_shell::init()); + .plugin(tauri_plugin_shell::init()) + .plugin(tauri_plugin_dialog::init()); // 条件注册单实例插件 let builder = if single_instance_enabled { @@ -448,8 +449,17 @@ fn main() { check_node_environment, install_tool, check_update, + check_update_for_instance, + refresh_all_tool_versions, check_all_updates, update_tool, + update_tool_instance, + validate_tool_path, + add_manual_tool_instance, + scan_installer_for_tool_path, + scan_all_tool_candidates, + detect_single_tool, + detect_tool_without_save, // 请使用新的 pm_ 系列命令 save_global_config, get_global_config, @@ -520,8 +530,6 @@ fn main() { add_wsl_tool_instance, add_ssh_tool_instance, delete_tool_instance, - has_tools_in_database, - detect_and_save_tools, // 引导管理命令 get_onboarding_status, save_onboarding_progress, diff --git a/src-tauri/src/models/tool.rs b/src-tauri/src/models/tool.rs index 4339a24..02c41f1 100644 --- a/src-tauri/src/models/tool.rs +++ b/src-tauri/src/models/tool.rs @@ -38,6 +38,7 @@ pub enum InstallMethod { Official, // 官方脚本 Npm, // npm install Brew, // Homebrew (macOS) + Other, // 其他(不支持APP内快捷更新) } impl Tool { @@ -252,6 +253,8 @@ pub struct ToolInstance { pub version: Option, /// 实际安装路径 pub install_path: Option, + /// 安装器路径(如 npm/brew 的可执行文件路径,用于更新) + pub installer_path: Option, /// WSL发行版名称(仅WSL类型使用) pub wsl_distro: Option, /// SSH配置(仅SSH类型使用) @@ -283,6 +286,7 @@ impl ToolInstance { installed, version, install_path, + installer_path: None, wsl_distro: None, ssh_config: None, is_builtin: true, @@ -314,6 +318,7 @@ impl ToolInstance { installed, version, install_path, + installer_path: None, wsl_distro: Some(distro_name), ssh_config: None, is_builtin: false, @@ -347,6 +352,7 @@ impl ToolInstance { installed, version, install_path, + installer_path: None, wsl_distro: None, ssh_config: Some(ssh_config), is_builtin: false, diff --git a/src-tauri/src/services/tool/db.rs b/src-tauri/src/services/tool/db.rs index 85db4d9..b907811 100644 --- a/src-tauri/src/services/tool/db.rs +++ b/src-tauri/src/services/tool/db.rs @@ -92,6 +92,7 @@ impl ToolInstanceDB { installed: instance.installed, version: instance.version.clone(), install_path: instance.install_path.clone(), + installer_path: instance.installer_path.clone(), // 新增 install_method: instance.install_method.clone(), is_builtin: instance.is_builtin, created_at: instance.created_at, @@ -326,6 +327,7 @@ impl ToolInstanceDB { installed: installed_int != 0, version: row.get(5)?, install_path: row.get(6)?, + installer_path: None, // 旧数据没有,需要后续检测 wsl_distro: row.get(7)?, ssh_config, is_builtin: is_builtin_int != 0, @@ -384,6 +386,7 @@ mod tests { installed: true, version: Some("1.0.0".to_string()), install_path: Some("/usr/local/bin/test".to_string()), + installer_path: Some("/usr/local/bin/npm".to_string()), wsl_distro: None, ssh_config: None, is_builtin: true, diff --git a/src-tauri/src/services/tool/detectors/claude_code.rs b/src-tauri/src/services/tool/detectors/claude_code.rs index 09aadec..243d013 100644 --- a/src-tauri/src/services/tool/detectors/claude_code.rs +++ b/src-tauri/src/services/tool/detectors/claude_code.rs @@ -115,6 +115,9 @@ impl ToolDetector for ClaudeCodeDetector { InstallMethod::Brew => { anyhow::bail!("Claude Code 不支持 Homebrew 安装,请使用官方安装或 npm") } + InstallMethod::Other => { + anyhow::bail!("不支持 APP 内安装,请手动安装") + } } } diff --git a/src-tauri/src/services/tool/detectors/codex.rs b/src-tauri/src/services/tool/detectors/codex.rs index 74a458b..e81ccf8 100644 --- a/src-tauri/src/services/tool/detectors/codex.rs +++ b/src-tauri/src/services/tool/detectors/codex.rs @@ -110,6 +110,9 @@ impl ToolDetector for CodeXDetector { } InstallMethod::Npm => self.install_npm(executor, force).await, InstallMethod::Brew => self.install_brew(executor).await, + InstallMethod::Other => { + anyhow::bail!("不支持 APP 内安装,请手动安装") + } } } diff --git a/src-tauri/src/services/tool/detectors/gemini_cli.rs b/src-tauri/src/services/tool/detectors/gemini_cli.rs index 6e43742..4ad990d 100644 --- a/src-tauri/src/services/tool/detectors/gemini_cli.rs +++ b/src-tauri/src/services/tool/detectors/gemini_cli.rs @@ -95,7 +95,7 @@ impl ToolDetector for GeminiCLIDetector { ) -> Result<()> { match method { InstallMethod::Npm => self.install_npm(executor, force).await, - InstallMethod::Official | InstallMethod::Brew => { + InstallMethod::Official | InstallMethod::Brew | InstallMethod::Other => { anyhow::bail!("Gemini CLI 仅支持 npm 安装") } } diff --git a/src-tauri/src/services/tool/registry.rs b/src-tauri/src/services/tool/registry.rs index 8b38f09..2006394 100644 --- a/src-tauri/src/services/tool/registry.rs +++ b/src-tauri/src/services/tool/registry.rs @@ -139,37 +139,155 @@ impl ToolRegistry { (None, None, None) }; + // 检测安装器路径(基于安装方法) + let installer_path = if let (true, Some(method)) = (installed, &install_method) { + match method { + InstallMethod::Npm => { + // 检测 npm 路径:先用 which/where + let npm_detect_cmd = if cfg!(target_os = "windows") { + "where npm" + } else { + "which npm" + }; + + match self.command_executor.execute_async(npm_detect_cmd).await { + result if result.success => { + let path = result.stdout.lines().next().unwrap_or("").trim(); + if !path.is_empty() { + Some(path.to_string()) + } else { + None + } + } + _ => None, + } + } + InstallMethod::Brew => { + // 检测 brew 路径(仅 macOS) + match self.command_executor.execute_async("which brew").await { + result if result.success => { + let path = result.stdout.trim(); + if !path.is_empty() { + Some(path.to_string()) + } else { + None + } + } + _ => None, + } + } + _ => None, + } + } else { + None + }; + tracing::debug!( - "工具 {} 检测结果: installed={}, version={:?}, path={:?}, method={:?}", + "工具 {} 检测结果: installed={}, version={:?}, path={:?}, method={:?}, installer={:?}", tool_name, installed, version, install_path, - install_method + install_method, + installer_path ); // 创建 ToolInstance(需要获取 Tool 的完整信息) let tool = Tool::by_id(tool_id).unwrap_or_else(|| { - // 如果 Tool 定义不存在,从 Detector 构建最小化 Tool - Tool { - id: tool_id.to_string(), - name: tool_name.to_string(), - group_name: format!("{} 专用分组", tool_name), - npm_package: detector.npm_package().to_string(), - check_command: detector.check_command().to_string(), - config_dir: detector.config_dir(), - config_file: detector.config_file().to_string(), - env_vars: crate::models::EnvVars { - api_key: String::new(), - base_url: String::new(), - }, - use_proxy_for_version_check: detector.use_proxy_for_version_check(), + tracing::warn!("未找到工具定义: {}, 使用对应的静态方法", tool_id); + match tool_id { + "claude-code" => Tool::claude_code(), + "codex" => Tool::codex(), + "gemini-cli" => Tool::gemini_cli(), + _ => panic!("未知工具ID: {}", tool_id), } }); - let mut instance = ToolInstance::from_tool_local(&tool, installed, version, install_path); - instance.install_method = install_method; // 设置检测到的安装方式 - instance + let now = chrono::Utc::now().timestamp(); + let instance_id = format!("{}-local-{}", tool_id, now); + + ToolInstance { + instance_id, + base_id: tool.id.clone(), + tool_name: tool.name.clone(), + tool_type: ToolType::Local, + install_method, + installed, + version, + install_path, + installer_path, // 使用检测到的安装器路径 + wsl_distro: None, + ssh_config: None, + is_builtin: true, + created_at: now, + updated_at: now, + } + } + + /// 检测单个本地工具并持久化(公开方法) + /// + /// 工作流程: + /// 1. 删除该工具的所有现有本地实例(避免重复) + /// 2. 执行检测 + /// 3. 检查路径是否与其他工具冲突 + /// 4. 如果检测到且无冲突,保存到数据库 + /// + /// 返回:工具实例 + pub async fn detect_and_persist_single_tool(&self, tool_id: &str) -> Result { + let detector = self + .detector_registry + .get(tool_id) + .ok_or_else(|| anyhow::anyhow!("未找到工具 {} 的检测器", tool_id))?; + + tracing::info!("开始检测单个工具: {}", tool_id); + + // 1. 删除该工具的所有本地实例(避免重复) + let db = self.db.lock().await; + let all_instances = db.get_all_instances()?; + for inst in &all_instances { + if inst.base_id == tool_id && inst.tool_type == ToolType::Local { + tracing::info!("删除旧实例: {}", inst.instance_id); + let _ = db.delete_instance(&inst.instance_id); + } + } + drop(db); + + // 2. 执行检测 + let instance = self.detect_single_tool_by_detector(detector).await; + + // 3. 检查路径冲突(如果检测到路径) + if instance.installed { + if let Some(detected_path) = &instance.install_path { + let db = self.db.lock().await; + let all_instances = db.get_all_instances()?; + drop(db); + + // 检查是否有其他工具使用了相同路径 + if let Some(existing) = all_instances.iter().find(|inst| { + inst.install_path.as_ref() == Some(detected_path) + && inst.tool_type == ToolType::Local + && inst.base_id != tool_id // 排除同一工具 + }) { + return Err(anyhow::anyhow!( + "路径冲突:检测到的路径 {} 已被 {} 使用", + detected_path, + existing.tool_name + )); + } + } + } + + // 4. 保存到数据库 + let db = self.db.lock().await; + if instance.installed { + db.upsert_instance(&instance)?; + tracing::info!("工具 {} 检测并保存成功", instance.tool_name); + } else { + tracing::info!("工具 {} 未检测到", instance.tool_name); + } + drop(db); + + Ok(instance) } /// 刷新本地工具状态(重新检测,更新存在的,删除不存在的) @@ -357,16 +475,7 @@ impl ToolRegistry { pub async fn get_local_tool_status(&self) -> Result> { tracing::debug!("获取本地工具轻量级状态"); - // 检查数据库是否有本地工具数据 - let has_data = self.has_local_tools_in_db().await?; - - if !has_data { - tracing::info!("数据库为空,执行首次检测并持久化"); - // 首次检测并保存到数据库 - self.detect_and_persist_local_tools().await?; - } - - // 从数据库读取所有实例 + // 从数据库读取所有实例(不主动检测) let grouped = self.get_all_grouped().await?; // 转换为轻量级 ToolStatus diff --git a/src-tauri/src/services/tool/tools_config.rs b/src-tauri/src/services/tool/tools_config.rs index 27cc015..0d6c023 100644 --- a/src-tauri/src/services/tool/tools_config.rs +++ b/src-tauri/src/services/tool/tools_config.rs @@ -41,6 +41,8 @@ pub struct LocalToolInstance { pub installed: bool, pub version: Option, pub install_path: Option, + #[serde(default)] + pub installer_path: Option, // 安装器路径(如 npm/brew 路径) pub install_method: Option, pub is_builtin: bool, pub created_at: i64, @@ -124,6 +126,7 @@ impl ToolsConfig { installed: local.installed, version: local.version.clone(), install_path: local.install_path.clone(), + installer_path: local.installer_path.clone(), // 新增 wsl_distro: None, ssh_config: None, is_builtin: local.is_builtin, @@ -143,6 +146,7 @@ impl ToolsConfig { installed: wsl.installed, version: wsl.version.clone(), install_path: wsl.install_path.clone(), + installer_path: None, // WSL环境暂不支持 wsl_distro: Some(wsl.distro_name.clone()), ssh_config: None, is_builtin: wsl.is_builtin, @@ -162,6 +166,7 @@ impl ToolsConfig { installed: ssh.installed, version: ssh.version.clone(), install_path: ssh.install_path.clone(), + installer_path: None, // SSH环境暂不支持 wsl_distro: None, ssh_config: Some(ssh.ssh_config.clone()), is_builtin: ssh.is_builtin, @@ -213,6 +218,7 @@ impl ToolsConfig { installed: instance.installed, version: instance.version, install_path: instance.install_path, + installer_path: instance.installer_path, // 新增 install_method: instance.install_method, is_builtin: instance.is_builtin, created_at: instance.created_at, @@ -284,6 +290,7 @@ mod tests { installed: true, version: Some("2.0.5".to_string()), install_path: Some("/usr/local/bin/claude".to_string()), + installer_path: Some("/usr/local/bin/npm".to_string()), install_method: Some(InstallMethod::Npm), is_builtin: true, created_at: 1733299200, diff --git a/src-tauri/src/utils/installer_scanner.rs b/src-tauri/src/utils/installer_scanner.rs new file mode 100644 index 0000000..0262bb7 --- /dev/null +++ b/src-tauri/src/utils/installer_scanner.rs @@ -0,0 +1,183 @@ +// 安装器路径扫描工具 +// +// 从工具路径智能扫描安装器路径(npm、brew 等) + +use crate::models::InstallMethod; +use crate::utils::PlatformInfo; +use std::path::PathBuf; + +/// 工具候选结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct ToolCandidate { + /// 工具可执行文件路径 + pub tool_path: String, + /// 安装器路径 + pub installer_path: Option, + /// 安装方法 + pub install_method: InstallMethod, + /// 版本号 + pub version: String, +} + +/// 安装器候选结果 +#[derive(Debug, Clone, serde::Serialize)] +pub struct InstallerCandidate { + /// 安装器路径 + pub path: String, + /// 安装器类型 + pub installer_type: InstallMethod, + /// 扫描级别(1=同级目录, 2=上级目录) + pub level: u8, +} + +/// 从工具路径扫描安装器 +/// +/// 策略: +/// 1. 第一级:工具所在目录 +/// 2. 第二级:上级目录 +/// +/// 示例: +/// - 工具:~/.nvm/versions/node/v18.16.0/bin/gemini +/// - 第一级扫描:~/.nvm/versions/node/v18.16.0/bin/ +/// - 第二级扫描:~/.nvm/versions/node/v18.16.0/ +pub fn scan_installer_paths(tool_path: &str) -> Vec { + let mut candidates = Vec::new(); + let tool_path_buf = PathBuf::from(tool_path); + + // 1. 获取工具所在目录 + let tool_dir = match tool_path_buf.parent() { + Some(dir) => dir, + None => return candidates, + }; + + // 2. 定义安装器名称(包含所有可能的扩展名) + let installer_configs = [ + ("npm", InstallMethod::Npm), + ("npm.cmd", InstallMethod::Npm), + ("npm.exe", InstallMethod::Npm), + ("pnpm", InstallMethod::Npm), + ("pnpm.cmd", InstallMethod::Npm), + ("pnpm.exe", InstallMethod::Npm), + ("yarn", InstallMethod::Npm), + ("yarn.cmd", InstallMethod::Npm), + ("yarn.exe", InstallMethod::Npm), + ("brew", InstallMethod::Brew), + ]; + + // 3. 第一级扫描:工具所在目录 + for (name, installer_type) in &installer_configs { + let installer_path = tool_dir.join(name); + if installer_path.is_file() { + candidates.push(InstallerCandidate { + path: installer_path.to_string_lossy().to_string(), + installer_type: installer_type.clone(), + level: 1, + }); + } + } + + // 4. 第二级扫描:上级目录 + if let Some(parent_dir) = tool_dir.parent() { + for (name, installer_type) in &installer_configs { + let installer_path = parent_dir.join(name); + if installer_path.is_file() { + // 避免重复:检查路径是否已在候选列表中 + let path_str = installer_path.to_string_lossy().to_string(); + if !candidates.iter().any(|c| c.path == path_str) { + candidates.push(InstallerCandidate { + path: path_str, + installer_type: installer_type.clone(), + level: 2, + }); + } + } + } + } + + // 5. 排序:同级 npm > 上级 npm > 同级 brew > 上级 brew + candidates.sort_by_key(|c| { + let type_priority = match c.installer_type { + InstallMethod::Npm => 1, + InstallMethod::Brew => 2, + _ => 3, + }; + (c.level, type_priority) + }); + + candidates +} + +/// 扫描所有可能的工具实例(用于自动扫描) +/// +/// 工作流程: +/// 1. 获取硬编码路径列表 +/// 2. 在每个路径中查找工具可执行文件 +/// 3. 返回所有找到的工具路径 +/// +/// 注意:版本检测和安装器扫描在命令层完成 +pub fn scan_tool_executables(tool_id: &str) -> Vec { + let platform = PlatformInfo::current(); + let search_paths_str = platform.build_enhanced_path(); + + // 解析 PATH 环境变量 + let separator = platform.path_separator(); + let search_paths: Vec<&str> = search_paths_str.split(separator).collect(); + + // 工具ID到可执行文件名的映射 + let executable_name = match tool_id { + "claude-code" => "claude", + "gemini-cli" => "gemini", + "codex" => "codex", + _ => tool_id, // 默认使用 tool_id + }; + + // 工具可执行文件名(包含扩展名) + let tool_names = if cfg!(target_os = "windows") { + vec![ + format!("{}.cmd", executable_name), + format!("{}.exe", executable_name), + format!("{}.bat", executable_name), + executable_name.to_string(), + ] + } else { + vec![executable_name.to_string()] + }; + + let mut found_paths = Vec::new(); + + // 在所有路径中查找工具 + for search_path in search_paths { + let search_dir = PathBuf::from(search_path); + if !search_dir.is_dir() { + continue; + } + + for tool_name in &tool_names { + let tool_path = search_dir.join(tool_name); + if tool_path.is_file() { + let path_str = tool_path.to_string_lossy().to_string(); + // 避免重复 + if !found_paths.contains(&path_str) { + found_paths.push(path_str); + } + } + } + } + + found_paths +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn test_scan_installer_paths() { + // 测试路径扫描逻辑 + // 注意:实际测试需要在真实文件系统中进行 + let tool_path = "/usr/local/bin/claude"; + let candidates = scan_installer_paths(tool_path); + // 应该在 /usr/local/bin/ 和 /usr/local/ 中查找 + println!("Found {} candidates", candidates.len()); + } +} diff --git a/src-tauri/src/utils/mod.rs b/src-tauri/src/utils/mod.rs index 47e182c..a0b77e2 100644 --- a/src-tauri/src/utils/mod.rs +++ b/src-tauri/src/utils/mod.rs @@ -1,11 +1,13 @@ pub mod command; pub mod config; pub mod file_helpers; +pub mod installer_scanner; pub mod platform; pub mod wsl_executor; pub use command::*; pub use config::*; pub use file_helpers::*; +pub use installer_scanner::*; pub use platform::*; pub use wsl_executor::*; diff --git a/src/App.tsx b/src/App.tsx index 04a8cd9..494a05d 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useCallback } from 'react'; -import { listen } from '@tauri-apps/api/event'; +import { listen, emit } from '@tauri-apps/api/event'; import { invoke } from '@tauri-apps/api/core'; import { AppSidebar } from '@/components/layout/AppSidebar'; import { CloseActionDialog } from '@/components/dialogs/CloseActionDialog'; @@ -55,6 +55,7 @@ function App() { const [selectedProxyToolId, setSelectedProxyToolId] = useState(undefined); const [settingsInitialTab, setSettingsInitialTab] = useState('basic'); const [settingsRestrictToTab, setSettingsRestrictToTab] = useState(undefined); + const [restrictedPage, setRestrictedPage] = useState(undefined); // 引导状态管理 const [showOnboarding, setShowOnboarding] = useState(false); @@ -283,11 +284,51 @@ function App() { }, ); + // 监听统一引导导航事件(标准化) + const unlistenOnboardingNavigate = listen<{ + targetPage: string; + restrictToTab?: string; + autoAction?: string; + }>('onboarding-navigate', (event) => { + const { targetPage, restrictToTab, autoAction } = event.payload || {}; + + console.log('[Onboarding Nav] 接收导航事件:', { targetPage, restrictToTab, autoAction }); + + // 设置页面限制 + setRestrictedPage(targetPage); + + // 处理设置页面的特殊逻辑 + if (targetPage === 'settings' && restrictToTab) { + setSettingsInitialTab(restrictToTab); + setSettingsRestrictToTab(restrictToTab); + } + + // 跳转到目标页面 + setActiveTab(targetPage as TabType); + + // 延迟触发自动动作(等待页面渲染和事件监听建立) + if (autoAction) { + console.log('[Onboarding Nav] 将在 500ms 后触发自动动作:', autoAction); + setTimeout(() => { + console.log('[Onboarding Nav] 触发自动动作:', autoAction); + emit(autoAction); + }, 500); + } + }); + + // 监听清除引导限制 + const unlistenClearRestriction = listen('clear-onboarding-restriction', () => { + setRestrictedPage(undefined); + setSettingsRestrictToTab(undefined); + }); + return () => { unlistenUpdateAvailable.then((fn) => fn()); unlistenRequestCheck.then((fn) => fn()); unlistenNotFound.then((fn) => fn()); unlistenOpenSettings.then((fn) => fn()); + unlistenOnboardingNavigate.then((fn) => fn()); + unlistenClearRestriction.then((fn) => fn()); }; }, [toast]); @@ -371,14 +412,19 @@ function App() { setActiveTab(tab as TabType)} - restrictNavigation={!!settingsRestrictToTab} + restrictNavigation={!!settingsRestrictToTab || !!restrictedPage} + allowedPage={restrictedPage || (settingsRestrictToTab ? 'settings' : undefined)} /> {/* 主内容区域 */}
{activeTab === 'dashboard' && } {activeTab === 'tool-management' && ( - + )} {activeTab === 'install' && } {activeTab === 'balance' && } diff --git a/src/components/Onboarding/steps/v3/ToolDetectionStep.tsx b/src/components/Onboarding/steps/v3/ToolDetectionStep.tsx index 58bdc37..d7eacba 100644 --- a/src/components/Onboarding/steps/v3/ToolDetectionStep.tsx +++ b/src/components/Onboarding/steps/v3/ToolDetectionStep.tsx @@ -1,190 +1,104 @@ // filepath: e:\DuckCoding\src\components\Onboarding\steps\v3\ToolDetectionStep.tsx -import { useState, useEffect, useRef } from 'react'; +import { useEffect } from 'react'; import { StepProps } from '../../../../types/onboarding'; -import { detectAndSaveTools } from '@/lib/tauri-commands'; -import type { ToolInstance } from '@/types/tool-management'; - -// 工具信息定义 -const TOOLS = [ - { id: 'claude-code', name: 'Claude Code', icon: '🤖' }, - { id: 'codex', name: 'CodeX', icon: '📦' }, - { id: 'gemini-cli', name: 'Gemini CLI', icon: '✨' }, -]; - -type DetectionStatus = 'pending' | 'detecting' | 'done' | 'error'; - -interface ToolDetectionState { - status: DetectionStatus; - installed: boolean; - version: string | null; -} - -export default function ToolDetectionStep({ onNext }: StepProps) { - const [detecting, setDetecting] = useState(false); - const [toolStates, setToolStates] = useState>(() => { - const initial: Record = {}; - TOOLS.forEach((tool) => { - initial[tool.id] = { status: 'pending', installed: false, version: null }; - }); - return initial; - }); - const [error, setError] = useState(null); - const [completed, setCompleted] = useState(false); - - // 使用 ref 追踪是否已开始检测,防止重复执行 - const hasStartedRef = useRef(false); - - const runDetection = async () => { - if (detecting) return; - - setDetecting(true); - setError(null); - - // 设置所有工具为检测中状态 - setToolStates((prev) => { - const updated = { ...prev }; - TOOLS.forEach((tool) => { - updated[tool.id] = { ...updated[tool.id], status: 'detecting' }; - }); - return updated; - }); +import type { OnboardingNavigationPayload } from '../../../../types/onboarding'; +import { emit, listen } from '@tauri-apps/api/event'; +export default function ToolDetectionStep({ onNext, onPrevious, isFirst }: StepProps) { + const handleGoToToolManagement = async () => { try { - // 调用后端并行检测 - const results = await detectAndSaveTools(); - - // 更新各工具状态 - setToolStates((prev) => { - const updated = { ...prev }; - results.forEach((instance: ToolInstance) => { - if (updated[instance.base_id]) { - updated[instance.base_id] = { - status: 'done', - installed: instance.installed, - version: instance.version ?? null, - }; - } - }); - // 确保没有结果的工具也标记为完成 - TOOLS.forEach((tool) => { - if (updated[tool.id].status !== 'done') { - updated[tool.id] = { status: 'done', installed: false, version: null }; - } - }); - return updated; - }); - - setCompleted(true); - } catch (err) { - setError(err instanceof Error ? err.message : '检测失败'); - setToolStates((prev) => { - const updated = { ...prev }; - TOOLS.forEach((tool) => { - if (updated[tool.id].status === 'detecting') { - updated[tool.id] = { ...updated[tool.id], status: 'error' }; - } - }); - return updated; - }); - } finally { - setDetecting(false); + // 隐藏引导界面 + await emit('hide-onboarding'); + // 使用标准化导航事件 + await emit('onboarding-navigate', { + targetPage: 'tool-management', + autoAction: 'open-add-instance-dialog', + } as OnboardingNavigationPayload); + } catch (error) { + console.error('打开工具管理页面失败:', error); } }; - // 组件挂载时自动开始检测(仅执行一次) + const handleNext = async () => { + // 清除限制并恢复引导界面 + await emit('clear-onboarding-restriction'); + await emit('show-onboarding'); + onNext(); + }; + + // 监听继续引导事件 useEffect(() => { - if (hasStartedRef.current) return; - hasStartedRef.current = true; - runDetection(); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + const unlisten = listen('continue-onboarding', async () => { + await emit('clear-onboarding-restriction'); + await emit('show-onboarding'); + onNext(); + }); - const installedCount = Object.values(toolStates).filter( - (s) => s.status === 'done' && s.installed, - ).length; + return () => { + unlisten.then((fn) => fn()); + }; + }, [onNext]); return (
- 🔍 + 🛠️
-

检测系统工具

- -

正在检测您系统中已安装的 AI 编程工具...

- -
- {TOOLS.map((tool) => { - const state = toolStates[tool.id]; - return ( -
-
{tool.icon}
-
-
{tool.name}
-
- {state.status === 'pending' && 等待检测} - {state.status === 'detecting' && ( - - 检测中... - - )} - {state.status === 'done' && state.installed && ( - - 已安装 {state.version && v{state.version}} - - )} - {state.status === 'done' && !state.installed && ( - 未安装 - )} - {state.status === 'error' && 检测失败} -
-
-
- {state.status === 'done' && state.installed && ( - - )} - {state.status === 'done' && !state.installed && ( - - )} -
-
- ); - })} -
- - {error && ( -
-

{error}

- +

配置工具实例

+ +

+ DuckCoding 支持管理多个 AI 编程工具实例,您可以在工具管理页面添加和配置它们 +

+ +
+
💡
+
+

如何添加工具实例?

+

点击下方的「前往配置」按钮,在工具管理页面点击「添加实例」,然后选择:

+
    +
  • + 本地环境:支持自动扫描或手动指定路径 +
  • +
  • + WSL 环境:在 Windows 子系统 Linux 中使用工具 +
  • +
- )} +
- {completed && ( -
- {installedCount > 0 ? ( -

- 检测到 {installedCount} 个已安装的工具 -

- ) : ( -

未检测到已安装的工具,您可以稍后在工具管理页面安装

- )} -
- )} +
+

支持的添加方式

+
    +
  • + 自动扫描:自动检测系统中已安装的工具(npm、Homebrew 等) +
  • +
  • + 手动指定:选择工具可执行文件路径 +
  • +
+
- + +
+ + +
+ +

+ 提示:点击「前往配置」会打开工具管理页面,配置完成后点击「下一步」继续引导 +

); diff --git a/src/components/layout/AppSidebar.tsx b/src/components/layout/AppSidebar.tsx index f75875d..0ef918b 100644 --- a/src/components/layout/AppSidebar.tsx +++ b/src/components/layout/AppSidebar.tsx @@ -37,6 +37,7 @@ interface AppSidebarProps { activeTab: string; onTabChange: (tab: string) => void; restrictNavigation?: boolean; + allowedPage?: string; // 新增:允许访问的页面(引导模式下) } // 导航项配置 @@ -54,7 +55,12 @@ const secondaryItems = [ { id: 'settings', label: '设置', icon: SettingsIcon }, ]; -export function AppSidebar({ activeTab, onTabChange, restrictNavigation }: AppSidebarProps) { +export function AppSidebar({ + activeTab, + onTabChange, + restrictNavigation, + allowedPage, +}: AppSidebarProps) { const { toast } = useToast(); const { theme, actualTheme, setTheme } = useTheme(); @@ -68,13 +74,16 @@ export function AppSidebar({ activeTab, onTabChange, restrictNavigation }: AppSi }, [isCollapsed]); const handleTabChange = (tab: string) => { - if (restrictNavigation && tab !== activeTab) { - toast({ - title: '请先完成引导', - description: '完成当前引导步骤后即可访问其他页面', - variant: 'default', - }); - return; + if (restrictNavigation) { + // 只允许访问 allowedPage + if (allowedPage && tab !== allowedPage) { + toast({ + title: '请先完成引导', + description: '完成当前引导步骤后即可访问其他页面', + variant: 'default', + }); + return; + } } onTabChange(tab); }; @@ -96,7 +105,7 @@ export function AppSidebar({ activeTab, onTabChange, restrictNavigation }: AppSi : 'hover:bg-accent hover:shadow-md hover:scale-[1.02]' }`} onClick={() => handleTabChange(item.id)} - disabled={restrictNavigation && activeTab !== item.id} + disabled={restrictNavigation && allowedPage ? item.id !== allowedPage : false} > {isActive && !isCollapsed && (
diff --git a/src/lib/tauri-commands.ts b/src/lib/tauri-commands.ts index e5d5f86..41180a5 100644 --- a/src/lib/tauri-commands.ts +++ b/src/lib/tauri-commands.ts @@ -229,10 +229,44 @@ export async function checkUpdate(tool: string): Promise { return await invoke('check_update', { tool }); } +/** + * 检查工具更新(基于实例ID,使用配置的路径检测版本) + * @param instanceId 工具实例ID + * @returns 更新信息 + */ +export async function checkUpdateForInstance(instanceId: string): Promise { + return await invoke('check_update_for_instance', { instanceId }); +} + +/** + * 刷新数据库中所有工具的版本号(使用配置的路径检测) + * @returns 更新后的工具状态列表 + */ +export async function refreshAllToolVersions(): Promise { + return await invoke('refresh_all_tool_versions'); +} + export async function checkAllUpdates(): Promise { return await invoke('check_all_updates'); } +/** + * 更新工具实例(使用配置的安装器路径) + * @param instanceId 工具实例ID + * @param force 是否强制更新 + * @returns 更新结果 + */ +export async function updateToolInstance( + instanceId: string, + force?: boolean, +): Promise { + return await invoke('update_tool_instance', { instanceId, force }); +} + +/** + * 更新工具(旧版本,已废弃) + * @deprecated 请使用 updateToolInstance + */ export async function updateTool(tool: string, force?: boolean): Promise { return await invoke('update_tool', { tool, force }); } @@ -738,22 +772,6 @@ export async function deleteToolInstance(instanceId: string): Promise { return await invoke('delete_tool_instance', { instanceId }); } -/** - * 检查数据库中是否已有本地工具数据 - * @returns 是否已有本地工具数据 - */ -export async function hasToolsInDatabase(): Promise { - return await invoke('has_tools_in_database'); -} - -/** - * 检测本地工具并保存到数据库(用于新手引导) - * @returns 检测到的工具实例列表 - */ -export async function detectAndSaveTools(): Promise { - return await invoke('detect_and_save_tools'); -} - // ==================== 单实例模式配置命令 ==================== /** @@ -868,3 +886,94 @@ export async function updateProxyConfig(toolId: ToolId, config: ToolProxyConfig) export async function getAllProxyConfigs(): Promise> { return invoke>('get_all_proxy_configs'); } + +/** + * 验证用户指定的工具路径是否有效 + * @param toolId 工具ID + * @param path 工具可执行文件路径 + * @returns 版本号字符串 + */ +export async function validateToolPath(toolId: string, path: string): Promise { + return invoke('validate_tool_path', { toolId, path }); +} + +/** + * 工具候选结果 + */ +export interface ToolCandidate { + tool_path: string; + installer_path: string | null; + install_method: string; // "Npm" | "Brew" | "Official" | "Other" + version: string; +} + +/** + * 安装器候选结果 + */ +export interface InstallerCandidate { + path: string; + installer_type: string; // "Npm" | "Brew" | "Official" | "Other" + level: number; // 1=同级目录, 2=上级目录 +} + +/** + * 扫描所有工具候选(用于自动扫描) + * @param toolId 工具ID + * @returns 工具候选列表 + */ +export async function scanAllToolCandidates(toolId: string): Promise { + return invoke('scan_all_tool_candidates', { toolId }); +} + +/** + * 扫描工具路径的安装器 + * @param toolPath 工具可执行文件路径 + * @returns 安装器候选列表 + */ +export async function scanInstallerForToolPath(toolPath: string): Promise { + return invoke('scan_installer_for_tool_path', { toolPath }); +} + +/** + * 手动添加工具实例(保存用户指定的路径) + * @param toolId 工具ID + * @param path 工具可执行文件路径 + * @param installMethod 安装方法("npm" | "brew" | "official" | "other") + * @param installerPath 安装器路径(非 other 类型时必填) + * @returns 工具状态信息 + */ +export async function addManualToolInstance( + toolId: string, + path: string, + installMethod: string, + installerPath?: string, +): Promise { + return invoke('add_manual_tool_instance', { + toolId, + path, + installMethod, + installerPath, + }); +} + +/** + * 检测单个工具但不保存(仅用于预览) + * @param toolId 工具ID + * @returns 工具状态信息 + */ +export async function detectToolWithoutSave(toolId: string): Promise { + return invoke('detect_tool_without_save', { toolId }); +} + +/** + * 检测单个工具并保存到数据库 + * @param toolId 工具ID + * @param forceRedetect 是否强制重新检测(默认 false,会优先读取数据库) + * @returns 工具状态信息 + */ +export async function detectSingleTool( + toolId: string, + forceRedetect?: boolean, +): Promise { + return invoke('detect_single_tool', { toolId, forceRedetect }); +} diff --git a/src/pages/DashboardPage/hooks/useDashboard.ts b/src/pages/DashboardPage/hooks/useDashboard.ts index 7f6b085..5783558 100644 --- a/src/pages/DashboardPage/hooks/useDashboard.ts +++ b/src/pages/DashboardPage/hooks/useDashboard.ts @@ -36,6 +36,7 @@ export function useDashboard(initialTools: ToolStatus[]) { if (updateInfo && updateInfo.success && tool.installed) { return { ...tool, + version: updateInfo.current_version || tool.version, // 使用返回的 current_version hasUpdate: updateInfo.has_update, latestVersion: updateInfo.latest_version || null, mirrorVersion: updateInfo.mirror_version || null, @@ -90,6 +91,7 @@ export function useDashboard(initialTools: ToolStatus[]) { if (tool.id === toolId && tool.installed) { return { ...tool, + version: updateInfo.current_version || tool.version, // 使用返回的 current_version hasUpdate: updateInfo.has_update, latestVersion: updateInfo.latest_version || null, mirrorVersion: updateInfo.mirror_version || null, diff --git a/src/pages/DashboardPage/index.tsx b/src/pages/DashboardPage/index.tsx index c45a4f1..5fbee7f 100644 --- a/src/pages/DashboardPage/index.tsx +++ b/src/pages/DashboardPage/index.tsx @@ -8,7 +8,7 @@ import { UpdateCheckBanner } from './components/UpdateCheckBanner'; import { useDashboard } from './hooks/useDashboard'; import { getToolDisplayName } from '@/utils/constants'; import { useToast } from '@/hooks/use-toast'; -import { refreshToolStatus, type ToolStatus } from '@/lib/tauri-commands'; +import { refreshAllToolVersions, type ToolStatus } from '@/lib/tauri-commands'; interface DashboardPageProps { tools: ToolStatus[]; @@ -44,19 +44,19 @@ export function DashboardPage({ tools: toolsProp, loading: loadingProp }: Dashbo // window.dispatchEvent(new CustomEvent('refresh-tools')); // }; - // 手动刷新工具状态(清除缓存重新检测) + // 手动刷新工具状态(刷新数据库版本号) const handleRefreshToolStatus = async () => { setRefreshing(true); try { - const newTools = await refreshToolStatus(); + const newTools = await refreshAllToolVersions(); updateTools(newTools); toast({ - title: '检测完成', - description: '工具安装状态已更新', + title: '刷新完成', + description: '工具版本号已更新', }); } catch (error) { toast({ - title: '检测失败', + title: '刷新失败', description: String(error), variant: 'destructive', }); diff --git a/src/pages/Onboarding/components/ManualPathDialog.tsx b/src/pages/Onboarding/components/ManualPathDialog.tsx new file mode 100644 index 0000000..2e14f87 --- /dev/null +++ b/src/pages/Onboarding/components/ManualPathDialog.tsx @@ -0,0 +1,219 @@ +import { useState } from 'react'; +import { open } from '@tauri-apps/plugin-dialog'; +import { + Dialog, + DialogHeader, + DialogTitle, + DialogDescription, + DialogFooter, +} from '@/components/ui/dialog'; +import * as DialogPrimitive from '@radix-ui/react-dialog'; +import { X, InfoIcon, Loader2 } from 'lucide-react'; +import { Button } from '@/components/ui/button'; +import { Input } from '@/components/ui/input'; +import { Alert, AlertDescription, AlertTitle } from '@/components/ui/alert'; +import { validateToolPath } from '@/lib/tauri-commands'; +import { useToast } from '@/hooks/use-toast'; +import { cn } from '@/lib/utils'; + +interface ManualPathDialogProps { + toolId: string; + toolName: string; + onConfirm: (toolId: string, path: string) => Promise; + onCancel: () => void; +} + +export function ManualPathDialog({ toolId, toolName, onConfirm, onCancel }: ManualPathDialogProps) { + const [path, setPath] = useState(''); + const [validating, setValidating] = useState(false); + const [validationError, setValidationError] = useState(null); + const [confirming, setConfirming] = useState(false); + const { toast } = useToast(); + + // 获取常见安装路径示例 + const getCommonPaths = () => { + const isWindows = navigator.platform.toLowerCase().includes('win'); + if (isWindows) { + return [ + `C:\\Users\\用户名\\AppData\\Roaming\\npm\\${toolId}.cmd`, + `C:\\Users\\用户名\\.npm-global\\${toolId}.cmd`, + `C:\\Program Files\\${toolName}\\${toolId}.exe`, + ]; + } else { + return [ + `~/.npm-global/bin/${toolId}`, + `/usr/local/bin/${toolId}`, + `/opt/homebrew/bin/${toolId}`, + `~/.local/bin/${toolId}`, + ]; + } + }; + + // 打开文件选择器 + const handleBrowse = async () => { + try { + const isWindows = navigator.platform.toLowerCase().includes('win'); + const selected = await open({ + directory: false, + multiple: false, + title: `选择 ${toolName} 可执行文件`, + filters: [ + { + name: '可执行文件', + extensions: isWindows ? ['exe', 'cmd', 'bat'] : [], + }, + ], + }); + + if (selected && typeof selected === 'string') { + setPath(selected); + handleValidate(selected); + } + } catch (error) { + toast({ + variant: 'destructive', + title: '打开文件选择器失败', + description: String(error), + }); + } + }; + + // 验证路径 + const handleValidate = async (pathToValidate: string) => { + if (!pathToValidate.trim()) { + setValidationError('请输入路径'); + return; + } + + setValidating(true); + setValidationError(null); + + try { + await validateToolPath(toolId, pathToValidate); + // 验证成功,清除错误 + } catch (error) { + setValidationError(String(error)); + } finally { + setValidating(false); + } + }; + + // 确认保存 + const handleConfirm = async () => { + if (!path || validating || validationError) return; + + setConfirming(true); + try { + await onConfirm(toolId, path); + } catch (error) { + toast({ + variant: 'destructive', + title: '保存路径失败', + description: String(error), + }); + } finally { + setConfirming(false); + } + }; + + return ( + !open && onCancel()}> + + {/* 自定义 Overlay,z-index 高于 Onboarding (10000) */} + + {/* 自定义 Content,z-index 高于 Overlay */} + + {/* 关闭按钮 */} + + + Close + + + + 手动指定 {toolName} 路径 + 自动扫描未找到该工具,请选择可执行文件路径 + + +
+ {/* 常见路径提示 */} + + + 常见安装路径 + +
    + {getCommonPaths().map((examplePath, index) => ( +
  • + {examplePath} +
  • + ))} +
+
+
+ + {/* 路径输入 */} +
+ { + setPath(e.target.value); + setValidationError(null); + }} + onBlur={() => { + if (path) handleValidate(path); + }} + placeholder="输入或浏览选择可执行文件" + disabled={validating || confirming} + /> + +
+ + {/* 验证状态 */} + {validating && ( +
+ + 验证中... +
+ )} + + {validationError && ( + + {validationError} + + )} +
+ + + + + +
+
+
+ ); +} diff --git a/src/pages/ToolManagementPage/components/AddInstanceDialog.tsx b/src/pages/ToolManagementPage/components/AddInstanceDialog.tsx index 0ce8f6d..db0a3df 100644 --- a/src/pages/ToolManagementPage/components/AddInstanceDialog.tsx +++ b/src/pages/ToolManagementPage/components/AddInstanceDialog.tsx @@ -7,6 +7,7 @@ import { } from '@/components/ui/dialog'; import { Button } from '@/components/ui/button'; import { Label } from '@/components/ui/label'; +import { Input } from '@/components/ui/input'; import { Select, SelectContent, @@ -14,39 +15,95 @@ import { SelectTrigger, SelectValue, } from '@/components/ui/select'; -import { RadioGroup, RadioGroupItem } from '@/components/ui/radio-group'; +import { Alert, AlertDescription } from '@/components/ui/alert'; import { useState, useEffect, useCallback } from 'react'; +import { Loader2, InfoIcon, CheckCircle2 } from 'lucide-react'; +import { open as openDialog } from '@tauri-apps/plugin-dialog'; import type { SSHConfig } from '@/types/tool-management'; -import { listWslDistributions } from '@/lib/tauri-commands'; +import { + listWslDistributions, + validateToolPath, + addManualToolInstance, + scanInstallerForToolPath, + scanAllToolCandidates, + type InstallerCandidate, + type ToolCandidate, +} from '@/lib/tauri-commands'; import { useToast } from '@/hooks/use-toast'; +import { cn } from '@/lib/utils'; interface AddInstanceDialogProps { open: boolean; onClose: () => void; onAdd: ( baseId: string, - type: 'wsl' | 'ssh', + type: 'local' | 'wsl' | 'ssh', sshConfig?: SSHConfig, distroName?: string, ) => Promise; } +const TOOLS = [ + { id: 'claude-code', name: 'Claude Code' }, + { id: 'codex', name: 'CodeX' }, + { id: 'gemini-cli', name: 'Gemini CLI' }, +]; + +const ENV_TYPES = [ + { id: 'local', name: '本地环境', description: '在本机直接运行工具' }, + { id: 'wsl', name: 'WSL 环境', description: 'Windows子系统Linux环境', disabled: true }, + { id: 'ssh', name: 'SSH 远程', description: '远程服务器环境(开发中)', disabled: true }, +]; + +const LOCAL_METHODS = [ + { id: 'auto', name: '自动扫描', description: '自动检测系统中已安装的工具' }, + { id: 'manual', name: '手动指定', description: '选择工具可执行文件路径' }, +]; + +const INSTALL_METHODS = [ + { id: 'npm', name: 'npm', description: '使用 npm 安装' }, + { id: 'brew', name: 'Homebrew', description: '使用 brew 安装(仅 macOS)' }, + { id: 'official', name: '官方脚本', description: '使用官方安装脚本' }, + { id: 'other', name: '其他', description: '不支持APP内快捷更新' }, +]; + export function AddInstanceDialog({ open, onClose, onAdd }: AddInstanceDialogProps) { const { toast } = useToast(); + const [step, setStep] = useState(1); // 当前步骤:1=选择工具和方式,2=配置详情 const [baseId, setBaseId] = useState('claude-code'); - const [envType, setEnvType] = useState<'wsl' | 'ssh'>('wsl'); + const [envType, setEnvType] = useState<'local' | 'wsl' | 'ssh'>('local'); + const [localMethod, setLocalMethod] = useState<'auto' | 'manual'>('auto'); + const [manualPath, setManualPath] = useState(''); + const [installMethod, setInstallMethod] = useState<'npm' | 'brew' | 'official' | 'other'>('npm'); + const [installerPath, setInstallerPath] = useState(''); + const [installerCandidates, setInstallerCandidates] = useState([]); + const [toolCandidates, setToolCandidates] = useState([]); + const [selectedToolCandidate, setSelectedToolCandidate] = useState(null); + const [showCustomInstaller, setShowCustomInstaller] = useState(false); + const [validating, setValidating] = useState(false); + const [validationError, setValidationError] = useState(null); + const [scanning, setScanning] = useState(false); + const [scanResult, setScanResult] = useState<{ installed: boolean; version: string } | null>( + null, + ); const [loading, setLoading] = useState(false); const [wslDistros, setWslDistros] = useState([]); const [selectedDistro, setSelectedDistro] = useState(''); const [loadingDistros, setLoadingDistros] = useState(false); + const toolNames: Record = { + 'claude-code': 'Claude Code', + codex: 'CodeX', + 'gemini-cli': 'Gemini CLI', + }; + const loadWslDistros = useCallback(async () => { setLoadingDistros(true); try { const distros = await listWslDistributions(); setWslDistros(distros); if (distros.length > 0) { - setSelectedDistro(distros[0]); // 默认选择第一个 + setSelectedDistro(distros[0]); } } catch (err) { toast({ @@ -60,97 +117,799 @@ export function AddInstanceDialog({ open, onClose, onAdd }: AddInstanceDialogPro } }, [toast]); - // 当选择WSL时,加载发行版列表 useEffect(() => { if (open && envType === 'wsl') { loadWslDistros(); } }, [open, envType, loadWslDistros]); - const handleSubmit = async () => { - if (envType === 'ssh') { - // SSH功能暂不实现 - return; + // 重置扫描结果:当用户更改工具、环境类型或添加方式时 + useEffect(() => { + setScanResult(null); + setToolCandidates([]); + setSelectedToolCandidate(null); + setInstallerCandidates([]); + }, [baseId, envType, localMethod]); + + // 对话框打开时重置所有状态 + useEffect(() => { + if (open) { + setStep(1); + setBaseId('claude-code'); + setEnvType('local'); + setLocalMethod('auto'); + setManualPath(''); + setInstallMethod('npm'); + setInstallerPath(''); + setInstallerCandidates([]); + setToolCandidates([]); + setSelectedToolCandidate(null); + setShowCustomInstaller(false); + setValidating(false); + setValidationError(null); + setScanResult(null); + // selectedDistro 不重置,因为可能从第一个 useEffect 设置 } + }, [open]); - if (envType === 'wsl' && !selectedDistro) { + const getCommonPaths = () => { + const isWindows = navigator.platform.toLowerCase().includes('win'); + if (isWindows) { + return [ + `C:\\Users\\用户名\\AppData\\Roaming\\npm\\${baseId}.cmd`, + `C:\\Users\\用户名\\.npm-global\\${baseId}.cmd`, + `C:\\Program Files\\${toolNames[baseId]}\\${baseId}.exe`, + ]; + } else { + return [ + `~/.npm-global/bin/${baseId}`, + `/usr/local/bin/${baseId}`, + `/opt/homebrew/bin/${baseId}`, + `~/.local/bin/${baseId}`, + ]; + } + }; + + const handleBrowse = async () => { + try { + const isWindows = navigator.platform.toLowerCase().includes('win'); + const selected = await openDialog({ + directory: false, + multiple: false, + title: `选择 ${toolNames[baseId]} 可执行文件`, + filters: [ + { + name: '可执行文件', + extensions: isWindows ? ['exe', 'cmd', 'bat'] : [], + }, + ], + }); + + if (selected && typeof selected === 'string') { + setManualPath(selected); + handleValidate(selected); + } + } catch (error) { toast({ - title: '请选择WSL发行版', variant: 'destructive', + title: '打开文件选择器失败', + description: String(error), }); + } + }; + + // 浏览选择安装器路径 + const handleBrowseInstaller = async () => { + try { + const isWindows = navigator.platform.toLowerCase().includes('win'); + const selected = await openDialog({ + directory: false, + multiple: false, + title: `选择安装器可执行文件(${installMethod})`, + filters: [ + { + name: '可执行文件', + extensions: isWindows ? ['exe', 'cmd', 'bat'] : [], + }, + ], + }); + + if (selected && typeof selected === 'string') { + setInstallerPath(selected); + } + } catch (error) { + toast({ + variant: 'destructive', + title: '打开文件选择器失败', + description: String(error), + }); + } + }; + + const handleValidate = async (pathToValidate: string) => { + if (!pathToValidate.trim()) { + setValidationError('请输入路径'); return; } - setLoading(true); + setValidating(true); + setValidationError(null); + try { - await onAdd(baseId, envType, undefined, selectedDistro); - onClose(); - // 重置状态 - setBaseId('claude-code'); - setEnvType('wsl'); - setSelectedDistro(''); + await validateToolPath(baseId, pathToValidate); + } catch (error) { + setValidationError(String(error)); } finally { - setLoading(false); + setValidating(false); + } + }; + + // 执行扫描/验证(不保存) + const handleScan = async () => { + if (envType !== 'local') return; + + console.log('[AddInstance] 开始扫描,工具:', baseId, '方式:', localMethod); + setScanning(true); + setScanResult(null); + + try { + if (localMethod === 'auto') { + // 自动扫描(扫描所有可能的工具实例) + console.log('[AddInstance] 调用 scanAllToolCandidates,工具:', baseId); + const candidates = await scanAllToolCandidates(baseId); + console.log('[AddInstance] 扫描到', candidates.length, '个工具候选'); + setToolCandidates(candidates); + + if (candidates.length === 0) { + toast({ + variant: 'destructive', + title: '未检测到工具', + description: `未在系统中检测到 ${toolNames[baseId]}`, + }); + } else { + toast({ + title: '扫描完成', + description: `找到 ${candidates.length} 个 ${toolNames[baseId]} 实例`, + }); + + // 如果只有一个候选,自动选择 + if (candidates.length === 1) { + setSelectedToolCandidate(candidates[0]); + setScanResult({ installed: true, version: candidates[0].version }); + } + } + } else { + // 手动验证路径(不保存)并扫描安装器 + if (!manualPath) { + toast({ + variant: 'destructive', + title: '请选择路径', + }); + return; + } + if (validationError) { + toast({ + variant: 'destructive', + title: '路径验证失败', + description: validationError, + }); + return; + } + + console.log('[AddInstance] 验证路径:', manualPath); + const version = await validateToolPath(baseId, manualPath); + console.log('[AddInstance] 验证结果:', version); + setScanResult({ installed: true, version }); + + // 扫描安装器路径 + console.log('[AddInstance] 扫描安装器路径'); + try { + const installerResults = await scanInstallerForToolPath(manualPath); + console.log('[AddInstance] 扫描到', installerResults.length, '个安装器候选'); + setInstallerCandidates(installerResults); + + // 自动选择第一个候选 + if (installerResults.length > 0) { + setInstallerPath(installerResults[0].path); + // 根据安装器类型设置 installMethod + const installerType = installerResults[0].installer_type.toLowerCase(); + if (installerType.includes('npm')) { + setInstallMethod('npm'); + } else if (installerType.includes('brew')) { + setInstallMethod('brew'); + } + } + } catch (error) { + console.error('[AddInstance] 扫描安装器失败:', error); + setInstallerCandidates([]); + } + + toast({ + title: '验证成功', + description: `${toolNames[baseId]} v${version}`, + }); + } + } catch (error) { + console.error('[AddInstance] 扫描/验证失败:', error); + toast({ + variant: 'destructive', + title: '扫描失败', + description: String(error), + }); + setScanResult(null); + } finally { + setScanning(false); + } + }; + + const handleSubmit = async () => { + if (envType === 'local') { + // 本地环境:保存已扫描的实例 + if (!scanResult || !scanResult.installed) { + toast({ + variant: 'destructive', + title: '无可用结果', + description: '请先执行扫描', + }); + return; + } + + setLoading(true); + try { + if (localMethod === 'auto') { + // 自动扫描:使用选中的候选 + if (!selectedToolCandidate) { + toast({ + variant: 'destructive', + title: '请选择工具实例', + description: '请从扫描结果中选择一个实例', + }); + return; + } + + console.log( + '[AddInstance] 保存自动扫描结果,工具:', + baseId, + '候选:', + selectedToolCandidate, + ); + + // 确定安装方法字符串 + const methodStr = selectedToolCandidate.install_method.toLowerCase(); + + await addManualToolInstance( + baseId, + selectedToolCandidate.tool_path, + methodStr, + selectedToolCandidate.installer_path || undefined, + ); + + toast({ + title: '添加成功', + description: `${toolNames[baseId]} v${selectedToolCandidate.version}`, + }); + } else { + // 手动指定:验证并保存路径 + console.log('[AddInstance] 保存手动指定路径:', manualPath, '安装器:', installMethod); + + // 验证:非 other 类型必须提供安装器路径 + if (installMethod !== 'other' && !installerPath) { + toast({ + variant: 'destructive', + title: '请选择安装器路径', + description: `${INSTALL_METHODS.find((m) => m.id === installMethod)?.name} 需要提供安装器路径`, + }); + return; + } + + await addManualToolInstance( + baseId, + manualPath, + installMethod, + installerPath || undefined, + ); + toast({ + title: '添加成功', + description: `${toolNames[baseId]} 已成功添加`, + }); + } + + await onAdd(baseId, 'local'); + handleClose(); + } catch (error) { + toast({ + variant: 'destructive', + title: '添加失败', + description: String(error), + }); + } finally { + setLoading(false); + } + } else if (envType === 'ssh') { + return; + } else if (envType === 'wsl') { + if (!selectedDistro) { + toast({ + title: '请选择WSL发行版', + variant: 'destructive', + }); + return; + } + + setLoading(true); + try { + await onAdd(baseId, envType, undefined, selectedDistro); + handleClose(); + } finally { + setLoading(false); + } } }; const handleClose = () => { - if (!loading) { + if (!loading && !scanning) { onClose(); - // 重置状态 + setStep(1); setBaseId('claude-code'); - setEnvType('wsl'); + setEnvType('local'); + setLocalMethod('auto'); + setManualPath(''); + setInstallMethod('npm'); + setInstallerPath(''); + setInstallerCandidates([]); + setToolCandidates([]); + setSelectedToolCandidate(null); + setShowCustomInstaller(false); + setValidating(false); + setValidationError(null); setSelectedDistro(''); + setScanResult(null); } }; + const handleNext = () => { + // 验证第一步的选择 + if (envType === 'wsl' && !selectedDistro) { + toast({ + variant: 'destructive', + title: '请选择 WSL 发行版', + }); + return; + } + setStep(2); + }; + + const handleBack = () => { + setStep(1); + setScanResult(null); + setToolCandidates([]); + setSelectedToolCandidate(null); + setInstallerCandidates([]); + }; + return ( - - + !isOpen && !loading && onClose()} modal> + e.preventDefault()}> 添加工具实例 -
- {/* 选择工具 */} -
- - -
- - {/* 选择环境类型 */} -
- - setEnvType(value as 'wsl' | 'ssh')} - > -
- - +
+ {/* 第一步:选择工具、环境类型、添加方式 */} + {step === 1 && ( + <> + {/* 选择工具 */} +
+ +
+ {TOOLS.map((tool) => ( + + ))} +
-
- - + + {/* 选择环境类型 */} +
+ +
+ {ENV_TYPES.map((env) => ( + + ))} +
- -
+ + {/* 本地环境:选择添加方式 */} + {envType === 'local' && ( +
+ +
+ {LOCAL_METHODS.map((method) => ( + + ))} +
+
+ )} + + {/* WSL 发行版选择 */} + {envType === 'wsl' && ( +
+ + {loadingDistros ? ( +
+ 加载中... +
+ ) : wslDistros.length === 0 ? ( +
+

+ 未检测到WSL发行版,请先安装WSL +

+
+ ) : ( + + )} +
+ )} + + )} + + {/* 第二步:配置详情/扫描 */} + {step === 2 && envType === 'local' && ( + <> + {localMethod === 'auto' && ( + <> + + + + 将自动扫描系统中已安装的 {toolNames[baseId]},包括 npm、Homebrew 等安装方式 + + + +
+ + + {/* 显示候选列表(多个结果时) */} + {toolCandidates.length > 1 && ( +
+ +
+ {toolCandidates.map((candidate, index) => ( + + ))} +
+
+ )} + + {/* 单个候选时直接显示 */} + {toolCandidates.length === 1 && selectedToolCandidate && ( + + + +
+
✓ 路径:{selectedToolCandidate.tool_path}
+
版本:{selectedToolCandidate.version}
+
安装器:{selectedToolCandidate.installer_path || '未检测到'}
+
+
+
+ )} +
+ + )} + + {localMethod === 'manual' && ( + <> + + + +

常见安装路径:

+
    + {getCommonPaths().map((path, index) => ( +
  • + {path} +
  • + ))} +
+
+
+ + {/* 工具路径输入 */} +
+ +
+ { + setManualPath(e.target.value); + setValidationError(null); + setScanResult(null); + setInstallerCandidates([]); + setShowCustomInstaller(false); + }} + onBlur={() => { + if (manualPath) handleValidate(manualPath); + }} + placeholder="输入或浏览选择" + disabled={validating || loading || scanning} + /> + +
+ + {validating && ( +
+ + 验证中... +
+ )} + + {validationError && ( + + {validationError} + + )} + + {/* 验证路径按钮 */} + + + {/* 验证成功提示 */} + {scanResult && scanResult.installed && ( + + + + ✓ 验证成功:{toolNames[baseId]} v{scanResult.version} + + + )} +
+ + {/* 安装器配置(验证成功后显示) */} + {scanResult && scanResult.installed && ( + <> + {/* 情况A:扫描到安装器且未点击自定义 - 显示下拉选择 */} + {installerCandidates.length > 0 && !showCustomInstaller ? ( +
+
+ + +
+ +

+ 已自动扫描到 {installerCandidates.length}{' '} + 个安装器,点击「自定义」可手动配置 +

+
+ ) : ( + /* 情况B:点击自定义 或 没扫描到 - 显示安装器类型和路径输入 */ + <> +
+ +
+ {INSTALL_METHODS.map((method) => ( + + ))} +
+
+ + {/* 安装器路径输入(非 other 时显示) */} + {installMethod !== 'other' && ( +
+ +
+ setInstallerPath(e.target.value)} + placeholder={`如: ${navigator.platform.toLowerCase().includes('win') ? 'C:\\Program Files\\nodejs\\npm.cmd' : '/usr/local/bin/npm'}`} + disabled={loading || scanning} + /> + +
+

+ {installerCandidates.length === 0 + ? '未检测到安装器,请手动选择或留空(无法快捷更新)' + : '手动指定安装器路径'} +

+
+ )} + + {/* Other 类型警告 */} + {installMethod === 'other' && ( + + + + 「其他」类型不支持 APP 内快捷更新 +
+ 您需要手动更新此工具 +
+
+ )} + + )} + + )} + + )} + + )} {/* WSL发行版选择 */} - {envType === 'wsl' && ( + {step === 1 && envType === 'wsl' && (
{loadingDistros ? ( @@ -194,15 +953,41 @@ export function AddInstanceDialog({ open, onClose, onAdd }: AddInstanceDialogPro
- - + {step === 1 ? ( + <> + + + + ) : ( + <> + + + + )}
diff --git a/src/pages/ToolManagementPage/components/ToolListSection.tsx b/src/pages/ToolManagementPage/components/ToolListSection.tsx index 84efa8c..6d973ef 100644 --- a/src/pages/ToolManagementPage/components/ToolListSection.tsx +++ b/src/pages/ToolManagementPage/components/ToolListSection.tsx @@ -147,7 +147,7 @@ export function ToolListSection({ -
+ {/* 引导模式提示 */} + {restrictNavigation && ( + + + + 当前处于引导模式,请点击「添加实例」按钮配置工具。完成后点击右下角悬浮按钮继续引导。 + + + )} + {/* 加载状态 */} {(loadingProp || dataLoading) && (
diff --git a/src/types/onboarding.ts b/src/types/onboarding.ts index d447b4f..abf7914 100644 --- a/src/types/onboarding.ts +++ b/src/types/onboarding.ts @@ -44,6 +44,18 @@ export interface StepProps { isLast: boolean; } +/** + * 标准化引导导航配置(用于 Tauri 事件通信) + */ +export interface OnboardingNavigationPayload { + /** 目标页面 ID(如 'settings', 'tool-management') */ + targetPage: string; + /** 限制到特定 tab(仅设置页面使用,如 'proxy') */ + restrictToTab?: string; + /** 自动触发的动作(如 'open-add-instance-dialog') */ + autoAction?: string; +} + /** * 引导版本配置映射类型 */