diff --git a/.github/workflows/rust.yml b/.github/workflows/rust.yml index 7f45e44e..ff5d8588 100644 --- a/.github/workflows/rust.yml +++ b/.github/workflows/rust.yml @@ -2,7 +2,7 @@ name: 'publish' on: push: - branches: [ "main" ] + branches: "main" jobs: publish-tauri: @@ -14,8 +14,9 @@ jobs: include: - platform: 'macos-latest' # for ARM based macs args: '--target aarch64-apple-darwin' - - platform: 'macos-latest' # for Intel based macs - args: '--target x86_64-apple-darwin' + # Blender no longer supports Intel based macs. May phase out in the future + # - platform: 'macos-latest' # for Intel based macs + # args: '--target x86_64-apple-darwin' - platform: 'ubuntu-22.04' # for linux distro args: '' - platform: 'windows-latest' @@ -34,8 +35,9 @@ jobs: if: matrix.platform == 'ubuntu-22.04' run: | sudo apt-get update - sudo apt-get install -y libewbkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf + sudo apt-get install -y libwebkit2gtk-4.1-dev libappindicator3-dev librsvg2-dev patchelf +# TODO: Find a way to fix SQLX error: "set `DATABASE_URL` to use query macros online, or run `cargo sqlx prepare` to update the query cache" - uses: tauri-apps/tauri-action@v0 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.gitignore b/.gitignore index 7cc8b3ab..2c636367 100644 --- a/.gitignore +++ b/.gitignore @@ -38,3 +38,8 @@ target/ *ServerSettings.json Cargo.lock *.env + +# schemas always update and appear diff on every git changes +src-tauri/gen/* +blender_rs/examples/assets/*.png +src-tauri/.sqlx/ \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json index e516d16e..dad199d9 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -8,13 +8,9 @@ "name": "dbg Dev client", "type": "lldb", "request": "launch", - "program": "${workspaceRoot}/target/debug/blendfarm", + "program": "${workspaceRoot}/src-tauri/target/debug/blendfarm", "args": [ - // "build", - // "--manifest-path=./src-tauri/Cargo.toml", - // "--no-default-features" - "--client", - "true" + "client" ], "cwd": "${workspaceRoot}", // "preLaunchTask": "ui:dev" diff --git a/.vscode/settings.json b/.vscode/settings.json index 86407a2a..88a2c18b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,4 +1,5 @@ { "rust-analyzer.showUnlinkedFileNotification": false, - "todo-tree.tree.scanMode": "workspace" + "todo-tree.tree.scanMode": "workspace", + "makefile.configureOnOpen": false } \ No newline at end of file diff --git a/Makefile b/Makefile new file mode 100644 index 00000000..dcb20805 --- /dev/null +++ b/Makefile @@ -0,0 +1,19 @@ +default: + cd ./src-tauri/ + cargo tauri dev + +build: + cd ./src-tauri/ + cargo tauri build + # maybe a command to bundle a release and upload gpg keys / etc? + +rebuild_database: .sqlx + cd ./src-tauri/ # navigate to Tauri's codebase + cargo sqlx db reset -y # create the database file + cargo sqlx prepare # create cache sql result that satisfy cargo compiler + +test: + cd ./src-tauri/ && cargo test + +clean: + rm -rf ./src-tauri/target ./src-tauri/ \ No newline at end of file diff --git a/README.md b/README.md index efba7321..6e6db8fc 100644 --- a/README.md +++ b/README.md @@ -4,8 +4,13 @@ This project is inspired by the original project - [LogicReinc](https://github.com/LogicReinc/LogicReinc.BlendFarm) -# A Word from Developer: -This is still a experimental program I'm working on. If you find bugs or problem with this tool, please do not heistate to create an issue, I will review them when I get to the next milestone step. +## A Word from Developer: + +This is still a experimental program I'm working on. If you find bugs or problem with this tool, please create an issue and I will review them when I can. Much of the codebase is experimental of what I've learned over my rust journey. + +## TLDR + +Run `make` from makefile directory in terminal - This will install dependencies, compile, build, and run Blendfarm from the releases folder. For more info, please read below. ### Why I created this application: @@ -17,7 +22,7 @@ I humbly present you BlendFarm 2.0, a open-source software completely re-written [libp2p](https://docs.libp2p.io/) - Peer 2 Peer decenteralize network service that enables network discovery service (mDNS), communication (gossipsub), and file share (kad/DHT). -[Blender](https://github.com/tiberiumboy/BlendFarm/tree/main/blender) - Custom library I wrote that acts as a blender CLI wrapper to install, invoke, and launch Blender application. +[Blender](https://github.com/tiberiumboy/BlendFarm/tree/main/blender) - Custom library I authored that acts as a blender CLI wrapper to install, invoke, and launch Blender application. [Blend](https://docs.rs/blend/latest/blend/) - Used to read blender file without blender application to enable extracting information to the user with pre-configured setup (Eevee/Cycle, frame range, Cameras, resolution, last blender version used, etc). @@ -50,24 +55,28 @@ Blender's limitation applies to this project's scope limitation. If a feature is ## Getting Started -There are several ways to start; the first and easiest would be to download the files and simply run the executable, the second way is to download the source code and compile on your computer to run and start. +Download the latest build from the [release](https://github.com/tiberiumboy/BlendFarm/releases) page. Verify the content using checksum. Then unpack, `chmod +x ./blendfarm` for UNIX users, and run `./blendfarm`. This launches BlendFarm's GUI Manager window. Passing the `client` argument will run the program as worker node. This mode consume your computer to install and run blender! There can only be one client instances per machine. -### TLDR: +### To compile -First and foremost - this commands may be subject to change in the future. (Need to find a better way to handle Clap subcommand with tauri's cli plugin - for now, I'm treating it as an argument) +Run `make` from `Blendfarm` directory. Instruction inside [Makefile](./Makefile) guides step by step instructions to navigate, run, and compile. -First - Install tauri-cli as this component is needed to run `cargo tauri` command. Run the following command: -`cargo install tauri-cli --version ^2.0.0-rc --locked` + -*Note- For windows, you must encapsulate the version in double quotes! + +To launch the application in developer mode, navigate to `./src-tauri/` directory and run `cargo tauri dev`. To run Tauri app - run the following command under `/BlendFarm/` directory - `cargo tauri dev` -To run the client app - run the following command under `/BlendFarm/src-tauri/` directory - `cargo run -- client` +To run the client app - run the following command under `/BlendFarm/src-tauri/` directory - `cargo tauri dev -- -- client` ### Network: -Under the hood, this program uses libp2p with [QUIC transport](https://docs.libp2p.io/concepts/transports/quic/). This treat this computer as both a server and a client. Wrapped in a containerized struct, I am using [mdns](https://docs.libp2p.io/concepts/discovery-routing/mdns/) for network discovery service (to find other network farm node on the network so that you don't have to connect manually), [gossipsub]() for private message procedure call ( how node interacts with other nodes), and kad for file transfer protocol (how node distribute blend, image, and blender binary files across the network). With the power of trio combined, it is the perfect solution for making network farm accessible, easy to start up, and robost. Have a read into [libp2p](https://libp2p.io/) if this interest your project needs! +Under the hood, this program uses libp2p with [QUIC transport](https://docs.libp2p.io/concepts/transports/quic/). This treat this computer as both a server and a client. Wrapped in a containerized struct, I am using [mdns](https://docs.libp2p.io/concepts/discovery-routing/mdns/) for network discovery service (to find other network farm node on the network so that you don't have to connect manually), [gossipsub]() for private message procedure call (how computer talks to another computer), and kad for file transfer protocol (how node distribute blend, image, and blender binary files across the network). With the power of trio combined, it is the perfect solution for making network farm accessible, easy to start up, and robost. Have a read into [libp2p](https://libp2p.io/) if this interest your project needs! + +## Developer blogs +I am using Obsidian to keep track of changes and blogs, which helps provide project clarity and goals. Please check out the [obsidian folder](./obsidian/blendfarm/Context.md) for all of the change logs. B; + B-->A; +``` diff --git a/blender/examples/test/main.rs b/blender/examples/test/main.rs deleted file mode 100644 index 62be5d82..00000000 --- a/blender/examples/test/main.rs +++ /dev/null @@ -1,17 +0,0 @@ -use blender::models::home::BlenderHome; - -fn test_download_blender_home_link() { - let home = BlenderHome::new().expect("Unable to get data"); - let newest = home.as_ref().first().unwrap(); - let link = newest.fetch_latest(); - match link { - Ok(link) => { - dbg!(link); - } - Err(e) => println!("Something wrong - {e}"), - } -} - -fn main() { - test_download_blender_home_link(); -} diff --git a/blender/src/lib.rs b/blender/src/lib.rs deleted file mode 100644 index 518b8cea..00000000 --- a/blender/src/lib.rs +++ /dev/null @@ -1,4 +0,0 @@ -pub mod blender; -pub mod manager; -pub mod models; -pub mod page_cache; diff --git a/blender/src/main.rs b/blender/src/main.rs deleted file mode 100644 index e7a11a96..00000000 --- a/blender/src/main.rs +++ /dev/null @@ -1,3 +0,0 @@ -fn main() { - println!("Hello, world!"); -} diff --git a/blender/src/models.rs b/blender/src/models.rs deleted file mode 100644 index af15e149..00000000 --- a/blender/src/models.rs +++ /dev/null @@ -1,11 +0,0 @@ -pub mod args; -pub mod blender_peek_response; -pub mod blender_render_setting; -pub mod category; -pub mod device; -pub mod download_link; -pub mod engine; -pub mod format; -pub mod home; -pub mod mode; -pub mod status; diff --git a/blender/src/models/blender_peek_response.rs b/blender/src/models/blender_peek_response.rs deleted file mode 100644 index 3aa81433..00000000 --- a/blender/src/models/blender_peek_response.rs +++ /dev/null @@ -1,23 +0,0 @@ -use std::path::PathBuf; - -use semver::Version; -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Clone, Serialize, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct BlenderPeekResponse { - pub last_version: Version, - pub render_width: i32, - pub render_height: i32, - pub frame_start: i32, - pub frame_end: i32, - #[serde(rename = "FPS")] - pub fps: u16, - pub samples: i32, - pub cameras: Vec, - pub selected_camera: String, - pub scenes: Vec, - pub selected_scene: String, - pub engine: String, - pub output: PathBuf, -} diff --git a/blender/src/models/blender_render_setting.rs b/blender/src/models/blender_render_setting.rs deleted file mode 100644 index 3f90b660..00000000 --- a/blender/src/models/blender_render_setting.rs +++ /dev/null @@ -1,165 +0,0 @@ -use super::{ - args::Args, blender_peek_response::BlenderPeekResponse, device::Device, engine::Engine, - format::Format, -}; -use serde::{de::Visitor, ser::SerializeStruct, Deserialize, Serialize}; -use std::{ops::Range, path::PathBuf}; -use uuid::Uuid; - -// In the python script, this Window values gets assigned to border of scn.render.border_* -// Here - I'm calling it as window instead. -#[derive(Debug, Clone, PartialEq)] -pub struct Window { - pub x: Range, - pub y: Range, -} - -impl Default for Window { - fn default() -> Self { - Self { - x: Range { - start: 0.0, - end: 1.0, - }, - y: Range { - start: 0.0, - end: 1.0, - }, - } - } -} - -impl Serialize for Window { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - let mut state = serializer.serialize_struct("Border", 4)?; - state.serialize_field("X", &self.x.start)?; - state.serialize_field("X2", &self.x.end)?; - state.serialize_field("Y", &self.y.start)?; - state.serialize_field("Y2", &self.y.end)?; - state.end() - } -} - -impl<'de> Deserialize<'de> for Window { - fn deserialize(deserializer: D) -> Result - where - D: serde::Deserializer<'de>, - { - struct WindowVisitor; - - impl<'de> Visitor<'de> for WindowVisitor { - type Value = Window; - - fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { - formatter.write_str("struct Border") - } - - fn visit_seq(self, mut seq: V) -> Result - where - V: serde::de::SeqAccess<'de>, - { - let x = seq.next_element()?.unwrap_or(0.0); - let x2 = seq.next_element()?.unwrap_or(1.0); - let y = seq.next_element()?.unwrap_or(0.0); - let y2 = seq.next_element()?.unwrap_or(1.0); - Ok(Window { - x: Range { start: x, end: x2 }, - y: Range { start: y, end: y2 }, - }) - } - } - - const FIELDS: &[&str] = &["X", "X2", "Y", "Y2"]; - deserializer.deserialize_struct("Window", FIELDS, WindowVisitor) - } -} - -#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] -#[serde(rename_all = "PascalCase")] -pub struct BlenderRenderSetting { - #[serde(rename = "TaskID")] - pub id: Uuid, - pub output: PathBuf, - pub scene: String, - pub camera: String, - pub cores: usize, - pub compute_unit: i32, - #[serde(rename = "FPS")] - pub fps: u16, // u32 convert into string for xml-rpc. BEWARE! - pub border: Window, - pub tile_width: i32, - pub tile_height: i32, - pub samples: i32, - pub width: i32, - pub height: i32, - pub engine: i32, - #[serde(rename = "RenderFormat")] - pub format: Format, - // discourage? - pub crop: bool, -} - -impl BlenderRenderSetting { - fn new( - output: PathBuf, - scene: String, - camera: String, - compute_unit: Device, - fps: u16, - border: Window, - tile_width: i32, - tile_height: i32, - samples: i32, - width: i32, - height: i32, - engine: Engine, - format: Format, - ) -> Self { - let id = Uuid::new_v4(); - Self { - id, - output, - scene, - camera, - cores: std::thread::available_parallelism().unwrap().get(), - compute_unit: compute_unit as i32, - fps, - border, - tile_width, - tile_height, - samples, - width, - height, - engine: engine as i32, - format, - crop: false, - } - } - - pub fn parse_from(args: &Args, info: &BlenderPeekResponse) -> Self { - let output = args.output.clone(); - let compute_unit = args.device.clone(); - let border = Default::default(); - let engine = args.engine.clone(); - let format = args.format.clone(); - - BlenderRenderSetting::new( - output.to_owned(), - info.selected_scene.to_owned(), - info.selected_camera.to_owned(), - compute_unit.to_owned(), - info.fps, - border, - -1, - -1, - info.samples, - info.render_width, - info.render_height, - engine, - format, - ) - } -} diff --git a/blender/src/models/device.rs b/blender/src/models/device.rs deleted file mode 100644 index 6dbb7a16..00000000 --- a/blender/src/models/device.rs +++ /dev/null @@ -1,43 +0,0 @@ -use serde::{Deserialize, Serialize}; - -/* -Developer blog- -The only reason why we need to add number that may or may not match blender's enum number list -is because we're passing in the arguments to the python file instead of Blender CLI. -Once I get this part of the code working, then I'll go back and refactor python to make this less ugly and hackable. -*/ - -// TODO: Once python code is working with this rust code - refactor python to reduce this garbage mess below: -#[derive(Debug, Clone, Default, PartialEq, Deserialize, Serialize)] -#[allow(dead_code, non_camel_case_types)] -pub enum Device { - #[default] - CPU = 0, - CUDA = 1, - OPENCL = 2, - CUDA_GPUONLY = 3, - OPENCL_GPUONLY = 4, - HIP = 5, - HIP_GPUONLY = 6, - METAL = 7, - METAL_GPUONLY = 8, - ONEAPI = 9, - ONEAPI_GPUONLY = 10, - OPTIX = 11, - OPTIX_GPUONLY = 12, -} - -// Append +CPU to a GPU device to render on both CPU and GPU. -impl ToString for Device { - fn to_string(&self) -> String { - match self { - Device::CPU => "CPU".to_owned(), - Device::CUDA => "CUDA".to_owned(), - Device::OPTIX => "OPTIX".to_owned(), - Device::HIP => "HIP".to_owned(), - Device::ONEAPI => "ONEAPI".to_owned(), - Device::METAL => "METAL".to_owned(), - _ => todo!("to be implemented after getting this to work with python!"), - } - } -} diff --git a/blender/src/models/engine.rs b/blender/src/models/engine.rs deleted file mode 100644 index 65cbe6bd..00000000 --- a/blender/src/models/engine.rs +++ /dev/null @@ -1,20 +0,0 @@ -use serde::{Deserialize, Serialize}; - -#[derive(Debug, Copy, Default, Clone, Serialize, Deserialize, PartialEq, Eq)] -pub enum Engine { - Cycles = 0, - #[default] - Eevee = 1, - OptiX = 2, -} - -impl ToString for Engine { - fn to_string(&self) -> String { - match self { - Engine::Cycles => "CYCLES".to_owned(), - // Blender 4.2 introduce a new enum called BLENDER_EEVEE_NEXT, which is currently handle in python file atm. - Engine::Eevee => "EEVEE".to_owned(), - Engine::OptiX => "WORKBENCH".to_owned(), - } - } -} diff --git a/blender/src/models/format.rs b/blender/src/models/format.rs deleted file mode 100644 index 39739c79..00000000 --- a/blender/src/models/format.rs +++ /dev/null @@ -1,68 +0,0 @@ -use serde::{Deserialize, Serialize}; -use std::str::FromStr; - -pub enum FormatError { - InvalidInput, -} - -// More context: https://docs.blender.org/manual/en/latest/advanced/command_line/arguments.html#format-options -#[derive(Debug, Clone, Default, PartialEq, Deserialize)] -pub enum Format { - TGA, - RAWTGA, - JPEG, - IRIS, - AVIRAW, - AVIJPEG, - #[default] - PNG, - BMP, - HDR, - TIFF, -} - -impl Serialize for Format { - fn serialize(&self, serializer: S) -> Result - where - S: serde::Serializer, - { - serializer.serialize_str(&self.to_string()) - } -} - -impl FromStr for Format { - type Err = FormatError; - - fn from_str(s: &str) -> Result { - match s.to_uppercase().as_str() { - "TGA" => Ok(Format::TGA), - "RAWTGA" => Ok(Format::RAWTGA), - "JPEG" => Ok(Format::JPEG), - "IRIS" => Ok(Format::IRIS), - "AVIRAW" => Ok(Format::AVIRAW), - "AVIJPEG" => Ok(Format::AVIJPEG), - "PNG" => Ok(Format::PNG), - "BMP" => Ok(Format::BMP), - "HDR" => Ok(Format::HDR), - "TIFF" => Ok(Format::TIFF), - _ => Err(FormatError::InvalidInput), - } - } -} - -impl ToString for Format { - fn to_string(&self) -> String { - match self { - Format::TGA => "TARGA".to_owned(), - Format::RAWTGA => "RAWTARGA".to_owned(), - Format::JPEG => "JPEG".to_owned(), - Format::IRIS => "IRIS".to_owned(), - Format::AVIRAW => "AVIRAW".to_owned(), - Format::AVIJPEG => "AVIJPEG".to_owned(), - Format::PNG => "PNG".to_owned(), - Format::BMP => "BMP".to_owned(), - Format::HDR => "HDR".to_owned(), - Format::TIFF => "TIFF".to_owned(), - } - } -} diff --git a/blender/src/models/home.rs b/blender/src/models/home.rs deleted file mode 100644 index 83ec555e..00000000 --- a/blender/src/models/home.rs +++ /dev/null @@ -1,72 +0,0 @@ -use super::category::BlenderCategory; -use crate::page_cache::PageCache; -use regex::Regex; -use std::io::{Error, ErrorKind, Result}; -use url::Url; - -#[derive(Debug)] -pub struct BlenderHome { - // might use this as a ref? - list: Vec, - // I'd like to reuse this component throughout blender program. If I need to access a web page, this should be used. - cache: PageCache, -} - -impl BlenderHome { - fn get_content(cache: &mut PageCache) -> Result> { - let parent = Url::parse("https://download.blender.org/release/").unwrap(); - let content = cache.fetch(&parent)?; - - // Omit any blender version 2.8 and below - let pattern = r#".*)\">(?Blender(?[3-9]|\d{2,}).(?\d*).*)\/<\/a>"#; - let regex = Regex::new(pattern).map_err(|e| { - Error::new( - ErrorKind::InvalidData, - format!("Unable to create new Regex pattern! {e:?}"), - ) - })?; - - let mut list: Vec = regex - .captures_iter(&content) - .map(|c| { - let (_, [url, name, major, minor]) = c.extract(); - let url = parent.join(url).ok()?; - let major = major.parse().ok()?; - let minor = minor.parse().ok()?; - Some(BlenderCategory::new(name.to_owned(), url, major, minor)) - }) - .flatten() - .collect(); - - list.sort_by(|a, b| b.cmp(a)); - Ok(list) - } - - // I need to have this reference regardless. Offline or online mode. - pub fn new() -> Result { - // TODO: Verify this-: In original source code - there's a comment implying we should use cache as much as possible to avoid possible IP lacklisted. - let mut cache = PageCache::load()?; - let list = match Self::get_content(&mut cache) { - Ok(col) => col, - // maybe the user is offline, we don't know, and that's ok! This shouldn't stop the program from running - // TODO: It would be nice to indicate that we're running in offline mode. Disable some feature such as download blender from web. - Err(e) => { - eprintln!("Unable to get content! {e:?}"); - Vec::new() - } - }; - Ok(Self { list, cache }) - } - - pub fn refresh(&mut self) -> Result<()> { - let content = Self::get_content(&mut self.cache)?; - self.list = content; - Ok(()) - } -} - -impl AsRef> for BlenderHome { - fn as_ref(&self) -> &Vec { - &self.list - } -} diff --git a/blender/src/models/mode.rs b/blender/src/models/mode.rs deleted file mode 100644 index b716f0cb..00000000 --- a/blender/src/models/mode.rs +++ /dev/null @@ -1,23 +0,0 @@ -// use std::default; -use std::ops::Range; -use serde::{Deserialize, Serialize}; - -// context for serde: https://serde.rs/enum-representations.html -#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)] -pub enum Mode { - // JSON: "Frame": "i32", - // render a single frame. - Frame(i32), - - // JSON: "Animation": {"start":"i32", "end":"i32"} - // contains the target start frame to the end target frame. - Animation(Range), - - // future project - allow network node to only render section of the frame instead of whole to visualize realtime rendering view solution. - // JSON: "Section": {"frame":"i32", "coord":{"i32", "i32"}, "size": {"i32", "i32"} } - // Section { - // frame: i32, - // coord: (i32, i32), - // size: (i32, i32), - // }, -} diff --git a/blender/src/models/status.rs b/blender/src/models/status.rs deleted file mode 100644 index 57ffafeb..00000000 --- a/blender/src/models/status.rs +++ /dev/null @@ -1,14 +0,0 @@ -use crate::blender::BlenderError; -use std::path::PathBuf; - -// TODO Find good use of this? -#[derive(Debug)] -pub enum Status { - Idle, - Running { status: String }, - Log { status: String }, - Warning { message: String }, - Error(BlenderError), - Completed { frame: i32, result: PathBuf }, - Exit, -} diff --git a/blender/src/render.py b/blender/src/render.py deleted file mode 100644 index a42b3cfe..00000000 --- a/blender/src/render.py +++ /dev/null @@ -1,254 +0,0 @@ -# TODO: Refactor this so it's less code to read through. -# Sybren mention that Cycle will perform better if the render was sent out as a batch instead of individual renders. -# TODO: See if there's a way to adjust blender render batch if possible? - -#Start -import bpy # type: ignore -import xmlrpc.client -from multiprocessing import cpu_count - -isPre3 = bpy.app.version < (3,0,0) -# Eventually this might get removed due to getting actual value from blend file instead -isPreEeveeNext = bpy.app.version < (4, 2, 0) - -if(isPre3): - print('Detected Blender >= 3.0.0\n') - -scn = bpy.context.scene - -def useDevices(type, allowGPU, allowCPU): - cyclesPref = bpy.context.preferences.addons["cycles"].preferences - - #For older Blender Builds - if (isPre3): - cyclesPref.compute_device_type = type - devs = cyclesPref.get_devices() - cuda_devices, opencl_devices = cyclesPref.get_devices() - print(cyclesPref.compute_device_type) - - devices = None - if(type == "CUDA"): - devices = cuda_devices - elif(type == "OPTIX"): - devices = cuda_devices - else: - devices = opencl_devices - for d in devices: - d.use = (allowCPU and d.type == "CPU") or (allowGPU and d.type != "CPU") - print(type + " Device:", d["name"], d["use"]) - #For Blender Builds >= 3.0 - else: - cyclesPref.compute_device_type = type - - print(cyclesPref.compute_device_type) - - devices = None - if(type == "CUDA"): - devices = cyclesPref.get_devices_for_type("CUDA") - elif(type == "OPTIX"): - devices = cyclesPref.get_devices_for_type("OPTIX") - elif(type == "HIP"): - devices = cyclesPref.get_devices_for_type("HIP") - elif(type == "METAL"): - devices = cyclesPref.get_devices_for_type("METAL") - elif(type == "ONEAPI"): - devices = cyclesPref.get_devices_for_type("ONEAPI") - else: - devices = cyclesPref.get_devices_for_type("OPENCL") - print("Devices Found:", devices) - if(len(devices) == 0): - raise Exception("No devices found for type " + type + ", Unsupported hardware or platform?") - for d in devices: - d.use = (allowCPU and d.type == "CPU") or (allowGPU and d.type != "CPU") - print(type + " Device:", d["name"], d["use"]) - -#Renders provided settings with id to path -def renderWithSettings(renderSettings, frame): - global scn - - # Scene parse - scen = renderSettings["Scene"] - if(scen is None): - scen = "" - if(scen != "" + scn.name != scen): - print("Rendering specified scene " + scen + "\n") - scn = bpy.data.scenes[scen] - if(scn is None): - raise Exception("Unknown Scene :" + scen) - - # set render format - renderFormat = renderSettings["RenderFormat"] - if (not renderFormat): - scn.render.image_settings.file_format = "PNG" - else: - scn.render.image_settings.file_format = renderFormat - - # Set threading - scn.render.threads_mode = 'FIXED' - scn.render.threads = max(cpu_count(), int(renderSettings["Cores"])) - - if (isPre3): - scn.render.tile_x = int(renderSettings["TileWidth"]) - scn.render.tile_y = int(renderSettings["TileHeight"]) - else: - print("Blender > 3.0 doesn't support tile size, thus ignored") - - # Set constraints - scn.render.use_border = True - scn.render.use_crop_to_border = renderSettings["Crop"] - if not renderSettings["Crop"]: - scn.render.film_transparent = True - - scn.render.border_min_x = float(renderSettings["Border"]["X"]) - scn.render.border_max_x = float(renderSettings["Border"]["X2"]) - scn.render.border_min_y = float(renderSettings["Border"]["Y"]) - scn.render.border_max_y = float(renderSettings["Border"]["Y2"]) - - #Set Camera - camera = renderSettings["Camera"] - if(camera != None and camera != "" and bpy.data.objects[camera]): - scn.camera = bpy.data.objects[camera] - - #Set Resolution - scn.render.resolution_x = int(renderSettings["Width"]) - scn.render.resolution_y = int(renderSettings["Height"]) - scn.render.resolution_percentage = 100 - - #Set Samples - scn.cycles.samples = int(renderSettings["Samples"]) - scn.render.use_persistent_data = True - - #Render Device - renderType = int(renderSettings["ComputeUnit"]) - engine = int(renderSettings["Engine"]) - - if(engine == 2): #Optix - optixGPU = renderType == 1 or renderType == 3 or renderType == 11 or renderType == 12; #CUDA or CUDA_GPU_ONLY - optixCPU = renderType != 3 and renderType != 12; #!CUDA_GPU_ONLY && !OPTIX_GPU_ONLY - if(optixCPU and not optixGPU): - scn.cycles.device = "CPU" - else: - scn.cycles.device = "GPU" - useDevices("OPTIX", optixGPU, optixCPU) - else: #Cycles/Eevee - if renderType == 0: #CPU - scn.cycles.device = "CPU" - print("Use CPU") - elif renderType == 1: #Cuda - useDevices("CUDA", True, True) - scn.cycles.device = "GPU" - print("Use Cuda") - elif renderType == 2: #OpenCL - useDevices("OPENCL", True, True) - scn.cycles.device = "GPU" - print("Use OpenCL") - elif renderType == 3: #Cuda (GPU Only) - useDevices("CUDA", True, False) - scn.cycles.device = 'GPU' - print("Use Cuda (GPU)") - elif renderType == 4: #OpenCL (GPU Only) - useDevices("OPENCL", True, False) - scn.cycles.device = 'GPU' - print("Use OpenCL (GPU)") - elif renderType == 5: #HIP - useDevices("HIP", True, False) - scn.cycles.device = 'GPU' - print("Use HIP") - elif renderType == 6: #HIP (GPU Only) - useDevices("HIP", True, True) - scn.cycles.device = 'GPU' - print("Use HIP (GPU)") - elif renderType == 7: #METAL - useDevices("METAL", True, True) - scn.cycles.device = 'GPU' - print("Use METAL") - elif renderType == 8: #METAL (GPU Only) - useDevices("METAL", True, False) - scn.cycles.device = 'GPU' - print("Use METAL (GPU)") - elif renderType == 9: #ONEAPI - useDevices("ONEAPI", True, True) - scn.cycles.device = 'GPU' - print("Use ONEAPI") - elif renderType == 10: #ONEAPI (GPU Only) - useDevices("ONEAPI", True, False) - scn.cycles.device = 'GPU' - print("Use ONEAPI (GPU)") - elif renderType == 11: #OptiX - useDevices("OPTIX", True, True) - scn.cycles.device = "GPU" - print("Use OptiX") - elif renderType == 12: #OptiX (GPU Only) - useDevices("OPTIX", True, False) - scn.cycles.device = "GPU" - print("Use OptiX (GPU)") - - # At the moment, we should derive to use the file settings instead of asking user to manually adjust. Remove denoiser if possible - #Denoiser - Disable this until I can figure out how to fetch this info from Blend lib - # denoise = renderSettings["Denoiser"] - # if denoise is not None: - # if denoise == "None": - # scn.cycles.use_denoising = False - # elif len(denoise) > 0: - # scn.cycles.use_denoising = True - # scn.cycles.denoiser = denoise - - # Set Frames Per Second - fps = renderSettings["FPS"] - if fps is not None and fps > 0: - scn.render.fps = fps - - # blender uses the new BLENDER_EEVEE_NEXT enum for blender4.2 and above. - if(engine == 1): #Eevee - if(isPreEeveeNext): - print("Using EEVEE") - scn.render.engine = "BLENDER_EEVEE" - else: - print("Using EEVEE_NEXT") - scn.render.engine = "BLENDER_EEVEE_NEXT" - else: - scn.render.engine = "CYCLES" - - # Set frame - scn.frame_set(frame) - - # Set Output - scn.render.filepath = renderSettings["Output"] + '/' + str(frame).zfill(5) - id = str(renderSettings["TaskID"]) - - # Render - print("RENDER_START: " + id + "\n", flush=True) - # TODO: Research what use_viewport does? - bpy.ops.render.render(animation=False, write_still=True, use_viewport=False, layer="", scene = scen) - print("SUCCESS: " + id + "\n", flush=True) - -def runBatch(): - # Fatal exception was thrown [Errno 61] Connection refused - see if it's the firewall? - proxy = xmlrpc.client.ServerProxy("http://localhost:8081") - - # Do i need to send in RPC like this or can it just be a value instead? - renderSettings = None - try: - renderSettings = proxy.fetch_info(1) - except Exception as e: - print("Fail to call fetch_info over xml_rpc: " + str(e)) - return - - # Loop over batches - while True: - try: - frame = proxy.next_render_queue(1) - renderWithSettings(renderSettings, frame) - except Exception as e: - print(e) - break - - print("BATCH_COMPLETE\n") - -#Main - -try: - runBatch() - -except Exception as e: - print("EXCEPTION:" + str(e)) \ No newline at end of file diff --git a/blender/Cargo.toml b/blender_rs/Cargo.toml similarity index 57% rename from blender/Cargo.toml rename to blender_rs/Cargo.toml index 8c32e74f..79dbb8f4 100644 --- a/blender/Cargo.toml +++ b/blender_rs/Cargo.toml @@ -10,28 +10,24 @@ edition = "2021" [dependencies] dirs = "6.0.0" regex = "^1.11.1" -semver = { version = "^1.0.25", features = ["serde"] } -serde = { version = "^1.0.216", features = ["derive"] } -serde_json = "^1.0.138" +semver = { version = "^1.0", features = ["serde"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" url = { version = "^2.5.4", features = ["serde"] } -thiserror = "^2.0.11" -uuid = { version = "^1.13.1", features = ["serde", "v4"] } -ureq = { version = "^3.0.5" } +thiserror = "^2.0" +uuid = { version = "^1.20", features = ["serde", "v4"] } +ureq = { version = "^3.0" } blend = "0.8.0" -tokio = { version = "1.42.0", features = ["full"] } -# hack to get updated patches - og inactive for 6 years +tokio = { version = "^1.49", features = ["full"] } +# xml-rpc will merge into this project some day in the future, as it's just a http server protocol. xml-rpc = { git = "https://github.com/tiberiumboy/xml-rpc-rs.git" } [target.'cfg(target_os = "windows")'.dependencies] -zip = "^2.2.2" +zip = "^7" [target.'cfg(target_os = "macos")'.dependencies] dmg = { version = "^0.1" } [target.'cfg(target_os = "linux")'.dependencies] xz = { version = "^0.1" } -tar = { version = "^0.4.43" } - - -# [features] -# manager = ["ureq", "xz", "tar", "dmg"] +tar = { version = "^0.4" } diff --git a/blender/README.md b/blender_rs/README.md similarity index 100% rename from blender/README.md rename to blender_rs/README.md diff --git a/blender/examples/assets/test.blend b/blender_rs/examples/assets/test.blend similarity index 100% rename from blender/examples/assets/test.blend rename to blender_rs/examples/assets/test.blend diff --git a/blender/examples/download/README.md b/blender_rs/examples/download/README.md similarity index 100% rename from blender/examples/download/README.md rename to blender_rs/examples/download/README.md diff --git a/blender/examples/download/main.rs b/blender_rs/examples/download/main.rs similarity index 100% rename from blender/examples/download/main.rs rename to blender_rs/examples/download/main.rs diff --git a/blender/examples/peek/main.rs b/blender_rs/examples/peek/main.rs similarity index 100% rename from blender/examples/peek/main.rs rename to blender_rs/examples/peek/main.rs diff --git a/blender/examples/render/README.md b/blender_rs/examples/render/README.md similarity index 100% rename from blender/examples/render/README.md rename to blender_rs/examples/render/README.md diff --git a/blender/examples/render/main.rs b/blender_rs/examples/render/main.rs similarity index 51% rename from blender/examples/render/main.rs rename to blender_rs/examples/render/main.rs index fe9fe8ce..0ee71e00 100644 --- a/blender/examples/render/main.rs +++ b/blender_rs/examples/render/main.rs @@ -1,36 +1,10 @@ use blender::blender::Manager; -use blender::models::{args::Args, status::Status}; -use std::ops::Range; +use blender::models::engine::Engine; +use blender::models::{args::Args, event::BlenderEvent}; +use std::ops::RangeInclusive; use std::path::PathBuf; use std::sync::{Arc, RwLock}; -// This struct will hold information necessary to render what next frame blender requested. -// You can create your own custom class to hold how blender should render per frame. -#[derive(Debug)] -struct Test { - start: i32, - end: i32, -} - -impl Test { - pub fn new(frame_range: Range) -> Self { - Self { - start: frame_range.start, - end: frame_range.end, - } - } - - // denotes the function on how to render the frame - pub fn get_next_frame(&mut self) -> Option { - if self.start <= self.end { - let val = self.start; - self.start = self.start + 1; - return Some(val); - } - None - } -} - async fn render_with_manager() { let args = std::env::args().collect::>(); let blend_path = match args.get(1) { @@ -38,14 +12,20 @@ async fn render_with_manager() { Some(p) => PathBuf::from(p), }; + let blend_file = BlendFile::new(blend_path).unwrap("Expects a valid blend file to continue!"); + // Get latest blender installed, or install latest blender from web. let mut manager = Manager::load(); - let blender = match manager.latest_local_avail() { - Some(blender) => blender, - None => manager + println!("Fetch latest available blender to use"); + + let blender = manager.latest_local_avail().unwrap_or_else(|| { + println!("No local blender installation found! Downloading latest from internet..."); + manager .download_latest_version() - .expect("Should be able to download blender! Are you not connected to the internet?"), - }; + .expect("Should be able to download blender! Are you not connected to the internet?") + }); + + println!("Prepare blender configuration..."); // Here we ask for the output path, for now we set our path in the same directory as our executable path. // This information will be display after render has been completed successfully. @@ -53,34 +33,34 @@ async fn render_with_manager() { let output = PathBuf::from("./examples/assets/"); // Create blender argument - let args = Args::new(blend_path, output); - let frames = Test::new(Range { start: 2, end: 10 }); - let frames = Arc::new(RwLock::new(frames)); + let args = Args::new(blend_file, output, Engine::BLENDER_EEVEE_NEXT); + let frames = Arc::new(RwLock::new(RangeInclusive::new(2, 10))); // render the frame. Completed render will return the path of the rendered frame, error indicates failure to render due to blender incompatible hardware settings or configurations. (CPU vs GPU / Metal vs OpenGL) let listener = blender - .render(args, move || { - let mut frame = frames.write().unwrap(); - frame.get_next_frame() - }) + .render(args, move || frames.write().unwrap().next()) .await; // Handle blender status while let Ok(status) = listener.recv() { match status { - Status::Completed { frame, result } => { + BlenderEvent::Completed { frame, result } => { println!("[Completed] {frame} {result:?}"); } - Status::Log { status } => { - println!("[Info] {}", status); + BlenderEvent::Rendering { current, total } => { + let percent = (current / total) * 100.0; + println!("[Rendering] {current} out of {total} (%{percent})"); + } + BlenderEvent::Error(e) => { + println!("[ERR] {e}"); } - Status::Running { status } => { - println!("[Running] {}", status); + BlenderEvent::Warning(msg) => { + println!("[WARN] {msg}"); } - Status::Error(e) => { - println!("[ERROR] {:?}", e); + BlenderEvent::Log(msg) => { + println!("[LOG] {msg}") } - Status::Exit => { + BlenderEvent::Exit => { println!("[Exit]"); } _ => { diff --git a/blender_rs/examples/test/main.rs b/blender_rs/examples/test/main.rs new file mode 100644 index 00000000..e8a54661 --- /dev/null +++ b/blender_rs/examples/test/main.rs @@ -0,0 +1,18 @@ +use blender::manager::Manager; + +fn test_download_blender_home_link() { + let mut manager = Manager::load(); + let link = manager + .latest_local_avail() + .or(manager.download_latest_version().map_or(None, |l| Some(l))); + match link { + Some(link) => { + dbg!(link); + } + None => println!("No blender found and unable to connect to internet! Skipping!"), + } +} + +fn main() { + test_download_blender_home_link(); +} diff --git a/blender_rs/src/blend_file.rs b/blender_rs/src/blend_file.rs new file mode 100644 index 00000000..b086e97a --- /dev/null +++ b/blender_rs/src/blend_file.rs @@ -0,0 +1,189 @@ +use std::{ + num::ParseIntError, + path::{Path, PathBuf}, +}; + +use blend::Blend; +use semver::Version; +use serde::{Deserialize, Serialize}; + +use crate::{ + blender::{BlenderError, Frame}, + models::{ + blender_scene::{BlenderScene, Camera, Sample, SceneName}, + engine::Engine, + format::Format, + peek_response::PeekResponse, + render_setting::{FrameRate, RenderSetting}, + window::Window, + }, +}; + +#[derive(Debug, Clone, PartialEq, Default, Serialize, Deserialize)] +pub struct SceneInfo { + pub scenes: Vec, + pub cameras: Vec, + pub frame_start: Frame, + pub frame_end: Frame, + render_width: i32, + render_height: i32, + fps: FrameRate, + sample: Sample, + output: PathBuf, + engine: Engine, +} + +impl SceneInfo { + pub fn selected_camera(&self) -> String { + self.cameras.get(0).unwrap_or(&"".to_owned()).to_owned() + } + + pub fn selected_scene(&self) -> String { + self.scenes.get(0).unwrap_or(&"".to_owned()).to_owned() + } + + pub fn process(mut self, blend: &Blend) -> Result { + // this denotes how many scene objects there are. + for obj in blend.instances_with_code(*b"SC") { + let scene = obj.get("id").get_string("name").replace("SC", ""); // not the correct name usage? + let render = &obj.get("r"); // get render data + + self.engine = match render.get_string("engine") { + x if x.contains("NEXT") => Engine::BLENDER_EEVEE_NEXT, + x if x.contains("EEVEE") => Engine::BLENDER_EEVEE, + x if x.contains("OPTIX") => Engine::OPTIX, + _ => Engine::CYCLES, + }; + + self.sample = obj.get("eevee").get_i32("taa_render_samples"); + + // Issue, Cannot find cycles info! Blender show that it should be here under SCscene, just like eevee, but I'm looking it over and over and it's not there? Where is cycle? + // Use this for development only! + // Self::explore_value(&obj.get("eevee")); + + self.render_width = render.get_i32("xsch"); + self.render_height = render.get_i32("ysch"); + self.frame_start = render.get_i32("sfra"); + self.frame_end = render.get_i32("efra"); + self.fps = render.get_u16("frs_sec"); + self.output = render + .get_string("pic") + .parse::() + .map_err(|e| BlenderError::PythonError(e.to_string()))?; + + self.scenes.push(scene); + } + + // interesting - I'm picking up the wrong camera here? + for obj in blend.instances_with_code(*b"CA") { + let camera = obj.get("id").get_string("name").replace("CA", ""); + self.cameras.push(camera); + } + + Ok(self) + } + + // TODO: See about not using clone if possible? + pub fn render_setting(&self) -> RenderSetting { + RenderSetting::new( + self.output.clone(), + self.render_width.clone(), + self.render_height.clone(), + self.sample.clone(), + self.fps.clone(), + self.engine.clone(), + Format::default(), + Window::default(), + ) + } + + pub fn peek_response(&self, version: &Version) -> PeekResponse { + let selected_scene = self.selected_scene(); + let selected_camera = self.selected_camera(); + + let render_setting: RenderSetting = self.render_setting(); + let current = BlenderScene::new(selected_scene, selected_camera, render_setting); + + PeekResponse::new( + version.clone(), + self.frame_start, + self.frame_end, + self.cameras.clone(), + self.scenes.clone(), + current, + ) + } +} + +// A struct to hold valid blend file with compatible partial version. +// we can extract additional data if we need to? +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct BlendFile { + inner: PathBuf, + major: u16, + minor: u16, + scene_info: SceneInfo, + render_setting: RenderSetting, +} + +impl BlendFile { + pub fn new(path_to_blend_file: &Path) -> Result { + let blend = Blend::from_path(&path_to_blend_file) + // TODO: try to handle BlendParseError? Future work + .map_err(|e| { + BlenderError::InvalidFile(format!("Received BlenderParseError! {e:?}").to_owned()) + })?; + + // blender version are display as three digits number, e.g. 404 is major: 4, minor: 4. + // treat this as a u16 major = u16 / 100, minor = u16 % 100; + let str_version = std::str::from_utf8(&blend.blend.header.version) + .map_err(|e| BlenderError::InvalidFile(e.to_string()))?; + + let value: u16 = str_version + .parse() + .map_err(|e: ParseIntError| BlenderError::InvalidFile(e.to_string()))?; + let major = value / 100; + let minor = value % 100; + + let scene_info = SceneInfo::default().process(&blend)?; + let render_setting = scene_info.render_setting(); + + Ok(BlendFile { + inner: path_to_blend_file.to_path_buf(), + major, + minor, + render_setting, + scene_info, + }) + } + + pub fn get_partial_version(&self) -> (u16, u16) { + (self.major, self.minor) + } + + pub fn peek_response(&self, version: &Version) -> PeekResponse { + self.scene_info.peek_response(version) + } + + pub fn to_path(&self) -> &Path { + self.inner.as_path() + } +} + +impl Into for BlendFile { + fn into(self) -> PathBuf { + self.inner + } +} + +impl Into for BlendFile { + fn into(self) -> RenderSetting { + self.render_setting + } +} + +impl Into for BlendFile { + fn into(self) -> SceneInfo { + self.scene_info + } +} diff --git a/blender/src/blender.rs b/blender_rs/src/blender.rs similarity index 53% rename from blender/src/blender.rs rename to blender_rs/src/blender.rs index dcef7da2..1a6ad84b 100644 --- a/blender/src/blender.rs +++ b/blender_rs/src/blender.rs @@ -1,6 +1,10 @@ /* Developer blog: +Spending time on replacing xml-rpc-rs due to maintainers not willing to replace rouille plugin that supports this implementations. +I would instead incorporate the functionality of XML-RPC protocol myself instead of relying third party packages. +Reading the wikipedia - https://en.wikipedia.org/wiki/XML-RPC#Usage - xml-rpc is done via simple http server. + Currently, there is no error handling situation from blender side of things. If blender crash, we will resume the rest of the code in attempt to parse the data. This will eventually lead to a program crash because we couldn't parse the information we expect from stdout. Todo peek into stderr and see if @@ -12,15 +16,12 @@ Currently, there is no error handling situation from blender side of things. If - They mention to enforce compute methods, do not mix cpu and gpu. (Why?) Trial: -- Try docker? - try loading .dll from blender? See if it's possible? -- Learning Unsafe Rust and using FFI - going to try and find blender's library code that rust can bind to. - - todo: see about cbindgen/cxx? Advantage: - can support M-series ARM processor. - Original tool Doesn't composite video for you - We can make ffmpeg wrapper? - This will be a feature but not in this level of implementation. -- LogicReinc uses JSON to load batch file - no way to adjust frame after job sent. This version we establish IPC for python to ask next frame. We have better control what to render next. +- LogicReinc uses JSON to load batch file - difficult to adjust frame(s) after job sent. I'm creating an IPC between this program and python to ask next frame. To improve actions over blender. Disadvantage: - Currently rely on python script to do custom render within blender. @@ -54,31 +55,33 @@ TODO: of just letting BlendFarm do all the work. */ extern crate xml_rpc; +use crate::blend_file::BlendFile; +use crate::manager::BlenderConfig; pub use crate::manager::{Manager, ManagerError}; pub use crate::models::args::Args; -use crate::models::{ - blender_peek_response::BlenderPeekResponse, blender_render_setting::BlenderRenderSetting, - status::Status, -}; +use crate::models::config::BlenderConfiguration; +use crate::models::event::BlenderEvent; +use crate::models::peek_response::PeekResponse; -use blend::Blend; #[cfg(test)] use blend::Instance; use regex::Regex; use semver::Version; use serde::{Deserialize, Serialize}; -use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +// use std::net::{IpAddr, Ipv4Addr, SocketAddr}; use std::process::{Command, Stdio}; use std::sync::Arc; use std::{ fs, io::{BufRead, BufReader}, path::{Path, PathBuf}, - sync::mpsc::{self, Receiver}, + sync::mpsc::{self, Receiver, Sender}, }; use thiserror::Error; use tokio::spawn; -use xml_rpc::{Fault, Server}; +use xml_rpc::xmlfmt::XmlResponse; +use xml_rpc::{Params, Value}; +use xml_rpc::server::Server; // TODO: this is ugly, and I want to get rid of this. How can I improve this? // Backstory: Win and linux can be invoked via their direct app link. However, MacOS .app is just a bundle, which contains the executable inside. @@ -99,6 +102,8 @@ pub enum BlenderError { RenderError(String), #[error("Unable to launch blender! Received Python errors: {0}")] PythonError(String), + #[error("Unable to fetch info from blender home service! Are you connected to the internet and is blender foundation still around?")] + ServiceOffline, } /// Blender structure to hold path to executable and version of blender installed. @@ -180,6 +185,26 @@ impl Blender { dirs::config_dir().unwrap().join("BlendFarm") } + // the difference between this function and getting executable are + // a) MacOs is special. Executable reference a path inside app bundle. + // b) This returns valid dir location to open to for user to look at from file POV + pub fn get_relative_path(&self) -> &Path { + if cfg!(target_os = "macos") { + &self + .executable + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + .parent() + .unwrap() + } else { + &self.executable.parent().unwrap() + } + } + /// Return the executable path to blender (Entry point for CLI) pub fn get_executable(&self) -> &Path { &self.executable @@ -204,6 +229,11 @@ impl Blender { /// let blender = Blender::from_executable(Pathbuf::from("path/to/blender")).unwrap(); /// ``` pub fn from_executable(executable: impl AsRef) -> Result { + // TODO: this is ugly, and I want to get rid of this. How can I improve this? + // Backstory: Win and linux can be invoked via their direct app link. However, MacOS .app is just a bundle, which contains the executable inside. + // To run process::Command, I must properly reference the executable path inside the blender.app on MacOS, using the hardcoded path below. + const MACOS_PATH: &str = "Contents/MacOS/Blender"; + // check and verify that the executable exist. // first line for validating blender executable. let path = executable.as_ref(); @@ -218,8 +248,7 @@ impl Blender { path }; - // this should be clear and explicit that I must have a valid path? How can I do this? - // does it need a wrapper? + // this should be clear and explicit that I must have a valid path? if !path.exists() { return Err(BlenderError::ExecutableNotFound(path.to_path_buf())); } @@ -294,104 +323,9 @@ impl Blender { } } - /// Peek is a function design to read and fetch information about the blender file. - pub async fn peek(blend_file: &PathBuf) -> Result { - let blend = Blend::from_path(&blend_file) - .map_err(|_| BlenderError::InvalidFile("Received BlenderParseError".to_owned()))?; - - // blender version are display as three digits number, e.g. 404 is major: 4, minor: 4. - // treat this as a u16 major = u16 / 100, minor = u16 % 100; - let value: u64 = std::str::from_utf8(&blend.blend.header.version) - .expect("Fail to parse version into utf8") - .parse() - .expect("Fail to parse string to value"); - let major = value / 100; - let minor = value % 100; - - // using scope to drop manager usage. - let blend_version = { - let manager = Manager::load(); - - // Get the latest patch from blender home - match manager - .home - .as_ref() - .iter() - .find(|v| v.major.eq(&major) && v.minor.eq(&minor)) - { - // TODO: Find a better way to handle this without using unwrap - Some(v) => v.fetch_latest().unwrap().as_ref().clone(), - // potentially could be a problem, if there's no internet connection, then we can't rely on zero patch? - // For now this will do. - None => Version::new(major.into(), minor.into(), 0), - } - }; - - let mut scenes: Vec = Vec::new(); - let mut cameras: Vec = Vec::new(); - - let mut frame_start: i32 = 0; - let mut frame_end: i32 = 0; - let mut render_width: i32 = 0; - let mut render_height: i32 = 0; - let mut fps: u16 = 0; - let mut samples: i32 = 0; - let mut output: PathBuf = PathBuf::new(); - let mut engine = String::from(""); - - // this denotes how many scene objects there are. - for obj in blend.instances_with_code(*b"SC") { - let scene = obj.get("id").get_string("name").replace("SC", ""); // not the correct name usage? - // get render data - let render = &obj.get("r"); - - engine = render.get_string("engine"); // will show BLENDER_EEVEE_NEXT properly - samples = obj.get("eevee").get_i32("taa_render_samples"); - - // Issue, Cannot find cycles info! Blender show that it should be here under SCscene, just like eevee, but I'm looking it over and over and it's not there? Where is cycle? - // Use this for development only! - // Self::explore_value(&obj.get("eevee")); - - render_width = render.get_i32("xsch"); - render_height = render.get_i32("ysch"); - frame_start = render.get_i32("sfra"); - frame_end = render.get_i32("efra"); - fps = render.get_u16("frs_sec"); - output = render - .get_string("pic") - .parse::() - .map_err(|e| BlenderError::PythonError(e.to_string()))?; - - scenes.push(scene); - } - - // interesting - I'm picking up the wrong camera here? - for obj in blend.instances_with_code(*b"CA") { - let camera = obj.get("id").get_string("name").replace("CA", ""); - cameras.push(camera); - } - - let selected_camera = cameras.get(0).unwrap_or(&"".to_owned()).to_owned(); - let selected_scene = scenes.get(0).unwrap_or(&"".to_owned()).to_owned(); - - // parse i32 into u16 - let result = BlenderPeekResponse { - last_version: blend_version, - render_width, - render_height, - frame_start, - frame_end, - fps, - samples, - cameras, - selected_camera, - scenes, - selected_scene, - engine, - output, - }; - - Ok(result) + // TODO: Replace this to modify the struct to get next batch of rendering queue. + fn local_get_next_render_que(&mut self, _params: Params) -> XmlResponse { + Ok(Value::Int(1).into()) } /// Render one frame - can we make the assumption that ProjectFile may have configuration predefined Or is that just a system global setting to apply on? @@ -404,149 +338,262 @@ impl Blender { /// let final_output = blender.render(&args).unwrap(); /// ``` // so instead of just returning the string of render result or blender error, we'll simply use the single producer to produce result from this class. - pub async fn render(&self, args: Args, get_next_frame: F) -> Receiver - where - F: Fn() -> Option + Send + Sync + 'static, + // issue here is that we need to lock thread. If we are rendering, we need to be able to call abort. + pub async fn render(&self, args: Args) -> Receiver { - let (rx, tx) = mpsc::channel::(); - let (signal, listener) = mpsc::channel::(); + let (signal, listener) = mpsc::channel::(); + // can + let blend_info: PeekResponse = args.file.peek_response(&self.version); + // this is the only place used for BlenderRenderSetting... thoughts? + let settings = BlenderConfiguration::parse_from(&args, &blend_info, &self.version); + self.setup_listening_server(settings, listener) + .await; + + let (rx, tx) = mpsc::channel::(); let executable = self.executable.clone(); - let blend_info = Self::peek(&args.file) - .await - .expect("Fail to parse blend file!"); // TODO: Need to clean this error up a bit. + spawn(async move { + Blender::setup_listening_blender(&args, executable, rx, signal).await; + }); + + // channel to invoke commands to blender while blender is running. + tx + } + + fn next_render_queue_callback(_params: Params) -> XmlResponse { + // here, they're asking for next render queue callback. + // in this case here, we don't care about the params, ? Why is Params called? + XmlResponse::Ok(Value::Int(42).into()) + } + + async fn setup_listening_server( + &mut self, + settings: BlenderConfiguration, + listener: Receiver, + ) -> Result<(), BlenderError> + { + // Read here - https://en.wikipedia.org/wiki/XML-RPC#Usage + /* + In XML-RPC, a client performs an RPC by sending an HTTP request + to a server that implements XML-RPC and receives the HTTP response. + + A call can have multiple parameters and one result. + The protocol defines a few data types for the parameters and result. + Some of these data types are complex, i.e. nested. For example, + you can have a parameter that is an array of five integers. + + The parameters/result structure and the set of data types are meant to + mirror those used in common programming languages. + + Identification of clients for authorization purposes can be achieved + using popular HTTP security methods. Basic access authentication + can be used for identification and authentication. + + In comparison to RESTful protocols, where resource representations (documents) + are transferred, XML-RPC is designed to call methods. The practical difference + is just that XML-RPC is much more structured, which means common library code + can be used to implement clients and servers and there is less design and + documentation work for a specific application protocol. + + [citation needed] One salient technical difference between typical RESTful + protocols and XML-RPC is that many RESTful protocols use the HTTP URI + for parameter information, whereas with XML-RPC, the URI just identifies the server. + */ - // this is the only place used for BlenderRenderSetting... thoughts? - let settings = BlenderRenderSetting::parse_from(&args, &blend_info); let global_settings = Arc::new(settings); + let socket = 8081; - let socket = SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), 8081); - let mut server = Server::new(); + // I think in order for me to make this working example, I need to create a struct that is memory bound to different threads, and read when available. This isolate mutation of variable and object that needs to be thread-safetly. + // TODO: remove expect() once we have this working again. + let mut server = Server::new(socket).expect("Unable to open socket for xml_rpc!"); - server.register_simple("next_render_queue", move |_i: i32| match get_next_frame() { + server.register("next_render_queue".to_owned(),Box::new(self.local_get_next_render_que as fn())); + /* + server.register("next_render_queue".to_owned(), move |params| match get_next_frame() { Some(frame) => Ok(frame), + + // this is our only way to stop python script. None => Err(Fault::new(1, "No more frames to render!")), }); - - server.register_simple("fetch_info", move |_i: i32| { - let setting = (*global_settings).clone(); - Ok(setting) - }); - - let bind_server = server - .bind(&socket) - .expect("Unable to open socket for xml_rpc!"); + */ + + // let me understand this better. + // In this listening server, I'm setting up a xml-rpc server to listen to all of the blender python script. + // When blender calls fetch_info, we provide back the global_settings we have from job information. + server.register("fetch_info".to_owned(), Box::new(move |params| { + let settings = *global_settings.clone(); + // How come we're using unwrap? seems dangerous and sketchy + match serde_json::to_string(&settings) { + Ok(setting) => Ok(Value::String(setting).into()), + // Err(e) => Err(XmlResponse::Err(Valu(-1, e.to_string())) + Err(e) => Err(Value::fault(-1, e.to_string())) + } + })); // spin up XML-RPC server spawn(async move { loop { // if the program shut down or if we've completed the render, then we should stop the server match listener.try_recv() { - Ok(Status::Exit) => break, - _ => bind_server.poll(), + Ok(BlenderEvent::Exit) => break, + e => println!("Listener received unconditionally: {e:?}"), + // _ => server.poll(), } } }); - spawn(async move { - let script_path = Blender::get_config_path().join("render.py"); - if !script_path.exists() { - let data = include_bytes!("./render.py"); - fs::write(&script_path, data).unwrap(); + Ok(()) + } + + fn setup_args(blend_file: &BlendFile) -> Result, BlenderError> { + let script_path = Blender::get_config_path().join("render.py"); + if !script_path.exists() { + let data = include_bytes!("./render.py"); + fs::write(&script_path, data).map_err(|e| BlenderError::PythonError(e.to_string()))?; + } + + let path = blend_file.to_path().as_os_str(); + + Ok(vec![ + "--factory-startup".to_owned(), + "-noaudio".into(), + "-b".into(), + path.to_str().unwrap().to_owned(), + "-P".into(), + script_path.to_str().unwrap().into(), + ]) + } + + // setup xml-rpc listening server for blender's IPC + async fn setup_listening_blender>( + args: &Args, + executable: T, + rx: Sender, + signal: Sender, + ) -> Result<(), BlenderError> { + let col = Self::setup_args(&args.file)?; + + // TODO: Find a way to remove unwrap() + let stdout = Command::new(executable.as_ref()) + .args(col) + .stdout(Stdio::piped()) + .spawn() + .unwrap() + .stdout + .unwrap(); + + let reader = BufReader::new(stdout); + + // parse stdout for human to read + let mut frame: i32 = 0; + + reader.lines().for_each(|line| { + if let Ok(line) = line { + Self::handle_blender_stdio(line, &mut frame, &rx, &signal); + }; + }); + + Ok(()) + } + + // TODO: This function updates a value above this scope -> See if we can just return the value instead? + // TODO: Can we use stream instead? how can we parse data from blender into recognizable style? + fn handle_blender_stdio( + line: String, + frame: &mut i32, + rx: &Sender, + signal: &Sender, + ) { + match line { + // TODO: find a more elegant way to parse the string std out and handle invocation action. + line if line.contains("Fra:") => { + let col = line.split('|').collect::>(); + + // this seems a bit expensive? + let init = col[0].split(" ").next(); + if let Some(value) = init { + *frame = value.replace("Fra:", "").parse().unwrap_or(*frame); + } + let last = col.last().unwrap().trim(); + let slice = last.split(' ').collect::>(); + let msg = match slice[0] { + "Rendering" => { + let current = slice[1].parse::().unwrap(); + let total = slice[3].parse::().unwrap(); + BlenderEvent::Rendering { current, total } + } + _ => BlenderEvent::Unhandled(line), + }; + rx.send(msg).unwrap(); } - let col = vec![ - "--factory-startup".to_string(), - "-noaudio".to_owned(), - "-b".to_owned(), - args.file.to_str().unwrap().to_string(), - "-P".to_owned(), - script_path.to_str().unwrap().to_string(), - ]; - - let stdout = Command::new(executable) - .args(col) - .stdout(Stdio::piped()) - .spawn() - .unwrap() - .stdout + line if line.starts_with("Time:") => { + rx.send(BlenderEvent::Log(line)).unwrap(); + } + // Python logs get injected to stdio + line if line.starts_with("SUCCESS:") => { + // somehow I received an error from sending? + rx.send(BlenderEvent::Log(line)).unwrap(); + } + line if line.starts_with("LOG:") => { + rx.send(BlenderEvent::Log(line)).unwrap(); + } + line if line.contains("Use:") => { + rx.send(BlenderEvent::Log(line)).unwrap(); + } + line if line.contains("RENDER_START:") => { + rx.send(BlenderEvent::Log(line)).unwrap(); + } + + // it would be nice if we can somehow make this as a struct or enum of types? + line if line.contains("Saved:") => { + // TODO: Test this for OSX compatibility + let location = line.split('\'').collect::>(); + let result = PathBuf::from(location[1]); + rx.send(BlenderEvent::Completed { + frame: *frame, + result, + }) .unwrap(); + } - let reader = BufReader::new(stdout); - let mut frame: i32 = 0; + // Strange how this was thrown, but doesn't report back to this program? + line if line.starts_with("EXCEPTION:") => { + signal.send(BlenderEvent::Exit).unwrap(); + rx.send(BlenderEvent::Error(line.to_owned())).unwrap(); + } - // parse stdout for human to read - reader.lines().for_each(|line| { - if let Ok(line) = line { - match line { - line if line.contains("Fra:") => { - let col = line.split('|').collect::>(); + line if line.starts_with("COMPLETED") => { + signal.send(BlenderEvent::Exit).unwrap(); + rx.send(BlenderEvent::Exit).unwrap(); + } - // this seems a bit expensive? - let init = col[0].split(" ").next(); - if let Some(value) = init { - frame = value.replace("Fra:", "").parse().unwrap_or(1); - } - let last = col.last().unwrap().trim(); - let slice = last.split(' ').collect::>(); - let msg = match slice[0] { - "Rendering" => { - let current = slice[1].parse::().unwrap(); - let total = slice[3].parse::().unwrap(); - let percentage = current / total * 100.0; - let render_perc = format!("{} {:.2}%", last, percentage); - Status::Running { - status: render_perc, - } - } - "Sample" => Status::Running { - status: last.to_owned(), - }, - _ => Status::Log { - status: last.to_owned(), - }, - }; - rx.send(msg).unwrap(); - } - line if line.contains("Saved:") => { - let location = line.split('\'').collect::>(); - let result = PathBuf::from(location[1]); - rx.send(Status::Completed { frame, result }).unwrap(); - } - // Strange how this was thrown, but doesn't report back to this program? - line if line.contains("EXCEPTION:") => { - signal.send(Status::Exit).unwrap(); - rx.send(Status::Error(BlenderError::PythonError(line.to_owned()))) - .unwrap(); - } - line if line.contains("Warning:") => { - rx.send(Status::Warning { - message: line.to_owned(), - }) - .unwrap(); - } - line if line.contains("Error:") => { - let msg = Status::Error(BlenderError::RenderError(line.to_owned())); - rx.send(msg).unwrap(); - } - line if line.contains("Blender quit") => { - signal.send(Status::Exit).unwrap(); - rx.send(Status::Exit).unwrap(); - } - line if !line.is_empty() => { - let msg = Status::Running { - status: line.to_owned(), - }; - rx.send(msg).unwrap(); - } - _ => { - // Only empty log entry would show up here... - } - }; - }; - }); - }); - tx + // TODO: Warning keyword is used multiple of times. Consider removing warning apart and submit remaining content above + line if line.contains("Warning:") => { + rx.send(BlenderEvent::Warning(line.to_owned())).unwrap(); + } + + line if line.contains("Error:") => { + let msg = BlenderEvent::Error(line.to_owned()); + rx.send(msg).unwrap(); + } + + line if line.contains("Blender quit") => { + // ignoring this... + println!("Blender quit! Should we handle something about this here at this point of time?"); + } + + // any unhandle handler is submitted raw in console output here. + line if !line.is_empty() => { + // somehow it was able to pick up the blender version and commit hash value? + let msg = format!("[Unhandle Blender Event]:{line}"); + let event = BlenderEvent::Unhandled(msg); + rx.send(event).unwrap(); + } + _ => { + // Only empty log entry would show up here... + } + }; } } diff --git a/blender_rs/src/constant.rs b/blender_rs/src/constant.rs new file mode 100644 index 00000000..197ef20e --- /dev/null +++ b/blender_rs/src/constant.rs @@ -0,0 +1,3 @@ +pub const MAX_VALID_DAYS: u64 = 30; +pub const MAX_FRAME_CHUNK_SIZE: i32 = 35; +pub const MIN_THRESHOLD_FETCH: usize = 2; diff --git a/blender_rs/src/lib.rs b/blender_rs/src/lib.rs new file mode 100644 index 00000000..e9dc426a --- /dev/null +++ b/blender_rs/src/lib.rs @@ -0,0 +1,9 @@ +#![crate_type = "lib"] +#![crate_name = "blender"] + +pub mod blend_file; +pub mod blender; +pub mod constant; +pub mod manager; +pub mod models; +pub mod page_cache; diff --git a/blender_rs/src/main.rs b/blender_rs/src/main.rs new file mode 100644 index 00000000..092c95a2 --- /dev/null +++ b/blender_rs/src/main.rs @@ -0,0 +1,8 @@ +use std::env::current_dir; + +fn main() { + if let Ok(path) = current_dir() { + let project_path = path.to_string_lossy(); + println!("Please read the example to learn more about Blender crate - ${}/examples/render/README.md", project_path); + } +} diff --git a/blender/src/manager.rs b/blender_rs/src/manager.rs similarity index 53% rename from blender/src/manager.rs rename to blender_rs/src/manager.rs index 374a0ee9..62c9903a 100644 --- a/blender/src/manager.rs +++ b/blender_rs/src/manager.rs @@ -1,3 +1,4 @@ +use crate::blend_file::{BlendFile, SceneInfo}; /* Developer blog: This manager class will serve the following purpose: @@ -6,14 +7,21 @@ - If user fetch for list of installation, verify all path exist before returning the list. - Implements download and install code */ -use crate::blender::Blender; -use crate::models::{category::BlenderCategory, download_link::DownloadLink, home::BlenderHome}; - +use crate::blender::{Blender, BlenderError}; +use crate::models::blender_scene::BlenderScene; +use crate::models::peek_response::PeekResponse; +use crate::models::render_setting::RenderSetting; +use crate::models::{category::BlenderCategory, download_link::DownloadLink}; +use crate::page_cache::PageCache; + +use regex::Regex; use semver::Version; use serde::{Deserialize, Serialize}; +use std::io::{Error, ErrorKind}; use std::path::Path; use std::{fs, path::PathBuf}; use thiserror::Error; +use url::Url; // I would like this to be a feature only crate. blender by itself should be lightweight and interface with the program directly. // could also implement serde as optionals? @@ -51,18 +59,30 @@ pub enum ManagerError { #[derive(Debug, Serialize, Deserialize)] pub struct BlenderConfig { + /// List of installed blenders blenders: Vec, + + /// Install path. By default set to `$HOME/Downloads/Blender` install_path: PathBuf, + + /// Auto save on drop auto_save: bool, } -// I wanted to keep this struct private only to this library crate? -#[derive(Debug)] +impl BlenderConfig { + /// Remove any invalid blender path entry from BlenderConfig + pub fn remove_invalid_blender_path(&mut self) { + self.blenders.retain(|x| x.get_executable().exists()); + } +} + pub struct Manager { /// Store all known installation of blender directory information config: BlenderConfig, - pub home: BlenderHome, // for now let's make this public - has_modified: bool, + list: Vec, + download_links: Vec, + cache: PageCache, + has_modified: bool, // detect if the configuration has changed. } impl Default for Manager { @@ -75,22 +95,67 @@ impl Default for Manager { install_path, auto_save: true, }; + let mut cache = + PageCache::load().expect("Page Cache should have permission to load content!"); + + let list = Self::fetch_categories(&mut cache).unwrap_or_else(|_| Vec::new()); + Self { config, - home: BlenderHome::new().expect("Unable to load blender home!"), + list, + download_links: Vec::new(), + cache, has_modified: false, } } } impl Manager { + fn fetch_categories(cache: &mut PageCache) -> Result, Error> { + let parent = Url::parse("https://download.blender.org/release/").unwrap(); + let content = cache.fetch(&parent)?; + + // Omit any blender version 2.8 and below + let pattern = + r#".*)\">Blender(?[3-9]|\d{2,}).(?\d*).*\/<\/a>"#; + let regex = Regex::new(pattern).map_err(|e| { + Error::new( + ErrorKind::InvalidData, + format!("Unable to create new Regex pattern! {e:?}"), + ) + })?; + + let mut list: Vec = regex + .captures_iter(&content) + .map(|c| { + let (_, [url, major, minor]) = c.extract(); + let url = parent.join(url).ok()?; + let major = major.parse().ok()?; + let minor = minor.parse().ok()?; + Some(BlenderCategory::new(url, major, minor)) + }) + .flatten() + .collect(); + + list.sort_by(|a, b| b.cmp(a)); + Ok(list) + } + fn set_config(&mut self, config: BlenderConfig) -> &mut Self { self.config = config; self } - pub fn get_config_dir() -> PathBuf { - let path = dirs::config_dir().unwrap().join("BlendFarm"); + /// Returns the directory path where the configuration file is stored. + /// This is stored under the library usage of dirs::config_dir() + "BlendFarm" - the application name by default. + /// This ensure directory must exist before returning PathBuf, else report back as permission issue. We must have a place to save the files to. + pub fn get_config_dir(user_pref: Option) -> PathBuf { + let path = match user_pref { + Some(path) => path.join("BlendFarm"), + None => dirs::config_dir().unwrap().join("BlendFarm"), + }; + + // ensure path location must exist - we guarantee permission access here. fs::create_dir_all(&path).expect("Unable to create directory!"); path } @@ -98,31 +163,30 @@ impl Manager { // this path should always be fixed and stored under machine specific. // this path should not be shared across machines. fn get_config_path() -> PathBuf { - Self::get_config_dir().join("BlenderManager.json") + // TODO: see about getting user pref? + Self::get_config_dir(None).join("BlenderManager.json") } - // Download the specific version from download.blender.org - pub fn download(&mut self, version: &Version) -> Result { + /// Download Blender of matching version, install on this machine, and returns blender struct. + /// This function will update PageCache if not previously visited. Hence mutation requirement. + pub fn download_blender(&mut self, version: &Version) -> Result { // TODO: As a extra security measure, I would like to verify the hash of the content before extracting the files. let arch = std::env::consts::ARCH.to_owned(); let os = std::env::consts::OS.to_owned(); - let category = self - .home - .as_ref() - .iter() - .find(|&b| b.major.eq(&version.major) && b.minor.eq(&version.minor)) - .ok_or(ManagerError::DownloadNotFound { - arch, - os, - url: "".to_owned(), - })?; + let download_link = + self.get_blender_link_by_version(version) + .ok_or(ManagerError::DownloadNotFound { + arch, + os, + url: format!( + "Blender version {}.{} was not found!", + version.major, version.minor + ), + })?; - let download_link = category - .retrieve(version) - .map_err(|e| ManagerError::FetchError(e.to_string()))?; - - let destination = self.config.install_path.join(&category.name); + // need to fetch category name such as "Blender4.1" + let destination = self.config.install_path.join(&download_link.name); // got a permission denied here? Interesting? // I need to figure out why and how I can stop this from happening? @@ -135,6 +199,7 @@ impl Manager { let blender = Blender::from_executable(destination) .map_err(|e| ManagerError::BlenderError { source: e })?; + self.add_blender(blender.clone()); self.save().unwrap(); Ok(blender) @@ -161,7 +226,8 @@ impl Manager { let path = Self::get_config_path(); let mut data = Self::default(); if let Ok(content) = fs::read_to_string(&path) { - if let Ok(config) = serde_json::from_str(&content) { + if let Ok(mut config) = serde_json::from_str::(&content) { + config.remove_invalid_blender_path(); data.set_config(config); return data; } else { @@ -180,6 +246,43 @@ impl Manager { data } + /// Peek is a function design to read and fetch information about the blender file. + pub async fn peek(&mut self, blendfile: BlendFile) -> Result { + let (major, minor) = blendfile.get_partial_version(); + // simple upcast + let (major, minor) = (major as u64, minor as u64); + + // using scope to drop manager usage. + let blend_version = { + // TODO: Refactor this script so we can ask the manager to fetch the information without accessing category at all. + match self.have_blender_partial(major, minor) { + Some(blend) => blend.get_version().clone(), + None => self + .get_latest_version_patch(major, minor) + .unwrap_or(Version::new(major, minor, 0)), + } + }; + + let scene_info: SceneInfo = blendfile.into(); + let selected_scene = scene_info.selected_scene(); + let selected_camera = scene_info.selected_camera(); + + let render_setting: RenderSetting = scene_info.render_setting(); + let current = BlenderScene::new(selected_scene, selected_camera, render_setting); + + // TODO: Rethink structure? + let result = PeekResponse::new( + blend_version, // Why? + scene_info.frame_start, + scene_info.frame_end, + scene_info.cameras, + scene_info.scenes, + current, + ); + + Ok(result) + } + pub fn get_install_path(&self) -> &Path { &self.config.install_path } @@ -243,18 +346,35 @@ impl Manager { /// Deletes the parent directory that blender reside in. This might be a dangerous function as this involves removing the directory blender executable is in. /// TODO: verify that this doesn't break macos path executable... Why mac gotta be special with appbundle? - pub fn delete_blender(&mut self, _blender: &Blender) { + pub fn delete_blender(&mut self, blender: &Blender) { // this deletes blender from the system. You have been warn! - // todo!("Exercise with caution!"); - // fs::remove_dir_all(_blender.get_executable().parent().unwrap()).unwrap(); - self.remove_blender(_blender); + // BEWARE - MacOS is special that the executable path is referencing inside the bundle. I would need to get the app path instead of the bundle inside. + if std::env::consts::OS == "macos" { + panic!( + "Need to handle mac app path reference instead of path inside bundle! {:?}", + blender.get_executable() + ); + } + // I'm still concern about this, why are we deleting the parent? Need to perform unit test for this to make sure it doesn't delete anything else. + fs::remove_dir_all(blender.get_executable().parent().unwrap()).unwrap(); + self.remove_blender(blender); } - // TODO: Name ambiguous - clarify method name to clear and explicit + // TODO: Name ambiguous - clarify method name to be clear and explicit + /// This will first check if blender is installed locally, otherwise download the version online. pub fn fetch_blender(&mut self, version: &Version) -> Result { match self.have_blender(version) { Some(blender) => Ok(blender.clone()), - None => self.download(version), + None => self.download_blender(version), + } + } + + // TODO: Refactor this method to provide already established DownloadLinks from the manager instead. + // Category struct is going away and will be used to fetch download links only. Nothing more beyond that. + pub fn fetch_download_list(&self) -> Option> { + match &self.download_links.is_empty() { + false => Some(self.download_links.clone()), + true => None, } } @@ -265,37 +385,71 @@ impl Manager { .find(|x| x.get_version().eq(version)) } + pub fn have_blender_partial(&self, major: u64, minor: u64) -> Option<&Blender> { + self.config.blenders.iter().find(|x| { + let v = x.get_version(); + v.major.eq(&major) && v.minor.eq(&minor) + }) + } + + // TODO: Try to remove unwrap as much as possible /// Fetch the latest version of blender available from Blender.org /// this function might be ambiguous. Should I use latest_local or latest_online? pub fn latest_local_avail(&mut self) -> Option { // in this case I need to contact Manager class or BlenderDownloadLink somewhere and fetch the latest blender information let mut data = self.config.blenders.clone(); data.sort(); - data.first().map(|v: &Blender| v.to_owned()) + let value = data.first().map(|v| v.to_owned()); + value } // find a way to hold reference to blender home here? + // split this function pub fn download_latest_version(&mut self) -> Result { // in this case - we need to fetch the latest version from somewhere, download.blender.org will let us fetch the parent before we need to dive into - let list = self.home.as_ref(); // TODO: Find a way to replace these unwrap() - let category = list.first().unwrap(); - let destination = self.config.install_path.join(&category.name); + let category = &self.list.first().map_or(Err(ManagerError::RequestError("Category list is empty! Did you clear the cache? Please connect to the internet to retrieve blender download list".to_string())), |c| Ok(c))?; + + // TODO how do I get around this? I moved PageCache to manager class instead of BlenderHome. + // This kinda open up a whole can of worms. + let link = category.fetch_latest(&mut self.cache).unwrap(); + let destination = self.config.install_path.join(&link.get_parent()); // got a permission denied here? Interesting? // I need to figure out why and how I can stop this from happening? fs::create_dir_all(&destination).unwrap(); - let link = category.fetch_latest().unwrap(); let path = link .download_and_extract(&destination) .map_err(|e| ManagerError::IoError(e.to_string()))?; - dbg!(&path); - let blender = - Blender::from_executable(path).map_err(|e| ManagerError::BlenderError { source: e })?; + + // I would expect this to always work? + let blender = Blender::from_executable(path).expect("Invalid Blender executable!"); self.config.blenders.push(blender.clone()); Ok(blender) } + + pub fn get_blender_link_by_version(&mut self, version: &Version) -> Option { + self.list + .iter() + .find(|c| c.version_match(version)) + .map_or(None, |c| { + c.retrieve(version, &mut self.cache) + .map_or(None, |l| Some(l)) + }) + } + + // I may want to change this to see if I'm picking the one from locally installed or from remote + pub fn get_latest_version_patch(&mut self, major: u64, minor: u64) -> Option { + // Get the latest patch from blender home + self.list + .iter() + .find(|v| v.partial_version_match(major, minor)) + .map_or(None, |c| { + c.fetch_latest(&mut self.cache) + .map_or(None, |l| Some(l.get_version().clone())) + }) + } } impl AsRef for Manager { @@ -304,11 +458,17 @@ impl AsRef for Manager { } } +// impl AsRef> for Manager { +// fn as_ref(&self) -> &Vec { +// &self.list +// } +// } + impl Drop for Manager { fn drop(&mut self) { if self.has_modified || self.config.auto_save { if let Err(e) = self.save() { - println!("Error saving manager file: {}", e); + eprintln!("Error saving manager file: {}", e); } } } diff --git a/blender_rs/src/models.rs b/blender_rs/src/models.rs new file mode 100644 index 00000000..33e69509 --- /dev/null +++ b/blender_rs/src/models.rs @@ -0,0 +1,13 @@ +pub mod args; +pub mod blender_scene; +pub(crate) mod category; +pub(crate) mod config; +pub mod device; +pub mod download_link; +pub mod engine; +pub mod event; +pub mod format; +pub mod mode; +pub mod peek_response; +pub mod render_setting; +pub mod window; diff --git a/blender/src/models/args.rs b/blender_rs/src/models/args.rs similarity index 51% rename from blender/src/models/args.rs rename to blender_rs/src/models/args.rs index 9f434c9a..2ba0092a 100644 --- a/blender/src/models/args.rs +++ b/blender_rs/src/models/args.rs @@ -10,39 +10,44 @@ FEATURE - See if python allows pointers/buffer access to obtain job render progress - Allows node to send host progress result. Possibly viewport network rendering? Do note that blender is open source - it's not impossible to create FFI that interfaces blender directly, but rather, there's no support to perform this kind of action. - - BlendFarm code shows that they heavily rely on using python code to perform exact operation. - Question is, do I want to use their code, or do I want to stick with CLI instead? - I'll try implement both solution, CLI for version and other basic commands, python for advance features and upgrade? */ - // May Subject to change. - -use crate::models::{device::Device, engine::Engine, format::Format}; +use crate::{ + blend_file::BlendFile, + models::{engine::Engine, format::Format}, +}; use serde::{Deserialize, Serialize}; use std::path::PathBuf; +use super::device::Processor; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub enum HardwareMode { + CPU, + GPU, + BOTH, +} + // ref: https://docs.blender.org/manual/en/latest/advanced/command_line/render.html #[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] pub struct Args { - pub file: PathBuf, // required - pub output: PathBuf, // optional - pub engine: Engine, // optional - pub device: Device, // optional - pub format: Format, // optional - default to Png - pub use_continuation: bool, // optional - default to false + pub file: BlendFile, // required + pub output: PathBuf, // optional + pub engine: Engine, // optional + pub processor: Processor, + pub mode: HardwareMode, // optional + pub format: Format, // optional - default to Png } impl Args { - pub fn new(file: PathBuf, output: PathBuf) -> Self { + pub fn new(file: BlendFile, output: PathBuf, engine: Engine) -> Self { Args { file: file, output: output, - // TODO: Change this so that we can properly reflect the engine used by A) Blendfile B) User request, and C) allowlist from machine config - engine: Default::default(), - device: Default::default(), - format: Default::default(), - use_continuation: false, + processor: Processor::NONE, + mode: HardwareMode::CPU, + engine, + format: Format::default(), } } } diff --git a/blender_rs/src/models/blender_scene.rs b/blender_rs/src/models/blender_scene.rs new file mode 100644 index 00000000..4b842f0a --- /dev/null +++ b/blender_rs/src/models/blender_scene.rs @@ -0,0 +1,30 @@ +use serde::{Deserialize, Serialize}; +use super::render_setting::RenderSetting; + +pub type SceneName = String; +pub type Camera = String; +pub type Sample = i32; + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct BlenderScene { + /// Name of the scene + pub scene: SceneName, + /// Camera reference name to render from + pub camera: Camera, + /// Render Settings + pub render_setting: RenderSetting, +} + +impl BlenderScene { + pub fn new( + scene: SceneName, + camera: Camera, + render_setting: RenderSetting, + ) -> Self { + Self { + scene, + camera, + render_setting, + } + } +} \ No newline at end of file diff --git a/blender/src/models/category.rs b/blender_rs/src/models/category.rs similarity index 75% rename from blender/src/models/category.rs rename to blender_rs/src/models/category.rs index 5400f3cc..e8bfea04 100644 --- a/blender/src/models/category.rs +++ b/blender_rs/src/models/category.rs @@ -6,13 +6,10 @@ use std::env::consts; use thiserror::Error; use url::Url; -// I'd like to relocate this to a different file. Possibly home? -#[derive(Debug)] -pub struct BlenderCategory { - pub name: String, - pub url: Url, - pub major: u64, - pub minor: u64, +pub(crate) struct BlenderCategory { + url: Url, + major: u64, + minor: u64, } #[derive(Debug, Error)] @@ -38,7 +35,7 @@ impl BlenderCategory { } /// Return extension matching to the current operating system (Only display Windows(.zip), Linux(.tar.xz), or macos(.dmg)). - pub fn get_extension() -> Result { + pub(crate) fn get_extension() -> Result { match consts::OS { "windows" => Ok(".zip".to_owned()), "macos" => Ok(".dmg".to_owned()), @@ -47,21 +44,21 @@ impl BlenderCategory { } } - pub fn new(name: String, url: Url, major: u64, minor: u64) -> Self { - Self { - name, - url, - major, - minor, - } + pub fn partial_version_match(&self, major: u64, minor: u64) -> bool { + self.major.eq(&major) && self.minor.eq(&minor) + } + + pub fn version_match(&self, version: &Version) -> bool { + self.partial_version_match(version.major, version.minor) + } + + pub fn new(url: Url, major: u64, minor: u64) -> Self { + Self { url, major, minor } } - // TODO - implement thiserror? // for some reason I was fetching this multiple of times already. This seems expensive to call for some reason? - // also, strange enough, the pattern didn't pick up anything? - pub fn fetch(&self) -> Result, BlenderCategoryError> { - // TODO: Find a way to recycle PageCache from BlenderHome - let mut cache = PageCache::load()?; // I really hate the fact that I have to create a new instance for this. + pub fn fetch(&self, cache: &mut PageCache) -> Result, BlenderCategoryError> { + // this function is called everytime fetch is called. This seems to be slowing down the performance for this application usage. let content = cache.fetch(&self.url).map_err(BlenderCategoryError::Io)?; let arch = Self::get_valid_arch()?; let ext = Self::get_extension().map_err(BlenderCategoryError::UnsupportedOS)?; @@ -78,7 +75,6 @@ impl BlenderCategory { ); let regex = Regex::new(&pattern).unwrap(); - // for (_, [url, name, patch]) in let vec = regex .captures_iter(&content) .filter_map(|c| { @@ -93,15 +89,23 @@ impl BlenderCategory { Ok(vec) } - pub fn fetch_latest(&self) -> Result { - let mut list = self.fetch()?; + // internal function use - depends on PageCache + pub(crate) fn fetch_latest( + &self, + cache: &mut PageCache, + ) -> Result { + let mut list = self.fetch(cache)?; list.sort_by(|a, b| b.cmp(a)); let entry = list.first().ok_or(BlenderCategoryError::NotFound)?; Ok(entry.clone()) } - pub fn retrieve(&self, version: &Version) -> Result { - let list = self.fetch()?; + pub fn retrieve( + &self, + version: &Version, + cache: &mut PageCache, + ) -> Result { + let list = self.fetch(cache)?; let entry = list .iter() .find(|dl| dl.as_ref().eq(version)) diff --git a/blender_rs/src/models/config.rs b/blender_rs/src/models/config.rs new file mode 100644 index 00000000..1a7589b6 --- /dev/null +++ b/blender_rs/src/models/config.rs @@ -0,0 +1,82 @@ +use std::path::PathBuf; +use super::{args::{Args, HardwareMode}, blender_scene::{BlenderScene, Sample}, device::Processor, engine::Engine, format::Format, peek_response::PeekResponse}; +use semver::Version; +use uuid::Uuid; +use serde::{Serialize, Deserialize}; + +// Blender 4.2 introduce a new enum called BLENDER_EEVEE_NEXT, which is currently handle in python file atm. +const EEVEE_SWITCH: Version = Version::new(4, 2, 0); + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct BlenderConfiguration { + #[serde(rename = "TaskID")] + id: Uuid, + // output various + output: PathBuf, + scene_info: BlenderScene, + cores: usize, + processor: Processor, + hardware_mode: HardwareMode, + // TODO: May be phased out? + tile_width: i32, + tile_height: i32, + sample: Sample, + engine: Engine, + format: Format, + // Py:- Value assign to use_crop_to_border, additionally, false set film_transparent true + crop: bool, +} + +impl BlenderConfiguration { + fn new( + output: PathBuf, + scene_info: BlenderScene, + processor: Processor, + hardware_mode: HardwareMode, + tile_width: i32, + tile_height: i32, + samples: Sample, + engine: Engine, + format: Format, + ) -> Self { + Self { + id: Uuid::new_v4(), + output, + scene_info, + cores: std::thread::available_parallelism().unwrap().get(), + processor, + hardware_mode, + tile_width, + tile_height, + sample: samples, + engine, + format, + crop: false, + } + } + + /// Args are user provided value - this should not correlate to the machine's hardware (CUDA/OPTIX/GPU usage) + pub fn parse_from(args: &Args, info: &PeekResponse, version: &Version) -> Self { + BlenderConfiguration::new( + args.output.clone(), + info.current.clone(), + args.processor.clone(), + args.mode.clone(), + -1, + -1, + info.current.render_setting.sample, + match info.current.render_setting.engine { + Engine::BLENDER_EEVEE | Engine::BLENDER_EEVEE_NEXT => { + if version.ge(&EEVEE_SWITCH) { + Engine::BLENDER_EEVEE_NEXT + } else { + Engine::BLENDER_EEVEE + } + } + _ => info.current.render_setting.engine + }, + info.current.render_setting.format, + ) + } +} diff --git a/blender_rs/src/models/device.rs b/blender_rs/src/models/device.rs new file mode 100644 index 00000000..8089137b --- /dev/null +++ b/blender_rs/src/models/device.rs @@ -0,0 +1,19 @@ +use serde::{Deserialize, Serialize}; + +/* +Developer blog- +The only reason why we need to add number that may or may not match blender's enum number list +is because we're passing in the arguments to the python file instead of Blender CLI. +Once I get this part of the code working, then I'll go back and refactor python code. +*/ + +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] +// TODO: Find a way to convert enum into String literal for json de/serialize +pub enum Processor { + NONE, + CUDA, + OPTIX, + HIP, + ONEAPI, + // is there METAL? +} \ No newline at end of file diff --git a/blender/src/models/download_link.rs b/blender_rs/src/models/download_link.rs similarity index 97% rename from blender/src/models/download_link.rs rename to blender_rs/src/models/download_link.rs index f89f8e3a..313759d2 100644 --- a/blender/src/models/download_link.rs +++ b/blender_rs/src/models/download_link.rs @@ -16,8 +16,6 @@ pub struct DownloadLink { } impl DownloadLink { - /* private function impl */ - pub fn new(name: String, url: Url, version: Version) -> Self { Self { name, url, version } } @@ -26,6 +24,14 @@ impl DownloadLink { &self.version } + pub fn get_parent(&self) -> String { + format!("Blender{}.{}", self.version.major, self.version.minor) + } + + pub fn get_url(&self) -> &Url { + &self.url + } + // Currently being used for MacOS (I wonder if I need to do the same for windows?) #[cfg(target_os = "macos")] fn copy_dir_all(src: impl AsRef, dst: impl AsRef) -> Result<(), Error> { diff --git a/blender_rs/src/models/engine.rs b/blender_rs/src/models/engine.rs new file mode 100644 index 00000000..98074ea2 --- /dev/null +++ b/blender_rs/src/models/engine.rs @@ -0,0 +1,12 @@ +use serde::{Deserialize, Serialize}; + +#[derive(Default, Debug, Copy, Clone, Serialize, Deserialize, PartialEq, Eq)] +pub enum Engine { + #[allow(non_camel_case_types)] + BLENDER_EEVEE, // Pre 4.2.0 + #[allow(non_camel_case_types)] + BLENDER_EEVEE_NEXT, // After 4.2.0 + #[default] + CYCLES, + OPTIX, +} diff --git a/blender_rs/src/models/event.rs b/blender_rs/src/models/event.rs new file mode 100644 index 00000000..41fdc238 --- /dev/null +++ b/blender_rs/src/models/event.rs @@ -0,0 +1,13 @@ +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum BlenderEvent { + Log(String), + Warning(String), + Rendering { current: f32, total: f32 }, + Completed { frame: i32, result: PathBuf }, + Unhandled(String), + Exit, + Error(String), +} diff --git a/blender_rs/src/models/format.rs b/blender_rs/src/models/format.rs new file mode 100644 index 00000000..fbb85af7 --- /dev/null +++ b/blender_rs/src/models/format.rs @@ -0,0 +1,21 @@ +use serde::{Deserialize, Serialize}; + +pub enum FormatError { + InvalidInput, +} + +// More context: https://docs.blender.org/manual/en/latest/advanced/command_line/arguments.html#format-options +#[derive(Debug, Copy, Clone, Default, PartialEq, Serialize, Deserialize)] +pub enum Format { + TGA, + RAWTGA, + JPEG, + IRIS, + AVIRAW, + AVIJPEG, + #[default] + PNG, + BMP, + HDR, + TIFF, +} \ No newline at end of file diff --git a/blender_rs/src/models/mode.rs b/blender_rs/src/models/mode.rs new file mode 100644 index 00000000..5ede95a9 --- /dev/null +++ b/blender_rs/src/models/mode.rs @@ -0,0 +1,49 @@ +use serde::{Deserialize, Serialize}; +use std::{num::ParseIntError, ops::Range}; + +// context for serde: https://serde.rs/enum-representations.html +#[derive(Debug, Clone, PartialEq, Hash, Serialize, Deserialize)] +pub enum RenderMode { + // JSON: "Frame": "i32", + // render a single frame. + Frame(i32), + + // JSON: "Animation": {"start":"i32", "end":"i32"} + // contains the target start frame to the end target frame. + Animation(Range), + + // future project - allow network node to only render section of the frame instead of whole to visualize realtime rendering view solution. + // JSON: "Section": {"frame":"i32", "coord":{"i32", "i32"}, "size": {"i32", "i32"} } + // Section { + // frame: i32, + // coord: (i32, i32), + // size: (i32, i32), + // }, +} + +impl RenderMode { + pub fn new(start: i32, end: i32) -> RenderMode { + let mut start = start; + let mut end = end; + + // start needs to be the lowest number of all. If it's backward, flip it around. + if start > end { + (start, end) = (end, start); + } + + if start + 1 == end { + RenderMode::Frame(start) + } else { + let range = Range { start, end }; + RenderMode::Animation(range) + } + } + + pub fn try_new(start: &str, end: &str) -> Result { + // stop if the parser fail to parse. + let start = start.parse::()?; + let end = end.parse::()?; + + Ok(RenderMode::new(start, end)) + } +} diff --git a/blender_rs/src/models/peek_response.rs b/blender_rs/src/models/peek_response.rs new file mode 100644 index 00000000..5e9c2247 --- /dev/null +++ b/blender_rs/src/models/peek_response.rs @@ -0,0 +1,31 @@ +use crate::blender::Frame; +use super::blender_scene::{BlenderScene, Camera, SceneName}; +use semver::Version; +use serde::{Deserialize, Serialize}; + +// TODO: Find a way to get preference saved Processor from the file? +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "PascalCase")] +pub struct PeekResponse { + pub last_version: Version, + pub current: BlenderScene, + pub frame_start: Frame, + pub frame_end: Frame, + #[serde(rename = "FPS")] + pub cameras: Vec, + pub scenes: Vec, +} + +impl PeekResponse { + pub fn new(last_version: Version, frame_start: Frame, frame_end: Frame, cameras: Vec, scenes: Vec, + current: BlenderScene ) -> Self { + Self { + last_version, + frame_start, + frame_end, + cameras, + scenes, + current + } + } +} \ No newline at end of file diff --git a/blender_rs/src/models/render_setting.rs b/blender_rs/src/models/render_setting.rs new file mode 100644 index 00000000..99d83ce2 --- /dev/null +++ b/blender_rs/src/models/render_setting.rs @@ -0,0 +1,51 @@ +use crate::blender::Frame; +use super::{blender_scene::Sample, engine::Engine, format::Format, window::Window}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; + +pub type FrameRate = u16; // u32 convert into string for xml-rpc. BEWARE! + +#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)] +pub struct RenderSetting { + /// output of where our stored image will save to + output: PathBuf, + /// Render frame Width + pub width: Frame, // Not to be confused with animation frame + /// Render frame height + pub height: Frame, // Not to be confused with animation frame + /// Samples capture from the scene + pub sample: Sample, + /// Frame per second + #[serde(rename = "FPS")] + pub fps: FrameRate, + /// What render engine to use (Optix/CUDA) + pub engine: Engine, + /// Image format + pub format: Format, + /// Borders + pub border: Window, +} + +impl RenderSetting { + pub fn new(output: PathBuf, width: Frame, height: Frame, sample: Sample, fps: FrameRate, engine: Engine, format: Format, border: Window ) -> Self { + Self { + output, + width, + height, + sample, + fps, + engine, + format, + border + } + } + + pub fn set_output(mut self, output: PathBuf ) -> Self { + self.output = output; + self + } + + pub fn get_output(&self) -> &PathBuf { + &self.output + } +} \ No newline at end of file diff --git a/blender_rs/src/models/window.rs b/blender_rs/src/models/window.rs new file mode 100644 index 00000000..3783a01b --- /dev/null +++ b/blender_rs/src/models/window.rs @@ -0,0 +1,74 @@ +use serde::{de::Visitor, ser::SerializeStruct, Deserialize, Serialize}; +use std::ops::Range; + +// In the python script, this Window values gets assigned to border of scn.render.border_* +// Here - I'm calling it as window instead. +#[derive(Debug, Clone, PartialEq)] +pub struct Window { + pub x: Range, + pub y: Range, +} + +impl Default for Window { + fn default() -> Self { + Self { + x: Range { + start: 0.0, + end: 1.0, + }, + y: Range { + start: 0.0, + end: 1.0, + }, + } + } +} + +// TODO: Remove this as this may no longer be needed +impl Serialize for Window { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + let mut state = serializer.serialize_struct("Border", 4)?; + state.serialize_field("X", &self.x.start)?; + state.serialize_field("X2", &self.x.end)?; + state.serialize_field("Y", &self.y.start)?; + state.serialize_field("Y2", &self.y.end)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for Window { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + struct WindowVisitor; + + impl<'de> Visitor<'de> for WindowVisitor { + type Value = Window; + + fn expecting(&self, formatter: &mut std::fmt::Formatter) -> std::fmt::Result { + formatter.write_str("struct Border") + } + + fn visit_seq(self, mut seq: V) -> Result + where + V: serde::de::SeqAccess<'de>, + { + let x = seq.next_element()?.unwrap_or(0.0); + let x2 = seq.next_element()?.unwrap_or(1.0); + let y = seq.next_element()?.unwrap_or(0.0); + let y2 = seq.next_element()?.unwrap_or(1.0); + Ok(Window { + x: Range { start: x, end: x2 }, + y: Range { start: y, end: y2 }, + }) + } + } + + const FIELDS: &[&str] = &["X", "X2", "Y", "Y2"]; + deserializer.deserialize_struct("Window", FIELDS, WindowVisitor) + } +} diff --git a/blender/src/page_cache.rs b/blender_rs/src/page_cache.rs similarity index 79% rename from blender/src/page_cache.rs rename to blender_rs/src/page_cache.rs index 618f185d..3ad4f60e 100644 --- a/blender/src/page_cache.rs +++ b/blender_rs/src/page_cache.rs @@ -1,3 +1,4 @@ +use crate::constant::MAX_VALID_DAYS; use regex::Regex; use serde::{Deserialize, Serialize}; use std::io::{Error, Read, Result}; @@ -9,7 +10,6 @@ use url::Url; // rely the cache creation date on file metadata. #[derive(Debug, Deserialize, Serialize, Default)] pub struct PageCache { - // Url is not serialized? cache: HashMap, was_modified: bool, } @@ -29,45 +29,44 @@ impl PageCache { // fetch path to cache file fn get_cache_path() -> Result { - let path = Self::get_dir()?; - Ok(path.join("cache.json")) + Ok(Self::get_dir()?.join("cache.json")) } // private method, only used to save when cache has changed. fn save(&mut self) -> Result<()> { self.was_modified = false; let data = serde_json::to_string(&self).expect("Unable to deserialize data!"); - let path = Self::get_cache_path()?; - fs::write(path, data)?; + fs::write(Self::get_cache_path()?, data)?; Ok(()) } // TODO: Impl a way to verify cache is not old or out of date. What's a good refresh cache time? 2 weeks? server_settings config? pub fn load() -> Result { - let expiration = SystemTime::now(); + let current = SystemTime::now(); // use define path to cache file let path = Self::get_cache_path()?; - let created_date = match fs::metadata(&path) { - Ok(metadata) => { - if metadata.is_file() { - metadata.created().unwrap_or(SystemTime::now()) - } else { - SystemTime::now() - } - } - Err(_) => SystemTime::now(), + let fallback = SystemTime::now(); + let data = fs::metadata(&path); + let created_date = match data { + Ok(m) => m + .is_file() + .then(|| m.created().unwrap_or(fallback)) + .unwrap_or_else(|| fallback), + _ => fallback, }; - let data = match expiration.duration_since(created_date) { - Ok(_duration) => { - // let sec = duration.as_secs() / (60 * 60 * 24); - // println!("Cache file is {sec} day old!"); + let data = match current.duration_since(created_date) { + Ok(duration) if duration.as_secs() < MAX_VALID_DAYS * 3600 * 24 => { + println!( + "Time still valid: Remaining {}hrs", + duration.as_secs() / 3600 - (MAX_VALID_DAYS * 24) + ); match fs::read_to_string(path) { Ok(data) => serde_json::from_str(&data).unwrap_or(Self::default()), - Err(_) => Self::default(), + _ => Self::default(), } } - Err(_) => Self::default(), + _ => Self::default(), }; Ok(data) @@ -78,12 +77,11 @@ impl PageCache { let mut file_name = url.to_string(); // Rule: find any invalid file name characters + // TODO: Is there a way to make this shared statically? Doesn't seems like it's being used anywhere? let re = Regex::new(r#"[/\\?%*:|."<>]"#).unwrap(); // remove trailing slash - if file_name.ends_with('/') { - file_name.pop(); - } + file_name.ends_with('/').then(|| file_name.pop()); // Replace any invalid characters with hyphens re.replace_all(&file_name, "-").to_string() diff --git a/blender_rs/src/render.py b/blender_rs/src/render.py new file mode 100644 index 00000000..a1f9c19b --- /dev/null +++ b/blender_rs/src/render.py @@ -0,0 +1,156 @@ +# Sybren mention that Cycle will perform better if the render was sent out as a batch instead of individual renders. +# TODO: See if there's a way to adjust blender render batch if possible? + +#Start +import bpy # type: ignore +import xmlrpc.client +import json +from multiprocessing import cpu_count + +isPre3 = bpy.app.version < (3,0,0) + +def eprint(msg): + print("EXCEPTION:" + str(msg) + "\n") + +def log(msg): + print("LOG:" + str(msg) + "\n") + +# hardware:[CPU,GPU,BOTH], kind: [NONE, CUDA, OPTIX, HIP, ONEAPI, (METAL?)] +# Eventually in the future we could distribute to a point of using certain GPU for certain render? +def configureSystemRenderDevices(kind, hardware): + log("Setting up Cycles Render Devices") + pref = bpy.context.preferences.addons["cycles"].preferences + pref.compute_device_type = kind + + devices = None + #For older Blender Builds + if (isPre3): + cuda_devices, opencl_devices = pref.get_devices() + + if(kind in ["CUDA","OPTIX"]): + devices = cuda_devices + else: + devices = opencl_devices + #For Blender Builds >= 3.0 + else: + devices = pref.get_devices_for_type(pref.compute_device_type) + + for d in devices: + # devices do not show GPU, instead they show what your GPU supports (CUDA for RTX) + # CPU GPU ALL + d.use = (d.type == hardware) or (d.type != 'CPU' and hardware == 'GPU') or ( hardware == "BOTH") + +def setRenderSettings(scn, renderSetting, hardware): + # this attribute only accepts 'CPU' or 'GPU' - only available in Cycles Render Engine + scn.cycles.device = hardware + + #Set Samples + scn.cycles.samples = renderSetting["sample"] + scn.render.use_persistent_data = True + + # Set Frames Per Second + fps = renderSetting["FPS"] + if fps is not None and fps > 0: + scn.render.fps = fps + + #Set Resolution + scn.render.resolution_x = renderSetting["width"] + scn.render.resolution_y = renderSetting["height"] + scn.render.resolution_percentage = 100 + + # Set borders + border = renderSetting["border"] + scn.render.border_min_x = border["X"] + scn.render.border_max_x = border["X2"] + scn.render.border_min_y = border["Y"] + scn.render.border_max_y = border["Y2"] + +# Setup blender configs +def setupBlenderSettings(scn, config): + # Scene parse + sceneInfo = config["SceneInfo"] + + #Set Camera + camera = sceneInfo["camera"] + if(camera != None and camera != "" and bpy.data.objects[camera]): + scn.camera = bpy.data.objects[camera] + + # set scene render engine + scn.render.engine = config["Engine"] + + # set render format + file_format = config["Format"] + if(file_format is not None): + scn.render.image_settings.file_format = file_format + + # Set threading + threads = config["Cores"] + scn.render.threads_mode = 'FIXED' + scn.render.threads = max(cpu_count(), threads) + + # is this still possible? not sure if we still need this? + if (isPre3): + scn.render.tile_x = config["TileWidth"] + scn.render.tile_y = config["TileHeight"] + + # Set constraints + scn.render.use_border = True + scn.render.use_crop_to_border = config["Crop"] + if not config["Crop"]: + scn.render.film_transparent = True + + hardware = config["HardwareMode"] + # set render settings + setRenderSettings(scn, sceneInfo["render_setting"], hardware) + + # Conifgure System Render Devices + configureSystemRenderDevices(config["Processor"], hardware) + +#Renders provided settings with id to path +def renderFrame(scn, config, scene, frame): + # Set frame and output + scn.frame_set(frame) + scn.render.filepath = config["Output"] + '/' + str(frame).zfill(5) + + # Render + id = str(config["TaskID"]) + print("RENDER_START: " + id + "\n", flush=True) + + # TODO: Research what use_viewport does? + bpy.ops.render.render(animation=False, write_still=True, use_viewport=False, layer="", scene=scene) + print("SUCCESS: " + id + "\n", flush=True) + +def main(): + proxy = xmlrpc.client.ServerProxy("http://localhost:8081") + config = None + try: + config = json.loads(proxy.fetch_info(1)) + except Exception as e: + eprint(e) + return + + # Gather scene info + scn = bpy.context.scene + scene = config["SceneInfo"]["scene"] + + # set current scene + if(scene is not None and scene != "" and scn.name != scene): + log("Overriding default scene - using target scene: " + scene + "\n") + scn = bpy.data.scenes[scene] + if(scn is None): + raise Exception("Scene name does not exist:" + scene) + + # configure the scene + setupBlenderSettings(scn, config) + + # Loop over batches + while True: + try: + frame = proxy.next_render_queue(1) + except: + break + renderFrame(scn, config, scene, frame) + + print("COMPLETED") + +main() \ No newline at end of file diff --git a/obsidian/.obsidian/app.json b/obsidian/.obsidian/app.json new file mode 100644 index 00000000..e609a07e --- /dev/null +++ b/obsidian/.obsidian/app.json @@ -0,0 +1,3 @@ +{ + "promptDelete": false +} \ No newline at end of file diff --git a/obsidian/.obsidian/appearance.json b/obsidian/.obsidian/appearance.json new file mode 100644 index 00000000..9e26dfee --- /dev/null +++ b/obsidian/.obsidian/appearance.json @@ -0,0 +1 @@ +{} \ No newline at end of file diff --git a/obsidian/.obsidian/core-plugins.json b/obsidian/.obsidian/core-plugins.json new file mode 100644 index 00000000..436f43cf --- /dev/null +++ b/obsidian/.obsidian/core-plugins.json @@ -0,0 +1,30 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false +} \ No newline at end of file diff --git a/obsidian/.obsidian/workspace.json b/obsidian/.obsidian/workspace.json new file mode 100644 index 00000000..bd002b6c --- /dev/null +++ b/obsidian/.obsidian/workspace.json @@ -0,0 +1,185 @@ +{ + "main": { + "id": "8feadfda19abf729", + "type": "split", + "children": [ + { + "id": "851a88eb97dcae8a", + "type": "tabs", + "children": [ + { + "id": "1610ed8efcefe535", + "type": "leaf", + "state": { + "type": "empty", + "state": {}, + "icon": "lucide-file", + "title": "New tab" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "9a6ebaaf46a183c1", + "type": "split", + "children": [ + { + "id": "6773760fa693399a", + "type": "tabs", + "children": [ + { + "id": "4cd97139ec477c9a", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical" + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "7a0a8e5d4082139f", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "225092051244b87c", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "60591356ab10c700", + "type": "split", + "children": [ + { + "id": "3c3953cdabdfb81d", + "type": "tabs", + "children": [ + { + "id": "8b521025b8d5e6d8", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks" + } + }, + { + "id": "d9ed51fe2faaa56c", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links" + } + }, + { + "id": "7382b9f98d4a9384", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "acabdbc43090b872", + "type": "leaf", + "state": { + "type": "outline", + "state": { + "file": "blendfarm/Bugs/Unit test fail - cannot validate .blend file path.md" + }, + "icon": "lucide-list", + "title": "Outline of Unit test fail - cannot validate .blend file path" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false + } + }, + "active": "1610ed8efcefe535", + "lastOpenFiles": [ + "blendfarm/Bugs/Unable to discover localhost with no internet connection is established or provided..md", + "blendfarm/Bugs/Node identification not store in database.md", + "blendfarm/Bugs/Render not saved to database.md", + "blendfarm/Task/Features.md", + "blendfarm/Bugs/Unit test fail - symbol _EMBED_INFO_PLIST already defined.md", + "blendfarm/Bugs/Deleting Blender from UI cause app to crash..md", + "blendfarm/Bugs/Program cannot discover itself on the same network.md", + "blendfarm/Task/Task.md", + "blendfarm/Images/RenderJobDialog.png", + "blendfarm/Images/RenderJobDialog.png", + "blendfarm/Images/SettingPage.png", + "blendfarm/Images/RemoteJobPage.png", + "blendfarm/Pages/Settings.md", + "blendfarm/Pages/Render Job window.md", + "blendfarm/Pages/Remote Render.md", + "blendfarm/Task/TODO.md", + "blendfarm/Context.md", + "blendfarm/Network code notes.md", + "blendfarm/Yamux.md", + "blendfarm/Bugs/Import Job does nothing.md", + "blendfarm/Bugs/Cannot open dialog.md", + "main/Untitled.md", + "main/Main Story.md" + ] +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/app.json b/obsidian/blendfarm/.obsidian/app.json new file mode 100644 index 00000000..5b10960c --- /dev/null +++ b/obsidian/blendfarm/.obsidian/app.json @@ -0,0 +1,5 @@ +{ + "alwaysUpdateLinks": true, + "promptDelete": false, + "useMarkdownLinks": true +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/appearance.json b/obsidian/blendfarm/.obsidian/appearance.json new file mode 100644 index 00000000..5a3f401a --- /dev/null +++ b/obsidian/blendfarm/.obsidian/appearance.json @@ -0,0 +1,4 @@ +{ + "accentColor": "", + "theme": "obsidian" +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/core-plugins-migration.json b/obsidian/blendfarm/.obsidian/core-plugins-migration.json new file mode 100644 index 00000000..436f43cf --- /dev/null +++ b/obsidian/blendfarm/.obsidian/core-plugins-migration.json @@ -0,0 +1,30 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/core-plugins.json b/obsidian/blendfarm/.obsidian/core-plugins.json new file mode 100644 index 00000000..8e719d83 --- /dev/null +++ b/obsidian/blendfarm/.obsidian/core-plugins.json @@ -0,0 +1,33 @@ +{ + "file-explorer": true, + "global-search": true, + "switcher": true, + "graph": true, + "backlink": true, + "canvas": true, + "outgoing-link": true, + "tag-pane": true, + "properties": false, + "page-preview": true, + "daily-notes": true, + "templates": true, + "note-composer": true, + "command-palette": true, + "slash-command": false, + "editor-status": true, + "bookmarks": true, + "markdown-importer": false, + "zk-prefixer": false, + "random-note": false, + "outline": true, + "word-count": true, + "slides": false, + "audio-recorder": false, + "workspaces": false, + "file-recovery": true, + "publish": false, + "sync": false, + "webviewer": false, + "footnotes": false, + "bases": true +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/graph.json b/obsidian/blendfarm/.obsidian/graph.json new file mode 100644 index 00000000..564de653 --- /dev/null +++ b/obsidian/blendfarm/.obsidian/graph.json @@ -0,0 +1,22 @@ +{ + "collapse-filter": true, + "search": "", + "showTags": false, + "showAttachments": false, + "hideUnresolved": false, + "showOrphans": true, + "collapse-color-groups": true, + "colorGroups": [], + "collapse-display": true, + "showArrow": false, + "textFadeMultiplier": 0, + "nodeSizeMultiplier": 1, + "lineSizeMultiplier": 1, + "collapse-forces": true, + "centerStrength": 0.518713248970312, + "repelStrength": 10, + "linkStrength": 1, + "linkDistance": 250, + "scale": 0.9070762383010631, + "close": true +} \ No newline at end of file diff --git a/obsidian/blendfarm/.obsidian/workspace.json b/obsidian/blendfarm/.obsidian/workspace.json new file mode 100644 index 00000000..a014bfde --- /dev/null +++ b/obsidian/blendfarm/.obsidian/workspace.json @@ -0,0 +1,201 @@ +{ + "main": { + "id": "78337d635dbb6873", + "type": "split", + "children": [ + { + "id": "d16ff2f5029d2146", + "type": "tabs", + "children": [ + { + "id": "212dd14b152c2710", + "type": "leaf", + "state": { + "type": "markdown", + "state": { + "file": "Bugs/Render not saved to database.md", + "mode": "source", + "source": false + }, + "icon": "lucide-file", + "title": "Render not saved to database" + } + } + ] + } + ], + "direction": "vertical" + }, + "left": { + "id": "e6315f2e9a577efa", + "type": "split", + "children": [ + { + "id": "eca3db1b7ab0c78d", + "type": "tabs", + "children": [ + { + "id": "b8e74c2efd380365", + "type": "leaf", + "state": { + "type": "file-explorer", + "state": { + "sortOrder": "alphabetical", + "autoReveal": false + }, + "icon": "lucide-folder-closed", + "title": "Files" + } + }, + { + "id": "73232a02b1c2e739", + "type": "leaf", + "state": { + "type": "search", + "state": { + "query": "", + "matchingCase": false, + "explainSearch": false, + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical" + }, + "icon": "lucide-search", + "title": "Search" + } + }, + { + "id": "137e05f70b72093a", + "type": "leaf", + "state": { + "type": "bookmarks", + "state": {}, + "icon": "lucide-bookmark", + "title": "Bookmarks" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300 + }, + "right": { + "id": "2cc1b4442ff01725", + "type": "split", + "children": [ + { + "id": "6cfb792e40cb1461", + "type": "tabs", + "children": [ + { + "id": "4a7e73098dd67e05", + "type": "leaf", + "state": { + "type": "backlink", + "state": { + "collapseAll": false, + "extraContext": false, + "sortOrder": "alphabetical", + "showSearch": false, + "searchQuery": "", + "backlinkCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-coming-in", + "title": "Backlinks" + } + }, + { + "id": "cbd94ab7fb0d96c5", + "type": "leaf", + "state": { + "type": "outgoing-link", + "state": { + "linksCollapsed": false, + "unlinkedCollapsed": true + }, + "icon": "links-going-out", + "title": "Outgoing links" + } + }, + { + "id": "c81e9aaf518413f3", + "type": "leaf", + "state": { + "type": "tag", + "state": { + "sortOrder": "frequency", + "useHierarchy": true + }, + "icon": "lucide-tags", + "title": "Tags" + } + }, + { + "id": "4c4e869fbb38e6d7", + "type": "leaf", + "state": { + "type": "outline", + "state": {}, + "icon": "lucide-list", + "title": "Outline" + } + } + ] + } + ], + "direction": "horizontal", + "width": 300, + "collapsed": true + }, + "left-ribbon": { + "hiddenItems": { + "bases:Create new base": false, + "switcher:Open quick switcher": false, + "graph:Open graph view": false, + "canvas:Create new canvas": false, + "daily-notes:Open today's daily note": false, + "templates:Insert template": false, + "command-palette:Open command palette": false + } + }, + "active": "212dd14b152c2710", + "lastOpenFiles": [ + "Bugs/Unable to discover localhost with no internet connection is established or provided..md", + "Bugs/Unit test fail - symbol _EMBED_INFO_PLIST already defined.md", + "Bugs/Buglist.md", + "Home.md", + "README.md", + "Network code notes.md", + "Pages/Pagelist.md", + "Task/Features.md", + "Task/TODO.md", + "Yamux.md", + "Job list disappear after switching window.md", + "Makefile.md", + "About.md", + "Task/Task.md", + "Bugs/Import Job does nothing.md", + "Bugs/Deleting Blender from UI cause app to crash..md", + "Bugs/Unit test fail - cannot validate .blend file path.md", + "Images/dialog_open_bug.png", + "Bugs/Cannot open dialog.md", + "Images/SettingPage.png", + "Images/RenderJobDialog.png", + "Images/RemoteJobPage.png", + "Small tiny things that annoys me.md", + "Task/Small tiny things that annoys me.md", + "Pages/Render Job window.md", + "Pages/Settings.md", + "Pages/Remote Render.md", + "Bugs/Dialog.open plugin not found.md", + "Bugs/Job list disappear after switching window.md", + "Bugs/Missing Blender installation path.md", + "Images/Setting_page.png", + "Images", + "Pages", + "Task", + "Bugs" + ] +} \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Buglist.md b/obsidian/blendfarm/Bugs/Buglist.md new file mode 100644 index 00000000..4d8d7e77 --- /dev/null +++ b/obsidian/blendfarm/Bugs/Buglist.md @@ -0,0 +1,8 @@ +[Deleting Blender from UI cause app to crash.](Deleting%20Blender%20from%20UI%20cause%20app%20to%20crash..md) +[Node identification not store in database](Node%20identification%20not%20store%20in%20database.md) +[Program cannot discover itself on the same network](Program%20cannot%20discover%20itself%20on%20the%20same%20network.md) +[Render not saved to database](Render%20not%20saved%20to%20database.md) +[Unable to discover localhost with no internet connection is established or provided.](Unable%20to%20discover%20localhost%20with%20no%20internet%20connection%20is%20established%20or%20provided..md) +[Unit test fail - symbol _EMBED_INFO_PLIST already defined](Unit%20test%20fail%20-%20symbol%20_EMBED_INFO_PLIST%20already%20defined.md) + +Todo: \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Deleting Blender from UI cause app to crash..md b/obsidian/blendfarm/Bugs/Deleting Blender from UI cause app to crash..md new file mode 100644 index 00000000..193a6c51 --- /dev/null +++ b/obsidian/blendfarm/Bugs/Deleting Blender from UI cause app to crash..md @@ -0,0 +1,6 @@ +Seems like the code was not implemented to delete local content of blender file. +We should provide a dialog asking user to disconnect blender link or delete local content where blender is store/installed. + +Expected behaviour - when user deletes blender from the settings.rs, it should delete the blender content from the local machine and clear the row entry from settings page (Refresh/update?). + +Actual behaviour - Program will crashed on macos - we need to verify that the path is correct and not linked to the executable inside appbundle \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Node identification not store in database.md b/obsidian/blendfarm/Bugs/Node identification not store in database.md new file mode 100644 index 00000000..6b4e1f37 --- /dev/null +++ b/obsidian/blendfarm/Bugs/Node identification not store in database.md @@ -0,0 +1,3 @@ +Expected behaviour - when a client becomes available and connected to the manager, a new record is added to the database containing computer information in JSON format. + +Actual behaviour - no record is stored when a node is discovered and established. \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Program cannot discover itself on the same network.md b/obsidian/blendfarm/Bugs/Program cannot discover itself on the same network.md new file mode 100644 index 00000000..695c5abc --- /dev/null +++ b/obsidian/blendfarm/Bugs/Program cannot discover itself on the same network.md @@ -0,0 +1,5 @@ +If the wifi connection is disabled and there are no other network bridge/adapter, this program cannot identify itself. + +Expected behaviour - When starting up both manager and client (order does not matter) - The program should be able to establish connection while in offline mode. It shouldn't be able to peer out internet connection, but it should simply invoke the job when resources are available locally. + +Actual behaviour - The program continues to fail to send message out stating "NoPeersSubscribedToTopic" and unable to discover each other node in offline mode. Both manager and client fail to discover each other, despite listening on correct address and port. (No loopback?) \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Render not saved to database.md b/obsidian/blendfarm/Bugs/Render not saved to database.md new file mode 100644 index 00000000..7ab38392 --- /dev/null +++ b/obsidian/blendfarm/Bugs/Render not saved to database.md @@ -0,0 +1,3 @@ +Expected behaviour - Whenever client node completes a render image, before broadcasting out to the network for status update, a new record is appended to database containing information related to the job task. This information should be persistent across app lifespan. + +Actual Behaviour - No data is saved to the database. \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Unable to discover localhost with no internet connection is established or provided..md b/obsidian/blendfarm/Bugs/Unable to discover localhost with no internet connection is established or provided..md new file mode 100644 index 00000000..764a1fec --- /dev/null +++ b/obsidian/blendfarm/Bugs/Unable to discover localhost with no internet connection is established or provided..md @@ -0,0 +1,7 @@ +Currently in the offline mode (Airplane mode/wifi turned off/no ethernet adapter/etc), having both manager and client will not discover itself. + +Todo - contact the community or research online on how to achieve loopback rules for the firewall? I thought it was possible to communicate through a separate channel? + +Expected behavior, Both manager and client should discover itself and begin communication within 0.0.0.0 address + +Actual behavior: Client and manager unable to find each other. \ No newline at end of file diff --git a/obsidian/blendfarm/Bugs/Unit test fail - symbol _EMBED_INFO_PLIST already defined.md b/obsidian/blendfarm/Bugs/Unit test fail - symbol _EMBED_INFO_PLIST already defined.md new file mode 100644 index 00000000..77a02e26 --- /dev/null +++ b/obsidian/blendfarm/Bugs/Unit test fail - symbol _EMBED_INFO_PLIST already defined.md @@ -0,0 +1,23 @@ +Currently unit test fails when generating a new context. I am not sure why I received this error message? I'm on a airplane with no wifi or internet connection whatsoever, so this makes troubleshooting a bit difficult to perform while in air. + +Expected behaviour - Should be able to run unit test and return result. + +Actual behaviour - unable to run unit test as the compiler complains about symbol embed_info_plist is already defined. + +**error****: symbol `_EMBED_INFO_PLIST` is already defined** + +   **-->** src/routes/job.rs:301:23 + +    **|** + +**301** **|**         let context = tauri::generate_context!("tauri.conf.json"); + +    **|**                       **^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^** + +    **|** + +    **=** **note**: this error originates in the macro `$crate::embed_info_plist_bytes` which comes from the expansion of the macro `tauri::generate_context` (in Nightly builds, run with -Z macro-backtrace for more info) + + +TODO: +Try running with `-Z macro-backtrace` Chances are, need to clean and rebuild mac directory. Don't think I've ran into this problem again? Verify this. \ No newline at end of file diff --git a/obsidian/blendfarm/Home.md b/obsidian/blendfarm/Home.md new file mode 100644 index 00000000..e6c3d17d --- /dev/null +++ b/obsidian/blendfarm/Home.md @@ -0,0 +1,7 @@ +[About](README.md) +[Features](./Task/Features.md) +[TODO](./Task/TODO.md) +[Task](./Task/Task.md) +[Buglist](Buglist.md) +[Pagelist](Pagelist.md) +[Network code notes](Network%20code%20notes.md) diff --git a/obsidian/blendfarm/Images/RemoteJobPage.png b/obsidian/blendfarm/Images/RemoteJobPage.png new file mode 100644 index 00000000..dbd6581f Binary files /dev/null and b/obsidian/blendfarm/Images/RemoteJobPage.png differ diff --git a/obsidian/blendfarm/Images/RenderJobDialog.png b/obsidian/blendfarm/Images/RenderJobDialog.png new file mode 100644 index 00000000..ac257095 Binary files /dev/null and b/obsidian/blendfarm/Images/RenderJobDialog.png differ diff --git a/obsidian/blendfarm/Images/SettingPage.png b/obsidian/blendfarm/Images/SettingPage.png new file mode 100644 index 00000000..e6f9652b Binary files /dev/null and b/obsidian/blendfarm/Images/SettingPage.png differ diff --git a/obsidian/blendfarm/Network code notes.md b/obsidian/blendfarm/Network code notes.md new file mode 100644 index 00000000..c80bfafd --- /dev/null +++ b/obsidian/blendfarm/Network code notes.md @@ -0,0 +1,12 @@ +Server must first create a socket object +then we bind the socket to the address, port. +afterward, we listen to the ports. +finally, we accept the listen call. + +client must create socket object, makes connection. +client will "connect" through socket. +client must connect to the same ip:port as the server socket. + +once the server accepts, we now have a connection established between client and server to freely exchange data between those two connections. + +socket file descriptor that can write and read data. diff --git a/obsidian/blendfarm/Pages/Pagelist.md b/obsidian/blendfarm/Pages/Pagelist.md new file mode 100644 index 00000000..55b671cf --- /dev/null +++ b/obsidian/blendfarm/Pages/Pagelist.md @@ -0,0 +1,3 @@ +[Remote Render](Remote%20Render.md) +[Render Job window](Render%20Job%20window.md) +[Settings](Settings.md) diff --git a/obsidian/blendfarm/Pages/Remote Render.md b/obsidian/blendfarm/Pages/Remote Render.md new file mode 100644 index 00000000..2525921a --- /dev/null +++ b/obsidian/blendfarm/Pages/Remote Render.md @@ -0,0 +1,5 @@ +![[RemoteJobPage.png]] +This page display all jobs that this server utilize. You can click on the individual job run to see more detail information below. + +Features: +Let user open project in specific blender?) diff --git a/obsidian/blendfarm/Pages/Render Job window.md b/obsidian/blendfarm/Pages/Render Job window.md new file mode 100644 index 00000000..d82b7e21 --- /dev/null +++ b/obsidian/blendfarm/Pages/Render Job window.md @@ -0,0 +1,11 @@ +![[RenderJobDialog.png]] +This window appears when you create a new job from the remote render setting page. + +Project file path allows user to select which project to create a new render job from. Once a job is created, this path will be used to distribute the project file to the client node. +Rendering mode lets the user define what job this is. There are currently two options for now, Frame and animation. +Frame will let the user pick a frame from the project file and start a rendering job to render that scene's target frame window. + +Feature: +Find a way to load project information for which camera/frame to select from? + +Bug: diff --git a/obsidian/blendfarm/Pages/Settings.md b/obsidian/blendfarm/Pages/Settings.md new file mode 100644 index 00000000..5fb0339f --- /dev/null +++ b/obsidian/blendfarm/Pages/Settings.md @@ -0,0 +1,17 @@ +![[SettingPage.png]] +This page will list out all of the configuration this program can provide to the user. This allows to define custom blender installation path - render cache path to store all completed job renders, and local blender installation. + +The Blender Installation Path outlines where the program can download and install blender from Blender's download page (https://download.blender.org/release) + +[Obsolete(This feature is only meant for the client to utilize, host have no purposes for this?)] +Blender File Cache Path is used for the client computer to utilize where to store and keep incoming project files from the server (host) + +Render Cache Directory is used to store and keep all of the completed render images from the host and client node. When the client is completed with the render job, the client will send the completed image to the host, and the host will store the image to the provided path. + +Blender Installation + +Add from local storage lets the user of the machine to locally point to Blender installed path. (Dev - should we also distribute this to other client if os/arch matches?) + +Install version expose all of blender's latest version available from the website, and install Blender automatically for the user. + +The list below display all known blender installation the program can utilize and access. This list will only appear after validating Blender's executable path. (Feature - Allow user to run blender from here?) \ No newline at end of file diff --git a/obsidian/blendfarm/README.md b/obsidian/blendfarm/README.md new file mode 100644 index 00000000..85f5cfe7 --- /dev/null +++ b/obsidian/blendfarm/README.md @@ -0,0 +1 @@ +Blendfarm is a network service application, similar to flamango, but with memory safety in mind for high level applications and maintain uptime distribution across system schematics. \ No newline at end of file diff --git a/obsidian/blendfarm/Task/Features.md b/obsidian/blendfarm/Task/Features.md new file mode 100644 index 00000000..143a9196 --- /dev/null +++ b/obsidian/blendfarm/Task/Features.md @@ -0,0 +1,7 @@ +[ ] - Find a way to allow GUI interface to run as client mode for non cli users. +[ ] - Consider using channel to stream data - https://v2.tauri.app/develop/calling-frontend/#channels +[ ] - Before release - find a way to add an auto updater? https://v2.tauri.app/plugin/updater/ +[ ] - Provide user feedback when download/installing blender from the web. +[ ] - Implement FFmpeg usage so that we can generate preview gif images within our preview window. +[ ] - Write a python plugin to display Blender Manager from blender. We could operate blendfarm as cli mode within blender? +[ ] - Allow FFI interface to blenderManager from blender using python as a add-on scripts. diff --git a/obsidian/blendfarm/Task/TODO.md b/obsidian/blendfarm/Task/TODO.md new file mode 100644 index 00000000..95af2c30 --- /dev/null +++ b/obsidian/blendfarm/Task/TODO.md @@ -0,0 +1,9 @@ +Get network iron out and established. - Need to make a flow diagram of network support and how this is suppose to be treated. + +Job - display job event +Node - display node activity + +Update pages and image to reflect new UI layout design + +Currently the manager can send the job to the client and can successfully run the run on the client. However the client isn't sending the job info to the manager machine. The job completes, with render details information stored in database, but there's no calling to fetch the image to the host machine or send information about the node status/completion of the job. + diff --git a/obsidian/blendfarm/Task/Task.md b/obsidian/blendfarm/Task/Task.md new file mode 100644 index 00000000..d7233d8a --- /dev/null +++ b/obsidian/blendfarm/Task/Task.md @@ -0,0 +1,16 @@ +Handle conditions where the blendfarm is running offline. +- Let user continue to run the program, but notify the user they're running offline. +- Currently - offline mode cause panic- prevents application from starting up. + +Currently, unable to send files to other server, or run jobs locally? + +Clean up remote_render.tsx view so that it shows a table at the top - then a general overview of information display below + +Find a way to run unit test - Here are a few examples. +- starting server upp +- connect to client +- send a temp file +- test render +- expect a image back. + +Finalize setting page. What needs to be done there? \ No newline at end of file diff --git a/src-tauri/Cargo.toml b/src-tauri/Cargo.toml index b2a3deee..35b29b4c 100644 --- a/src-tauri/Cargo.toml +++ b/src-tauri/Cargo.toml @@ -1,11 +1,11 @@ [package] name = "blendfarm" "authors" = ["Jordan Bejar"] -description = "A open-source, cross-platform, stand-alone Network Render farm for Blender" +description = "A Network Render Farm Manager and Service" license = "MIT" repository = "https://github.com/tiberiumboy/BlendFarm" -edition = "2021" -version = "0.1.0" +edition = "2024" +version = "0.1.2" [lib] name = "blenderfarm_lib" @@ -19,11 +19,11 @@ lto = "thin" [profile.release] lto = true strip = true -opt-level = "z" +opt-level = "s" panic = "abort" codegen-units = 1 incremental = true -debug = 0 +# debug = 0 [profile.dev.package.sqlx-macros] opt-level = 3 @@ -33,12 +33,7 @@ opt-level = 3 tauri-build = { version = "^2.0", features = [] } [dependencies] -anyhow = "^1.0.95" -async-trait = "^0.1.86" -async-std = "^1.13" -blend = "^0.8" -blender = { path = "./../blender/" } -libp2p = { version = "^0.55", features = [ +libp2p = { version = "^0.56", features = [ "mdns", "macros", "gossipsub", @@ -51,10 +46,17 @@ libp2p = { version = "^0.55", features = [ "quic", "kad", ] } -libp2p-request-response = { version = "^0.28", features = ["cbor"] } -bincode = "1.3.3" +anyhow = "^1.0" dirs = "^6.0" -semver = "^1.0.25" +async-trait = "^0.1" +async-std = "^1.13" +blend = "^0.8" +blender = { path = "./../blender_rs/" } +postcard = "^1.1.3" +dunce = "^1.0" +libp2p-request-response = { version = "^0.29", features = ["cbor"] } +futures = "^0.3" +semver = "^1.0" # Use to extract system information machine-info = "^1.0.9" thiserror = "^2.0.11" @@ -64,30 +66,35 @@ tauri-plugin-os = "^2.2" tauri-plugin-persisted-scope = "^2.2" tauri-plugin-shell = "^2.2" tokio = { version = "^1.43", features = ["full"] } -clap = { version = "^4.5.29", features = ["derive"] } -futures = "0.3.31" -sqlx = { version = "0.8.2", features = [ +clap = { version = "^4.5", features = ["derive"] } +sqlx = { version = "^0.8", features = [ "runtime-tokio", "tls-native-tls", "sqlite", + "uuid", + "json", ] } -tauri-plugin-sql = { version = "2", features = ["sqlite"] } -dotenvy = "0.15.7" +dotenvy = "^0.15" # TODO: Compile restriction: Test and deploy using stable version of Rust! Recommends development on Nightly releases -maud = "0.27.0" +maud = "^0.27" +urlencoding = "^2.1" +bitflags = "2.10.0" # this came autogenerated. I don't think I will develop this in the future, but would consider this as an april fools joke. Yes I totally would. [target.'cfg(not(any(target_os = "android", target_os = "ios")))'.dependencies] -tauri-plugin-cli = "^2.2.0" -tauri = { version = "^2.2.5", features = ["protocol-asset"] } -serde = { version = "^1.0.217", features = ["derive"] } -serde_json = "^1.0.138" -uuid = { version = "^1.13.1", features = [ +tauri-plugin-cli = "^2.2" +tauri = { version = "^2.6", features = ["protocol-asset", "tray-icon", "test"] } +serde = { version = "^1.0", features = ["derive"] } +serde_json = "^1.0" +uuid = { version = "^1.3", features = [ "v4", "fast-rng", "macro-diagnostics", "serde", ] } +[dev-dependencies] +ntest = "*" + # [build] # rustflags = ["-C", "link-arg=-fuse-ld=lld"] diff --git a/src-tauri/gen/schemas/acl-manifests.json b/src-tauri/gen/schemas/acl-manifests.json deleted file mode 100644 index 72cdddca..00000000 --- a/src-tauri/gen/schemas/acl-manifests.json +++ /dev/null @@ -1 +0,0 @@ -{"cli":{"default_permission":{"identifier":"default","description":"Allows reading the CLI matches","permissions":["allow-cli-matches"]},"permissions":{"allow-cli-matches":{"identifier":"allow-cli-matches","description":"Enables the cli_matches command without any pre-configured scope.","commands":{"allow":["cli_matches"],"deny":[]}},"deny-cli-matches":{"identifier":"deny-cli-matches","description":"Denies the cli_matches command without any pre-configured scope.","commands":{"allow":[],"deny":["cli_matches"]}}},"permission_sets":{},"global_scope_schema":null},"core":{"default_permission":{"identifier":"default","description":"Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n","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"]},"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-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-name":{"identifier":"allow-name","description":"Enables the name command without any pre-configured scope.","commands":{"allow":["name"],"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-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-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-name":{"identifier":"deny-name","description":"Denies the name command without any pre-configured scope.","commands":{"allow":[],"deny":["name"]}},"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-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.","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.","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.","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.","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.","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.","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-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-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-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-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-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-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-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-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-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},"fs":{"default_permission":{"identifier":"default","description":"This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n","permissions":["create-app-specific-dirs","read-app-specific-dirs-recursive","deny-default"]},"permissions":{"allow-copy-file":{"identifier":"allow-copy-file","description":"Enables the copy_file command without any pre-configured scope.","commands":{"allow":["copy_file"],"deny":[]}},"allow-create":{"identifier":"allow-create","description":"Enables the create command without any pre-configured scope.","commands":{"allow":["create"],"deny":[]}},"allow-exists":{"identifier":"allow-exists","description":"Enables the exists command without any pre-configured scope.","commands":{"allow":["exists"],"deny":[]}},"allow-fstat":{"identifier":"allow-fstat","description":"Enables the fstat command without any pre-configured scope.","commands":{"allow":["fstat"],"deny":[]}},"allow-ftruncate":{"identifier":"allow-ftruncate","description":"Enables the ftruncate command without any pre-configured scope.","commands":{"allow":["ftruncate"],"deny":[]}},"allow-lstat":{"identifier":"allow-lstat","description":"Enables the lstat command without any pre-configured scope.","commands":{"allow":["lstat"],"deny":[]}},"allow-mkdir":{"identifier":"allow-mkdir","description":"Enables the mkdir command without any pre-configured scope.","commands":{"allow":["mkdir"],"deny":[]}},"allow-open":{"identifier":"allow-open","description":"Enables the open command without any pre-configured scope.","commands":{"allow":["open"],"deny":[]}},"allow-read":{"identifier":"allow-read","description":"Enables the read command without any pre-configured scope.","commands":{"allow":["read"],"deny":[]}},"allow-read-dir":{"identifier":"allow-read-dir","description":"Enables the read_dir command without any pre-configured scope.","commands":{"allow":["read_dir"],"deny":[]}},"allow-read-file":{"identifier":"allow-read-file","description":"Enables the read_file command without any pre-configured scope.","commands":{"allow":["read_file"],"deny":[]}},"allow-read-text-file":{"identifier":"allow-read-text-file","description":"Enables the read_text_file command without any pre-configured scope.","commands":{"allow":["read_text_file"],"deny":[]}},"allow-read-text-file-lines":{"identifier":"allow-read-text-file-lines","description":"Enables the read_text_file_lines command without any pre-configured scope.","commands":{"allow":["read_text_file_lines","read_text_file_lines_next"],"deny":[]}},"allow-read-text-file-lines-next":{"identifier":"allow-read-text-file-lines-next","description":"Enables the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":["read_text_file_lines_next"],"deny":[]}},"allow-remove":{"identifier":"allow-remove","description":"Enables the remove command without any pre-configured scope.","commands":{"allow":["remove"],"deny":[]}},"allow-rename":{"identifier":"allow-rename","description":"Enables the rename command without any pre-configured scope.","commands":{"allow":["rename"],"deny":[]}},"allow-seek":{"identifier":"allow-seek","description":"Enables the seek command without any pre-configured scope.","commands":{"allow":["seek"],"deny":[]}},"allow-size":{"identifier":"allow-size","description":"Enables the size command without any pre-configured scope.","commands":{"allow":["size"],"deny":[]}},"allow-stat":{"identifier":"allow-stat","description":"Enables the stat command without any pre-configured scope.","commands":{"allow":["stat"],"deny":[]}},"allow-truncate":{"identifier":"allow-truncate","description":"Enables the truncate command without any pre-configured scope.","commands":{"allow":["truncate"],"deny":[]}},"allow-unwatch":{"identifier":"allow-unwatch","description":"Enables the unwatch command without any pre-configured scope.","commands":{"allow":["unwatch"],"deny":[]}},"allow-watch":{"identifier":"allow-watch","description":"Enables the watch command without any pre-configured scope.","commands":{"allow":["watch"],"deny":[]}},"allow-write":{"identifier":"allow-write","description":"Enables the write command without any pre-configured scope.","commands":{"allow":["write"],"deny":[]}},"allow-write-file":{"identifier":"allow-write-file","description":"Enables the write_file command without any pre-configured scope.","commands":{"allow":["write_file","open","write"],"deny":[]}},"allow-write-text-file":{"identifier":"allow-write-text-file","description":"Enables the write_text_file command without any pre-configured scope.","commands":{"allow":["write_text_file"],"deny":[]}},"create-app-specific-dirs":{"identifier":"create-app-specific-dirs","description":"This permissions allows to create the application specific directories.\n","commands":{"allow":["mkdir","scope-app-index"],"deny":[]}},"deny-copy-file":{"identifier":"deny-copy-file","description":"Denies the copy_file command without any pre-configured scope.","commands":{"allow":[],"deny":["copy_file"]}},"deny-create":{"identifier":"deny-create","description":"Denies the create command without any pre-configured scope.","commands":{"allow":[],"deny":["create"]}},"deny-exists":{"identifier":"deny-exists","description":"Denies the exists command without any pre-configured scope.","commands":{"allow":[],"deny":["exists"]}},"deny-fstat":{"identifier":"deny-fstat","description":"Denies the fstat command without any pre-configured scope.","commands":{"allow":[],"deny":["fstat"]}},"deny-ftruncate":{"identifier":"deny-ftruncate","description":"Denies the ftruncate command without any pre-configured scope.","commands":{"allow":[],"deny":["ftruncate"]}},"deny-lstat":{"identifier":"deny-lstat","description":"Denies the lstat command without any pre-configured scope.","commands":{"allow":[],"deny":["lstat"]}},"deny-mkdir":{"identifier":"deny-mkdir","description":"Denies the mkdir command without any pre-configured scope.","commands":{"allow":[],"deny":["mkdir"]}},"deny-open":{"identifier":"deny-open","description":"Denies the open command without any pre-configured scope.","commands":{"allow":[],"deny":["open"]}},"deny-read":{"identifier":"deny-read","description":"Denies the read command without any pre-configured scope.","commands":{"allow":[],"deny":["read"]}},"deny-read-dir":{"identifier":"deny-read-dir","description":"Denies the read_dir command without any pre-configured scope.","commands":{"allow":[],"deny":["read_dir"]}},"deny-read-file":{"identifier":"deny-read-file","description":"Denies the read_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_file"]}},"deny-read-text-file":{"identifier":"deny-read-text-file","description":"Denies the read_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file"]}},"deny-read-text-file-lines":{"identifier":"deny-read-text-file-lines","description":"Denies the read_text_file_lines command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines"]}},"deny-read-text-file-lines-next":{"identifier":"deny-read-text-file-lines-next","description":"Denies the read_text_file_lines_next command without any pre-configured scope.","commands":{"allow":[],"deny":["read_text_file_lines_next"]}},"deny-remove":{"identifier":"deny-remove","description":"Denies the remove command without any pre-configured scope.","commands":{"allow":[],"deny":["remove"]}},"deny-rename":{"identifier":"deny-rename","description":"Denies the rename command without any pre-configured scope.","commands":{"allow":[],"deny":["rename"]}},"deny-seek":{"identifier":"deny-seek","description":"Denies the seek command without any pre-configured scope.","commands":{"allow":[],"deny":["seek"]}},"deny-size":{"identifier":"deny-size","description":"Denies the size command without any pre-configured scope.","commands":{"allow":[],"deny":["size"]}},"deny-stat":{"identifier":"deny-stat","description":"Denies the stat command without any pre-configured scope.","commands":{"allow":[],"deny":["stat"]}},"deny-truncate":{"identifier":"deny-truncate","description":"Denies the truncate command without any pre-configured scope.","commands":{"allow":[],"deny":["truncate"]}},"deny-unwatch":{"identifier":"deny-unwatch","description":"Denies the unwatch command without any pre-configured scope.","commands":{"allow":[],"deny":["unwatch"]}},"deny-watch":{"identifier":"deny-watch","description":"Denies the watch command without any pre-configured scope.","commands":{"allow":[],"deny":["watch"]}},"deny-webview-data-linux":{"identifier":"deny-webview-data-linux","description":"This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-webview-data-windows":{"identifier":"deny-webview-data-windows","description":"This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.","commands":{"allow":[],"deny":[]}},"deny-write":{"identifier":"deny-write","description":"Denies the write command without any pre-configured scope.","commands":{"allow":[],"deny":["write"]}},"deny-write-file":{"identifier":"deny-write-file","description":"Denies the write_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_file"]}},"deny-write-text-file":{"identifier":"deny-write-text-file","description":"Denies the write_text_file command without any pre-configured scope.","commands":{"allow":[],"deny":["write_text_file"]}},"read-all":{"identifier":"read-all","description":"This enables all read related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists","watch","unwatch"],"deny":[]}},"read-app-specific-dirs-recursive":{"identifier":"read-app-specific-dirs-recursive","description":"This permission allows recursive read functionality on the application\nspecific base directories. \n","commands":{"allow":["read_dir","read_file","read_text_file","read_text_file_lines","read_text_file_lines_next","exists","scope-app-recursive"],"deny":[]}},"read-dirs":{"identifier":"read-dirs","description":"This enables directory read and file metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists"],"deny":[]}},"read-files":{"identifier":"read-files","description":"This enables file read related commands without any pre-configured accessible paths.","commands":{"allow":["read_file","read","open","read_text_file","read_text_file_lines","read_text_file_lines_next","seek","stat","lstat","fstat","exists"],"deny":[]}},"read-meta":{"identifier":"read-meta","description":"This enables all index or metadata related commands without any pre-configured accessible paths.","commands":{"allow":["read_dir","stat","lstat","fstat","exists","size"],"deny":[]}},"scope":{"identifier":"scope","description":"An empty permission you can use to modify the global scope.","commands":{"allow":[],"deny":[]}},"scope-app":{"identifier":"scope-app","description":"This scope permits access to all files and list content of top level directories in the application folders.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"},{"path":"$APPDATA"},{"path":"$APPDATA/*"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"},{"path":"$APPCACHE"},{"path":"$APPCACHE/*"},{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-app-index":{"identifier":"scope-app-index","description":"This scope permits to list all files and folders in the application directories.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPDATA"},{"path":"$APPLOCALDATA"},{"path":"$APPCACHE"},{"path":"$APPLOG"}]}},"scope-app-recursive":{"identifier":"scope-app-recursive","description":"This scope permits recursive access to the complete application folders, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"},{"path":"$APPDATA"},{"path":"$APPDATA/**"},{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"},{"path":"$APPCACHE"},{"path":"$APPCACHE/**"},{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-appcache":{"identifier":"scope-appcache","description":"This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/*"}]}},"scope-appcache-index":{"identifier":"scope-appcache-index","description":"This scope permits to list all files and folders in the `$APPCACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"}]}},"scope-appcache-recursive":{"identifier":"scope-appcache-recursive","description":"This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCACHE"},{"path":"$APPCACHE/**"}]}},"scope-appconfig":{"identifier":"scope-appconfig","description":"This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/*"}]}},"scope-appconfig-index":{"identifier":"scope-appconfig-index","description":"This scope permits to list all files and folders in the `$APPCONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"}]}},"scope-appconfig-recursive":{"identifier":"scope-appconfig-recursive","description":"This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPCONFIG"},{"path":"$APPCONFIG/**"}]}},"scope-appdata":{"identifier":"scope-appdata","description":"This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/*"}]}},"scope-appdata-index":{"identifier":"scope-appdata-index","description":"This scope permits to list all files and folders in the `$APPDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"}]}},"scope-appdata-recursive":{"identifier":"scope-appdata-recursive","description":"This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPDATA"},{"path":"$APPDATA/**"}]}},"scope-applocaldata":{"identifier":"scope-applocaldata","description":"This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/*"}]}},"scope-applocaldata-index":{"identifier":"scope-applocaldata-index","description":"This scope permits to list all files and folders in the `$APPLOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"}]}},"scope-applocaldata-recursive":{"identifier":"scope-applocaldata-recursive","description":"This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOCALDATA"},{"path":"$APPLOCALDATA/**"}]}},"scope-applog":{"identifier":"scope-applog","description":"This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/*"}]}},"scope-applog-index":{"identifier":"scope-applog-index","description":"This scope permits to list all files and folders in the `$APPLOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"}]}},"scope-applog-recursive":{"identifier":"scope-applog-recursive","description":"This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$APPLOG"},{"path":"$APPLOG/**"}]}},"scope-audio":{"identifier":"scope-audio","description":"This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/*"}]}},"scope-audio-index":{"identifier":"scope-audio-index","description":"This scope permits to list all files and folders in the `$AUDIO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"}]}},"scope-audio-recursive":{"identifier":"scope-audio-recursive","description":"This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$AUDIO"},{"path":"$AUDIO/**"}]}},"scope-cache":{"identifier":"scope-cache","description":"This scope permits access to all files and list content of top level directories in the `$CACHE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/*"}]}},"scope-cache-index":{"identifier":"scope-cache-index","description":"This scope permits to list all files and folders in the `$CACHE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"}]}},"scope-cache-recursive":{"identifier":"scope-cache-recursive","description":"This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CACHE"},{"path":"$CACHE/**"}]}},"scope-config":{"identifier":"scope-config","description":"This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/*"}]}},"scope-config-index":{"identifier":"scope-config-index","description":"This scope permits to list all files and folders in the `$CONFIG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"}]}},"scope-config-recursive":{"identifier":"scope-config-recursive","description":"This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$CONFIG"},{"path":"$CONFIG/**"}]}},"scope-data":{"identifier":"scope-data","description":"This scope permits access to all files and list content of top level directories in the `$DATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/*"}]}},"scope-data-index":{"identifier":"scope-data-index","description":"This scope permits to list all files and folders in the `$DATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"}]}},"scope-data-recursive":{"identifier":"scope-data-recursive","description":"This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DATA"},{"path":"$DATA/**"}]}},"scope-desktop":{"identifier":"scope-desktop","description":"This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/*"}]}},"scope-desktop-index":{"identifier":"scope-desktop-index","description":"This scope permits to list all files and folders in the `$DESKTOP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"}]}},"scope-desktop-recursive":{"identifier":"scope-desktop-recursive","description":"This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DESKTOP"},{"path":"$DESKTOP/**"}]}},"scope-document":{"identifier":"scope-document","description":"This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/*"}]}},"scope-document-index":{"identifier":"scope-document-index","description":"This scope permits to list all files and folders in the `$DOCUMENT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"}]}},"scope-document-recursive":{"identifier":"scope-document-recursive","description":"This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOCUMENT"},{"path":"$DOCUMENT/**"}]}},"scope-download":{"identifier":"scope-download","description":"This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/*"}]}},"scope-download-index":{"identifier":"scope-download-index","description":"This scope permits to list all files and folders in the `$DOWNLOAD`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"}]}},"scope-download-recursive":{"identifier":"scope-download-recursive","description":"This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$DOWNLOAD"},{"path":"$DOWNLOAD/**"}]}},"scope-exe":{"identifier":"scope-exe","description":"This scope permits access to all files and list content of top level directories in the `$EXE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/*"}]}},"scope-exe-index":{"identifier":"scope-exe-index","description":"This scope permits to list all files and folders in the `$EXE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"}]}},"scope-exe-recursive":{"identifier":"scope-exe-recursive","description":"This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$EXE"},{"path":"$EXE/**"}]}},"scope-font":{"identifier":"scope-font","description":"This scope permits access to all files and list content of top level directories in the `$FONT` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/*"}]}},"scope-font-index":{"identifier":"scope-font-index","description":"This scope permits to list all files and folders in the `$FONT`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"}]}},"scope-font-recursive":{"identifier":"scope-font-recursive","description":"This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$FONT"},{"path":"$FONT/**"}]}},"scope-home":{"identifier":"scope-home","description":"This scope permits access to all files and list content of top level directories in the `$HOME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/*"}]}},"scope-home-index":{"identifier":"scope-home-index","description":"This scope permits to list all files and folders in the `$HOME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"}]}},"scope-home-recursive":{"identifier":"scope-home-recursive","description":"This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$HOME"},{"path":"$HOME/**"}]}},"scope-localdata":{"identifier":"scope-localdata","description":"This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/*"}]}},"scope-localdata-index":{"identifier":"scope-localdata-index","description":"This scope permits to list all files and folders in the `$LOCALDATA`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"}]}},"scope-localdata-recursive":{"identifier":"scope-localdata-recursive","description":"This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOCALDATA"},{"path":"$LOCALDATA/**"}]}},"scope-log":{"identifier":"scope-log","description":"This scope permits access to all files and list content of top level directories in the `$LOG` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/*"}]}},"scope-log-index":{"identifier":"scope-log-index","description":"This scope permits to list all files and folders in the `$LOG`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"}]}},"scope-log-recursive":{"identifier":"scope-log-recursive","description":"This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$LOG"},{"path":"$LOG/**"}]}},"scope-picture":{"identifier":"scope-picture","description":"This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/*"}]}},"scope-picture-index":{"identifier":"scope-picture-index","description":"This scope permits to list all files and folders in the `$PICTURE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"}]}},"scope-picture-recursive":{"identifier":"scope-picture-recursive","description":"This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PICTURE"},{"path":"$PICTURE/**"}]}},"scope-public":{"identifier":"scope-public","description":"This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/*"}]}},"scope-public-index":{"identifier":"scope-public-index","description":"This scope permits to list all files and folders in the `$PUBLIC`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"}]}},"scope-public-recursive":{"identifier":"scope-public-recursive","description":"This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$PUBLIC"},{"path":"$PUBLIC/**"}]}},"scope-resource":{"identifier":"scope-resource","description":"This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/*"}]}},"scope-resource-index":{"identifier":"scope-resource-index","description":"This scope permits to list all files and folders in the `$RESOURCE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"}]}},"scope-resource-recursive":{"identifier":"scope-resource-recursive","description":"This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RESOURCE"},{"path":"$RESOURCE/**"}]}},"scope-runtime":{"identifier":"scope-runtime","description":"This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/*"}]}},"scope-runtime-index":{"identifier":"scope-runtime-index","description":"This scope permits to list all files and folders in the `$RUNTIME`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"}]}},"scope-runtime-recursive":{"identifier":"scope-runtime-recursive","description":"This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$RUNTIME"},{"path":"$RUNTIME/**"}]}},"scope-temp":{"identifier":"scope-temp","description":"This scope permits access to all files and list content of top level directories in the `$TEMP` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/*"}]}},"scope-temp-index":{"identifier":"scope-temp-index","description":"This scope permits to list all files and folders in the `$TEMP`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"}]}},"scope-temp-recursive":{"identifier":"scope-temp-recursive","description":"This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMP"},{"path":"$TEMP/**"}]}},"scope-template":{"identifier":"scope-template","description":"This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/*"}]}},"scope-template-index":{"identifier":"scope-template-index","description":"This scope permits to list all files and folders in the `$TEMPLATE`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"}]}},"scope-template-recursive":{"identifier":"scope-template-recursive","description":"This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$TEMPLATE"},{"path":"$TEMPLATE/**"}]}},"scope-video":{"identifier":"scope-video","description":"This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/*"}]}},"scope-video-index":{"identifier":"scope-video-index","description":"This scope permits to list all files and folders in the `$VIDEO`folder.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"}]}},"scope-video-recursive":{"identifier":"scope-video-recursive","description":"This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.","commands":{"allow":[],"deny":[]},"scope":{"allow":[{"path":"$VIDEO"},{"path":"$VIDEO/**"}]}},"write-all":{"identifier":"write-all","description":"This enables all write related commands without any pre-configured accessible paths.","commands":{"allow":["mkdir","create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}},"write-files":{"identifier":"write-files","description":"This enables all file write related commands without any pre-configured accessible paths.","commands":{"allow":["create","copy_file","remove","rename","truncate","ftruncate","write","write_file","write_text_file"],"deny":[]}}},"permission_sets":{"allow-app-meta":{"identifier":"allow-app-meta","description":"This allows non-recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-index"]},"allow-app-meta-recursive":{"identifier":"allow-app-meta-recursive","description":"This allows full recursive read access to metadata of the application folders, including file listing and statistics.","permissions":["read-meta","scope-app-recursive"]},"allow-app-read":{"identifier":"allow-app-read","description":"This allows non-recursive read access to the application folders.","permissions":["read-all","scope-app"]},"allow-app-read-recursive":{"identifier":"allow-app-read-recursive","description":"This allows full recursive read access to the complete application folders, files and subdirectories.","permissions":["read-all","scope-app-recursive"]},"allow-app-write":{"identifier":"allow-app-write","description":"This allows non-recursive write access to the application folders.","permissions":["write-all","scope-app"]},"allow-app-write-recursive":{"identifier":"allow-app-write-recursive","description":"This allows full recursive write access to the complete application folders, files and subdirectories.","permissions":["write-all","scope-app-recursive"]},"allow-appcache-meta":{"identifier":"allow-appcache-meta","description":"This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-index"]},"allow-appcache-meta-recursive":{"identifier":"allow-appcache-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-appcache-recursive"]},"allow-appcache-read":{"identifier":"allow-appcache-read","description":"This allows non-recursive read access to the `$APPCACHE` folder.","permissions":["read-all","scope-appcache"]},"allow-appcache-read-recursive":{"identifier":"allow-appcache-read-recursive","description":"This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["read-all","scope-appcache-recursive"]},"allow-appcache-write":{"identifier":"allow-appcache-write","description":"This allows non-recursive write access to the `$APPCACHE` folder.","permissions":["write-all","scope-appcache"]},"allow-appcache-write-recursive":{"identifier":"allow-appcache-write-recursive","description":"This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.","permissions":["write-all","scope-appcache-recursive"]},"allow-appconfig-meta":{"identifier":"allow-appconfig-meta","description":"This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-index"]},"allow-appconfig-meta-recursive":{"identifier":"allow-appconfig-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-appconfig-recursive"]},"allow-appconfig-read":{"identifier":"allow-appconfig-read","description":"This allows non-recursive read access to the `$APPCONFIG` folder.","permissions":["read-all","scope-appconfig"]},"allow-appconfig-read-recursive":{"identifier":"allow-appconfig-read-recursive","description":"This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["read-all","scope-appconfig-recursive"]},"allow-appconfig-write":{"identifier":"allow-appconfig-write","description":"This allows non-recursive write access to the `$APPCONFIG` folder.","permissions":["write-all","scope-appconfig"]},"allow-appconfig-write-recursive":{"identifier":"allow-appconfig-write-recursive","description":"This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.","permissions":["write-all","scope-appconfig-recursive"]},"allow-appdata-meta":{"identifier":"allow-appdata-meta","description":"This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-index"]},"allow-appdata-meta-recursive":{"identifier":"allow-appdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-appdata-recursive"]},"allow-appdata-read":{"identifier":"allow-appdata-read","description":"This allows non-recursive read access to the `$APPDATA` folder.","permissions":["read-all","scope-appdata"]},"allow-appdata-read-recursive":{"identifier":"allow-appdata-read-recursive","description":"This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["read-all","scope-appdata-recursive"]},"allow-appdata-write":{"identifier":"allow-appdata-write","description":"This allows non-recursive write access to the `$APPDATA` folder.","permissions":["write-all","scope-appdata"]},"allow-appdata-write-recursive":{"identifier":"allow-appdata-write-recursive","description":"This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.","permissions":["write-all","scope-appdata-recursive"]},"allow-applocaldata-meta":{"identifier":"allow-applocaldata-meta","description":"This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-index"]},"allow-applocaldata-meta-recursive":{"identifier":"allow-applocaldata-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-applocaldata-recursive"]},"allow-applocaldata-read":{"identifier":"allow-applocaldata-read","description":"This allows non-recursive read access to the `$APPLOCALDATA` folder.","permissions":["read-all","scope-applocaldata"]},"allow-applocaldata-read-recursive":{"identifier":"allow-applocaldata-read-recursive","description":"This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-applocaldata-recursive"]},"allow-applocaldata-write":{"identifier":"allow-applocaldata-write","description":"This allows non-recursive write access to the `$APPLOCALDATA` folder.","permissions":["write-all","scope-applocaldata"]},"allow-applocaldata-write-recursive":{"identifier":"allow-applocaldata-write-recursive","description":"This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-applocaldata-recursive"]},"allow-applog-meta":{"identifier":"allow-applog-meta","description":"This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-index"]},"allow-applog-meta-recursive":{"identifier":"allow-applog-meta-recursive","description":"This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.","permissions":["read-meta","scope-applog-recursive"]},"allow-applog-read":{"identifier":"allow-applog-read","description":"This allows non-recursive read access to the `$APPLOG` folder.","permissions":["read-all","scope-applog"]},"allow-applog-read-recursive":{"identifier":"allow-applog-read-recursive","description":"This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["read-all","scope-applog-recursive"]},"allow-applog-write":{"identifier":"allow-applog-write","description":"This allows non-recursive write access to the `$APPLOG` folder.","permissions":["write-all","scope-applog"]},"allow-applog-write-recursive":{"identifier":"allow-applog-write-recursive","description":"This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.","permissions":["write-all","scope-applog-recursive"]},"allow-audio-meta":{"identifier":"allow-audio-meta","description":"This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-index"]},"allow-audio-meta-recursive":{"identifier":"allow-audio-meta-recursive","description":"This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.","permissions":["read-meta","scope-audio-recursive"]},"allow-audio-read":{"identifier":"allow-audio-read","description":"This allows non-recursive read access to the `$AUDIO` folder.","permissions":["read-all","scope-audio"]},"allow-audio-read-recursive":{"identifier":"allow-audio-read-recursive","description":"This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["read-all","scope-audio-recursive"]},"allow-audio-write":{"identifier":"allow-audio-write","description":"This allows non-recursive write access to the `$AUDIO` folder.","permissions":["write-all","scope-audio"]},"allow-audio-write-recursive":{"identifier":"allow-audio-write-recursive","description":"This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.","permissions":["write-all","scope-audio-recursive"]},"allow-cache-meta":{"identifier":"allow-cache-meta","description":"This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-index"]},"allow-cache-meta-recursive":{"identifier":"allow-cache-meta-recursive","description":"This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.","permissions":["read-meta","scope-cache-recursive"]},"allow-cache-read":{"identifier":"allow-cache-read","description":"This allows non-recursive read access to the `$CACHE` folder.","permissions":["read-all","scope-cache"]},"allow-cache-read-recursive":{"identifier":"allow-cache-read-recursive","description":"This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.","permissions":["read-all","scope-cache-recursive"]},"allow-cache-write":{"identifier":"allow-cache-write","description":"This allows non-recursive write access to the `$CACHE` folder.","permissions":["write-all","scope-cache"]},"allow-cache-write-recursive":{"identifier":"allow-cache-write-recursive","description":"This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.","permissions":["write-all","scope-cache-recursive"]},"allow-config-meta":{"identifier":"allow-config-meta","description":"This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-index"]},"allow-config-meta-recursive":{"identifier":"allow-config-meta-recursive","description":"This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.","permissions":["read-meta","scope-config-recursive"]},"allow-config-read":{"identifier":"allow-config-read","description":"This allows non-recursive read access to the `$CONFIG` folder.","permissions":["read-all","scope-config"]},"allow-config-read-recursive":{"identifier":"allow-config-read-recursive","description":"This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["read-all","scope-config-recursive"]},"allow-config-write":{"identifier":"allow-config-write","description":"This allows non-recursive write access to the `$CONFIG` folder.","permissions":["write-all","scope-config"]},"allow-config-write-recursive":{"identifier":"allow-config-write-recursive","description":"This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.","permissions":["write-all","scope-config-recursive"]},"allow-data-meta":{"identifier":"allow-data-meta","description":"This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-index"]},"allow-data-meta-recursive":{"identifier":"allow-data-meta-recursive","description":"This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.","permissions":["read-meta","scope-data-recursive"]},"allow-data-read":{"identifier":"allow-data-read","description":"This allows non-recursive read access to the `$DATA` folder.","permissions":["read-all","scope-data"]},"allow-data-read-recursive":{"identifier":"allow-data-read-recursive","description":"This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.","permissions":["read-all","scope-data-recursive"]},"allow-data-write":{"identifier":"allow-data-write","description":"This allows non-recursive write access to the `$DATA` folder.","permissions":["write-all","scope-data"]},"allow-data-write-recursive":{"identifier":"allow-data-write-recursive","description":"This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.","permissions":["write-all","scope-data-recursive"]},"allow-desktop-meta":{"identifier":"allow-desktop-meta","description":"This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-index"]},"allow-desktop-meta-recursive":{"identifier":"allow-desktop-meta-recursive","description":"This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.","permissions":["read-meta","scope-desktop-recursive"]},"allow-desktop-read":{"identifier":"allow-desktop-read","description":"This allows non-recursive read access to the `$DESKTOP` folder.","permissions":["read-all","scope-desktop"]},"allow-desktop-read-recursive":{"identifier":"allow-desktop-read-recursive","description":"This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["read-all","scope-desktop-recursive"]},"allow-desktop-write":{"identifier":"allow-desktop-write","description":"This allows non-recursive write access to the `$DESKTOP` folder.","permissions":["write-all","scope-desktop"]},"allow-desktop-write-recursive":{"identifier":"allow-desktop-write-recursive","description":"This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.","permissions":["write-all","scope-desktop-recursive"]},"allow-document-meta":{"identifier":"allow-document-meta","description":"This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-index"]},"allow-document-meta-recursive":{"identifier":"allow-document-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.","permissions":["read-meta","scope-document-recursive"]},"allow-document-read":{"identifier":"allow-document-read","description":"This allows non-recursive read access to the `$DOCUMENT` folder.","permissions":["read-all","scope-document"]},"allow-document-read-recursive":{"identifier":"allow-document-read-recursive","description":"This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["read-all","scope-document-recursive"]},"allow-document-write":{"identifier":"allow-document-write","description":"This allows non-recursive write access to the `$DOCUMENT` folder.","permissions":["write-all","scope-document"]},"allow-document-write-recursive":{"identifier":"allow-document-write-recursive","description":"This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.","permissions":["write-all","scope-document-recursive"]},"allow-download-meta":{"identifier":"allow-download-meta","description":"This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-index"]},"allow-download-meta-recursive":{"identifier":"allow-download-meta-recursive","description":"This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.","permissions":["read-meta","scope-download-recursive"]},"allow-download-read":{"identifier":"allow-download-read","description":"This allows non-recursive read access to the `$DOWNLOAD` folder.","permissions":["read-all","scope-download"]},"allow-download-read-recursive":{"identifier":"allow-download-read-recursive","description":"This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["read-all","scope-download-recursive"]},"allow-download-write":{"identifier":"allow-download-write","description":"This allows non-recursive write access to the `$DOWNLOAD` folder.","permissions":["write-all","scope-download"]},"allow-download-write-recursive":{"identifier":"allow-download-write-recursive","description":"This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.","permissions":["write-all","scope-download-recursive"]},"allow-exe-meta":{"identifier":"allow-exe-meta","description":"This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-index"]},"allow-exe-meta-recursive":{"identifier":"allow-exe-meta-recursive","description":"This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.","permissions":["read-meta","scope-exe-recursive"]},"allow-exe-read":{"identifier":"allow-exe-read","description":"This allows non-recursive read access to the `$EXE` folder.","permissions":["read-all","scope-exe"]},"allow-exe-read-recursive":{"identifier":"allow-exe-read-recursive","description":"This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.","permissions":["read-all","scope-exe-recursive"]},"allow-exe-write":{"identifier":"allow-exe-write","description":"This allows non-recursive write access to the `$EXE` folder.","permissions":["write-all","scope-exe"]},"allow-exe-write-recursive":{"identifier":"allow-exe-write-recursive","description":"This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.","permissions":["write-all","scope-exe-recursive"]},"allow-font-meta":{"identifier":"allow-font-meta","description":"This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-index"]},"allow-font-meta-recursive":{"identifier":"allow-font-meta-recursive","description":"This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.","permissions":["read-meta","scope-font-recursive"]},"allow-font-read":{"identifier":"allow-font-read","description":"This allows non-recursive read access to the `$FONT` folder.","permissions":["read-all","scope-font"]},"allow-font-read-recursive":{"identifier":"allow-font-read-recursive","description":"This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.","permissions":["read-all","scope-font-recursive"]},"allow-font-write":{"identifier":"allow-font-write","description":"This allows non-recursive write access to the `$FONT` folder.","permissions":["write-all","scope-font"]},"allow-font-write-recursive":{"identifier":"allow-font-write-recursive","description":"This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.","permissions":["write-all","scope-font-recursive"]},"allow-home-meta":{"identifier":"allow-home-meta","description":"This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-index"]},"allow-home-meta-recursive":{"identifier":"allow-home-meta-recursive","description":"This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.","permissions":["read-meta","scope-home-recursive"]},"allow-home-read":{"identifier":"allow-home-read","description":"This allows non-recursive read access to the `$HOME` folder.","permissions":["read-all","scope-home"]},"allow-home-read-recursive":{"identifier":"allow-home-read-recursive","description":"This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.","permissions":["read-all","scope-home-recursive"]},"allow-home-write":{"identifier":"allow-home-write","description":"This allows non-recursive write access to the `$HOME` folder.","permissions":["write-all","scope-home"]},"allow-home-write-recursive":{"identifier":"allow-home-write-recursive","description":"This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.","permissions":["write-all","scope-home-recursive"]},"allow-localdata-meta":{"identifier":"allow-localdata-meta","description":"This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-index"]},"allow-localdata-meta-recursive":{"identifier":"allow-localdata-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.","permissions":["read-meta","scope-localdata-recursive"]},"allow-localdata-read":{"identifier":"allow-localdata-read","description":"This allows non-recursive read access to the `$LOCALDATA` folder.","permissions":["read-all","scope-localdata"]},"allow-localdata-read-recursive":{"identifier":"allow-localdata-read-recursive","description":"This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["read-all","scope-localdata-recursive"]},"allow-localdata-write":{"identifier":"allow-localdata-write","description":"This allows non-recursive write access to the `$LOCALDATA` folder.","permissions":["write-all","scope-localdata"]},"allow-localdata-write-recursive":{"identifier":"allow-localdata-write-recursive","description":"This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.","permissions":["write-all","scope-localdata-recursive"]},"allow-log-meta":{"identifier":"allow-log-meta","description":"This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-index"]},"allow-log-meta-recursive":{"identifier":"allow-log-meta-recursive","description":"This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.","permissions":["read-meta","scope-log-recursive"]},"allow-log-read":{"identifier":"allow-log-read","description":"This allows non-recursive read access to the `$LOG` folder.","permissions":["read-all","scope-log"]},"allow-log-read-recursive":{"identifier":"allow-log-read-recursive","description":"This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.","permissions":["read-all","scope-log-recursive"]},"allow-log-write":{"identifier":"allow-log-write","description":"This allows non-recursive write access to the `$LOG` folder.","permissions":["write-all","scope-log"]},"allow-log-write-recursive":{"identifier":"allow-log-write-recursive","description":"This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.","permissions":["write-all","scope-log-recursive"]},"allow-picture-meta":{"identifier":"allow-picture-meta","description":"This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-index"]},"allow-picture-meta-recursive":{"identifier":"allow-picture-meta-recursive","description":"This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.","permissions":["read-meta","scope-picture-recursive"]},"allow-picture-read":{"identifier":"allow-picture-read","description":"This allows non-recursive read access to the `$PICTURE` folder.","permissions":["read-all","scope-picture"]},"allow-picture-read-recursive":{"identifier":"allow-picture-read-recursive","description":"This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["read-all","scope-picture-recursive"]},"allow-picture-write":{"identifier":"allow-picture-write","description":"This allows non-recursive write access to the `$PICTURE` folder.","permissions":["write-all","scope-picture"]},"allow-picture-write-recursive":{"identifier":"allow-picture-write-recursive","description":"This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.","permissions":["write-all","scope-picture-recursive"]},"allow-public-meta":{"identifier":"allow-public-meta","description":"This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-index"]},"allow-public-meta-recursive":{"identifier":"allow-public-meta-recursive","description":"This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.","permissions":["read-meta","scope-public-recursive"]},"allow-public-read":{"identifier":"allow-public-read","description":"This allows non-recursive read access to the `$PUBLIC` folder.","permissions":["read-all","scope-public"]},"allow-public-read-recursive":{"identifier":"allow-public-read-recursive","description":"This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["read-all","scope-public-recursive"]},"allow-public-write":{"identifier":"allow-public-write","description":"This allows non-recursive write access to the `$PUBLIC` folder.","permissions":["write-all","scope-public"]},"allow-public-write-recursive":{"identifier":"allow-public-write-recursive","description":"This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.","permissions":["write-all","scope-public-recursive"]},"allow-resource-meta":{"identifier":"allow-resource-meta","description":"This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-index"]},"allow-resource-meta-recursive":{"identifier":"allow-resource-meta-recursive","description":"This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.","permissions":["read-meta","scope-resource-recursive"]},"allow-resource-read":{"identifier":"allow-resource-read","description":"This allows non-recursive read access to the `$RESOURCE` folder.","permissions":["read-all","scope-resource"]},"allow-resource-read-recursive":{"identifier":"allow-resource-read-recursive","description":"This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["read-all","scope-resource-recursive"]},"allow-resource-write":{"identifier":"allow-resource-write","description":"This allows non-recursive write access to the `$RESOURCE` folder.","permissions":["write-all","scope-resource"]},"allow-resource-write-recursive":{"identifier":"allow-resource-write-recursive","description":"This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.","permissions":["write-all","scope-resource-recursive"]},"allow-runtime-meta":{"identifier":"allow-runtime-meta","description":"This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-index"]},"allow-runtime-meta-recursive":{"identifier":"allow-runtime-meta-recursive","description":"This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.","permissions":["read-meta","scope-runtime-recursive"]},"allow-runtime-read":{"identifier":"allow-runtime-read","description":"This allows non-recursive read access to the `$RUNTIME` folder.","permissions":["read-all","scope-runtime"]},"allow-runtime-read-recursive":{"identifier":"allow-runtime-read-recursive","description":"This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["read-all","scope-runtime-recursive"]},"allow-runtime-write":{"identifier":"allow-runtime-write","description":"This allows non-recursive write access to the `$RUNTIME` folder.","permissions":["write-all","scope-runtime"]},"allow-runtime-write-recursive":{"identifier":"allow-runtime-write-recursive","description":"This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.","permissions":["write-all","scope-runtime-recursive"]},"allow-temp-meta":{"identifier":"allow-temp-meta","description":"This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-index"]},"allow-temp-meta-recursive":{"identifier":"allow-temp-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.","permissions":["read-meta","scope-temp-recursive"]},"allow-temp-read":{"identifier":"allow-temp-read","description":"This allows non-recursive read access to the `$TEMP` folder.","permissions":["read-all","scope-temp"]},"allow-temp-read-recursive":{"identifier":"allow-temp-read-recursive","description":"This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.","permissions":["read-all","scope-temp-recursive"]},"allow-temp-write":{"identifier":"allow-temp-write","description":"This allows non-recursive write access to the `$TEMP` folder.","permissions":["write-all","scope-temp"]},"allow-temp-write-recursive":{"identifier":"allow-temp-write-recursive","description":"This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.","permissions":["write-all","scope-temp-recursive"]},"allow-template-meta":{"identifier":"allow-template-meta","description":"This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-index"]},"allow-template-meta-recursive":{"identifier":"allow-template-meta-recursive","description":"This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.","permissions":["read-meta","scope-template-recursive"]},"allow-template-read":{"identifier":"allow-template-read","description":"This allows non-recursive read access to the `$TEMPLATE` folder.","permissions":["read-all","scope-template"]},"allow-template-read-recursive":{"identifier":"allow-template-read-recursive","description":"This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["read-all","scope-template-recursive"]},"allow-template-write":{"identifier":"allow-template-write","description":"This allows non-recursive write access to the `$TEMPLATE` folder.","permissions":["write-all","scope-template"]},"allow-template-write-recursive":{"identifier":"allow-template-write-recursive","description":"This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.","permissions":["write-all","scope-template-recursive"]},"allow-video-meta":{"identifier":"allow-video-meta","description":"This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-index"]},"allow-video-meta-recursive":{"identifier":"allow-video-meta-recursive","description":"This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.","permissions":["read-meta","scope-video-recursive"]},"allow-video-read":{"identifier":"allow-video-read","description":"This allows non-recursive read access to the `$VIDEO` folder.","permissions":["read-all","scope-video"]},"allow-video-read-recursive":{"identifier":"allow-video-read-recursive","description":"This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["read-all","scope-video-recursive"]},"allow-video-write":{"identifier":"allow-video-write","description":"This allows non-recursive write access to the `$VIDEO` folder.","permissions":["write-all","scope-video"]},"allow-video-write-recursive":{"identifier":"allow-video-write-recursive","description":"This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.","permissions":["write-all","scope-video-recursive"]},"deny-default":{"identifier":"deny-default","description":"This denies access to dangerous Tauri relevant files and folders by default.","permissions":["deny-webview-data-linux","deny-webview-data-windows"]}},"global_scope_schema":{"$schema":"http://json-schema.org/draft-07/schema#","anyOf":[{"description":"A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"},{"properties":{"path":{"description":"A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.","type":"string"}},"required":["path"],"type":"object"}],"description":"FS scope entry.","title":"FsScopeEntry"}},"os":{"default_permission":{"identifier":"default","description":"This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n","permissions":["allow-arch","allow-exe-extension","allow-family","allow-locale","allow-os-type","allow-platform","allow-version"]},"permissions":{"allow-arch":{"identifier":"allow-arch","description":"Enables the arch command without any pre-configured scope.","commands":{"allow":["arch"],"deny":[]}},"allow-exe-extension":{"identifier":"allow-exe-extension","description":"Enables the exe_extension command without any pre-configured scope.","commands":{"allow":["exe_extension"],"deny":[]}},"allow-family":{"identifier":"allow-family","description":"Enables the family command without any pre-configured scope.","commands":{"allow":["family"],"deny":[]}},"allow-hostname":{"identifier":"allow-hostname","description":"Enables the hostname command without any pre-configured scope.","commands":{"allow":["hostname"],"deny":[]}},"allow-locale":{"identifier":"allow-locale","description":"Enables the locale command without any pre-configured scope.","commands":{"allow":["locale"],"deny":[]}},"allow-os-type":{"identifier":"allow-os-type","description":"Enables the os_type command without any pre-configured scope.","commands":{"allow":["os_type"],"deny":[]}},"allow-platform":{"identifier":"allow-platform","description":"Enables the platform command without any pre-configured scope.","commands":{"allow":["platform"],"deny":[]}},"allow-version":{"identifier":"allow-version","description":"Enables the version command without any pre-configured scope.","commands":{"allow":["version"],"deny":[]}},"deny-arch":{"identifier":"deny-arch","description":"Denies the arch command without any pre-configured scope.","commands":{"allow":[],"deny":["arch"]}},"deny-exe-extension":{"identifier":"deny-exe-extension","description":"Denies the exe_extension command without any pre-configured scope.","commands":{"allow":[],"deny":["exe_extension"]}},"deny-family":{"identifier":"deny-family","description":"Denies the family command without any pre-configured scope.","commands":{"allow":[],"deny":["family"]}},"deny-hostname":{"identifier":"deny-hostname","description":"Denies the hostname command without any pre-configured scope.","commands":{"allow":[],"deny":["hostname"]}},"deny-locale":{"identifier":"deny-locale","description":"Denies the locale command without any pre-configured scope.","commands":{"allow":[],"deny":["locale"]}},"deny-os-type":{"identifier":"deny-os-type","description":"Denies the os_type command without any pre-configured scope.","commands":{"allow":[],"deny":["os_type"]}},"deny-platform":{"identifier":"deny-platform","description":"Denies the platform command without any pre-configured scope.","commands":{"allow":[],"deny":["platform"]}},"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},"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 without any specific\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"}},"sql":{"default_permission":{"identifier":"default","description":"### Default Permissions\n\nThis permission set configures what kind of\ndatabase operations are available from the sql plugin.\n\n### Granted Permissions\n\nAll reading related operations are enabled.\nAlso allows to load or close a connection.\n\n","permissions":["allow-close","allow-load","allow-select"]},"permissions":{"allow-close":{"identifier":"allow-close","description":"Enables the close command without any pre-configured scope.","commands":{"allow":["close"],"deny":[]}},"allow-execute":{"identifier":"allow-execute","description":"Enables the execute command without any pre-configured scope.","commands":{"allow":["execute"],"deny":[]}},"allow-load":{"identifier":"allow-load","description":"Enables the load command without any pre-configured scope.","commands":{"allow":["load"],"deny":[]}},"allow-select":{"identifier":"allow-select","description":"Enables the select command without any pre-configured scope.","commands":{"allow":["select"],"deny":[]}},"deny-close":{"identifier":"deny-close","description":"Denies the close command without any pre-configured scope.","commands":{"allow":[],"deny":["close"]}},"deny-execute":{"identifier":"deny-execute","description":"Denies the execute command without any pre-configured scope.","commands":{"allow":[],"deny":["execute"]}},"deny-load":{"identifier":"deny-load","description":"Denies the load command without any pre-configured scope.","commands":{"allow":[],"deny":["load"]}},"deny-select":{"identifier":"deny-select","description":"Denies the select command without any pre-configured scope.","commands":{"allow":[],"deny":["select"]}}},"permission_sets":{},"global_scope_schema":null}} \ No newline at end of file diff --git a/src-tauri/gen/schemas/desktop-schema.json b/src-tauri/gen/schemas/desktop-schema.json deleted file mode 100644 index fd6f55d9..00000000 --- a/src-tauri/gen/schemas/desktop-schema.json +++ /dev/null @@ -1,5256 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CapabilityFile", - "description": "Capability formats accepted in a capability file.", - "anyOf": [ - { - "description": "A single capability.", - "allOf": [ - { - "$ref": "#/definitions/Capability" - } - ] - }, - { - "description": "A list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - }, - { - "description": "A list of capabilities.", - "type": "object", - "required": [ - "capabilities" - ], - "properties": { - "capabilities": { - "description": "The list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - } - } - } - ], - "definitions": { - "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", - "type": "object", - "required": [ - "identifier", - "permissions" - ], - "properties": { - "identifier": { - "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", - "type": "string" - }, - "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", - "default": "", - "type": "string" - }, - "remote": { - "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", - "anyOf": [ - { - "$ref": "#/definitions/CapabilityRemote" - }, - { - "type": "null" - } - ] - }, - "local": { - "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", - "default": true, - "type": "boolean" - }, - "windows": { - "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "webviews": { - "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "permissions": { - "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionEntry" - }, - "uniqueItems": true - }, - "platforms": { - "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Target" - } - } - } - }, - "CapabilityRemote": { - "description": "Configuration for remote URLs that are associated with the capability.", - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PermissionEntry": { - "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", - "anyOf": [ - { - "description": "Reference a permission or permission set by identifier.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - { - "description": "Reference a permission or permission set by identifier and extends its scope.", - "type": "object", - "allOf": [ - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-size" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-size" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - } - } - } - ] - } - }, - "deny": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - } - } - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - }, - "deny": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - "allow": { - "description": "Data that defines what is allowed by the scope.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - }, - "deny": { - "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - } - } - } - ], - "required": [ - "identifier" - ] - } - ] - }, - "Identifier": { - "description": "Permission identifier", - "oneOf": [ - { - "description": "Allows reading the CLI matches", - "type": "string", - "const": "cli:default" - }, - { - "description": "Enables the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:allow-cli-matches" - }, - { - "description": "Denies the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:deny-cli-matches" - }, - { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", - "type": "string", - "const": "core:default" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:app:default" - }, - { - "description": "Enables the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-hide" - }, - { - "description": "Enables the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-show" - }, - { - "description": "Enables the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-default-window-icon" - }, - { - "description": "Enables the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-name" - }, - { - "description": "Enables the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-set-app-theme" - }, - { - "description": "Enables the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-tauri-version" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-version" - }, - { - "description": "Denies the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-hide" - }, - { - "description": "Denies the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-show" - }, - { - "description": "Denies the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-default-window-icon" - }, - { - "description": "Denies the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-name" - }, - { - "description": "Denies the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-set-app-theme" - }, - { - "description": "Denies the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-tauri-version" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-version" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:event:default" - }, - { - "description": "Enables the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit" - }, - { - "description": "Enables the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit-to" - }, - { - "description": "Enables the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-listen" - }, - { - "description": "Enables the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-unlisten" - }, - { - "description": "Denies the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit" - }, - { - "description": "Denies the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit-to" - }, - { - "description": "Denies the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-listen" - }, - { - "description": "Denies the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-unlisten" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:image:default" - }, - { - "description": "Enables the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-bytes" - }, - { - "description": "Enables the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-path" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-new" - }, - { - "description": "Enables the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-rgba" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-size" - }, - { - "description": "Denies the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-bytes" - }, - { - "description": "Denies the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-path" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-new" - }, - { - "description": "Denies the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-rgba" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:menu:default" - }, - { - "description": "Enables the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-append" - }, - { - "description": "Enables the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-create-default" - }, - { - "description": "Enables the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-get" - }, - { - "description": "Enables the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-insert" - }, - { - "description": "Enables the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-checked" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-enabled" - }, - { - "description": "Enables the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-items" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-new" - }, - { - "description": "Enables the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-popup" - }, - { - "description": "Enables the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-prepend" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove" - }, - { - "description": "Enables the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove-at" - }, - { - "description": "Enables the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-accelerator" - }, - { - "description": "Enables the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-app-menu" - }, - { - "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" - }, - { - "description": "Enables the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-window-menu" - }, - { - "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" - }, - { - "description": "Enables the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-checked" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-enabled" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-icon" - }, - { - "description": "Enables the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-text" - }, - { - "description": "Enables the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-text" - }, - { - "description": "Denies the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-append" - }, - { - "description": "Denies the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-create-default" - }, - { - "description": "Denies the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-get" - }, - { - "description": "Denies the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-insert" - }, - { - "description": "Denies the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-checked" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-enabled" - }, - { - "description": "Denies the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-items" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-new" - }, - { - "description": "Denies the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-popup" - }, - { - "description": "Denies the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-prepend" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove" - }, - { - "description": "Denies the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove-at" - }, - { - "description": "Denies the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-accelerator" - }, - { - "description": "Denies the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-app-menu" - }, - { - "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" - }, - { - "description": "Denies the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-window-menu" - }, - { - "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" - }, - { - "description": "Denies the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-checked" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-enabled" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-icon" - }, - { - "description": "Denies the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-text" - }, - { - "description": "Denies the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-text" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:path:default" - }, - { - "description": "Enables the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-basename" - }, - { - "description": "Enables the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-dirname" - }, - { - "description": "Enables the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-extname" - }, - { - "description": "Enables the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-is-absolute" - }, - { - "description": "Enables the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-join" - }, - { - "description": "Enables the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-normalize" - }, - { - "description": "Enables the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve" - }, - { - "description": "Enables the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve-directory" - }, - { - "description": "Denies the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-basename" - }, - { - "description": "Denies the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-dirname" - }, - { - "description": "Denies the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-extname" - }, - { - "description": "Denies the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-is-absolute" - }, - { - "description": "Denies the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-join" - }, - { - "description": "Denies the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-normalize" - }, - { - "description": "Denies the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve" - }, - { - "description": "Denies the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve-directory" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:resources:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:allow-close" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:deny-close" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:tray:default" - }, - { - "description": "Enables the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-get-by-id" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-new" - }, - { - "description": "Enables the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-remove-by-id" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon" - }, - { - "description": "Enables the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon-as-template" - }, - { - "description": "Enables the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-menu" - }, - { - "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" - }, - { - "description": "Enables the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-temp-dir-path" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-title" - }, - { - "description": "Enables the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-tooltip" - }, - { - "description": "Enables the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-visible" - }, - { - "description": "Denies the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-get-by-id" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-new" - }, - { - "description": "Denies the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-remove-by-id" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon" - }, - { - "description": "Denies the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon-as-template" - }, - { - "description": "Denies the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-menu" - }, - { - "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" - }, - { - "description": "Denies the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-temp-dir-path" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-title" - }, - { - "description": "Denies the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-tooltip" - }, - { - "description": "Denies the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-visible" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:webview:default" - }, - { - "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-clear-all-browsing-data" - }, - { - "description": "Enables the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview" - }, - { - "description": "Enables the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview-window" - }, - { - "description": "Enables the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-get-all-webviews" - }, - { - "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" - }, - { - "description": "Enables the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-print" - }, - { - "description": "Enables the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-reparent" - }, - { - "description": "Enables the set_webview_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-background-color" - }, - { - "description": "Enables the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-focus" - }, - { - "description": "Enables the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-position" - }, - { - "description": "Enables the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-size" - }, - { - "description": "Enables the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-zoom" - }, - { - "description": "Enables the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-close" - }, - { - "description": "Enables the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-hide" - }, - { - "description": "Enables the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-position" - }, - { - "description": "Enables the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-show" - }, - { - "description": "Enables the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-size" - }, - { - "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-clear-all-browsing-data" - }, - { - "description": "Denies the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview" - }, - { - "description": "Denies the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview-window" - }, - { - "description": "Denies the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-get-all-webviews" - }, - { - "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" - }, - { - "description": "Denies the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-print" - }, - { - "description": "Denies the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-reparent" - }, - { - "description": "Denies the set_webview_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-background-color" - }, - { - "description": "Denies the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-focus" - }, - { - "description": "Denies the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-position" - }, - { - "description": "Denies the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-size" - }, - { - "description": "Denies the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-zoom" - }, - { - "description": "Denies the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-close" - }, - { - "description": "Denies the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-hide" - }, - { - "description": "Denies the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-position" - }, - { - "description": "Denies the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-show" - }, - { - "description": "Denies the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:window:default" - }, - { - "description": "Enables the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-available-monitors" - }, - { - "description": "Enables the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-center" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-close" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-create" - }, - { - "description": "Enables the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-current-monitor" - }, - { - "description": "Enables the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-cursor-position" - }, - { - "description": "Enables the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-destroy" - }, - { - "description": "Enables the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-get-all-windows" - }, - { - "description": "Enables the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-hide" - }, - { - "description": "Enables the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-position" - }, - { - "description": "Enables the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-size" - }, - { - "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-internal-toggle-maximize" - }, - { - "description": "Enables the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-closable" - }, - { - "description": "Enables the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-decorated" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-enabled" - }, - { - "description": "Enables the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-focused" - }, - { - "description": "Enables the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-fullscreen" - }, - { - "description": "Enables the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximizable" - }, - { - "description": "Enables the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximized" - }, - { - "description": "Enables the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimizable" - }, - { - "description": "Enables the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimized" - }, - { - "description": "Enables the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-resizable" - }, - { - "description": "Enables the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-visible" - }, - { - "description": "Enables the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-maximize" - }, - { - "description": "Enables the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-minimize" - }, - { - "description": "Enables the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-monitor-from-point" - }, - { - "description": "Enables the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-position" - }, - { - "description": "Enables the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-size" - }, - { - "description": "Enables the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-primary-monitor" - }, - { - "description": "Enables the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-request-user-attention" - }, - { - "description": "Enables the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-scale-factor" - }, - { - "description": "Enables the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-bottom" - }, - { - "description": "Enables the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-top" - }, - { - "description": "Enables the set_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-background-color" - }, - { - "description": "Enables the set_badge_count command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-badge-count" - }, - { - "description": "Enables the set_badge_label command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-badge-label" - }, - { - "description": "Enables the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-closable" - }, - { - "description": "Enables the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-content-protected" - }, - { - "description": "Enables the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-grab" - }, - { - "description": "Enables the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-icon" - }, - { - "description": "Enables the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-position" - }, - { - "description": "Enables the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-visible" - }, - { - "description": "Enables the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-decorations" - }, - { - "description": "Enables the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-effects" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-enabled" - }, - { - "description": "Enables the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-focus" - }, - { - "description": "Enables the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-fullscreen" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-icon" - }, - { - "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" - }, - { - "description": "Enables the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-max-size" - }, - { - "description": "Enables the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-maximizable" - }, - { - "description": "Enables the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-min-size" - }, - { - "description": "Enables the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-minimizable" - }, - { - "description": "Enables the set_overlay_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-overlay-icon" - }, - { - "description": "Enables the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-position" - }, - { - "description": "Enables the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-progress-bar" - }, - { - "description": "Enables the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-resizable" - }, - { - "description": "Enables the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-shadow" - }, - { - "description": "Enables the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size" - }, - { - "description": "Enables the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size-constraints" - }, - { - "description": "Enables the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-skip-taskbar" - }, - { - "description": "Enables the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-theme" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title" - }, - { - "description": "Enables the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title-bar-style" - }, - { - "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" - }, - { - "description": "Enables the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-show" - }, - { - "description": "Enables the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-dragging" - }, - { - "description": "Enables the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-resize-dragging" - }, - { - "description": "Enables the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-theme" - }, - { - "description": "Enables the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-title" - }, - { - "description": "Enables the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-toggle-maximize" - }, - { - "description": "Enables the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unmaximize" - }, - { - "description": "Enables the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unminimize" - }, - { - "description": "Denies the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-available-monitors" - }, - { - "description": "Denies the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-center" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-close" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-create" - }, - { - "description": "Denies the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-current-monitor" - }, - { - "description": "Denies the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-cursor-position" - }, - { - "description": "Denies the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-destroy" - }, - { - "description": "Denies the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-get-all-windows" - }, - { - "description": "Denies the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-hide" - }, - { - "description": "Denies the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-position" - }, - { - "description": "Denies the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-size" - }, - { - "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-internal-toggle-maximize" - }, - { - "description": "Denies the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-closable" - }, - { - "description": "Denies the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-decorated" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-enabled" - }, - { - "description": "Denies the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-focused" - }, - { - "description": "Denies the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-fullscreen" - }, - { - "description": "Denies the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximizable" - }, - { - "description": "Denies the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximized" - }, - { - "description": "Denies the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimizable" - }, - { - "description": "Denies the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimized" - }, - { - "description": "Denies the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-resizable" - }, - { - "description": "Denies the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-visible" - }, - { - "description": "Denies the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-maximize" - }, - { - "description": "Denies the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-minimize" - }, - { - "description": "Denies the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-monitor-from-point" - }, - { - "description": "Denies the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-position" - }, - { - "description": "Denies the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-size" - }, - { - "description": "Denies the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-primary-monitor" - }, - { - "description": "Denies the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-request-user-attention" - }, - { - "description": "Denies the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-scale-factor" - }, - { - "description": "Denies the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-bottom" - }, - { - "description": "Denies the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-top" - }, - { - "description": "Denies the set_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-background-color" - }, - { - "description": "Denies the set_badge_count command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-badge-count" - }, - { - "description": "Denies the set_badge_label command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-badge-label" - }, - { - "description": "Denies the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-closable" - }, - { - "description": "Denies the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-content-protected" - }, - { - "description": "Denies the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-grab" - }, - { - "description": "Denies the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-icon" - }, - { - "description": "Denies the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-position" - }, - { - "description": "Denies the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-visible" - }, - { - "description": "Denies the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-decorations" - }, - { - "description": "Denies the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-effects" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-enabled" - }, - { - "description": "Denies the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-focus" - }, - { - "description": "Denies the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-fullscreen" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-icon" - }, - { - "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" - }, - { - "description": "Denies the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-max-size" - }, - { - "description": "Denies the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-maximizable" - }, - { - "description": "Denies the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-min-size" - }, - { - "description": "Denies the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-minimizable" - }, - { - "description": "Denies the set_overlay_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-overlay-icon" - }, - { - "description": "Denies the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-position" - }, - { - "description": "Denies the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-progress-bar" - }, - { - "description": "Denies the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-resizable" - }, - { - "description": "Denies the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-shadow" - }, - { - "description": "Denies the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size" - }, - { - "description": "Denies the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size-constraints" - }, - { - "description": "Denies the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-skip-taskbar" - }, - { - "description": "Denies the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-theme" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title" - }, - { - "description": "Denies the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title-bar-style" - }, - { - "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" - }, - { - "description": "Denies the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-show" - }, - { - "description": "Denies the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-dragging" - }, - { - "description": "Denies the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-resize-dragging" - }, - { - "description": "Denies the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-theme" - }, - { - "description": "Denies the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-title" - }, - { - "description": "Denies the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-toggle-maximize" - }, - { - "description": "Denies the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unmaximize" - }, - { - "description": "Denies the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unminimize" - }, - { - "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", - "type": "string", - "const": "dialog:default" - }, - { - "description": "Enables the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-ask" - }, - { - "description": "Enables the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-confirm" - }, - { - "description": "Enables the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-message" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-open" - }, - { - "description": "Enables the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-save" - }, - { - "description": "Denies the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-ask" - }, - { - "description": "Denies the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-confirm" - }, - { - "description": "Denies the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-message" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-open" - }, - { - "description": "Denies the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-save" - }, - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-size" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-size" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - }, - { - "description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n", - "type": "string", - "const": "os:default" - }, - { - "description": "Enables the arch command without any pre-configured scope.", - "type": "string", - "const": "os:allow-arch" - }, - { - "description": "Enables the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:allow-exe-extension" - }, - { - "description": "Enables the family command without any pre-configured scope.", - "type": "string", - "const": "os:allow-family" - }, - { - "description": "Enables the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:allow-hostname" - }, - { - "description": "Enables the locale command without any pre-configured scope.", - "type": "string", - "const": "os:allow-locale" - }, - { - "description": "Enables the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:allow-os-type" - }, - { - "description": "Enables the platform command without any pre-configured scope.", - "type": "string", - "const": "os:allow-platform" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "os:allow-version" - }, - { - "description": "Denies the arch command without any pre-configured scope.", - "type": "string", - "const": "os:deny-arch" - }, - { - "description": "Denies the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:deny-exe-extension" - }, - { - "description": "Denies the family command without any pre-configured scope.", - "type": "string", - "const": "os:deny-family" - }, - { - "description": "Denies the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:deny-hostname" - }, - { - "description": "Denies the locale command without any pre-configured scope.", - "type": "string", - "const": "os:deny-locale" - }, - { - "description": "Denies the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:deny-os-type" - }, - { - "description": "Denies the platform command without any pre-configured scope.", - "type": "string", - "const": "os:deny-platform" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "os:deny-version" - }, - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - }, - { - "description": "### Default Permissions\n\nThis permission set configures what kind of\ndatabase operations are available from the sql plugin.\n\n### Granted Permissions\n\nAll reading related operations are enabled.\nAlso allows to load or close a connection.\n\n", - "type": "string", - "const": "sql:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-close" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-execute" - }, - { - "description": "Enables the load command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-load" - }, - { - "description": "Enables the select command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-select" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-close" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-execute" - }, - { - "description": "Denies the load command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-load" - }, - { - "description": "Denies the select command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-select" - } - ] - }, - "Value": { - "description": "All supported ACL values.", - "anyOf": [ - { - "description": "Represents a null JSON value.", - "type": "null" - }, - { - "description": "Represents a [`bool`].", - "type": "boolean" - }, - { - "description": "Represents a valid ACL [`Number`].", - "allOf": [ - { - "$ref": "#/definitions/Number" - } - ] - }, - { - "description": "Represents a [`String`].", - "type": "string" - }, - { - "description": "Represents a list of other [`Value`]s.", - "type": "array", - "items": { - "$ref": "#/definitions/Value" - } - }, - { - "description": "Represents a map of [`String`] keys to [`Value`]s.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Value" - } - } - ] - }, - "Number": { - "description": "A valid ACL number.", - "anyOf": [ - { - "description": "Represents an [`i64`].", - "type": "integer", - "format": "int64" - }, - { - "description": "Represents a [`f64`].", - "type": "number", - "format": "double" - } - ] - }, - "Target": { - "description": "Platform target.", - "oneOf": [ - { - "description": "MacOS.", - "type": "string", - "enum": [ - "macOS" - ] - }, - { - "description": "Windows.", - "type": "string", - "enum": [ - "windows" - ] - }, - { - "description": "Linux.", - "type": "string", - "enum": [ - "linux" - ] - }, - { - "description": "Android.", - "type": "string", - "enum": [ - "android" - ] - }, - { - "description": "iOS.", - "type": "string", - "enum": [ - "iOS" - ] - } - ] - }, - "ShellScopeEntryAllowedArg": { - "description": "A command argument allowed to be executed by the webview API.", - "anyOf": [ - { - "description": "A non-configurable argument that is passed to the command in the order it was specified.", - "type": "string" - }, - { - "description": "A variable that is set while calling the command from the webview API.", - "type": "object", - "required": [ - "validator" - ], - "properties": { - "raw": { - "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.", - "default": false, - "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" - } - }, - "additionalProperties": false - } - ] - }, - "ShellScopeEntryAllowedArgs": { - "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.", - "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.", - "type": "array", - "items": { - "$ref": "#/definitions/ShellScopeEntryAllowedArg" - } - } - ] - } - } -} \ No newline at end of file diff --git a/src-tauri/gen/schemas/linux-schema.json b/src-tauri/gen/schemas/linux-schema.json deleted file mode 100644 index 8d0d785a..00000000 --- a/src-tauri/gen/schemas/linux-schema.json +++ /dev/null @@ -1,5186 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CapabilityFile", - "description": "Capability formats accepted in a capability file.", - "anyOf": [ - { - "description": "A single capability.", - "allOf": [ - { - "$ref": "#/definitions/Capability" - } - ] - }, - { - "description": "A list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - }, - { - "description": "A list of capabilities.", - "type": "object", - "required": [ - "capabilities" - ], - "properties": { - "capabilities": { - "description": "The list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - } - } - } - ], - "definitions": { - "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", - "type": "object", - "required": [ - "identifier", - "permissions" - ], - "properties": { - "identifier": { - "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", - "type": "string" - }, - "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", - "default": "", - "type": "string" - }, - "remote": { - "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", - "anyOf": [ - { - "$ref": "#/definitions/CapabilityRemote" - }, - { - "type": "null" - } - ] - }, - "local": { - "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", - "default": true, - "type": "boolean" - }, - "windows": { - "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "webviews": { - "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "permissions": { - "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionEntry" - }, - "uniqueItems": true - }, - "platforms": { - "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Target" - } - } - } - }, - "CapabilityRemote": { - "description": "Configuration for remote URLs that are associated with the capability.", - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PermissionEntry": { - "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", - "anyOf": [ - { - "description": "Reference a permission or permission set by identifier.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - { - "description": "Reference a permission or permission set by identifier and extends its scope.", - "type": "object", - "allOf": [ - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "FS scope path.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "FS scope path.", - "type": "string" - } - } - } - ] - } - }, - "deny": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "FS scope path.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "FS scope path.", - "type": "string" - } - } - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - }, - "deny": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - "allow": { - "description": "Data that defines what is allowed by the scope.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - }, - "deny": { - "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - } - } - } - ], - "required": [ - "identifier" - ] - } - ] - }, - "Identifier": { - "description": "Permission identifier", - "oneOf": [ - { - "description": "Allows reading the CLI matches", - "type": "string", - "const": "cli:default" - }, - { - "description": "Enables the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:allow-cli-matches" - }, - { - "description": "Denies the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:deny-cli-matches" - }, - { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", - "type": "string", - "const": "core:default" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:app:default" - }, - { - "description": "Enables the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-hide" - }, - { - "description": "Enables the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-show" - }, - { - "description": "Enables the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-default-window-icon" - }, - { - "description": "Enables the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-name" - }, - { - "description": "Enables the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-set-app-theme" - }, - { - "description": "Enables the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-tauri-version" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-version" - }, - { - "description": "Denies the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-hide" - }, - { - "description": "Denies the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-show" - }, - { - "description": "Denies the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-default-window-icon" - }, - { - "description": "Denies the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-name" - }, - { - "description": "Denies the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-set-app-theme" - }, - { - "description": "Denies the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-tauri-version" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-version" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:event:default" - }, - { - "description": "Enables the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit" - }, - { - "description": "Enables the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit-to" - }, - { - "description": "Enables the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-listen" - }, - { - "description": "Enables the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-unlisten" - }, - { - "description": "Denies the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit" - }, - { - "description": "Denies the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit-to" - }, - { - "description": "Denies the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-listen" - }, - { - "description": "Denies the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-unlisten" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:image:default" - }, - { - "description": "Enables the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-bytes" - }, - { - "description": "Enables the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-path" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-new" - }, - { - "description": "Enables the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-rgba" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-size" - }, - { - "description": "Denies the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-bytes" - }, - { - "description": "Denies the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-path" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-new" - }, - { - "description": "Denies the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-rgba" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:menu:default" - }, - { - "description": "Enables the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-append" - }, - { - "description": "Enables the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-create-default" - }, - { - "description": "Enables the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-get" - }, - { - "description": "Enables the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-insert" - }, - { - "description": "Enables the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-checked" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-enabled" - }, - { - "description": "Enables the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-items" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-new" - }, - { - "description": "Enables the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-popup" - }, - { - "description": "Enables the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-prepend" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove" - }, - { - "description": "Enables the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove-at" - }, - { - "description": "Enables the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-accelerator" - }, - { - "description": "Enables the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-app-menu" - }, - { - "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" - }, - { - "description": "Enables the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-window-menu" - }, - { - "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" - }, - { - "description": "Enables the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-checked" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-enabled" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-icon" - }, - { - "description": "Enables the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-text" - }, - { - "description": "Enables the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-text" - }, - { - "description": "Denies the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-append" - }, - { - "description": "Denies the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-create-default" - }, - { - "description": "Denies the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-get" - }, - { - "description": "Denies the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-insert" - }, - { - "description": "Denies the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-checked" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-enabled" - }, - { - "description": "Denies the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-items" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-new" - }, - { - "description": "Denies the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-popup" - }, - { - "description": "Denies the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-prepend" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove" - }, - { - "description": "Denies the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove-at" - }, - { - "description": "Denies the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-accelerator" - }, - { - "description": "Denies the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-app-menu" - }, - { - "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" - }, - { - "description": "Denies the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-window-menu" - }, - { - "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" - }, - { - "description": "Denies the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-checked" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-enabled" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-icon" - }, - { - "description": "Denies the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-text" - }, - { - "description": "Denies the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-text" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:path:default" - }, - { - "description": "Enables the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-basename" - }, - { - "description": "Enables the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-dirname" - }, - { - "description": "Enables the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-extname" - }, - { - "description": "Enables the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-is-absolute" - }, - { - "description": "Enables the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-join" - }, - { - "description": "Enables the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-normalize" - }, - { - "description": "Enables the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve" - }, - { - "description": "Enables the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve-directory" - }, - { - "description": "Denies the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-basename" - }, - { - "description": "Denies the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-dirname" - }, - { - "description": "Denies the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-extname" - }, - { - "description": "Denies the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-is-absolute" - }, - { - "description": "Denies the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-join" - }, - { - "description": "Denies the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-normalize" - }, - { - "description": "Denies the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve" - }, - { - "description": "Denies the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve-directory" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:resources:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:allow-close" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:deny-close" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:tray:default" - }, - { - "description": "Enables the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-get-by-id" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-new" - }, - { - "description": "Enables the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-remove-by-id" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon" - }, - { - "description": "Enables the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon-as-template" - }, - { - "description": "Enables the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-menu" - }, - { - "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" - }, - { - "description": "Enables the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-temp-dir-path" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-title" - }, - { - "description": "Enables the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-tooltip" - }, - { - "description": "Enables the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-visible" - }, - { - "description": "Denies the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-get-by-id" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-new" - }, - { - "description": "Denies the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-remove-by-id" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon" - }, - { - "description": "Denies the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon-as-template" - }, - { - "description": "Denies the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-menu" - }, - { - "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" - }, - { - "description": "Denies the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-temp-dir-path" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-title" - }, - { - "description": "Denies the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-tooltip" - }, - { - "description": "Denies the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-visible" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:webview:default" - }, - { - "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-clear-all-browsing-data" - }, - { - "description": "Enables the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview" - }, - { - "description": "Enables the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview-window" - }, - { - "description": "Enables the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-get-all-webviews" - }, - { - "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" - }, - { - "description": "Enables the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-print" - }, - { - "description": "Enables the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-reparent" - }, - { - "description": "Enables the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-focus" - }, - { - "description": "Enables the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-position" - }, - { - "description": "Enables the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-size" - }, - { - "description": "Enables the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-zoom" - }, - { - "description": "Enables the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-close" - }, - { - "description": "Enables the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-hide" - }, - { - "description": "Enables the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-position" - }, - { - "description": "Enables the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-show" - }, - { - "description": "Enables the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-size" - }, - { - "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-clear-all-browsing-data" - }, - { - "description": "Denies the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview" - }, - { - "description": "Denies the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview-window" - }, - { - "description": "Denies the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-get-all-webviews" - }, - { - "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" - }, - { - "description": "Denies the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-print" - }, - { - "description": "Denies the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-reparent" - }, - { - "description": "Denies the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-focus" - }, - { - "description": "Denies the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-position" - }, - { - "description": "Denies the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-size" - }, - { - "description": "Denies the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-zoom" - }, - { - "description": "Denies the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-close" - }, - { - "description": "Denies the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-hide" - }, - { - "description": "Denies the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-position" - }, - { - "description": "Denies the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-show" - }, - { - "description": "Denies the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:window:default" - }, - { - "description": "Enables the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-available-monitors" - }, - { - "description": "Enables the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-center" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-close" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-create" - }, - { - "description": "Enables the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-current-monitor" - }, - { - "description": "Enables the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-cursor-position" - }, - { - "description": "Enables the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-destroy" - }, - { - "description": "Enables the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-get-all-windows" - }, - { - "description": "Enables the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-hide" - }, - { - "description": "Enables the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-position" - }, - { - "description": "Enables the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-size" - }, - { - "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-internal-toggle-maximize" - }, - { - "description": "Enables the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-closable" - }, - { - "description": "Enables the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-decorated" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-enabled" - }, - { - "description": "Enables the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-focused" - }, - { - "description": "Enables the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-fullscreen" - }, - { - "description": "Enables the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximizable" - }, - { - "description": "Enables the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximized" - }, - { - "description": "Enables the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimizable" - }, - { - "description": "Enables the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimized" - }, - { - "description": "Enables the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-resizable" - }, - { - "description": "Enables the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-visible" - }, - { - "description": "Enables the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-maximize" - }, - { - "description": "Enables the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-minimize" - }, - { - "description": "Enables the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-monitor-from-point" - }, - { - "description": "Enables the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-position" - }, - { - "description": "Enables the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-size" - }, - { - "description": "Enables the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-primary-monitor" - }, - { - "description": "Enables the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-request-user-attention" - }, - { - "description": "Enables the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-scale-factor" - }, - { - "description": "Enables the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-bottom" - }, - { - "description": "Enables the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-top" - }, - { - "description": "Enables the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-closable" - }, - { - "description": "Enables the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-content-protected" - }, - { - "description": "Enables the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-grab" - }, - { - "description": "Enables the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-icon" - }, - { - "description": "Enables the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-position" - }, - { - "description": "Enables the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-visible" - }, - { - "description": "Enables the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-decorations" - }, - { - "description": "Enables the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-effects" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-enabled" - }, - { - "description": "Enables the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-focus" - }, - { - "description": "Enables the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-fullscreen" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-icon" - }, - { - "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" - }, - { - "description": "Enables the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-max-size" - }, - { - "description": "Enables the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-maximizable" - }, - { - "description": "Enables the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-min-size" - }, - { - "description": "Enables the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-minimizable" - }, - { - "description": "Enables the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-position" - }, - { - "description": "Enables the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-progress-bar" - }, - { - "description": "Enables the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-resizable" - }, - { - "description": "Enables the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-shadow" - }, - { - "description": "Enables the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size" - }, - { - "description": "Enables the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size-constraints" - }, - { - "description": "Enables the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-skip-taskbar" - }, - { - "description": "Enables the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-theme" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title" - }, - { - "description": "Enables the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title-bar-style" - }, - { - "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" - }, - { - "description": "Enables the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-show" - }, - { - "description": "Enables the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-dragging" - }, - { - "description": "Enables the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-resize-dragging" - }, - { - "description": "Enables the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-theme" - }, - { - "description": "Enables the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-title" - }, - { - "description": "Enables the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-toggle-maximize" - }, - { - "description": "Enables the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unmaximize" - }, - { - "description": "Enables the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unminimize" - }, - { - "description": "Denies the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-available-monitors" - }, - { - "description": "Denies the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-center" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-close" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-create" - }, - { - "description": "Denies the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-current-monitor" - }, - { - "description": "Denies the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-cursor-position" - }, - { - "description": "Denies the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-destroy" - }, - { - "description": "Denies the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-get-all-windows" - }, - { - "description": "Denies the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-hide" - }, - { - "description": "Denies the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-position" - }, - { - "description": "Denies the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-size" - }, - { - "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-internal-toggle-maximize" - }, - { - "description": "Denies the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-closable" - }, - { - "description": "Denies the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-decorated" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-enabled" - }, - { - "description": "Denies the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-focused" - }, - { - "description": "Denies the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-fullscreen" - }, - { - "description": "Denies the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximizable" - }, - { - "description": "Denies the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximized" - }, - { - "description": "Denies the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimizable" - }, - { - "description": "Denies the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimized" - }, - { - "description": "Denies the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-resizable" - }, - { - "description": "Denies the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-visible" - }, - { - "description": "Denies the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-maximize" - }, - { - "description": "Denies the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-minimize" - }, - { - "description": "Denies the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-monitor-from-point" - }, - { - "description": "Denies the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-position" - }, - { - "description": "Denies the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-size" - }, - { - "description": "Denies the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-primary-monitor" - }, - { - "description": "Denies the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-request-user-attention" - }, - { - "description": "Denies the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-scale-factor" - }, - { - "description": "Denies the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-bottom" - }, - { - "description": "Denies the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-top" - }, - { - "description": "Denies the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-closable" - }, - { - "description": "Denies the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-content-protected" - }, - { - "description": "Denies the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-grab" - }, - { - "description": "Denies the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-icon" - }, - { - "description": "Denies the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-position" - }, - { - "description": "Denies the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-visible" - }, - { - "description": "Denies the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-decorations" - }, - { - "description": "Denies the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-effects" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-enabled" - }, - { - "description": "Denies the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-focus" - }, - { - "description": "Denies the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-fullscreen" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-icon" - }, - { - "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" - }, - { - "description": "Denies the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-max-size" - }, - { - "description": "Denies the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-maximizable" - }, - { - "description": "Denies the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-min-size" - }, - { - "description": "Denies the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-minimizable" - }, - { - "description": "Denies the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-position" - }, - { - "description": "Denies the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-progress-bar" - }, - { - "description": "Denies the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-resizable" - }, - { - "description": "Denies the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-shadow" - }, - { - "description": "Denies the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size" - }, - { - "description": "Denies the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size-constraints" - }, - { - "description": "Denies the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-skip-taskbar" - }, - { - "description": "Denies the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-theme" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title" - }, - { - "description": "Denies the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title-bar-style" - }, - { - "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" - }, - { - "description": "Denies the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-show" - }, - { - "description": "Denies the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-dragging" - }, - { - "description": "Denies the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-resize-dragging" - }, - { - "description": "Denies the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-theme" - }, - { - "description": "Denies the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-title" - }, - { - "description": "Denies the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-toggle-maximize" - }, - { - "description": "Denies the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unmaximize" - }, - { - "description": "Denies the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unminimize" - }, - { - "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", - "type": "string", - "const": "dialog:default" - }, - { - "description": "Enables the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-ask" - }, - { - "description": "Enables the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-confirm" - }, - { - "description": "Enables the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-message" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-open" - }, - { - "description": "Enables the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-save" - }, - { - "description": "Denies the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-ask" - }, - { - "description": "Denies the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-confirm" - }, - { - "description": "Denies the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-message" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-open" - }, - { - "description": "Denies the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-save" - }, - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - }, - { - "description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n", - "type": "string", - "const": "os:default" - }, - { - "description": "Enables the arch command without any pre-configured scope.", - "type": "string", - "const": "os:allow-arch" - }, - { - "description": "Enables the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:allow-exe-extension" - }, - { - "description": "Enables the family command without any pre-configured scope.", - "type": "string", - "const": "os:allow-family" - }, - { - "description": "Enables the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:allow-hostname" - }, - { - "description": "Enables the locale command without any pre-configured scope.", - "type": "string", - "const": "os:allow-locale" - }, - { - "description": "Enables the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:allow-os-type" - }, - { - "description": "Enables the platform command without any pre-configured scope.", - "type": "string", - "const": "os:allow-platform" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "os:allow-version" - }, - { - "description": "Denies the arch command without any pre-configured scope.", - "type": "string", - "const": "os:deny-arch" - }, - { - "description": "Denies the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:deny-exe-extension" - }, - { - "description": "Denies the family command without any pre-configured scope.", - "type": "string", - "const": "os:deny-family" - }, - { - "description": "Denies the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:deny-hostname" - }, - { - "description": "Denies the locale command without any pre-configured scope.", - "type": "string", - "const": "os:deny-locale" - }, - { - "description": "Denies the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:deny-os-type" - }, - { - "description": "Denies the platform command without any pre-configured scope.", - "type": "string", - "const": "os:deny-platform" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "os:deny-version" - }, - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - }, - { - "description": "### Default Permissions\n\nThis permission set configures what kind of\ndatabase operations are available from the sql plugin.\n\n### Granted Permissions\n\nAll reading related operations are enabled.\nAlso allows to load or close a connection.\n\n", - "type": "string", - "const": "sql:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-close" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-execute" - }, - { - "description": "Enables the load command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-load" - }, - { - "description": "Enables the select command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-select" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-close" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-execute" - }, - { - "description": "Denies the load command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-load" - }, - { - "description": "Denies the select command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-select" - } - ] - }, - "Value": { - "description": "All supported ACL values.", - "anyOf": [ - { - "description": "Represents a null JSON value.", - "type": "null" - }, - { - "description": "Represents a [`bool`].", - "type": "boolean" - }, - { - "description": "Represents a valid ACL [`Number`].", - "allOf": [ - { - "$ref": "#/definitions/Number" - } - ] - }, - { - "description": "Represents a [`String`].", - "type": "string" - }, - { - "description": "Represents a list of other [`Value`]s.", - "type": "array", - "items": { - "$ref": "#/definitions/Value" - } - }, - { - "description": "Represents a map of [`String`] keys to [`Value`]s.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Value" - } - } - ] - }, - "Number": { - "description": "A valid ACL number.", - "anyOf": [ - { - "description": "Represents an [`i64`].", - "type": "integer", - "format": "int64" - }, - { - "description": "Represents a [`f64`].", - "type": "number", - "format": "double" - } - ] - }, - "Target": { - "description": "Platform target.", - "oneOf": [ - { - "description": "MacOS.", - "type": "string", - "enum": [ - "macOS" - ] - }, - { - "description": "Windows.", - "type": "string", - "enum": [ - "windows" - ] - }, - { - "description": "Linux.", - "type": "string", - "enum": [ - "linux" - ] - }, - { - "description": "Android.", - "type": "string", - "enum": [ - "android" - ] - }, - { - "description": "iOS.", - "type": "string", - "enum": [ - "iOS" - ] - } - ] - }, - "ShellScopeEntryAllowedArg": { - "description": "A command argument allowed to be executed by the webview API.", - "anyOf": [ - { - "description": "A non-configurable argument that is passed to the command in the order it was specified.", - "type": "string" - }, - { - "description": "A variable that is set while calling the command from the webview API.", - "type": "object", - "required": [ - "validator" - ], - "properties": { - "raw": { - "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.", - "default": false, - "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" - } - }, - "additionalProperties": false - } - ] - }, - "ShellScopeEntryAllowedArgs": { - "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.", - "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.", - "type": "array", - "items": { - "$ref": "#/definitions/ShellScopeEntryAllowedArg" - } - } - ] - } - } -} \ No newline at end of file diff --git a/src-tauri/gen/schemas/macOS-schema.json b/src-tauri/gen/schemas/macOS-schema.json deleted file mode 100644 index fd6f55d9..00000000 --- a/src-tauri/gen/schemas/macOS-schema.json +++ /dev/null @@ -1,5256 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CapabilityFile", - "description": "Capability formats accepted in a capability file.", - "anyOf": [ - { - "description": "A single capability.", - "allOf": [ - { - "$ref": "#/definitions/Capability" - } - ] - }, - { - "description": "A list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - }, - { - "description": "A list of capabilities.", - "type": "object", - "required": [ - "capabilities" - ], - "properties": { - "capabilities": { - "description": "The list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - } - } - } - ], - "definitions": { - "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, ], \"platforms\": [\"macOS\",\"windows\"] } ```", - "type": "object", - "required": [ - "identifier", - "permissions" - ], - "properties": { - "identifier": { - "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", - "type": "string" - }, - "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", - "default": "", - "type": "string" - }, - "remote": { - "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", - "anyOf": [ - { - "$ref": "#/definitions/CapabilityRemote" - }, - { - "type": "null" - } - ] - }, - "local": { - "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", - "default": true, - "type": "boolean" - }, - "windows": { - "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "webviews": { - "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "permissions": { - "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ] ```", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionEntry" - }, - "uniqueItems": true - }, - "platforms": { - "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Target" - } - } - } - }, - "CapabilityRemote": { - "description": "Configuration for remote URLs that are associated with the capability.", - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PermissionEntry": { - "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", - "anyOf": [ - { - "description": "Reference a permission or permission set by identifier.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - { - "description": "Reference a permission or permission set by identifier and extends its scope.", - "type": "object", - "allOf": [ - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-size" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-size" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - } - } - } - ] - } - }, - "deny": { - "items": { - "title": "FsScopeEntry", - "description": "FS scope entry.", - "anyOf": [ - { - "description": "A path that can be accessed by the webview when using the fs APIs. FS scope path pattern.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - }, - { - "type": "object", - "required": [ - "path" - ], - "properties": { - "path": { - "description": "A path that can be accessed by the webview when using the fs APIs.\n\nThe pattern can start with a variable that resolves to a system base directory. The variables are: `$AUDIO`, `$CACHE`, `$CONFIG`, `$DATA`, `$LOCALDATA`, `$DESKTOP`, `$DOCUMENT`, `$DOWNLOAD`, `$EXE`, `$FONT`, `$HOME`, `$PICTURE`, `$PUBLIC`, `$RUNTIME`, `$TEMPLATE`, `$VIDEO`, `$RESOURCE`, `$APP`, `$LOG`, `$TEMP`, `$APPCONFIG`, `$APPDATA`, `$APPLOCALDATA`, `$APPCACHE`, `$APPLOG`.", - "type": "string" - } - } - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - }, - "deny": { - "items": { - "title": "ShellScopeEntry", - "description": "Shell scope entry.", - "anyOf": [ - { - "type": "object", - "required": [ - "cmd", - "name" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - }, - { - "type": "object", - "required": [ - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellScopeEntryAllowedArgs" - } - ] - }, - "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" - } - }, - "additionalProperties": false - } - ] - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - "allow": { - "description": "Data that defines what is allowed by the scope.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - }, - "deny": { - "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - } - } - } - ], - "required": [ - "identifier" - ] - } - ] - }, - "Identifier": { - "description": "Permission identifier", - "oneOf": [ - { - "description": "Allows reading the CLI matches", - "type": "string", - "const": "cli:default" - }, - { - "description": "Enables the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:allow-cli-matches" - }, - { - "description": "Denies the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:deny-cli-matches" - }, - { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", - "type": "string", - "const": "core:default" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:app:default" - }, - { - "description": "Enables the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-hide" - }, - { - "description": "Enables the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-show" - }, - { - "description": "Enables the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-default-window-icon" - }, - { - "description": "Enables the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-name" - }, - { - "description": "Enables the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-set-app-theme" - }, - { - "description": "Enables the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-tauri-version" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-version" - }, - { - "description": "Denies the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-hide" - }, - { - "description": "Denies the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-show" - }, - { - "description": "Denies the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-default-window-icon" - }, - { - "description": "Denies the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-name" - }, - { - "description": "Denies the set_app_theme command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-set-app-theme" - }, - { - "description": "Denies the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-tauri-version" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-version" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:event:default" - }, - { - "description": "Enables the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit" - }, - { - "description": "Enables the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit-to" - }, - { - "description": "Enables the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-listen" - }, - { - "description": "Enables the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-unlisten" - }, - { - "description": "Denies the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit" - }, - { - "description": "Denies the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit-to" - }, - { - "description": "Denies the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-listen" - }, - { - "description": "Denies the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-unlisten" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:image:default" - }, - { - "description": "Enables the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-bytes" - }, - { - "description": "Enables the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-path" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-new" - }, - { - "description": "Enables the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-rgba" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-size" - }, - { - "description": "Denies the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-bytes" - }, - { - "description": "Denies the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-path" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-new" - }, - { - "description": "Denies the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-rgba" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:menu:default" - }, - { - "description": "Enables the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-append" - }, - { - "description": "Enables the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-create-default" - }, - { - "description": "Enables the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-get" - }, - { - "description": "Enables the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-insert" - }, - { - "description": "Enables the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-checked" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-enabled" - }, - { - "description": "Enables the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-items" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-new" - }, - { - "description": "Enables the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-popup" - }, - { - "description": "Enables the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-prepend" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove" - }, - { - "description": "Enables the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove-at" - }, - { - "description": "Enables the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-accelerator" - }, - { - "description": "Enables the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-app-menu" - }, - { - "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" - }, - { - "description": "Enables the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-window-menu" - }, - { - "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" - }, - { - "description": "Enables the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-checked" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-enabled" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-icon" - }, - { - "description": "Enables the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-text" - }, - { - "description": "Enables the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-text" - }, - { - "description": "Denies the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-append" - }, - { - "description": "Denies the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-create-default" - }, - { - "description": "Denies the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-get" - }, - { - "description": "Denies the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-insert" - }, - { - "description": "Denies the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-checked" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-enabled" - }, - { - "description": "Denies the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-items" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-new" - }, - { - "description": "Denies the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-popup" - }, - { - "description": "Denies the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-prepend" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove" - }, - { - "description": "Denies the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove-at" - }, - { - "description": "Denies the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-accelerator" - }, - { - "description": "Denies the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-app-menu" - }, - { - "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" - }, - { - "description": "Denies the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-window-menu" - }, - { - "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" - }, - { - "description": "Denies the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-checked" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-enabled" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-icon" - }, - { - "description": "Denies the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-text" - }, - { - "description": "Denies the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-text" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:path:default" - }, - { - "description": "Enables the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-basename" - }, - { - "description": "Enables the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-dirname" - }, - { - "description": "Enables the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-extname" - }, - { - "description": "Enables the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-is-absolute" - }, - { - "description": "Enables the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-join" - }, - { - "description": "Enables the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-normalize" - }, - { - "description": "Enables the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve" - }, - { - "description": "Enables the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve-directory" - }, - { - "description": "Denies the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-basename" - }, - { - "description": "Denies the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-dirname" - }, - { - "description": "Denies the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-extname" - }, - { - "description": "Denies the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-is-absolute" - }, - { - "description": "Denies the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-join" - }, - { - "description": "Denies the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-normalize" - }, - { - "description": "Denies the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve" - }, - { - "description": "Denies the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve-directory" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:resources:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:allow-close" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:deny-close" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:tray:default" - }, - { - "description": "Enables the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-get-by-id" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-new" - }, - { - "description": "Enables the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-remove-by-id" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon" - }, - { - "description": "Enables the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon-as-template" - }, - { - "description": "Enables the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-menu" - }, - { - "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" - }, - { - "description": "Enables the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-temp-dir-path" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-title" - }, - { - "description": "Enables the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-tooltip" - }, - { - "description": "Enables the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-visible" - }, - { - "description": "Denies the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-get-by-id" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-new" - }, - { - "description": "Denies the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-remove-by-id" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon" - }, - { - "description": "Denies the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon-as-template" - }, - { - "description": "Denies the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-menu" - }, - { - "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" - }, - { - "description": "Denies the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-temp-dir-path" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-title" - }, - { - "description": "Denies the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-tooltip" - }, - { - "description": "Denies the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-visible" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:webview:default" - }, - { - "description": "Enables the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-clear-all-browsing-data" - }, - { - "description": "Enables the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview" - }, - { - "description": "Enables the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview-window" - }, - { - "description": "Enables the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-get-all-webviews" - }, - { - "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" - }, - { - "description": "Enables the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-print" - }, - { - "description": "Enables the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-reparent" - }, - { - "description": "Enables the set_webview_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-background-color" - }, - { - "description": "Enables the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-focus" - }, - { - "description": "Enables the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-position" - }, - { - "description": "Enables the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-size" - }, - { - "description": "Enables the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-zoom" - }, - { - "description": "Enables the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-close" - }, - { - "description": "Enables the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-hide" - }, - { - "description": "Enables the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-position" - }, - { - "description": "Enables the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-show" - }, - { - "description": "Enables the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-size" - }, - { - "description": "Denies the clear_all_browsing_data command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-clear-all-browsing-data" - }, - { - "description": "Denies the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview" - }, - { - "description": "Denies the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview-window" - }, - { - "description": "Denies the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-get-all-webviews" - }, - { - "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" - }, - { - "description": "Denies the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-print" - }, - { - "description": "Denies the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-reparent" - }, - { - "description": "Denies the set_webview_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-background-color" - }, - { - "description": "Denies the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-focus" - }, - { - "description": "Denies the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-position" - }, - { - "description": "Denies the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-size" - }, - { - "description": "Denies the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-zoom" - }, - { - "description": "Denies the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-close" - }, - { - "description": "Denies the webview_hide command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-hide" - }, - { - "description": "Denies the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-position" - }, - { - "description": "Denies the webview_show command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-show" - }, - { - "description": "Denies the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:window:default" - }, - { - "description": "Enables the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-available-monitors" - }, - { - "description": "Enables the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-center" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-close" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-create" - }, - { - "description": "Enables the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-current-monitor" - }, - { - "description": "Enables the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-cursor-position" - }, - { - "description": "Enables the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-destroy" - }, - { - "description": "Enables the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-get-all-windows" - }, - { - "description": "Enables the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-hide" - }, - { - "description": "Enables the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-position" - }, - { - "description": "Enables the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-size" - }, - { - "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-internal-toggle-maximize" - }, - { - "description": "Enables the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-closable" - }, - { - "description": "Enables the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-decorated" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-enabled" - }, - { - "description": "Enables the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-focused" - }, - { - "description": "Enables the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-fullscreen" - }, - { - "description": "Enables the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximizable" - }, - { - "description": "Enables the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximized" - }, - { - "description": "Enables the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimizable" - }, - { - "description": "Enables the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimized" - }, - { - "description": "Enables the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-resizable" - }, - { - "description": "Enables the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-visible" - }, - { - "description": "Enables the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-maximize" - }, - { - "description": "Enables the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-minimize" - }, - { - "description": "Enables the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-monitor-from-point" - }, - { - "description": "Enables the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-position" - }, - { - "description": "Enables the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-size" - }, - { - "description": "Enables the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-primary-monitor" - }, - { - "description": "Enables the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-request-user-attention" - }, - { - "description": "Enables the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-scale-factor" - }, - { - "description": "Enables the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-bottom" - }, - { - "description": "Enables the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-top" - }, - { - "description": "Enables the set_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-background-color" - }, - { - "description": "Enables the set_badge_count command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-badge-count" - }, - { - "description": "Enables the set_badge_label command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-badge-label" - }, - { - "description": "Enables the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-closable" - }, - { - "description": "Enables the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-content-protected" - }, - { - "description": "Enables the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-grab" - }, - { - "description": "Enables the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-icon" - }, - { - "description": "Enables the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-position" - }, - { - "description": "Enables the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-visible" - }, - { - "description": "Enables the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-decorations" - }, - { - "description": "Enables the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-effects" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-enabled" - }, - { - "description": "Enables the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-focus" - }, - { - "description": "Enables the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-fullscreen" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-icon" - }, - { - "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" - }, - { - "description": "Enables the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-max-size" - }, - { - "description": "Enables the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-maximizable" - }, - { - "description": "Enables the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-min-size" - }, - { - "description": "Enables the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-minimizable" - }, - { - "description": "Enables the set_overlay_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-overlay-icon" - }, - { - "description": "Enables the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-position" - }, - { - "description": "Enables the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-progress-bar" - }, - { - "description": "Enables the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-resizable" - }, - { - "description": "Enables the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-shadow" - }, - { - "description": "Enables the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size" - }, - { - "description": "Enables the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size-constraints" - }, - { - "description": "Enables the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-skip-taskbar" - }, - { - "description": "Enables the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-theme" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title" - }, - { - "description": "Enables the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title-bar-style" - }, - { - "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" - }, - { - "description": "Enables the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-show" - }, - { - "description": "Enables the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-dragging" - }, - { - "description": "Enables the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-resize-dragging" - }, - { - "description": "Enables the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-theme" - }, - { - "description": "Enables the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-title" - }, - { - "description": "Enables the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-toggle-maximize" - }, - { - "description": "Enables the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unmaximize" - }, - { - "description": "Enables the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unminimize" - }, - { - "description": "Denies the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-available-monitors" - }, - { - "description": "Denies the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-center" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-close" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-create" - }, - { - "description": "Denies the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-current-monitor" - }, - { - "description": "Denies the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-cursor-position" - }, - { - "description": "Denies the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-destroy" - }, - { - "description": "Denies the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-get-all-windows" - }, - { - "description": "Denies the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-hide" - }, - { - "description": "Denies the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-position" - }, - { - "description": "Denies the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-size" - }, - { - "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-internal-toggle-maximize" - }, - { - "description": "Denies the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-closable" - }, - { - "description": "Denies the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-decorated" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-enabled" - }, - { - "description": "Denies the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-focused" - }, - { - "description": "Denies the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-fullscreen" - }, - { - "description": "Denies the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximizable" - }, - { - "description": "Denies the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximized" - }, - { - "description": "Denies the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimizable" - }, - { - "description": "Denies the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimized" - }, - { - "description": "Denies the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-resizable" - }, - { - "description": "Denies the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-visible" - }, - { - "description": "Denies the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-maximize" - }, - { - "description": "Denies the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-minimize" - }, - { - "description": "Denies the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-monitor-from-point" - }, - { - "description": "Denies the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-position" - }, - { - "description": "Denies the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-size" - }, - { - "description": "Denies the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-primary-monitor" - }, - { - "description": "Denies the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-request-user-attention" - }, - { - "description": "Denies the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-scale-factor" - }, - { - "description": "Denies the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-bottom" - }, - { - "description": "Denies the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-top" - }, - { - "description": "Denies the set_background_color command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-background-color" - }, - { - "description": "Denies the set_badge_count command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-badge-count" - }, - { - "description": "Denies the set_badge_label command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-badge-label" - }, - { - "description": "Denies the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-closable" - }, - { - "description": "Denies the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-content-protected" - }, - { - "description": "Denies the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-grab" - }, - { - "description": "Denies the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-icon" - }, - { - "description": "Denies the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-position" - }, - { - "description": "Denies the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-visible" - }, - { - "description": "Denies the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-decorations" - }, - { - "description": "Denies the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-effects" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-enabled" - }, - { - "description": "Denies the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-focus" - }, - { - "description": "Denies the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-fullscreen" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-icon" - }, - { - "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" - }, - { - "description": "Denies the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-max-size" - }, - { - "description": "Denies the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-maximizable" - }, - { - "description": "Denies the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-min-size" - }, - { - "description": "Denies the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-minimizable" - }, - { - "description": "Denies the set_overlay_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-overlay-icon" - }, - { - "description": "Denies the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-position" - }, - { - "description": "Denies the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-progress-bar" - }, - { - "description": "Denies the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-resizable" - }, - { - "description": "Denies the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-shadow" - }, - { - "description": "Denies the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size" - }, - { - "description": "Denies the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size-constraints" - }, - { - "description": "Denies the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-skip-taskbar" - }, - { - "description": "Denies the set_theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-theme" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title" - }, - { - "description": "Denies the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title-bar-style" - }, - { - "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" - }, - { - "description": "Denies the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-show" - }, - { - "description": "Denies the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-dragging" - }, - { - "description": "Denies the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-resize-dragging" - }, - { - "description": "Denies the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-theme" - }, - { - "description": "Denies the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-title" - }, - { - "description": "Denies the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-toggle-maximize" - }, - { - "description": "Denies the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unmaximize" - }, - { - "description": "Denies the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unminimize" - }, - { - "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", - "type": "string", - "const": "dialog:default" - }, - { - "description": "Enables the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-ask" - }, - { - "description": "Enables the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-confirm" - }, - { - "description": "Enables the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-message" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-open" - }, - { - "description": "Enables the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-save" - }, - { - "description": "Denies the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-ask" - }, - { - "description": "Denies the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-confirm" - }, - { - "description": "Denies the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-message" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-open" - }, - { - "description": "Denies the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-save" - }, - { - "description": "This set of permissions describes the what kind of\nfile system access the `fs` plugin has enabled or denied by default.\n\n#### Granted Permissions\n\nThis default permission set enables read access to the\napplication specific directories (AppConfig, AppData, AppLocalData, AppCache,\nAppLog) and all files and sub directories created in it.\nThe location of these directories depends on the operating system,\nwhere the application is run.\n\nIn general these directories need to be manually created\nby the application at runtime, before accessing files or folders\nin it is possible.\n\nTherefore, it is also allowed to create all of these folders via\nthe `mkdir` command.\n\n#### Denied Permissions\n\nThis default permission set prevents access to critical components\nof the Tauri application by default.\nOn Windows the webview data folder access is denied.\n\n#### Included permissions within this default permission set:\n", - "type": "string", - "const": "fs:default" - }, - { - "description": "This allows non-recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta" - }, - { - "description": "This allows full recursive read access to metadata of the application folders, including file listing and statistics.", - "type": "string", - "const": "fs:allow-app-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the application folders.", - "type": "string", - "const": "fs:allow-app-read" - }, - { - "description": "This allows full recursive read access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-read-recursive" - }, - { - "description": "This allows non-recursive write access to the application folders.", - "type": "string", - "const": "fs:allow-app-write" - }, - { - "description": "This allows full recursive write access to the complete application folders, files and subdirectories.", - "type": "string", - "const": "fs:allow-app-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appcache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCACHE` folder.", - "type": "string", - "const": "fs:allow-appcache-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appcache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPCONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appconfig-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:allow-appconfig-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPCONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appconfig-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-appdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPDATA` folder.", - "type": "string", - "const": "fs:allow-appdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-appdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applocaldata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:allow-applocaldata-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applocaldata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$APPLOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-applog-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-read" - }, - { - "description": "This allows full recursive read access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$APPLOG` folder.", - "type": "string", - "const": "fs:allow-applog-write" - }, - { - "description": "This allows full recursive write access to the complete `$APPLOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-applog-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$AUDIO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-audio-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-read" - }, - { - "description": "This allows full recursive read access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$AUDIO` folder.", - "type": "string", - "const": "fs:allow-audio-write" - }, - { - "description": "This allows full recursive write access to the complete `$AUDIO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-audio-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CACHE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-cache-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-read" - }, - { - "description": "This allows full recursive read access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CACHE` folder.", - "type": "string", - "const": "fs:allow-cache-write" - }, - { - "description": "This allows full recursive write access to the complete `$CACHE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-cache-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$CONFIG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-config-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-read" - }, - { - "description": "This allows full recursive read access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$CONFIG` folder.", - "type": "string", - "const": "fs:allow-config-write" - }, - { - "description": "This allows full recursive write access to the complete `$CONFIG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-config-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-data-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-read" - }, - { - "description": "This allows full recursive read access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DATA` folder.", - "type": "string", - "const": "fs:allow-data-write" - }, - { - "description": "This allows full recursive write access to the complete `$DATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-data-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DESKTOP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-desktop-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-read" - }, - { - "description": "This allows full recursive read access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DESKTOP` folder.", - "type": "string", - "const": "fs:allow-desktop-write" - }, - { - "description": "This allows full recursive write access to the complete `$DESKTOP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-desktop-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOCUMENT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-document-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:allow-document-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOCUMENT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-document-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$DOWNLOAD` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-download-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-read" - }, - { - "description": "This allows full recursive read access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:allow-download-write" - }, - { - "description": "This allows full recursive write access to the complete `$DOWNLOAD` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-download-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$EXE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-exe-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-read" - }, - { - "description": "This allows full recursive read access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$EXE` folder.", - "type": "string", - "const": "fs:allow-exe-write" - }, - { - "description": "This allows full recursive write access to the complete `$EXE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-exe-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$FONT` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-font-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-read" - }, - { - "description": "This allows full recursive read access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$FONT` folder.", - "type": "string", - "const": "fs:allow-font-write" - }, - { - "description": "This allows full recursive write access to the complete `$FONT` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-font-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$HOME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-home-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-read" - }, - { - "description": "This allows full recursive read access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$HOME` folder.", - "type": "string", - "const": "fs:allow-home-write" - }, - { - "description": "This allows full recursive write access to the complete `$HOME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-home-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOCALDATA` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-localdata-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:allow-localdata-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOCALDATA` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-localdata-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$LOG` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-log-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-read" - }, - { - "description": "This allows full recursive read access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$LOG` folder.", - "type": "string", - "const": "fs:allow-log-write" - }, - { - "description": "This allows full recursive write access to the complete `$LOG` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-log-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PICTURE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-picture-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-read" - }, - { - "description": "This allows full recursive read access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PICTURE` folder.", - "type": "string", - "const": "fs:allow-picture-write" - }, - { - "description": "This allows full recursive write access to the complete `$PICTURE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-picture-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$PUBLIC` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-public-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-read" - }, - { - "description": "This allows full recursive read access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$PUBLIC` folder.", - "type": "string", - "const": "fs:allow-public-write" - }, - { - "description": "This allows full recursive write access to the complete `$PUBLIC` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-public-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RESOURCE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-resource-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-read" - }, - { - "description": "This allows full recursive read access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RESOURCE` folder.", - "type": "string", - "const": "fs:allow-resource-write" - }, - { - "description": "This allows full recursive write access to the complete `$RESOURCE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-resource-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$RUNTIME` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-runtime-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-read" - }, - { - "description": "This allows full recursive read access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$RUNTIME` folder.", - "type": "string", - "const": "fs:allow-runtime-write" - }, - { - "description": "This allows full recursive write access to the complete `$RUNTIME` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-runtime-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMP` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-temp-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMP` folder.", - "type": "string", - "const": "fs:allow-temp-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMP` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-temp-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$TEMPLATE` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-template-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-read" - }, - { - "description": "This allows full recursive read access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:allow-template-write" - }, - { - "description": "This allows full recursive write access to the complete `$TEMPLATE` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-template-write-recursive" - }, - { - "description": "This allows non-recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta" - }, - { - "description": "This allows full recursive read access to metadata of the `$VIDEO` folder, including file listing and statistics.", - "type": "string", - "const": "fs:allow-video-meta-recursive" - }, - { - "description": "This allows non-recursive read access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-read" - }, - { - "description": "This allows full recursive read access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-read-recursive" - }, - { - "description": "This allows non-recursive write access to the `$VIDEO` folder.", - "type": "string", - "const": "fs:allow-video-write" - }, - { - "description": "This allows full recursive write access to the complete `$VIDEO` folder, files and subdirectories.", - "type": "string", - "const": "fs:allow-video-write-recursive" - }, - { - "description": "This denies access to dangerous Tauri relevant files and folders by default.", - "type": "string", - "const": "fs:deny-default" - }, - { - "description": "Enables the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-copy-file" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-create" - }, - { - "description": "Enables the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-exists" - }, - { - "description": "Enables the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-fstat" - }, - { - "description": "Enables the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-ftruncate" - }, - { - "description": "Enables the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-lstat" - }, - { - "description": "Enables the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-mkdir" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-open" - }, - { - "description": "Enables the read command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read" - }, - { - "description": "Enables the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-dir" - }, - { - "description": "Enables the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-file" - }, - { - "description": "Enables the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file" - }, - { - "description": "Enables the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines" - }, - { - "description": "Enables the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-read-text-file-lines-next" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-remove" - }, - { - "description": "Enables the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-rename" - }, - { - "description": "Enables the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-seek" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-size" - }, - { - "description": "Enables the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-stat" - }, - { - "description": "Enables the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-truncate" - }, - { - "description": "Enables the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-unwatch" - }, - { - "description": "Enables the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-watch" - }, - { - "description": "Enables the write command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write" - }, - { - "description": "Enables the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-file" - }, - { - "description": "Enables the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:allow-write-text-file" - }, - { - "description": "This permissions allows to create the application specific directories.\n", - "type": "string", - "const": "fs:create-app-specific-dirs" - }, - { - "description": "Denies the copy_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-copy-file" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-create" - }, - { - "description": "Denies the exists command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-exists" - }, - { - "description": "Denies the fstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-fstat" - }, - { - "description": "Denies the ftruncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-ftruncate" - }, - { - "description": "Denies the lstat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-lstat" - }, - { - "description": "Denies the mkdir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-mkdir" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-open" - }, - { - "description": "Denies the read command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read" - }, - { - "description": "Denies the read_dir command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-dir" - }, - { - "description": "Denies the read_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-file" - }, - { - "description": "Denies the read_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file" - }, - { - "description": "Denies the read_text_file_lines command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines" - }, - { - "description": "Denies the read_text_file_lines_next command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-read-text-file-lines-next" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-remove" - }, - { - "description": "Denies the rename command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-rename" - }, - { - "description": "Denies the seek command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-seek" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-size" - }, - { - "description": "Denies the stat command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-stat" - }, - { - "description": "Denies the truncate command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-truncate" - }, - { - "description": "Denies the unwatch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-unwatch" - }, - { - "description": "Denies the watch command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-watch" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA` folder on linux as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-linux" - }, - { - "description": "This denies read access to the\n`$APPLOCALDATA/EBWebView` folder on windows as the webview data and configuration values are stored here.\nAllowing access can lead to sensitive information disclosure and should be well considered.", - "type": "string", - "const": "fs:deny-webview-data-windows" - }, - { - "description": "Denies the write command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write" - }, - { - "description": "Denies the write_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-file" - }, - { - "description": "Denies the write_text_file command without any pre-configured scope.", - "type": "string", - "const": "fs:deny-write-text-file" - }, - { - "description": "This enables all read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-all" - }, - { - "description": "This permission allows recursive read functionality on the application\nspecific base directories. \n", - "type": "string", - "const": "fs:read-app-specific-dirs-recursive" - }, - { - "description": "This enables directory read and file metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-dirs" - }, - { - "description": "This enables file read related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-files" - }, - { - "description": "This enables all index or metadata related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:read-meta" - }, - { - "description": "An empty permission you can use to modify the global scope.", - "type": "string", - "const": "fs:scope" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the application folders.", - "type": "string", - "const": "fs:scope-app" - }, - { - "description": "This scope permits to list all files and folders in the application directories.", - "type": "string", - "const": "fs:scope-app-index" - }, - { - "description": "This scope permits recursive access to the complete application folders, including sub directories and files.", - "type": "string", - "const": "fs:scope-app-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCACHE` folder.", - "type": "string", - "const": "fs:scope-appcache" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCACHE`folder.", - "type": "string", - "const": "fs:scope-appcache-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appcache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPCONFIG` folder.", - "type": "string", - "const": "fs:scope-appconfig" - }, - { - "description": "This scope permits to list all files and folders in the `$APPCONFIG`folder.", - "type": "string", - "const": "fs:scope-appconfig-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPCONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appconfig-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPDATA` folder.", - "type": "string", - "const": "fs:scope-appdata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPDATA`folder.", - "type": "string", - "const": "fs:scope-appdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-appdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOCALDATA` folder.", - "type": "string", - "const": "fs:scope-applocaldata" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOCALDATA`folder.", - "type": "string", - "const": "fs:scope-applocaldata-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applocaldata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$APPLOG` folder.", - "type": "string", - "const": "fs:scope-applog" - }, - { - "description": "This scope permits to list all files and folders in the `$APPLOG`folder.", - "type": "string", - "const": "fs:scope-applog-index" - }, - { - "description": "This scope permits recursive access to the complete `$APPLOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-applog-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$AUDIO` folder.", - "type": "string", - "const": "fs:scope-audio" - }, - { - "description": "This scope permits to list all files and folders in the `$AUDIO`folder.", - "type": "string", - "const": "fs:scope-audio-index" - }, - { - "description": "This scope permits recursive access to the complete `$AUDIO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-audio-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CACHE` folder.", - "type": "string", - "const": "fs:scope-cache" - }, - { - "description": "This scope permits to list all files and folders in the `$CACHE`folder.", - "type": "string", - "const": "fs:scope-cache-index" - }, - { - "description": "This scope permits recursive access to the complete `$CACHE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-cache-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$CONFIG` folder.", - "type": "string", - "const": "fs:scope-config" - }, - { - "description": "This scope permits to list all files and folders in the `$CONFIG`folder.", - "type": "string", - "const": "fs:scope-config-index" - }, - { - "description": "This scope permits recursive access to the complete `$CONFIG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-config-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DATA` folder.", - "type": "string", - "const": "fs:scope-data" - }, - { - "description": "This scope permits to list all files and folders in the `$DATA`folder.", - "type": "string", - "const": "fs:scope-data-index" - }, - { - "description": "This scope permits recursive access to the complete `$DATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-data-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DESKTOP` folder.", - "type": "string", - "const": "fs:scope-desktop" - }, - { - "description": "This scope permits to list all files and folders in the `$DESKTOP`folder.", - "type": "string", - "const": "fs:scope-desktop-index" - }, - { - "description": "This scope permits recursive access to the complete `$DESKTOP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-desktop-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOCUMENT` folder.", - "type": "string", - "const": "fs:scope-document" - }, - { - "description": "This scope permits to list all files and folders in the `$DOCUMENT`folder.", - "type": "string", - "const": "fs:scope-document-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOCUMENT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-document-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$DOWNLOAD` folder.", - "type": "string", - "const": "fs:scope-download" - }, - { - "description": "This scope permits to list all files and folders in the `$DOWNLOAD`folder.", - "type": "string", - "const": "fs:scope-download-index" - }, - { - "description": "This scope permits recursive access to the complete `$DOWNLOAD` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-download-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$EXE` folder.", - "type": "string", - "const": "fs:scope-exe" - }, - { - "description": "This scope permits to list all files and folders in the `$EXE`folder.", - "type": "string", - "const": "fs:scope-exe-index" - }, - { - "description": "This scope permits recursive access to the complete `$EXE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-exe-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$FONT` folder.", - "type": "string", - "const": "fs:scope-font" - }, - { - "description": "This scope permits to list all files and folders in the `$FONT`folder.", - "type": "string", - "const": "fs:scope-font-index" - }, - { - "description": "This scope permits recursive access to the complete `$FONT` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-font-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$HOME` folder.", - "type": "string", - "const": "fs:scope-home" - }, - { - "description": "This scope permits to list all files and folders in the `$HOME`folder.", - "type": "string", - "const": "fs:scope-home-index" - }, - { - "description": "This scope permits recursive access to the complete `$HOME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-home-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOCALDATA` folder.", - "type": "string", - "const": "fs:scope-localdata" - }, - { - "description": "This scope permits to list all files and folders in the `$LOCALDATA`folder.", - "type": "string", - "const": "fs:scope-localdata-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOCALDATA` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-localdata-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$LOG` folder.", - "type": "string", - "const": "fs:scope-log" - }, - { - "description": "This scope permits to list all files and folders in the `$LOG`folder.", - "type": "string", - "const": "fs:scope-log-index" - }, - { - "description": "This scope permits recursive access to the complete `$LOG` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-log-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PICTURE` folder.", - "type": "string", - "const": "fs:scope-picture" - }, - { - "description": "This scope permits to list all files and folders in the `$PICTURE`folder.", - "type": "string", - "const": "fs:scope-picture-index" - }, - { - "description": "This scope permits recursive access to the complete `$PICTURE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-picture-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$PUBLIC` folder.", - "type": "string", - "const": "fs:scope-public" - }, - { - "description": "This scope permits to list all files and folders in the `$PUBLIC`folder.", - "type": "string", - "const": "fs:scope-public-index" - }, - { - "description": "This scope permits recursive access to the complete `$PUBLIC` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-public-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RESOURCE` folder.", - "type": "string", - "const": "fs:scope-resource" - }, - { - "description": "This scope permits to list all files and folders in the `$RESOURCE`folder.", - "type": "string", - "const": "fs:scope-resource-index" - }, - { - "description": "This scope permits recursive access to the complete `$RESOURCE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-resource-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$RUNTIME` folder.", - "type": "string", - "const": "fs:scope-runtime" - }, - { - "description": "This scope permits to list all files and folders in the `$RUNTIME`folder.", - "type": "string", - "const": "fs:scope-runtime-index" - }, - { - "description": "This scope permits recursive access to the complete `$RUNTIME` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-runtime-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMP` folder.", - "type": "string", - "const": "fs:scope-temp" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMP`folder.", - "type": "string", - "const": "fs:scope-temp-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMP` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-temp-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$TEMPLATE` folder.", - "type": "string", - "const": "fs:scope-template" - }, - { - "description": "This scope permits to list all files and folders in the `$TEMPLATE`folder.", - "type": "string", - "const": "fs:scope-template-index" - }, - { - "description": "This scope permits recursive access to the complete `$TEMPLATE` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-template-recursive" - }, - { - "description": "This scope permits access to all files and list content of top level directories in the `$VIDEO` folder.", - "type": "string", - "const": "fs:scope-video" - }, - { - "description": "This scope permits to list all files and folders in the `$VIDEO`folder.", - "type": "string", - "const": "fs:scope-video-index" - }, - { - "description": "This scope permits recursive access to the complete `$VIDEO` folder, including sub directories and files.", - "type": "string", - "const": "fs:scope-video-recursive" - }, - { - "description": "This enables all write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-all" - }, - { - "description": "This enables all file write related commands without any pre-configured accessible paths.", - "type": "string", - "const": "fs:write-files" - }, - { - "description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n", - "type": "string", - "const": "os:default" - }, - { - "description": "Enables the arch command without any pre-configured scope.", - "type": "string", - "const": "os:allow-arch" - }, - { - "description": "Enables the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:allow-exe-extension" - }, - { - "description": "Enables the family command without any pre-configured scope.", - "type": "string", - "const": "os:allow-family" - }, - { - "description": "Enables the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:allow-hostname" - }, - { - "description": "Enables the locale command without any pre-configured scope.", - "type": "string", - "const": "os:allow-locale" - }, - { - "description": "Enables the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:allow-os-type" - }, - { - "description": "Enables the platform command without any pre-configured scope.", - "type": "string", - "const": "os:allow-platform" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "os:allow-version" - }, - { - "description": "Denies the arch command without any pre-configured scope.", - "type": "string", - "const": "os:deny-arch" - }, - { - "description": "Denies the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:deny-exe-extension" - }, - { - "description": "Denies the family command without any pre-configured scope.", - "type": "string", - "const": "os:deny-family" - }, - { - "description": "Denies the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:deny-hostname" - }, - { - "description": "Denies the locale command without any pre-configured scope.", - "type": "string", - "const": "os:deny-locale" - }, - { - "description": "Denies the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:deny-os-type" - }, - { - "description": "Denies the platform command without any pre-configured scope.", - "type": "string", - "const": "os:deny-platform" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "os:deny-version" - }, - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - }, - { - "description": "### Default Permissions\n\nThis permission set configures what kind of\ndatabase operations are available from the sql plugin.\n\n### Granted Permissions\n\nAll reading related operations are enabled.\nAlso allows to load or close a connection.\n\n", - "type": "string", - "const": "sql:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-close" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-execute" - }, - { - "description": "Enables the load command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-load" - }, - { - "description": "Enables the select command without any pre-configured scope.", - "type": "string", - "const": "sql:allow-select" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-close" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-execute" - }, - { - "description": "Denies the load command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-load" - }, - { - "description": "Denies the select command without any pre-configured scope.", - "type": "string", - "const": "sql:deny-select" - } - ] - }, - "Value": { - "description": "All supported ACL values.", - "anyOf": [ - { - "description": "Represents a null JSON value.", - "type": "null" - }, - { - "description": "Represents a [`bool`].", - "type": "boolean" - }, - { - "description": "Represents a valid ACL [`Number`].", - "allOf": [ - { - "$ref": "#/definitions/Number" - } - ] - }, - { - "description": "Represents a [`String`].", - "type": "string" - }, - { - "description": "Represents a list of other [`Value`]s.", - "type": "array", - "items": { - "$ref": "#/definitions/Value" - } - }, - { - "description": "Represents a map of [`String`] keys to [`Value`]s.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Value" - } - } - ] - }, - "Number": { - "description": "A valid ACL number.", - "anyOf": [ - { - "description": "Represents an [`i64`].", - "type": "integer", - "format": "int64" - }, - { - "description": "Represents a [`f64`].", - "type": "number", - "format": "double" - } - ] - }, - "Target": { - "description": "Platform target.", - "oneOf": [ - { - "description": "MacOS.", - "type": "string", - "enum": [ - "macOS" - ] - }, - { - "description": "Windows.", - "type": "string", - "enum": [ - "windows" - ] - }, - { - "description": "Linux.", - "type": "string", - "enum": [ - "linux" - ] - }, - { - "description": "Android.", - "type": "string", - "enum": [ - "android" - ] - }, - { - "description": "iOS.", - "type": "string", - "enum": [ - "iOS" - ] - } - ] - }, - "ShellScopeEntryAllowedArg": { - "description": "A command argument allowed to be executed by the webview API.", - "anyOf": [ - { - "description": "A non-configurable argument that is passed to the command in the order it was specified.", - "type": "string" - }, - { - "description": "A variable that is set while calling the command from the webview API.", - "type": "object", - "required": [ - "validator" - ], - "properties": { - "raw": { - "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.", - "default": false, - "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" - } - }, - "additionalProperties": false - } - ] - }, - "ShellScopeEntryAllowedArgs": { - "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.", - "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.", - "type": "array", - "items": { - "$ref": "#/definitions/ShellScopeEntryAllowedArg" - } - } - ] - } - } -} \ No newline at end of file diff --git a/src-tauri/gen/schemas/windows-schema.json b/src-tauri/gen/schemas/windows-schema.json deleted file mode 100644 index 60529716..00000000 --- a/src-tauri/gen/schemas/windows-schema.json +++ /dev/null @@ -1,2089 +0,0 @@ -{ - "$schema": "http://json-schema.org/draft-07/schema#", - "title": "CapabilityFile", - "description": "Capability formats accepted in a capability file.", - "anyOf": [ - { - "description": "A single capability.", - "allOf": [ - { - "$ref": "#/definitions/Capability" - } - ] - }, - { - "description": "A list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - }, - { - "description": "A list of capabilities.", - "type": "object", - "required": [ - "capabilities" - ], - "properties": { - "capabilities": { - "description": "The list of capabilities.", - "type": "array", - "items": { - "$ref": "#/definitions/Capability" - } - } - } - } - ], - "definitions": { - "Capability": { - "description": "A grouping and boundary mechanism developers can use to isolate access to the IPC layer.\n\nIt controls application windows fine grained access to the Tauri core, application, or plugin commands. If a window is not matching any capability then it has no access to the IPC layer at all.\n\nThis can be done to create groups of windows, based on their required system access, which can reduce impact of frontend vulnerabilities in less privileged windows. Windows can be added to a capability by exact name (e.g. `main-window`) or glob patterns like `*` or `admin-*`. A Window can have none, one, or multiple associated capabilities.\n\n## Example\n\n```json { \"identifier\": \"main-user-files-write\", \"description\": \"This capability allows the `main` window on macOS and Windows access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.\", \"windows\": [ \"main\" ], \"permissions\": [ \"core:default\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] }, \"platforms\": [\"macOS\",\"windows\"] } ```", - "type": "object", - "required": [ - "identifier", - "permissions" - ], - "properties": { - "identifier": { - "description": "Identifier of the capability.\n\n## Example\n\n`main-user-files-write`", - "type": "string" - }, - "description": { - "description": "Description of what the capability is intended to allow on associated windows.\n\nIt should contain a description of what the grouped permissions should allow.\n\n## Example\n\nThis capability allows the `main` window access to `filesystem` write related commands and `dialog` commands to enable programatic access to files selected by the user.", - "default": "", - "type": "string" - }, - "remote": { - "description": "Configure remote URLs that can use the capability permissions.\n\nThis setting is optional and defaults to not being set, as our default use case is that the content is served from our local application.\n\n:::caution Make sure you understand the security implications of providing remote sources with local system access. :::\n\n## Example\n\n```json { \"urls\": [\"https://*.mydomain.dev\"] } ```", - "anyOf": [ - { - "$ref": "#/definitions/CapabilityRemote" - }, - { - "type": "null" - } - ] - }, - "local": { - "description": "Whether this capability is enabled for local app URLs or not. Defaults to `true`.", - "default": true, - "type": "boolean" - }, - "windows": { - "description": "List of windows that are affected by this capability. Can be a glob pattern.\n\nOn multiwebview windows, prefer [`Self::webviews`] for a fine grained access control.\n\n## Example\n\n`[\"main\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "webviews": { - "description": "List of webviews that are affected by this capability. Can be a glob pattern.\n\nThis is only required when using on multiwebview contexts, by default all child webviews of a window that matches [`Self::windows`] are linked.\n\n## Example\n\n`[\"sub-webview-one\", \"sub-webview-two\"]`", - "type": "array", - "items": { - "type": "string" - } - }, - "permissions": { - "description": "List of permissions attached to this capability.\n\nMust include the plugin name as prefix in the form of `${plugin-name}:${permission-name}`. For commands directly implemented in the application itself only `${permission-name}` is required.\n\n## Example\n\n```json [ \"core:default\", \"shell:allow-open\", \"dialog:open\", { \"identifier\": \"fs:allow-write-text-file\", \"allow\": [{ \"path\": \"$HOME/test.txt\" }] } ```", - "type": "array", - "items": { - "$ref": "#/definitions/PermissionEntry" - }, - "uniqueItems": true - }, - "platforms": { - "description": "Limit which target platforms this capability applies to.\n\nBy default all platforms are targeted.\n\n## Example\n\n`[\"macOS\",\"windows\"]`", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Target" - } - } - } - }, - "CapabilityRemote": { - "description": "Configuration for remote URLs that are associated with the capability.", - "type": "object", - "required": [ - "urls" - ], - "properties": { - "urls": { - "description": "Remote domains this capability refers to using the [URLPattern standard](https://urlpattern.spec.whatwg.org/).\n\n## Examples\n\n- \"https://*.mydomain.dev\": allows subdomains of mydomain.dev - \"https://mydomain.dev/api/*\": allows any subpath of mydomain.dev/api", - "type": "array", - "items": { - "type": "string" - } - } - } - }, - "PermissionEntry": { - "description": "An entry for a permission value in a [`Capability`] can be either a raw permission [`Identifier`] or an object that references a permission and extends its scope.", - "anyOf": [ - { - "description": "Reference a permission or permission set by identifier.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - { - "description": "Reference a permission or permission set by identifier and extends its scope.", - "type": "object", - "allOf": [ - { - "if": { - "properties": { - "identifier": { - "anyOf": [ - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - } - ] - } - } - }, - "then": { - "properties": { - "allow": { - "items": { - "title": "Entry", - "description": "A command allowed to be executed by the webview API.", - "type": "object", - "required": [ - "args", - "cmd", - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellAllowedArgs" - } - ] - }, - "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" - }, - "sidecar": { - "description": "If this command is a sidecar command.", - "type": "boolean" - } - } - } - }, - "deny": { - "items": { - "title": "Entry", - "description": "A command allowed to be executed by the webview API.", - "type": "object", - "required": [ - "args", - "cmd", - "name", - "sidecar" - ], - "properties": { - "args": { - "description": "The allowed arguments for the command execution.", - "allOf": [ - { - "$ref": "#/definitions/ShellAllowedArgs" - } - ] - }, - "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" - }, - "sidecar": { - "description": "If this command is a sidecar command.", - "type": "boolean" - } - } - } - } - } - }, - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - } - } - }, - { - "properties": { - "identifier": { - "description": "Identifier of the permission or permission set.", - "allOf": [ - { - "$ref": "#/definitions/Identifier" - } - ] - }, - "allow": { - "description": "Data that defines what is allowed by the scope.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - }, - "deny": { - "description": "Data that defines what is denied by the scope. This should be prioritized by validation logic.", - "type": [ - "array", - "null" - ], - "items": { - "$ref": "#/definitions/Value" - } - } - } - } - ], - "required": [ - "identifier" - ] - } - ] - }, - "Identifier": { - "description": "Permission identifier", - "oneOf": [ - { - "description": "Allows reading the CLI matches", - "type": "string", - "const": "cli:default" - }, - { - "description": "Enables the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:allow-cli-matches" - }, - { - "description": "Denies the cli_matches command without any pre-configured scope.", - "type": "string", - "const": "cli:deny-cli-matches" - }, - { - "description": "Default core plugins set which includes:\n- 'core:path:default'\n- 'core:event:default'\n- 'core:window:default'\n- 'core:webview:default'\n- 'core:app:default'\n- 'core:image:default'\n- 'core:resources:default'\n- 'core:menu:default'\n- 'core:tray:default'\n", - "type": "string", - "const": "core:default" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:app:default" - }, - { - "description": "Enables the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-hide" - }, - { - "description": "Enables the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-app-show" - }, - { - "description": "Enables the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-default-window-icon" - }, - { - "description": "Enables the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-name" - }, - { - "description": "Enables the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-tauri-version" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:allow-version" - }, - { - "description": "Denies the app_hide command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-hide" - }, - { - "description": "Denies the app_show command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-app-show" - }, - { - "description": "Denies the default_window_icon command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-default-window-icon" - }, - { - "description": "Denies the name command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-name" - }, - { - "description": "Denies the tauri_version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-tauri-version" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "core:app:deny-version" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:event:default" - }, - { - "description": "Enables the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit" - }, - { - "description": "Enables the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-emit-to" - }, - { - "description": "Enables the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-listen" - }, - { - "description": "Enables the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:allow-unlisten" - }, - { - "description": "Denies the emit command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit" - }, - { - "description": "Denies the emit_to command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-emit-to" - }, - { - "description": "Denies the listen command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-listen" - }, - { - "description": "Denies the unlisten command without any pre-configured scope.", - "type": "string", - "const": "core:event:deny-unlisten" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:image:default" - }, - { - "description": "Enables the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-bytes" - }, - { - "description": "Enables the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-from-path" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-new" - }, - { - "description": "Enables the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-rgba" - }, - { - "description": "Enables the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:allow-size" - }, - { - "description": "Denies the from_bytes command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-bytes" - }, - { - "description": "Denies the from_path command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-from-path" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-new" - }, - { - "description": "Denies the rgba command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-rgba" - }, - { - "description": "Denies the size command without any pre-configured scope.", - "type": "string", - "const": "core:image:deny-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:menu:default" - }, - { - "description": "Enables the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-append" - }, - { - "description": "Enables the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-create-default" - }, - { - "description": "Enables the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-get" - }, - { - "description": "Enables the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-insert" - }, - { - "description": "Enables the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-checked" - }, - { - "description": "Enables the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-is-enabled" - }, - { - "description": "Enables the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-items" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-new" - }, - { - "description": "Enables the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-popup" - }, - { - "description": "Enables the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-prepend" - }, - { - "description": "Enables the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove" - }, - { - "description": "Enables the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-remove-at" - }, - { - "description": "Enables the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-accelerator" - }, - { - "description": "Enables the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-app-menu" - }, - { - "description": "Enables the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-help-menu-for-nsapp" - }, - { - "description": "Enables the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-window-menu" - }, - { - "description": "Enables the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-as-windows-menu-for-nsapp" - }, - { - "description": "Enables the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-checked" - }, - { - "description": "Enables the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-enabled" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-icon" - }, - { - "description": "Enables the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-set-text" - }, - { - "description": "Enables the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:allow-text" - }, - { - "description": "Denies the append command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-append" - }, - { - "description": "Denies the create_default command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-create-default" - }, - { - "description": "Denies the get command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-get" - }, - { - "description": "Denies the insert command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-insert" - }, - { - "description": "Denies the is_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-checked" - }, - { - "description": "Denies the is_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-is-enabled" - }, - { - "description": "Denies the items command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-items" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-new" - }, - { - "description": "Denies the popup command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-popup" - }, - { - "description": "Denies the prepend command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-prepend" - }, - { - "description": "Denies the remove command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove" - }, - { - "description": "Denies the remove_at command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-remove-at" - }, - { - "description": "Denies the set_accelerator command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-accelerator" - }, - { - "description": "Denies the set_as_app_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-app-menu" - }, - { - "description": "Denies the set_as_help_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-help-menu-for-nsapp" - }, - { - "description": "Denies the set_as_window_menu command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-window-menu" - }, - { - "description": "Denies the set_as_windows_menu_for_nsapp command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-as-windows-menu-for-nsapp" - }, - { - "description": "Denies the set_checked command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-checked" - }, - { - "description": "Denies the set_enabled command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-enabled" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-icon" - }, - { - "description": "Denies the set_text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-set-text" - }, - { - "description": "Denies the text command without any pre-configured scope.", - "type": "string", - "const": "core:menu:deny-text" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:path:default" - }, - { - "description": "Enables the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-basename" - }, - { - "description": "Enables the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-dirname" - }, - { - "description": "Enables the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-extname" - }, - { - "description": "Enables the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-is-absolute" - }, - { - "description": "Enables the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-join" - }, - { - "description": "Enables the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-normalize" - }, - { - "description": "Enables the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve" - }, - { - "description": "Enables the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:allow-resolve-directory" - }, - { - "description": "Denies the basename command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-basename" - }, - { - "description": "Denies the dirname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-dirname" - }, - { - "description": "Denies the extname command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-extname" - }, - { - "description": "Denies the is_absolute command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-is-absolute" - }, - { - "description": "Denies the join command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-join" - }, - { - "description": "Denies the normalize command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-normalize" - }, - { - "description": "Denies the resolve command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve" - }, - { - "description": "Denies the resolve_directory command without any pre-configured scope.", - "type": "string", - "const": "core:path:deny-resolve-directory" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:resources:default" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:allow-close" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:resources:deny-close" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:tray:default" - }, - { - "description": "Enables the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-get-by-id" - }, - { - "description": "Enables the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-new" - }, - { - "description": "Enables the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-remove-by-id" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon" - }, - { - "description": "Enables the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-icon-as-template" - }, - { - "description": "Enables the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-menu" - }, - { - "description": "Enables the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-show-menu-on-left-click" - }, - { - "description": "Enables the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-temp-dir-path" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-title" - }, - { - "description": "Enables the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-tooltip" - }, - { - "description": "Enables the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:allow-set-visible" - }, - { - "description": "Denies the get_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-get-by-id" - }, - { - "description": "Denies the new command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-new" - }, - { - "description": "Denies the remove_by_id command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-remove-by-id" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon" - }, - { - "description": "Denies the set_icon_as_template command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-icon-as-template" - }, - { - "description": "Denies the set_menu command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-menu" - }, - { - "description": "Denies the set_show_menu_on_left_click command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-show-menu-on-left-click" - }, - { - "description": "Denies the set_temp_dir_path command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-temp-dir-path" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-title" - }, - { - "description": "Denies the set_tooltip command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-tooltip" - }, - { - "description": "Denies the set_visible command without any pre-configured scope.", - "type": "string", - "const": "core:tray:deny-set-visible" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:webview:default" - }, - { - "description": "Enables the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview" - }, - { - "description": "Enables the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-create-webview-window" - }, - { - "description": "Enables the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-get-all-webviews" - }, - { - "description": "Enables the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-internal-toggle-devtools" - }, - { - "description": "Enables the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-print" - }, - { - "description": "Enables the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-reparent" - }, - { - "description": "Enables the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-focus" - }, - { - "description": "Enables the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-position" - }, - { - "description": "Enables the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-size" - }, - { - "description": "Enables the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-set-webview-zoom" - }, - { - "description": "Enables the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-close" - }, - { - "description": "Enables the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-position" - }, - { - "description": "Enables the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:allow-webview-size" - }, - { - "description": "Denies the create_webview command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview" - }, - { - "description": "Denies the create_webview_window command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-create-webview-window" - }, - { - "description": "Denies the get_all_webviews command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-get-all-webviews" - }, - { - "description": "Denies the internal_toggle_devtools command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-internal-toggle-devtools" - }, - { - "description": "Denies the print command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-print" - }, - { - "description": "Denies the reparent command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-reparent" - }, - { - "description": "Denies the set_webview_focus command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-focus" - }, - { - "description": "Denies the set_webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-position" - }, - { - "description": "Denies the set_webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-size" - }, - { - "description": "Denies the set_webview_zoom command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-set-webview-zoom" - }, - { - "description": "Denies the webview_close command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-close" - }, - { - "description": "Denies the webview_position command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-position" - }, - { - "description": "Denies the webview_size command without any pre-configured scope.", - "type": "string", - "const": "core:webview:deny-webview-size" - }, - { - "description": "Default permissions for the plugin.", - "type": "string", - "const": "core:window:default" - }, - { - "description": "Enables the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-available-monitors" - }, - { - "description": "Enables the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-center" - }, - { - "description": "Enables the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-close" - }, - { - "description": "Enables the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-create" - }, - { - "description": "Enables the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-current-monitor" - }, - { - "description": "Enables the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-cursor-position" - }, - { - "description": "Enables the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-destroy" - }, - { - "description": "Enables the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-get-all-windows" - }, - { - "description": "Enables the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-hide" - }, - { - "description": "Enables the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-position" - }, - { - "description": "Enables the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-inner-size" - }, - { - "description": "Enables the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-internal-toggle-maximize" - }, - { - "description": "Enables the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-closable" - }, - { - "description": "Enables the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-decorated" - }, - { - "description": "Enables the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-focused" - }, - { - "description": "Enables the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-fullscreen" - }, - { - "description": "Enables the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximizable" - }, - { - "description": "Enables the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-maximized" - }, - { - "description": "Enables the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimizable" - }, - { - "description": "Enables the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-minimized" - }, - { - "description": "Enables the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-resizable" - }, - { - "description": "Enables the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-is-visible" - }, - { - "description": "Enables the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-maximize" - }, - { - "description": "Enables the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-minimize" - }, - { - "description": "Enables the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-monitor-from-point" - }, - { - "description": "Enables the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-position" - }, - { - "description": "Enables the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-outer-size" - }, - { - "description": "Enables the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-primary-monitor" - }, - { - "description": "Enables the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-request-user-attention" - }, - { - "description": "Enables the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-scale-factor" - }, - { - "description": "Enables the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-bottom" - }, - { - "description": "Enables the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-always-on-top" - }, - { - "description": "Enables the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-closable" - }, - { - "description": "Enables the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-content-protected" - }, - { - "description": "Enables the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-grab" - }, - { - "description": "Enables the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-icon" - }, - { - "description": "Enables the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-position" - }, - { - "description": "Enables the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-cursor-visible" - }, - { - "description": "Enables the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-decorations" - }, - { - "description": "Enables the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-effects" - }, - { - "description": "Enables the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-focus" - }, - { - "description": "Enables the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-fullscreen" - }, - { - "description": "Enables the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-icon" - }, - { - "description": "Enables the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-ignore-cursor-events" - }, - { - "description": "Enables the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-max-size" - }, - { - "description": "Enables the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-maximizable" - }, - { - "description": "Enables the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-min-size" - }, - { - "description": "Enables the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-minimizable" - }, - { - "description": "Enables the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-position" - }, - { - "description": "Enables the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-progress-bar" - }, - { - "description": "Enables the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-resizable" - }, - { - "description": "Enables the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-shadow" - }, - { - "description": "Enables the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size" - }, - { - "description": "Enables the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-size-constraints" - }, - { - "description": "Enables the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-skip-taskbar" - }, - { - "description": "Enables the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title" - }, - { - "description": "Enables the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-title-bar-style" - }, - { - "description": "Enables the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-set-visible-on-all-workspaces" - }, - { - "description": "Enables the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-show" - }, - { - "description": "Enables the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-dragging" - }, - { - "description": "Enables the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-start-resize-dragging" - }, - { - "description": "Enables the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-theme" - }, - { - "description": "Enables the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-title" - }, - { - "description": "Enables the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-toggle-maximize" - }, - { - "description": "Enables the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unmaximize" - }, - { - "description": "Enables the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:allow-unminimize" - }, - { - "description": "Denies the available_monitors command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-available-monitors" - }, - { - "description": "Denies the center command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-center" - }, - { - "description": "Denies the close command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-close" - }, - { - "description": "Denies the create command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-create" - }, - { - "description": "Denies the current_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-current-monitor" - }, - { - "description": "Denies the cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-cursor-position" - }, - { - "description": "Denies the destroy command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-destroy" - }, - { - "description": "Denies the get_all_windows command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-get-all-windows" - }, - { - "description": "Denies the hide command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-hide" - }, - { - "description": "Denies the inner_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-position" - }, - { - "description": "Denies the inner_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-inner-size" - }, - { - "description": "Denies the internal_toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-internal-toggle-maximize" - }, - { - "description": "Denies the is_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-closable" - }, - { - "description": "Denies the is_decorated command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-decorated" - }, - { - "description": "Denies the is_focused command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-focused" - }, - { - "description": "Denies the is_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-fullscreen" - }, - { - "description": "Denies the is_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximizable" - }, - { - "description": "Denies the is_maximized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-maximized" - }, - { - "description": "Denies the is_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimizable" - }, - { - "description": "Denies the is_minimized command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-minimized" - }, - { - "description": "Denies the is_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-resizable" - }, - { - "description": "Denies the is_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-is-visible" - }, - { - "description": "Denies the maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-maximize" - }, - { - "description": "Denies the minimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-minimize" - }, - { - "description": "Denies the monitor_from_point command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-monitor-from-point" - }, - { - "description": "Denies the outer_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-position" - }, - { - "description": "Denies the outer_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-outer-size" - }, - { - "description": "Denies the primary_monitor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-primary-monitor" - }, - { - "description": "Denies the request_user_attention command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-request-user-attention" - }, - { - "description": "Denies the scale_factor command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-scale-factor" - }, - { - "description": "Denies the set_always_on_bottom command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-bottom" - }, - { - "description": "Denies the set_always_on_top command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-always-on-top" - }, - { - "description": "Denies the set_closable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-closable" - }, - { - "description": "Denies the set_content_protected command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-content-protected" - }, - { - "description": "Denies the set_cursor_grab command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-grab" - }, - { - "description": "Denies the set_cursor_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-icon" - }, - { - "description": "Denies the set_cursor_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-position" - }, - { - "description": "Denies the set_cursor_visible command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-cursor-visible" - }, - { - "description": "Denies the set_decorations command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-decorations" - }, - { - "description": "Denies the set_effects command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-effects" - }, - { - "description": "Denies the set_focus command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-focus" - }, - { - "description": "Denies the set_fullscreen command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-fullscreen" - }, - { - "description": "Denies the set_icon command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-icon" - }, - { - "description": "Denies the set_ignore_cursor_events command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-ignore-cursor-events" - }, - { - "description": "Denies the set_max_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-max-size" - }, - { - "description": "Denies the set_maximizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-maximizable" - }, - { - "description": "Denies the set_min_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-min-size" - }, - { - "description": "Denies the set_minimizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-minimizable" - }, - { - "description": "Denies the set_position command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-position" - }, - { - "description": "Denies the set_progress_bar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-progress-bar" - }, - { - "description": "Denies the set_resizable command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-resizable" - }, - { - "description": "Denies the set_shadow command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-shadow" - }, - { - "description": "Denies the set_size command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size" - }, - { - "description": "Denies the set_size_constraints command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-size-constraints" - }, - { - "description": "Denies the set_skip_taskbar command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-skip-taskbar" - }, - { - "description": "Denies the set_title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title" - }, - { - "description": "Denies the set_title_bar_style command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-title-bar-style" - }, - { - "description": "Denies the set_visible_on_all_workspaces command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-set-visible-on-all-workspaces" - }, - { - "description": "Denies the show command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-show" - }, - { - "description": "Denies the start_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-dragging" - }, - { - "description": "Denies the start_resize_dragging command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-start-resize-dragging" - }, - { - "description": "Denies the theme command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-theme" - }, - { - "description": "Denies the title command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-title" - }, - { - "description": "Denies the toggle_maximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-toggle-maximize" - }, - { - "description": "Denies the unmaximize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unmaximize" - }, - { - "description": "Denies the unminimize command without any pre-configured scope.", - "type": "string", - "const": "core:window:deny-unminimize" - }, - { - "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", - "type": "string", - "const": "dialog:default" - }, - { - "description": "Enables the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-ask" - }, - { - "description": "Enables the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-confirm" - }, - { - "description": "Enables the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-message" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-open" - }, - { - "description": "Enables the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:allow-save" - }, - { - "description": "Denies the ask command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-ask" - }, - { - "description": "Denies the confirm command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-confirm" - }, - { - "description": "Denies the message command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-message" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-open" - }, - { - "description": "Denies the save command without any pre-configured scope.", - "type": "string", - "const": "dialog:deny-save" - }, - { - "description": "This permission set configures which\noperating system information are available\nto gather from the frontend.\n\n#### Granted Permissions\n\nAll information except the host name are available.\n\n", - "type": "string", - "const": "os:default" - }, - { - "description": "Enables the arch command without any pre-configured scope.", - "type": "string", - "const": "os:allow-arch" - }, - { - "description": "Enables the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:allow-exe-extension" - }, - { - "description": "Enables the family command without any pre-configured scope.", - "type": "string", - "const": "os:allow-family" - }, - { - "description": "Enables the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:allow-hostname" - }, - { - "description": "Enables the locale command without any pre-configured scope.", - "type": "string", - "const": "os:allow-locale" - }, - { - "description": "Enables the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:allow-os-type" - }, - { - "description": "Enables the platform command without any pre-configured scope.", - "type": "string", - "const": "os:allow-platform" - }, - { - "description": "Enables the version command without any pre-configured scope.", - "type": "string", - "const": "os:allow-version" - }, - { - "description": "Denies the arch command without any pre-configured scope.", - "type": "string", - "const": "os:deny-arch" - }, - { - "description": "Denies the exe_extension command without any pre-configured scope.", - "type": "string", - "const": "os:deny-exe-extension" - }, - { - "description": "Denies the family command without any pre-configured scope.", - "type": "string", - "const": "os:deny-family" - }, - { - "description": "Denies the hostname command without any pre-configured scope.", - "type": "string", - "const": "os:deny-hostname" - }, - { - "description": "Denies the locale command without any pre-configured scope.", - "type": "string", - "const": "os:deny-locale" - }, - { - "description": "Denies the os_type command without any pre-configured scope.", - "type": "string", - "const": "os:deny-os-type" - }, - { - "description": "Denies the platform command without any pre-configured scope.", - "type": "string", - "const": "os:deny-platform" - }, - { - "description": "Denies the version command without any pre-configured scope.", - "type": "string", - "const": "os:deny-version" - }, - { - "description": "This permission set configures which\nshell functionality is exposed by default.\n\n#### Granted Permissions\n\nIt allows to use the `open` functionality without any specific\nscope pre-configured. It will allow opening `http(s)://`,\n`tel:` and `mailto:` links.\n", - "type": "string", - "const": "shell:default" - }, - { - "description": "Enables the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-execute" - }, - { - "description": "Enables the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-kill" - }, - { - "description": "Enables the open command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-open" - }, - { - "description": "Enables the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-spawn" - }, - { - "description": "Enables the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:allow-stdin-write" - }, - { - "description": "Denies the execute command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-execute" - }, - { - "description": "Denies the kill command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-kill" - }, - { - "description": "Denies the open command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-open" - }, - { - "description": "Denies the spawn command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-spawn" - }, - { - "description": "Denies the stdin_write command without any pre-configured scope.", - "type": "string", - "const": "shell:deny-stdin-write" - } - ] - }, - "Value": { - "description": "All supported ACL values.", - "anyOf": [ - { - "description": "Represents a null JSON value.", - "type": "null" - }, - { - "description": "Represents a [`bool`].", - "type": "boolean" - }, - { - "description": "Represents a valid ACL [`Number`].", - "allOf": [ - { - "$ref": "#/definitions/Number" - } - ] - }, - { - "description": "Represents a [`String`].", - "type": "string" - }, - { - "description": "Represents a list of other [`Value`]s.", - "type": "array", - "items": { - "$ref": "#/definitions/Value" - } - }, - { - "description": "Represents a map of [`String`] keys to [`Value`]s.", - "type": "object", - "additionalProperties": { - "$ref": "#/definitions/Value" - } - } - ] - }, - "Number": { - "description": "A valid ACL number.", - "anyOf": [ - { - "description": "Represents an [`i64`].", - "type": "integer", - "format": "int64" - }, - { - "description": "Represents a [`f64`].", - "type": "number", - "format": "double" - } - ] - }, - "Target": { - "description": "Platform target.", - "oneOf": [ - { - "description": "MacOS.", - "type": "string", - "enum": [ - "macOS" - ] - }, - { - "description": "Windows.", - "type": "string", - "enum": [ - "windows" - ] - }, - { - "description": "Linux.", - "type": "string", - "enum": [ - "linux" - ] - }, - { - "description": "Android.", - "type": "string", - "enum": [ - "android" - ] - }, - { - "description": "iOS.", - "type": "string", - "enum": [ - "iOS" - ] - } - ] - }, - "ShellAllowedArg": { - "description": "A command argument allowed to be executed by the webview API.", - "anyOf": [ - { - "description": "A non-configurable argument that is passed to the command in the order it was specified.", - "type": "string" - }, - { - "description": "A variable that is set while calling the command from the webview API.", - "type": "object", - "required": [ - "validator" - ], - "properties": { - "raw": { - "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.", - "default": false, - "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" - } - }, - "additionalProperties": false - } - ] - }, - "ShellAllowedArgs": { - "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 [`ShellAllowedArg`] will set those arguments as the only valid arguments to be passed to the attached command configuration.", - "anyOf": [ - { - "description": "Use a simple boolean to allow all or disable all arguments to this command configuration.", - "type": "boolean" - }, - { - "description": "A specific set of [`ShellAllowedArg`] that are valid to call for the command configuration.", - "type": "array", - "items": { - "$ref": "#/definitions/ShellAllowedArg" - } - } - ] - } - } -} \ No newline at end of file diff --git a/src-tauri/migrations/20250111160252_create_job_table.up.sql b/src-tauri/migrations/20250111160252_create_job_table.up.sql index 57ac73e8..093273ee 100644 --- a/src-tauri/migrations/20250111160252_create_job_table.up.sql +++ b/src-tauri/migrations/20250111160252_create_job_table.up.sql @@ -1,7 +1,7 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS jobs( id TEXT NOT NULL PRIMARY KEY, - mode BLOB NOT NULL, + mode TEXT NOT NULL, project_file TEXT NOT NULL, blender_version TEXT NOT NULL, output_path TEXT NOT NULL diff --git a/src-tauri/migrations/20250111160259_create_task_table.up.sql b/src-tauri/migrations/20250111160259_create_task_table.up.sql index 08b6c3a6..9be2f9ee 100644 --- a/src-tauri/migrations/20250111160259_create_task_table.up.sql +++ b/src-tauri/migrations/20250111160259_create_task_table.up.sql @@ -1,10 +1,8 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS tasks( id TEXT NOT NULL PRIMARY KEY, - peer_id TEXT NOT NULL, job_id TEXT NOT NULL, - blender_version TEXT NOT NULL, - blend_file_name TEXT NOT NULL, - start_frame INTEGER NOT NULL, - end_frame INTEGER NOT NULL + job TEXT NOT NULL, + start INTEGER NOT NULL, + end INTEGER NOT NULL ); \ No newline at end of file diff --git a/src-tauri/migrations/20250111160306_create_worker_table.up.sql b/src-tauri/migrations/20250111160306_create_worker_table.up.sql index 26618694..69e22520 100644 --- a/src-tauri/migrations/20250111160306_create_worker_table.up.sql +++ b/src-tauri/migrations/20250111160306_create_worker_table.up.sql @@ -1,5 +1,5 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS workers ( machine_id TEXT NOT NULL PRIMARY KEY, - spec BLOB NOT NULL + spec TEXT NOT NULL ); \ No newline at end of file diff --git a/src-tauri/migrations/20250111160855_create_renders_table.up.sql b/src-tauri/migrations/20250111160855_create_renders_table.up.sql index a3e9e672..3fce7d3a 100644 --- a/src-tauri/migrations/20250111160855_create_renders_table.up.sql +++ b/src-tauri/migrations/20250111160855_create_renders_table.up.sql @@ -1,8 +1,7 @@ -- Add up migration script here CREATE TABLE IF NOT EXISTS renders( - -- should be jobs_id + _ + frame number id TEXT NOT NULL PRIMARY KEY, - jobs_id TEXT NOT NULL, + job_id TEXT NOT NULL, frame INTEGER NOT NULL, render_path TEXT NOT NULL ); \ No newline at end of file diff --git a/src-tauri/migrations/20250612033123_create_advertise_table.down.sql b/src-tauri/migrations/20250612033123_create_advertise_table.down.sql new file mode 100644 index 00000000..2ef8362c --- /dev/null +++ b/src-tauri/migrations/20250612033123_create_advertise_table.down.sql @@ -0,0 +1 @@ +DROP TABLE IF EXISTS advertise; \ No newline at end of file diff --git a/src-tauri/migrations/20250612033123_create_advertise_table.up.sql b/src-tauri/migrations/20250612033123_create_advertise_table.up.sql new file mode 100644 index 00000000..ddcd70e8 --- /dev/null +++ b/src-tauri/migrations/20250612033123_create_advertise_table.up.sql @@ -0,0 +1,7 @@ +-- used to create records of previous advertisement in case of a unexpected shutdown. avoid memory usage as much as you can. +CREATE TABLE IF NOT EXISTS advertise( + id TEXT NOT NULL UNIQUE, -- primary key + -- See if we need anything special, but for now, just name and path both of which is protected keywords. + ad_name TEXT NOT NULL, -- name we broadcast + file_path TEXT NOT NULL -- path to file to respond +) \ No newline at end of file diff --git a/src-tauri/src/constant.rs b/src-tauri/src/constant.rs new file mode 100644 index 00000000..52e34bae --- /dev/null +++ b/src-tauri/src/constant.rs @@ -0,0 +1,7 @@ +pub const DATABASE_FILE_NAME: &str = "blendfarm.db"; +pub const WORKPLACE: &str = "workplace"; +pub const JOB_TOPIC: &str = "/job"; +pub const NODE_TOPIC: &str = "/node"; + +// why does the transfer have number at the trail end? look more into this? +pub const TRANSFER: &str = "/file-transfer/1"; diff --git a/src-tauri/src/domains/advertise_store.rs b/src-tauri/src/domains/advertise_store.rs new file mode 100644 index 00000000..1560f695 --- /dev/null +++ b/src-tauri/src/domains/advertise_store.rs @@ -0,0 +1,20 @@ +use crate::models::advertise::Advertise; +use thiserror::Error; +use uuid::Uuid; + +#[derive(Debug, Error)] +pub enum AdvertiseError { + #[error("Unknown")] + Unknown, + #[error("Received Database errors! {0}")] + DatabaseError(String), +} + +#[async_trait::async_trait] +pub trait AdvertiseStore { + async fn find(&self, id: Uuid) -> Result, AdvertiseError>; + async fn update(&self, advertise: Advertise) -> Result<(), AdvertiseError>; + async fn create(&self, advertise: Advertise) -> Result<(), AdvertiseError>; + async fn kill(&self, id: Uuid) -> Result<(), AdvertiseError>; + async fn all(&self) -> Result>, AdvertiseError>; +} diff --git a/src-tauri/src/domains/job_store.rs b/src-tauri/src/domains/job_store.rs index 49e15055..f88e2269 100644 --- a/src-tauri/src/domains/job_store.rs +++ b/src-tauri/src/domains/job_store.rs @@ -1,4 +1,7 @@ -use crate::{domains::task_store::TaskError, models::job::Job}; +use crate::{ + domains::task_store::TaskError, + models::job::{CreatedJobDto, NewJobDto}, +}; use serde::{Deserialize, Serialize}; use thiserror::Error; use uuid::Uuid; @@ -14,13 +17,15 @@ pub enum JobError { DatabaseError(String), #[error("Task error")] TaskError(#[from] TaskError), + #[error("Command error: {0}")] + Send(String), } #[async_trait::async_trait] pub trait JobStore { - async fn add_job(&mut self, job: Job) -> Result<(), JobError>; - async fn list_all(&self) -> Result, JobError>; - async fn get_job(&self, job_id: &Uuid) -> Result; - async fn update_job(&mut self, job: Job) -> Result<(), JobError>; + async fn add_job(&mut self, job: NewJobDto) -> Result; + async fn list_all(&self) -> Result, JobError>; + async fn get_job(&self, job_id: &Uuid) -> Result, JobError>; + async fn update_job(&mut self, job: CreatedJobDto) -> Result<(), JobError>; async fn delete_job(&mut self, id: &Uuid) -> Result<(), JobError>; } diff --git a/src-tauri/src/domains/mod.rs b/src-tauri/src/domains/mod.rs index e5c2d65c..3b3186a5 100644 --- a/src-tauri/src/domains/mod.rs +++ b/src-tauri/src/domains/mod.rs @@ -1,4 +1,6 @@ +pub mod activity_store; +pub mod advertise_store; pub mod job_store; -pub mod worker_store; +pub mod render_store; pub mod task_store; -pub mod activity_store; +pub mod worker_store; diff --git a/src-tauri/src/domains/render_store.rs b/src-tauri/src/domains/render_store.rs new file mode 100644 index 00000000..63ae6507 --- /dev/null +++ b/src-tauri/src/domains/render_store.rs @@ -0,0 +1,23 @@ +use crate::models::render_info::{CreatedRenderInfoDto, NewRenderInfoDto, RenderInfo}; +use thiserror::Error; +use uuid::Uuid; + +#[derive(Debug, Error)] +pub enum RenderError { + #[error("Missing file")] + MissingFileAtPath, + #[error("Database Errors")] + DatabaseError(String), +} + +#[async_trait::async_trait] +pub trait RenderStore { + async fn list_renders(&self) -> Result, RenderError>; + async fn create_renders( + &self, + render_info: NewRenderInfoDto, + ) -> Result; + async fn read_renders(&self, id: &Uuid) -> Result; + async fn update_renders(&mut self, render_info: RenderInfo) -> Result<(), RenderError>; + async fn delete_renders(&mut self, id: &Uuid) -> Result<(), RenderError>; +} diff --git a/src-tauri/src/domains/task_store.rs b/src-tauri/src/domains/task_store.rs index 11caa144..9ade5c75 100644 --- a/src-tauri/src/domains/task_store.rs +++ b/src-tauri/src/domains/task_store.rs @@ -1,4 +1,4 @@ -use crate::models::task::Task; +use crate::models::task::{CreatedTaskDto, Task}; use serde::{Deserialize, Serialize}; use thiserror::Error; use uuid::Uuid; @@ -11,16 +11,20 @@ pub enum TaskError { DatabaseError(String), #[error("Something wring with blender: {0}")] BlenderError(String), + #[error("Unable to get temp storage location")] + CacheError, } #[async_trait::async_trait] pub trait TaskStore { // append new task to queue - async fn add_task(&mut self, task: Task) -> Result<(), TaskError>; + async fn add_task(&self, task: Task) -> Result; // Poll task will pop task entry from database - async fn poll_task(&mut self) -> Result; + async fn poll_task(&self) -> Result, TaskError>; + // List pending task + async fn list_tasks(&self) -> Result>, TaskError>; // delete task by id - async fn delete_task(&mut self, task: Task) -> Result<(), TaskError>; + async fn delete_task(&self, id: &Uuid) -> Result<(), TaskError>; // delete all task with matching job id - async fn delete_job_task(&mut self, job_id: Uuid) -> Result<(), TaskError>; + async fn delete_job_task(&self, job_id: &Uuid) -> Result<(), TaskError>; } diff --git a/src-tauri/src/domains/worker_store.rs b/src-tauri/src/domains/worker_store.rs index 74a9633b..6a28a2b7 100644 --- a/src-tauri/src/domains/worker_store.rs +++ b/src-tauri/src/domains/worker_store.rs @@ -1,9 +1,11 @@ use crate::models::worker::{Worker, WorkerError}; +use libp2p::PeerId; #[async_trait::async_trait] pub trait WorkerStore { async fn add_worker(&mut self, worker: Worker) -> Result<(), WorkerError>; - async fn get_worker(&self, id: &str) -> Option; + async fn get_worker(&self, id: &PeerId) -> Option; async fn list_worker(&self) -> Result, WorkerError>; - async fn delete_worker(&mut self, machine_id: &str) -> Result<(), WorkerError>; + async fn delete_worker(&mut self, id: &PeerId) -> Result<(), WorkerError>; + async fn clear_worker(&mut self) -> Result<(), WorkerError>; } diff --git a/src-tauri/src/lib.rs b/src-tauri/src/lib.rs index c585c4db..bef91ab6 100644 --- a/src-tauri/src/lib.rs +++ b/src-tauri/src/lib.rs @@ -2,22 +2,17 @@ Developer blog: - Had a brain fart trying to figure out some ideas allowing me to run this application as either client or server Originally thought of using Clap library to parse in input, but when I run `cargo tauri dev -- test` the application fail to compile due to unknown arguments when running web framework? - This issue has been solved by alllowing certain argument to run. By default it will try to launch the client user interface of the application. - Additionally, I need to check into the argument and see if there's a way we could just allow user to run --server without ui interface? - Interesting thoughts for sure + This issue has been solved by allowing certain argument to run. By default it will launch the manager version of this application. 9/2/24 -- Decided to rely on using Tauri plugin for cli commands and subcommands. Use that instead of clap. Since Tauri already incorporates Clap anyway. - Had an idea that allows user remotely to locally add blender installation without using GUI interface, This would serves two purposes - allow user to expressly select which blender version they can choose from the remote machine and prevent multiple download instances for the node, in case the target machine does not have it pre-installed. - Eventually, I will need to find a way to spin up a virtual machine and run blender farm on that machine to see about getting networking protocol working in place. This will allow me to do two things - I can continue to develop without needing to fire up a remote machine to test this and verify all packet works as intended while I can run the code in parallel to see if there's any issue I need to work overhead. - This might be another big project to work over the summer to understand how network works in Rust. - -- I noticed that some of the function are getting called twice. Check and see what's going on with React UI side of things - Research into profiling front end ui to ensure the app is not invoking the same command twice. - +- Ended up refactoring the program out. each struct have their respective files and folder associated with their group of services. + I still have problem using libp2p. Originally had it working but it was locking up main thread and program from executing in async. + Going to rely on example until I get this program working again. [F] - find a way to allow GUI interface to run as client mode for non cli users. [F] - consider using channel to stream data https://v2.tauri.app/develop/calling-frontend/#channels [F] - Before release - find a way to add updater https://v2.tauri.app/plugin/updater/ @@ -28,24 +23,25 @@ Developer blog: // Prevents additional console window on Windows in release, DO NOT REMOVE!! #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] -use async_std::fs; use blender::manager::Manager as BlenderManager; use clap::{Parser, Subcommand}; -use domains::worker_store::WorkerStore; use dotenvy::dotenv; -use models::network; -use models::{app_state::AppState /* server_setting::ServerSetting */}; -use services::data_store::sqlite_job_store::SqliteJobStore; +use libp2p::Multiaddr; use services::data_store::sqlite_task_store::SqliteTaskStore; -use services::data_store::sqlite_worker_store::SqliteWorkerStore; use services::{blend_farm::BlendFarm, cli_app::CliApp, tauri_app::TauriApp}; -use sqlx::sqlite::SqlitePoolOptions; -use sqlx::SqlitePool; +use sqlx::{SqlitePool, sqlite::SqliteConnectOptions}; use std::sync::Arc; -use tokio::{spawn, sync::RwLock}; +use tokio::spawn; +use tokio::sync::RwLock; + +use crate::constant::{JOB_TOPIC, NODE_TOPIC}; +use crate::network::controller::Controller; +use crate::services::data_store::sqlite_renders_store::SqliteRenderStore; +pub mod constant; pub mod domains; pub mod models; +pub mod network; pub mod routes; pub mod services; @@ -60,74 +56,107 @@ enum Commands { Client, } -async fn config_sqlite_db() -> Result { - let mut path = BlenderManager::get_config_dir(); - path = path.join("blendfarm.db"); +async fn config_sqlite_db(file_name: &str) -> Result { + // TODO: Ask for user preference. + let user_pref = None; - // create file if it doesn't exist (.config/BlendFarm/blendfarm.db) - if !path.exists() { - let _ = fs::File::create(&path).await; - } + let path = BlenderManager::get_config_dir(user_pref).join(file_name); + let options = SqliteConnectOptions::new() + .filename(path) + .create_if_missing(true); + SqlitePool::connect_with(options).await +} + +async fn setup_connection(controller: &mut Controller) { + // Listen on all interfaces and whatever port OS assigns + let tcp: Multiaddr = "/ip4/0.0.0.0/tcp/0".parse().expect("Shouldn't fail"); + let udp: Multiaddr = "/ip4/0.0.0.0/udp/0/quic-v1" + .parse() + .expect("Shouldn't fail"); - // TODO: Consider thinking about the design behind this. Should we store database connection here or somewhere else? - let url = format!("sqlite://{}", path.as_os_str().to_str().unwrap()); - // macos: "sqlite:///Users/megamind/Library/Application Support/BlendFarm/blendfarm.db" - // dbg!(&url); - let pool = SqlitePoolOptions::new().connect(&url).await?; - sqlx::migrate!().run(&pool).await?; - Ok(pool) + controller.start_listening(tcp).await; + controller.start_listening(udp).await; + + // let's automatically listen to the topics mention above. + // all network interference must subscribe to these topics! + if let Err(e) = controller.subscribe(JOB_TOPIC).await { + eprintln!("Fail to subscribe job topic! {e:?}"); + }; + + if let Err(e) = controller.subscribe(NODE_TOPIC).await { + eprintln!("Fail to subscribe node topic! {e:?}") + }; } #[cfg_attr(mobile, tauri::mobile_entry_point)] pub async fn run() { dotenv().ok(); - // to run custom behaviour + // to collect user inputs for custom user preferences let cli = Cli::parse(); - let db = config_sqlite_db() + // TODO: Ask Cli for the secret_key + let secret_key = None; + + // TODO: insist on loading user_pref here? if there's a custom cli command that insist user path for server settings, we would ask them there. + // let user_pref = ServerSetting::load(); + + // initialize database connection + let db: sqlx::Pool = config_sqlite_db(constant::DATABASE_FILE_NAME) .await .expect("Must have database connection!"); // must have working network services - let (service, controller, receiver) = - network::new().await.expect("Fail to start network service"); + let (mut controller, receiver, server) = network::new(secret_key) + .await + .expect("Fail to start network service"); + + // Network service is spun up on separate thread. + spawn(async move { + server.run().await; + }); - // start network service async - spawn(service.run()); + setup_connection(&mut controller).await; + // TODO: Restructure this to allow running client from GUI mode. let _ = match cli.command { // run as client mode. Some(Commands::Client) => { - // could this be reconsidered? + // eventually I'll move this code into it's own separate codeblock let task_store = SqliteTaskStore::new(db.clone()); + let render_store = SqliteRenderStore::new(db.clone()); + + // we're sharing this across threads? let task_store = Arc::new(RwLock::new(task_store)); - CliApp::new(task_store) + let render_store = Arc::new(RwLock::new(render_store)); + + // here the client wants database connection to task table. Why not provide database connection instead? + CliApp::new(task_store, render_store) .run(controller, receiver) .await .map_err(|e| println!("Error running Cli app: {e:?}")) } // run as GUI mode. - _ => { - let job_store = SqliteJobStore::new(db.clone()); - let mut worker_store = SqliteWorkerStore::new(db.clone()); - - // Clear worker database before usage! - // TODO: Find a better way to optimize this - if let Ok(old_workers) = worker_store.list_worker().await { - for worker in old_workers { - let _ = &worker_store.delete_worker(&worker.machine_id).await; - } - } - - let job_store = Arc::new(RwLock::new(job_store)); - let worker_store = Arc::new(RwLock::new(worker_store)); - TauriApp::new(worker_store, job_store) - .await - .run(controller, receiver) - .await - .map_err(|e| eprintln!("Fail to run Tauri app! {e:?}")) - } + _ => TauriApp::new(&db) + .await + // we're clearing workers? + .clear_workers_collection() + .await + .run(controller, receiver) + .await + .map_err(|e| eprintln!("Fail to run Tauri app! {e:?}")), }; } + +#[cfg(test)] +mod test { + use crate::config_sqlite_db; + + #[tokio::test] + pub async fn validate_creating_database_structure() { + let database_file_name = "blendfarm.db"; + let conn = config_sqlite_db(database_file_name).await; + assert!(conn.is_ok()); + } +} diff --git a/src-tauri/src/models/advertise.rs b/src-tauri/src/models/advertise.rs new file mode 100644 index 00000000..4d9ed5fc --- /dev/null +++ b/src-tauri/src/models/advertise.rs @@ -0,0 +1,19 @@ +use std::path::PathBuf; +use uuid::Uuid; + +#[derive(Debug)] +pub struct Advertise { + pub id: Uuid, + pub ad_name: String, + pub file_path: PathBuf, +} + +impl Advertise { + pub fn new(ad_name: String, file_path: PathBuf) -> Self { + Self { + id: Uuid::new_v4(), + ad_name, + file_path, + } + } +} diff --git a/src-tauri/src/models/app_state.rs b/src-tauri/src/models/app_state.rs index 89e84d46..6af2fde2 100644 --- a/src-tauri/src/models/app_state.rs +++ b/src-tauri/src/models/app_state.rs @@ -1,18 +1,25 @@ -use super::server_setting::ServerSetting; -use crate::domains::{job_store::JobStore, worker_store::WorkerStore}; +use crate::models::server_setting::ServerSetting; use crate::services::tauri_app::UiCommand; -use blender::manager::Manager as BlenderManager; -use std::sync::Arc; -use tokio::sync::{RwLock, mpsc::Sender}; +use crate::models::setting_action::SettingsAction; +use futures::{channel::mpsc::{self, Sender, SendError}, SinkExt, StreamExt}; -pub type SafeLock = Arc>; - -// wonder if this is required? -// #[derive(Clone)] +#[derive(Clone)] pub struct AppState { - pub manager: SafeLock, - pub to_network: Sender, - pub setting: SafeLock, - pub job_db: SafeLock<(dyn JobStore + Send + Sync + 'static)>, - pub worker_db: SafeLock<(dyn WorkerStore + Send + Sync + 'static)>, + pub invoke: Sender, +} + +impl AppState { + + pub fn new( invoke: Sender ) -> Self { + Self { + invoke + } + } + + pub async fn get_settings(&mut self) -> Result { + let (sender, mut receiver) = mpsc::channel(1); + let event = UiCommand::Settings(SettingsAction::Get(sender)); + self.invoke.send(event).await?; + Ok(receiver.select_next_some().await) + } } diff --git a/src-tauri/src/models/behaviour.rs b/src-tauri/src/models/behaviour.rs index 49b9833c..12744f39 100644 --- a/src-tauri/src/models/behaviour.rs +++ b/src-tauri/src/models/behaviour.rs @@ -1,21 +1,31 @@ -use libp2p::{gossipsub, kad, mdns, ping, swarm::NetworkBehaviour}; +use libp2p::{ + gossipsub::{self}, + kad::{self}, + mdns, + swarm::NetworkBehaviour, +}; use libp2p_request_response::cbor; use serde::{Deserialize, Serialize}; #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FileRequest(pub String); + +// may be changed to use stream? #[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)] pub struct FileResponse(pub Vec); #[derive(NetworkBehaviour)] pub struct BlendFarmBehaviour { - pub ping: ping::Behaviour, // file transfer response protocol pub request_response: cbor::Behaviour, + // Communication between peers to pepers pub gossipsub: gossipsub::Behaviour, + // self discovery network service pub mdns: mdns::tokio::Behaviour, + // used to provide file availability - pub kad: kad::Behaviour, + pub kademlia: kad::Behaviour, } + diff --git a/src-tauri/src/models/blender_action.rs b/src-tauri/src/models/blender_action.rs new file mode 100644 index 00000000..ba9e9486 --- /dev/null +++ b/src-tauri/src/models/blender_action.rs @@ -0,0 +1,29 @@ +use std::path::PathBuf; + +use blender::blender::Blender; +use futures::channel::mpsc::Sender; +use semver::Version; + +use crate::services::tauri_app::{BlenderQuery, QueryMode}; + +#[derive(Debug)] +pub enum BlenderAction { + Add(PathBuf), + List(Sender>>, QueryMode), + Get(Version, Sender>), + Disconnect(Blender), // detach links associated with file path, but does not delete local installation! + Remove(Blender), // deletes local installation of blender, use it as last resort option. (E.g. force cache clear/reinstall/ corrupted copy) +} + +impl PartialEq for BlenderAction { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Add(l0), Self::Add(r0)) => l0 == r0, + (Self::List(.., l0), Self::List(.., r0)) => l0 == r0, + (Self::Get(l0, ..), Self::Get(r0, ..)) => l0 == r0, + (Self::Disconnect(l0), Self::Disconnect(r0)) => l0 == r0, + (Self::Remove(l0), Self::Remove(r0)) => l0 == r0, + _ => false, + } + } +} diff --git a/src-tauri/src/models/common.rs b/src-tauri/src/models/common.rs index dbf0de81..c5e414b4 100644 --- a/src-tauri/src/models/common.rs +++ b/src-tauri/src/models/common.rs @@ -1,8 +1,11 @@ -use std::path::PathBuf; +// use std::path::PathBuf; -use serde::{Deserialize, Serialize}; +// use ser dde::{Deserialize, Serialize}; + +/* // not sure if I still need this or keep it separated? +#[allow(dead_code)] #[derive(Serialize, Deserialize)] pub enum SenderMsg { FileRequest(String, usize), @@ -10,7 +13,10 @@ pub enum SenderMsg { Render(PathBuf, i32), } +#[allow(dead_code)] #[derive(Serialize, Deserialize)] pub enum ReceiverMsg { CanReceive(bool), } + +*/ diff --git a/src-tauri/src/models/computer_spec.rs b/src-tauri/src/models/computer_spec.rs index b1f09293..2058d377 100644 --- a/src-tauri/src/models/computer_spec.rs +++ b/src-tauri/src/models/computer_spec.rs @@ -1,10 +1,14 @@ +use libp2p::Multiaddr; use machine_info::Machine; use serde::{Deserialize, Serialize}; use std::env::consts; +pub type Hostname = String; + #[derive(Debug, Serialize, Deserialize, Clone)] pub struct ComputerSpec { - pub host: String, + pub multiaddr: Multiaddr, + pub host: Hostname, pub os: String, pub arch: String, pub memory: u64, @@ -14,7 +18,7 @@ pub struct ComputerSpec { } impl ComputerSpec { - pub fn new(machine: &mut Machine) -> Self { + pub fn new(multiaddr: Multiaddr, machine: &mut Machine) -> Self { let sys_info = machine.system_info(); let memory = &sys_info.memory; let host = &sys_info.hostname; @@ -25,6 +29,7 @@ impl ComputerSpec { let cores = &sys_info.total_processors; Self { + multiaddr, host: host.to_owned(), os: consts::OS.to_owned(), arch: consts::ARCH.to_owned(), diff --git a/src-tauri/src/models/constant.rs b/src-tauri/src/models/constant.rs new file mode 100644 index 00000000..7a38245f --- /dev/null +++ b/src-tauri/src/models/constant.rs @@ -0,0 +1,9 @@ +// TODO: make this user adjustable. +// Ideally, this should be store under BlendFarmUserSettings +// pub const MAX_FRAME_CHUNK_SIZE: i32 = 30; + +#[cfg(test)] +pub mod test { + pub const EXAMPLE_FILE: &str = "./../blender_rs/examples/assets/test.blend"; + pub const EXAMPLE_OUTPUT: &str = "./../blender_rs/examples/assets/"; +} \ No newline at end of file diff --git a/src-tauri/src/models/ffmpeg.rs b/src-tauri/src/models/ffmpeg.rs deleted file mode 100644 index 169b852a..00000000 --- a/src-tauri/src/models/ffmpeg.rs +++ /dev/null @@ -1,2 +0,0 @@ -// use this to invoke FFMpeg to composite frame into short video animation. -// this will be used on the manager side of application, as we want to composit incoming frame jobs into video to preview the final render image result. diff --git a/src-tauri/src/models/job.rs b/src-tauri/src/models/job.rs index 9a486476..0f734617 100644 --- a/src-tauri/src/models/job.rs +++ b/src-tauri/src/models/job.rs @@ -3,117 +3,239 @@ - Original idea behind this was to use PhantomData to mitigate the status of the job instead of reading from enum. Need to refresh materials about PhantomData, and how I can translate this data information for front end to update/reflect changes The idea is to change the struct to have state of the job. + I think the limitation for this is serialization/deserialization property. - I need to fetch the handles so that I can maintain and monitor all node activity. - TODO: See about migrating Sender code into this module? */ use super::task::Task; -use crate::domains::job_store::JobError; -use blender::models::mode::Mode; +use super::with_id::WithId; +use crate::{domains::job_store::JobError, models::project_file::ProjectFile}; +use blender::models::mode::RenderMode; +use futures::channel::mpsc::Sender; use semver::Version; use serde::{Deserialize, Serialize}; -use std::collections::HashMap; -use std::{hash::Hash, path::PathBuf}; +use std::{ops::Range, path::PathBuf}; use uuid::Uuid; +use crate::network::PeerIdString; #[derive(Debug, Serialize, Deserialize)] pub enum JobEvent { - Render(Task), + Render(PeerIdString, Task), Remove(Uuid), - RequestJob, + Failed(String), + RequestTask(PeerIdString), ImageCompleted { job_id: Uuid, frame: Frame, file_name: String, }, - JobComplete, + AskForCompletedJobFrameList(JobId), + ImageCompletedList { + job_id: JobId, + files: Vec, + }, + TaskComplete, // what's the difference between JobComplete and TaskComplete? Error(JobError), } +#[derive(Debug)] +pub enum JobAction { + Find(JobId, Sender>), + Update(CreatedJobDto), + Create(NewJobDto, Sender>), + Kill(JobId), + All(Sender>>), + // we will ask all of the node on the network if there's any completed job list. + // The node will advertise their collection of completed job + // the host will be responsible to compare with the current output files and + // see if there's any missing job. If there is missing frame then + // we will ask to fetch for that completed image back + AskForCompletedList(JobId), + Advertise(JobId), +} + +// Used to ignore sender types comparsion. We do not care about sender equality. +impl PartialEq for JobAction { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Find(l0, ..), Self::Find(r0, ..)) => l0 == r0, + (Self::Update(l0), Self::Update(r0)) => l0.id == r0.id, + (Self::Create(l0, ..), Self::Create(r0,.. )) => l0 == r0, + (Self::Kill(l0), Self::Kill(r0)) => l0 == r0, + (Self::All(..), Self::All(..)) => true, + (Self::AskForCompletedList(l0), Self::AskForCompletedList(r0)) => l0 == r0, + (Self::Advertise(l0), Self::Advertise(r0)) => l0 == r0, + _ => false, + } + } +} + +pub type JobId = Uuid; pub type Frame = i32; +pub type Output = PathBuf; +pub type NewJobDto = Job; +pub type CreatedJobDto = WithId; // This job is created by the manager and will be used to help determine the individual task created for the workers // we will derive this job into separate task for individual workers to process based on chunk size. -#[derive(Debug, Serialize, Deserialize, Clone, sqlx::FromRow)] +#[derive( + Debug, Serialize, Deserialize, Clone, sqlx::FromRow, sqlx::Encode, sqlx::Decode, PartialEq, +)] pub struct Job { - /// Unique job identifier - pub id: Uuid, /// contains the information to specify the kind of job to render (We could auto fill this from blender peek function?) - pub mode: Mode, + mode: RenderMode, + /// Path to blender files - pub project_file: PathBuf, + project_file: ProjectFile, + // target blender version - pub blender_version: Version, + blender_version: Version, + // target output destination - pub output: PathBuf, - // completed render data. - // TODO: discuss this? Let's map this out and see how we can better utilize this structure? - renders: HashMap, + output: Output, } impl Job { - /// Create a new job entry with provided all information intact. Used for holding database records - pub fn new( - id: Uuid, - mode: Mode, - project_file: PathBuf, - blender_version: Version, - output: PathBuf, - renders: HashMap, + // private - no validation, we trust that the validation is done from public api. + fn new( + mode: RenderMode, + project_file: ProjectFile, + blender_version: Version, // TODO: see if we can validate if this job uses the correct blender version + output: Output, // must be a valid directory ) -> Self { Self { - id, mode, project_file, blender_version, output, - renders, } } - /// Create a new job entry from the following parameter inputs + /// Create a new job entry with provided all information intact. Used for holding database records pub fn from( + mode: RenderMode, project_file: PathBuf, + version: Version, output: PathBuf, - blender_version: Version, - mode: Mode, - ) -> Self { - Self { - id: Uuid::new_v4(), - mode, - project_file, - blender_version, - output, - renders: Default::default(), + ) -> Result { + match ProjectFile::from(project_file) { + Ok(file) => Ok(Job::new(mode, file, version, output)), + Err(e) => Err(JobError::InvalidFile(e.to_string())), + } + } + + pub fn generate_task(self, id: Uuid) -> Option { + // in this case, a job would have break up into pieces for worker client to receive and start a new job + // first thing first, how can I tell if the job is completed or not? + let range = self.clone().into(); + let job_id = WithId { id, item: self }; + + match Task::from(job_id, range) { + Ok(task) => Some(task), + Err(e) => { + println!("Unable to make task? {e:?}"); + None + } } } - pub fn get_file_name(&self) -> &str { + // TODO: See if there's a better way to obtain file name, project path, and version + pub fn get_file_name_expected(&self) -> &str { + // this line could potentially break the application + // if the project file was malform or set to use directory instead. self.project_file.file_name().unwrap().to_str().unwrap() } +} - pub fn get_project_path(&self) -> &PathBuf { +impl AsRef for Job { + fn as_ref(&self) -> &ProjectFile { &self.project_file } +} - pub fn get_version(&self) -> &Version { +impl AsRef for Job { + fn as_ref(&self) -> &Version { &self.blender_version } } -impl AsRef for Job { - fn as_ref(&self) -> &Uuid { - &self.id +/// return the job output destination (Should be used on the host machine) +impl AsRef for Job { + fn as_ref(&self) -> &Output { + &self.output } } -impl PartialEq for Job { - fn eq(&self, other: &Self) -> bool { - self.id == other.id +impl AsRef for Job { + fn as_ref(&self) -> &RenderMode { + &self.mode } } -impl Hash for Job { - fn hash(&self, state: &mut H) { - self.id.hash(state); +// TODO: Clone/to_owned() is used here. +impl Into> for Job { + fn into(self) -> Range { + match self.mode { + RenderMode::Animation(range) => range.clone(), + RenderMode::Frame(frame) => Range { + start: frame.to_owned(), + end: frame.to_owned(), + }, + } } } + +#[cfg(test)] +pub(crate) mod test { + use super::*; + use crate::models::constant::test::{EXAMPLE_FILE, EXAMPLE_OUTPUT}; + use std::path::Path; + + pub fn scaffold_job() -> Job { + let mode = RenderMode::Frame(1); + let file = Path::new(EXAMPLE_FILE); + let project_file = file.to_path_buf(); + let project_file = + ProjectFile::from(project_file).expect("expect this to work without issue"); + let version = Version::new(4, 4, 0); + let dir = Path::new(EXAMPLE_OUTPUT); + let output = dir.to_path_buf(); + Job::new(mode, project_file, version, output) + } + + // we should at least try to test it against public api + #[test] + fn create_job_successful() { + let file = Path::new(EXAMPLE_FILE); + let mode = RenderMode::Frame(1); + let version = Version::new(1, 1, 1); + let output = Path::new("./test/"); + let job = Job::from( + mode.clone(), + file.to_path_buf(), + version.clone(), + output.to_path_buf(), + ); + + let project_file = + ProjectFile::from(file.to_path_buf()).expect("Should be valid project file"); + + assert!(job.is_ok()); + let job = job.unwrap(); + + assert_eq!(job.mode, mode); + assert_eq!(job.output, output); + assert_eq!(AsRef::::as_ref(&job), &project_file); + assert_eq!(AsRef::::as_ref(&job), &version); + assert_eq!( + job.get_file_name_expected(), + file.file_name() + .expect("Should have valid file name") + .to_str() + .expect("Shoudl have valid file name!") + ); + } + + #[test] + fn invalid_project_file_path_should_fail() {} +} diff --git a/src-tauri/src/models/message.rs b/src-tauri/src/models/message.rs deleted file mode 100644 index 90bdb38a..00000000 --- a/src-tauri/src/models/message.rs +++ /dev/null @@ -1,78 +0,0 @@ -use super::behaviour::FileResponse; -use super::computer_spec::ComputerSpec; -use super::job::JobEvent; -use futures::channel::oneshot; -use libp2p::{Multiaddr, PeerId}; -use libp2p_request_response::ResponseChannel; -use std::{collections::HashSet, error::Error}; -use thiserror::Error; - -#[derive(Debug, Error)] -pub enum NetworkError { - #[error("Unable to listen: {0}")] - UnableToListen(String), - #[error("Not Connected")] - NotConnected, - #[error("Invalid connection")] - Invalid, - #[error("Bad Input")] - BadInput, - #[error("Send Error: {0}")] - SendError(String), - #[error("No peers on network have this file available to download!")] - NoPeerProviderFound, - #[error("Unable to save download file: {0}")] - UnableToSave(String), - #[error("Timeout, unable to connect peer")] - Timeout, -} - -// Send commands to network. -#[derive(Debug)] -pub enum NetCommand { - IncomingWorker(PeerId), - Status(String), - SubscribeTopic(String), - UnsubscribeTopic(String), - JobStatus(PeerId, JobEvent), - // use this event to send message to a specific node - StartProviding { - file_name: String, - sender: oneshot::Sender<()>, - }, - GetProviders { - file_name: String, - sender: oneshot::Sender>, - }, - RequestFile { - peer_id: PeerId, - file_name: String, - sender: oneshot::Sender, Box>>, - }, - RespondFile { - file: Vec, - channel: ResponseChannel, - }, - Dial { - peer_id: PeerId, - peer_addr: Multiaddr, - sender: oneshot::Sender>>, - }, -} - -// TODO: Received network events. -#[derive(Debug)] -pub enum NetEvent { - // Share basic computer configuration for sharing Blender compatible executable over the network. (To help speed up the installation over the network.) - Status(PeerId, String), // Receive message status (To GUI?) Could I treat this like Chat messages? - OnConnected(PeerId), - NodeDiscovered(PeerId, ComputerSpec), - // TODO: Future impl. Use this to send computer activity - // Heartbeat() // share hardware statistic monitor heartbeat. (CPU/GPU/RAM activity readings) - NodeDisconnected(PeerId), // On Node disconnected - InboundRequest { - request: String, - channel: ResponseChannel, - }, - JobUpdate(PeerId, JobEvent), -} diff --git a/src-tauri/src/models/mod.rs b/src-tauri/src/models/mod.rs index 107650df..4b080c71 100644 --- a/src-tauri/src/models/mod.rs +++ b/src-tauri/src/models/mod.rs @@ -1,14 +1,17 @@ +pub mod advertise; pub mod app_state; pub mod behaviour; pub(crate) mod common; pub(crate) mod computer_spec; +pub(crate) mod constant; pub mod error; pub(crate) mod job; -pub mod message; -pub mod network; pub(crate) mod project_file; pub(crate) mod render_info; pub(crate) mod task; // pub mod render_queue; pub(crate) mod server_setting; +pub mod with_id; pub mod worker; +pub(crate) mod blender_action; +pub(crate) mod setting_action; diff --git a/src-tauri/src/models/network.rs b/src-tauri/src/models/network.rs deleted file mode 100644 index 58e01d01..00000000 --- a/src-tauri/src/models/network.rs +++ /dev/null @@ -1,676 +0,0 @@ -use super::behaviour::{BlendFarmBehaviour, FileRequest, FileResponse}; -use super::computer_spec::ComputerSpec; -use super::job::JobEvent; -use super::message::{NetCommand, NetEvent, NetworkError}; -use super::server_setting::ServerSetting; -use crate::models::behaviour::BlendFarmBehaviourEvent; -use core::str; -use futures::{channel::oneshot, prelude::*, StreamExt}; -use libp2p::kad::RecordKey; -use libp2p::multiaddr::Protocol; -use libp2p::{ - gossipsub::{self, IdentTopic}, - kad, mdns, ping, - swarm::{Swarm, SwarmEvent}, - tcp, Multiaddr, PeerId, StreamProtocol, SwarmBuilder, -}; -use libp2p_request_response::{OutboundRequestId, ProtocolSupport, ResponseChannel}; -use machine_info::Machine; -use std::collections::{hash_map, HashMap, HashSet}; -use std::error::Error; -use std::path::PathBuf; -use std::time::Duration; -use std::u64; -use tokio::sync::mpsc::{self, Receiver, Sender}; -use tokio::{io, select}; - -/* -Network Service - Provides simple network interface for peer-to-peer network for BlendFarm. -Includes mDNS () -*/ - -pub const STATUS: &str = "blendfarm/status"; -pub const SPEC: &str = "blendfarm/spec"; -pub const JOB: &str = "blendfarm/job"; -pub const HEARTBEAT: &str = "blendfarm/heartbeat"; -const TRANSFER: &str = "/file-transfer/1"; - -// the tuples return three objects -// the NetworkService holds the network loop operation -// the Network Controller to send command to network service -// the Receiver from network services -pub async fn new() -> Result<(NetworkService, NetworkController, Receiver), NetworkError> -{ - let duration = Duration::from_secs(u64::MAX); - // let id_keys = identity::Keypair::generate_ed25519(); - let tcp_config: tcp::Config = tcp::Config::default(); - - let mut swarm = SwarmBuilder::with_new_identity() - .with_tokio() - .with_tcp( - tcp_config, - libp2p::tls::Config::new, - libp2p::yamux::Config::default, - ) - .expect("Should be able to build with tcp configuration?") - .with_quic() - .with_behaviour(|key| { - let ping_config = ping::Config::default(); - let ping = ping::Behaviour::new(ping_config); - - let gossipsub_config = gossipsub::ConfigBuilder::default() - .heartbeat_interval(Duration::from_secs(10)) - // .validation_mode(gossipsub::ValidationMode::Strict) - // .message_id_fn(message_id_fn) - .build() - .map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))?; - - // p2p communication - let gossipsub = gossipsub::Behaviour::new( - gossipsub::MessageAuthenticity::Signed(key.clone()), - gossipsub_config, - ) - .expect("Fail to create gossipsub behaviour"); - - // network discovery usage - let mdns = - mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id()) - .expect("Fail to create mdns behaviour!"); - - // Used to provide file provision list - let kad = kad::Behaviour::new( - key.public().to_peer_id(), - kad::store::MemoryStore::new(key.public().to_peer_id()), - ); - - let rr_config = libp2p_request_response::Config::default(); - let protocol = [(StreamProtocol::new(TRANSFER), ProtocolSupport::Full)]; - let request_response = libp2p_request_response::Behaviour::new(protocol, rr_config); - - Ok(BlendFarmBehaviour { - ping, - request_response, - gossipsub, - mdns, - kad, - }) - }) - .expect("Expect to build behaviour") - .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(duration)) - .build(); - - let tcp: Multiaddr = "/ip4/0.0.0.0/tcp/0" - .parse() - .map_err(|_| NetworkError::BadInput)?; - - let udp: Multiaddr = "/ip4/0.0.0.0/udp/0/quic-v1" - .parse() - .map_err(|_| NetworkError::BadInput)?; - - // Begin listening on tcp and udp as server - swarm - .listen_on(tcp) - .map_err(|e| NetworkError::UnableToListen(e.to_string()))?; - - swarm - .listen_on(udp) - .map_err(|e| NetworkError::UnableToListen(e.to_string()))?; - - // set the kad as server mode - swarm.behaviour_mut().kad.set_mode(Some(kad::Mode::Server)); - - // the command sender is used for outside method to send message commands to network queue - let (command_sender, command_receiver) = mpsc::channel::(32); - - // the event sender is used to handle incoming network message. E.g. RunJob - let (event_sender, event_receiver) = mpsc::channel::(32); - - let local_peer_id = swarm.local_peer_id().clone(); - - Ok(( - NetworkService { - swarm, - command_receiver, - event_sender, - public_addr: None, - machine: Machine::new(), - pending_dial: Default::default(), - pending_get_providers: Default::default(), - pending_start_providing: Default::default(), - pending_request_file: Default::default(), - // pending_task: Default::default(), - }, - NetworkController { - sender: command_sender, - settings: ServerSetting::load(), - providing_files: Default::default(), - // there could be some other factor this this may not work as intended? Let's find out soon! - public_id: local_peer_id, - }, - event_receiver, - )) -} - -// strange that I don't have the local peer id? -#[derive(Clone)] -pub struct NetworkController { - sender: mpsc::Sender, - pub settings: ServerSetting, - // Use string to defer OS specific path system. This will be treated as a URI instead. /job_id/frame - pub providing_files: HashMap, - // making it public until we can figure out how to mitigate the usage of variable. - pub public_id: PeerId, -} - -impl NetworkController { - pub async fn subscribe_to_topic(&mut self, topic: String) { - self.sender - .send(NetCommand::SubscribeTopic(topic)) - .await - .unwrap(); - } - - pub async fn unsubscribe_from_topic(&mut self, topic: String) { - self.sender - .send(NetCommand::UnsubscribeTopic(topic)) - .await - .unwrap(); - } - - pub async fn send_status(&mut self, status: String) { - println!("[Status]: {status}"); - self.sender - .send(NetCommand::Status(status)) - .await - .expect("Command should not been dropped"); - } - - // How do I get the peers info I want to communicate with? - pub async fn send_job_message(&mut self, target: PeerId, event: JobEvent) { - self.sender - .send(NetCommand::JobStatus(target, event)) - .await - .expect("Command should not be dropped"); - } - - // Share computer info to - pub async fn share_computer_info(&mut self, peer_id: PeerId) { - self.sender - .send(NetCommand::IncomingWorker(peer_id)) - .await - .expect("Command should not have been dropped"); - } - - pub async fn start_providing(&mut self, file_name: String, path: PathBuf) { - let (sender, receiver) = oneshot::channel(); - println!("Start providing file {:?}", file_name); - self.providing_files.insert(file_name.clone(), path); - let cmd = NetCommand::StartProviding { file_name, sender }; - self.sender - .send(cmd) - .await - .expect("Command receiver not to be dropped"); - receiver.await.expect("Sender should not be dropped"); - } - - pub async fn get_providers(&mut self, file_name: &str) -> HashSet { - let (sender, receiver) = oneshot::channel(); - self.sender - .send(NetCommand::GetProviders { - file_name: file_name.to_string(), - sender, - }) - .await - .expect("Command receiver should not be dropped"); - receiver.await.expect("Sender should not be dropped") - } - - pub async fn get_file_from_peers( - &mut self, - file_name: &str, - destination: &PathBuf, - ) -> Result { - let providers = self.get_providers(&file_name).await; - if providers.is_empty() { - return Err(NetworkError::NoPeerProviderFound); - } - - let requests = providers.into_iter().map(|p| { - let mut client = self.clone(); - // should I just request a file from one peer instead? - async move { client.request_file(p, file_name).await }.boxed() - }); - - let content = match futures::future::select_ok(requests).await { - Ok(data) => data.0, - Err(e) => { - // Received a "Timeout" error? What does that mean? Should I try to reconnect? - eprintln!("No peer found? {e:?}"); - return Err(NetworkError::Timeout); - } - }; - - let file_path = destination.join(file_name); - match async_std::fs::write(file_path.clone(), content).await { - Ok(_) => Ok(file_path), - Err(e) => Err(NetworkError::UnableToSave(e.to_string())), - } - } - - pub async fn dial( - &mut self, - peer_id: PeerId, - peer_addr: Multiaddr, - ) -> Result<(), Box> { - let (sender, receiver) = oneshot::channel(); - self.sender - .send(NetCommand::Dial { - peer_id, - peer_addr, - sender, - }) - .await - .expect("Command receiver should not be dropped"); - receiver - .await - .expect("Command receiver should not be dropped") - } - - async fn request_file( - &mut self, - peer_id: PeerId, - file_name: &str, - ) -> Result, Box> { - let (sender, receiver) = oneshot::channel(); - self.sender - .send(NetCommand::RequestFile { - peer_id, - file_name: file_name.into(), - sender, - }) - .await - .expect("Command should not be dropped"); - receiver.await.expect("Sender should not be dropped") - } - - pub(crate) async fn respond_file( - &mut self, - file: Vec, - channel: ResponseChannel, - ) { - self.sender - .send(NetCommand::RespondFile { file, channel }) - .await - .expect("Command should not be dropped"); - } -} - -// this will help launch libp2p network. Should use QUIC whenever possible! -pub struct NetworkService { - // swarm behaviour - interface to the network - swarm: Swarm, - - // receive Network command - pub command_receiver: Receiver, - - // Used to collect computer information to distribute across network. - machine: Machine, - - // Send Network event to subscribers. - event_sender: Sender, - - public_addr: Option, - - // empheral key used to stored and communicate with. - pending_get_providers: HashMap>>, - pending_start_providing: HashMap>, - pending_request_file: - HashMap, Box>>>, - pending_dial: HashMap>>>, - // pending_task: HashMap>>>, -} - -impl NetworkService { - // send command - async fn handle_command(&mut self, cmd: NetCommand) { - match cmd { - NetCommand::Status(msg) => { - let data = msg.as_bytes(); - let topic = IdentTopic::new(STATUS); - if let Err(e) = self.swarm.behaviour_mut().gossipsub.publish(topic, data) { - eprintln!("Fail to send status over network! {e:?}"); - } - } - NetCommand::RequestFile { - peer_id, - file_name, - sender, - } => { - let request_id = self - .swarm - .behaviour_mut() - .request_response - .send_request(&peer_id, FileRequest(file_name.into())); - self.pending_request_file.insert(request_id, sender); - } - NetCommand::RespondFile { file, channel } => { - self.swarm - .behaviour_mut() - .request_response - .send_response(channel, FileResponse(file)) - .expect("Connection to peer may still be open?"); - } - NetCommand::IncomingWorker(peer_id) => { - let spec = ComputerSpec::new(&mut self.machine); - let data = bincode::serialize(&spec).unwrap(); - let topic = IdentTopic::new(SPEC); - let _ = self.swarm.dial(peer_id); - if let Err(e) = self.swarm.behaviour_mut().gossipsub.publish(topic, data) { - eprintln!("Fail to send identity to swarm! {e:?}"); - }; - } - NetCommand::GetProviders { file_name, sender } => { - let key = RecordKey::new(&file_name.as_bytes()); - let query_id = self.swarm.behaviour_mut().kad.get_providers(key.into()); - self.pending_get_providers.insert(query_id, sender); - } - NetCommand::StartProviding { file_name, sender } => { - let provider_key = RecordKey::new(&file_name.as_bytes()); - let query_id = self - .swarm - .behaviour_mut() - .kad - .start_providing(provider_key) - .expect("No store error."); - - self.pending_start_providing.insert(query_id, sender); - } - NetCommand::SubscribeTopic(topic) => { - let ident_topic = IdentTopic::new(topic); - self.swarm - .behaviour_mut() - .gossipsub - .subscribe(&ident_topic) - .unwrap(); - } - NetCommand::UnsubscribeTopic(topic) => { - let ident_topic = IdentTopic::new(topic); - self.swarm - .behaviour_mut() - .gossipsub - .unsubscribe(&ident_topic); - } - // what was I'm suppose to do here? - NetCommand::JobStatus(_peer_id, _event) => { - /* - // convert data into json format. - // let data = bincode::serialize(&status).unwrap(); - - // TODO: Read more about libp2p and how I can just connect to one machine and send that machine job status information. - // let _ = self.swarm.dial(target); - // once we have a tcp/udp/quik connection, we should only send one task over and end the pipe. - - // TODO: Find a way to send JobEvent to specific target machine? - // let topic = IdentTopic::new(JOB); - // if let Err(e) = self.swarm.behaviour_mut().gossipsub.publish(topic, data) { - // eprintln!("Fail to send job! {e:?}"); - // } - */ - - /* - Let's break this down, we receive a worker with peer_id and peer_addr, both of which will be used to establish communication - Once we establish a communication, that target peer will need to receive the pending task we have assigned for them. - For now, we will try to dial the target peer, and append the task to our network service pool of pending task. - */ - // self.pending_task.insert(peer_id, ); - } - NetCommand::Dial { - peer_id, - peer_addr, - sender, - } => { - if let hash_map::Entry::Vacant(e) = self.pending_dial.entry(peer_id) { - self.swarm - .behaviour_mut() - .kad - .add_address(&peer_id, peer_addr.clone()); - match self.swarm.dial(peer_addr.with(Protocol::P2p(peer_id))) { - Ok(()) => { - e.insert(sender); - } - Err(e) => { - let _ = sender.send(Err(Box::new(e))); - } - } - } - } - }; - } - - async fn handle_event(&mut self, event: SwarmEvent) { - match event { - SwarmEvent::Behaviour(BlendFarmBehaviourEvent::Mdns(mdns)) => { - self.handle_mdns(mdns).await - } - SwarmEvent::Behaviour(BlendFarmBehaviourEvent::Gossipsub(gossip)) => { - self.handle_gossip(gossip).await - } - SwarmEvent::Behaviour(BlendFarmBehaviourEvent::Kad(kad)) => { - self.handle_kademila(kad).await - } - SwarmEvent::Behaviour(BlendFarmBehaviourEvent::RequestResponse(rr)) => { - self.handle_response(rr).await - } - // Once the swarm establish connection, we then send the peer_id we connected to. - SwarmEvent::ConnectionEstablished { peer_id, .. } => { - self.event_sender - .send(NetEvent::OnConnected(peer_id)) - .await - .unwrap(); - } - SwarmEvent::ConnectionClosed { peer_id, .. } => { - self.event_sender - .send(NetEvent::NodeDisconnected(peer_id)) - .await - .unwrap(); - } - SwarmEvent::NewListenAddr { address, .. } => { - // hmm.. I need to capture the address here? - // how do I save the address? - if address.protocol_stack().any(|f| f.contains("tcp")) { - dbg!(&address); - self.public_addr = Some(address); - } - } - _ => { - println!("{event:?}"); - } - } - } - - async fn handle_response( - &mut self, - event: libp2p_request_response::Event, - ) { - match event { - libp2p_request_response::Event::Message { message, .. } => match message { - libp2p_request_response::Message::Request { - request, channel, .. - } => { - self.event_sender - .send(NetEvent::InboundRequest { - request: request.0, - channel, - }) - .await - .expect("Event receiver should not be dropped!"); - } - libp2p_request_response::Message::Response { - request_id, - response, - } => { - if let Err(e) = self - .pending_request_file - .remove(&request_id) - .expect("Request is still pending?") - .send(Ok(response.0)) - { - eprintln!("libp2p Response Error: {e:?}"); - } - } - }, - libp2p_request_response::Event::OutboundFailure { - request_id, error, .. - } => { - if let Err(e) = self - .pending_request_file - .remove(&request_id) - .expect("Request is still pending") - .send(Err(Box::new(error))) - { - eprintln!("libp2p outbound fail: {e:?}"); - } - } - libp2p_request_response::Event::ResponseSent { .. } => {} - _ => {} - } - } - - async fn handle_mdns(&mut self, event: mdns::Event) { - match event { - mdns::Event::Discovered(peers) => { - for (peer_id, address) in peers { - self.swarm - .behaviour_mut() - .gossipsub - .add_explicit_peer(&peer_id); - - // add the discover node to kademlia list. - self.swarm - .behaviour_mut() - .kad - .add_address(&peer_id, address); - } - } - mdns::Event::Expired(peers) => { - for (peer_id, ..) in peers { - self.swarm - .behaviour_mut() - .gossipsub - .remove_explicit_peer(&peer_id); - } - } - }; - } - - // TODO: Figure out how I can use the match operator for TopicHash. I'd like to use the TopicHash static variable above. - async fn handle_gossip(&mut self, event: gossipsub::Event) { - match event { - gossipsub::Event::Message { message, .. } => match message.topic.as_str() { - SPEC => { - let source = message.source.expect("Source cannot be empty!"); - let specs = - bincode::deserialize(&message.data).expect("Fail to parse Computer Specs!"); - if let Err(e) = self - .event_sender - .send(NetEvent::NodeDiscovered(source, specs)) - .await - { - eprintln!("Something failed? {e:?}"); - } - } - STATUS => { - let source = message.source.expect("Source cannot be empty!"); - let msg = String::from_utf8(message.data).unwrap(); - if let Err(e) = self.event_sender.send(NetEvent::Status(source, msg)).await { - eprintln!("Something failed? {e:?}"); - } - } - JOB => { - let peer_id = self.swarm.local_peer_id(); - let job_event = - bincode::deserialize(&message.data).expect("Fail to parse Job data!"); - if let Err(e) = self - .event_sender - .send(NetEvent::JobUpdate(peer_id.clone(), job_event)) - .await - { - eprintln!("Something failed? {e:?}"); - } - } - _ => { - let topic = message.topic.as_str(); - let data = String::from_utf8(message.data).unwrap(); - println!("Intercepted signal here? How to approach this? topic:{topic} | data:{data}"); - // TODO: We may intercept signal for other purpose here, how can I do that? - } - }, - _ => {} - } - } - - // Handle kademila events (Used for file sharing) - async fn handle_kademila(&mut self, event: kad::Event) { - match event { - kad::Event::OutboundQueryProgressed { - id, - result: kad::QueryResult::StartProviding(_), - .. - } => { - let sender: oneshot::Sender<()> = self - .pending_start_providing - .remove(&id) - .expect("Completed query to be previously pending."); - let _ = sender.send(()); - } - kad::Event::OutboundQueryProgressed { - id, - result: - kad::QueryResult::GetProviders(Ok(kad::GetProvidersOk::FoundProviders { - providers, - .. - })), - .. - } => { - if let Some(sender) = self.pending_get_providers.remove(&id) { - sender.send(providers).expect("Receiver not to be dropped"); - self.swarm - .behaviour_mut() - .kad - .query_mut(&id) - .unwrap() - .finish(); - } - } - kad::Event::OutboundQueryProgressed { - result: - kad::QueryResult::GetProviders(Ok( - kad::GetProvidersOk::FinishedWithNoAdditionalRecord { .. }, - )), - .. - } => {} - _ => {} - } - } - - pub async fn run(mut self) { - if let Err(e) = tokio::spawn(async move { - loop { - select! { - event = self.swarm.select_next_some() => self.handle_event(event).await, - Some(cmd) = self.command_receiver.recv() => self.handle_command(cmd).await, - } - } - }) - .await - { - println!("fail to start background pool for network run! {e:?}"); - } - } -} - -impl AsRef> for NetworkService { - fn as_ref(&self) -> &Receiver { - &self.command_receiver - } -} diff --git a/src-tauri/src/models/project_file.rs b/src-tauri/src/models/project_file.rs index 9e10168b..8882d31d 100644 --- a/src-tauri/src/models/project_file.rs +++ b/src-tauri/src/models/project_file.rs @@ -1,59 +1,120 @@ -/* use blend::Blend; -use semver::Version; use serde::{Deserialize, Serialize}; use std::{ - ops::Deref, path::{Path, PathBuf}, str::FromStr + ops::Deref, + path::{Path, PathBuf}, + str::FromStr, }; use thiserror::Error; #[derive(Debug, Error)] pub enum ProjectFileError { - // #[error("Invalid file type")] - // InvalidFileType, - #[error("Unexpected error - Programmer needs to specify exact error representation")] - UnexpectedError, // should never happen. + #[error("File type must be blend extension!")] + InvalidFileType, + #[error("Not a file!")] + MustBeFile, } #[derive(Debug, Serialize, Deserialize, Clone, PartialEq)] -pub struct ProjectFile> { - blender_version: Version, - path: T, +pub struct ProjectFile { + inner: PathBuf, } -impl ProjectFile { - pub fn new(src: PathBuf, version: Version) -> Result { - match Blend::from_path(&src) { - Ok(_data) => { - Ok(Self { - blender_version: version, - path: src, - }) - } - Err(_) => Err(ProjectFileError::InvalidFileType), +impl ProjectFile { + + // pathbuf must be validate, therefore method must be private + fn new(src: PathBuf) -> Self { + Self { + inner: src } } + + /// Validate path integrity + pub fn from

(src: P) -> Result + where P: AsRef + { + let path = src.as_ref(); + + // Blend expects a file. Stop here if argument is a directory. Do not continue. + if path.is_dir() { + return Err(ProjectFileError::InvalidFileType) + } + + if !path.exists() { + return Err(ProjectFileError::InvalidFileType) + } + + // expects a file existing, do not pass in directory or this program will crash. + if let Err(e) = Blend::from_path(path) { + eprintln!("{e:?}"); + return Err(ProjectFileError::InvalidFileType) + }; + + let buf = path.to_path_buf(); + Ok(Self::new(buf)) + } } -impl AsRef for ProjectFile { - fn as_ref(&self) -> &Version { - &self.blender_version +/* #region custom implementation */ + +impl Into for ProjectFile { + fn into(self) -> PathBuf { + self.inner } } -impl FromStr for ProjectFile { +impl FromStr for ProjectFile { type Err = ProjectFileError; + // questionable? fn from_str(s: &str) -> Result { - Ok(serde_json::from_str(s).map_err(|_| ProjectFileError::UnexpectedError)?) + Ok(serde_json::from_str(s).map_err(|_| ProjectFileError::InvalidFileType)?) } } -impl Deref for ProjectFile { +impl Deref for ProjectFile { type Target = Path; fn deref(&self) -> &Path { - &self.path + &self.inner } } -*/ \ No newline at end of file +/* #endregion */ + +#[cfg(test)] +mod test { + use super::*; + use crate::models::constant::test::EXAMPLE_FILE; + use std::path::Path; + + #[test] + fn create_project_file_successfully() { + let file = Path::new(EXAMPLE_FILE); + let project_file = ProjectFile::from(file.to_path_buf()); + assert!(project_file.is_ok()); + } + + #[test] + fn invalid_file_path_should_fail() { + let file = Path::new("./dir"); + let project_file = ProjectFile::from(file.to_path_buf()); + assert!(project_file.is_err()); + } + + #[test] + fn invalid_file_extension_should_fail() { + // with invalid extension (e.g. .txt) + { + let file = Path::new("./bad_extension.txt"); + let project_file = ProjectFile::from(file.to_path_buf()); + assert!(project_file.is_err()); + } + + // with no extension (e.g. dir) + { + let dir = Path::new("./"); + let project_file = ProjectFile::from(dir); + assert!(project_file.is_err()); + } + } +} diff --git a/src-tauri/src/models/render_info.rs b/src-tauri/src/models/render_info.rs index 3ee8d900..5d31fbba 100644 --- a/src-tauri/src/models/render_info.rs +++ b/src-tauri/src/models/render_info.rs @@ -1,19 +1,24 @@ -/* +use super::with_id::WithId; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; +use std::path::{Path, PathBuf}; +use uuid::Uuid; + +pub type CreatedRenderInfoDto = WithId; +pub type NewRenderInfoDto = RenderInfo; #[derive(Debug, Serialize, Deserialize, Clone, Hash, Eq, PartialEq)] pub struct RenderInfo { + pub job_id: Uuid, pub frame: i32, - pub path: PathBuf, + pub render_path: PathBuf, } impl RenderInfo { - pub fn new(frame: i32, path: &PathBuf) -> Self { + pub fn new(job_id: Uuid, frame: i32, path: impl AsRef) -> Self { Self { + job_id, frame, - path: path.clone(), + render_path: path.as_ref().to_path_buf(), } } } -*/ \ No newline at end of file diff --git a/src-tauri/src/models/server_setting.rs b/src-tauri/src/models/server_setting.rs index 810c847d..c28b1843 100644 --- a/src-tauri/src/models/server_setting.rs +++ b/src-tauri/src/models/server_setting.rs @@ -20,7 +20,7 @@ const BLEND_DIR: &str = "BlendFiles/"; /// Server settings information that the user can load and configure for this program to operate. /// It will save the list of blender installation on the machine to avoid duplicate download and installation. -#[derive(Debug, Clone, Serialize, Deserialize)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct ServerSetting { /// Public directory to store all finished render image. pub render_dir: PathBuf, @@ -57,7 +57,7 @@ impl ServerSetting { fs::create_dir_all(&path).expect("Unable to create directory!"); path } - + fn get_config_path() -> PathBuf { let path = Self::get_config_dir(); path.join(SETTINGS_FILE_NAME) diff --git a/src-tauri/src/models/setting_action.rs b/src-tauri/src/models/setting_action.rs new file mode 100644 index 00000000..8120eed7 --- /dev/null +++ b/src-tauri/src/models/setting_action.rs @@ -0,0 +1,18 @@ +use futures::channel::mpsc::Sender; +use crate::models::server_setting::ServerSetting; + +#[derive(Debug)] +pub enum SettingsAction { + Get(Sender), + Update(ServerSetting), +} + +impl PartialEq for SettingsAction { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Get(..), Self::Get(..)) => true, + (Self::Update(l0), Self::Update(r0)) => l0 == r0, + _ => false, + } + } +} \ No newline at end of file diff --git a/src-tauri/src/models/task.rs b/src-tauri/src/models/task.rs index 998e25e4..6c7d47cb 100644 --- a/src-tauri/src/models/task.rs +++ b/src-tauri/src/models/task.rs @@ -1,12 +1,16 @@ -use super::job::Job; -use crate::domains::task_store::TaskError; +use super::job::CreatedJobDto; +use crate::{ + domains::task_store::TaskError, + models::{job::Job, with_id::WithId}, +}; use blender::{ blender::{Args, Blender}, - models::status::Status, + constant::MIN_THRESHOLD_FETCH, + models::{engine::Engine, event::BlenderEvent}, }; -use libp2p::PeerId; -use semver::Version; use serde::{Deserialize, Serialize}; +use std::path::Path; +use std::sync::mpsc::Receiver; use std::{ ops::Range, path::PathBuf, @@ -14,76 +18,59 @@ use std::{ }; use uuid::Uuid; +pub type CreatedTaskDto = WithId; + /* Task is used to send Worker individual task to work on this can be customize to determine what and how many frames to render. - contains information about who requested the job in the first place so that the worker knows how to communicate back notification. */ #[derive(Debug, Clone, Serialize, Deserialize)] pub struct Task { - /// Unique id for this task - pub id: Uuid, - - /// peer's id that sent us this task, use this to callback - peer_id: Vec, - - /// reference to the job id - pub job_id: Uuid, + /// Id used to identify the job + job_id: Uuid, - /// target blender version to use - pub blender_version: Version, + /// job reference. + job: Job, - /// generic blender file name from job's reference. - pub blend_file_name: PathBuf, + // temp output destination - used to hold render image in temp on client machines + temp_output: PathBuf, /// Render range frame to perform the task pub range: Range, } // To better understand Task, this is something that will be save to the database and maintain a record copy for data recovery -// This act as a pending work order to fulfil when resources are available. +// This act as a pending work to fulfill when resources are available. impl Task { - pub fn new( - peer_id: PeerId, - job_id: Uuid, - blend_file_name: PathBuf, - blender_version: Version, - range: Range, - ) -> Self { + // private method, less validation. + fn new(job_id: Uuid, job: Job, temp_output: PathBuf, range: Range) -> Self { Self { - id: Uuid::new_v4(), - peer_id: peer_id.to_bytes(), job_id, - blend_file_name, - blender_version, + job, + temp_output, range, } } - pub fn from(peer_id: PeerId, job: Job, range: Range) -> Self { - Self { - id: Uuid::new_v4(), - peer_id: peer_id.to_bytes(), - job_id: job.id, - blend_file_name: PathBuf::from(job.project_file.file_name().unwrap()), - blender_version: job.blender_version, - range, + pub fn from(job: CreatedJobDto, range: Range) -> Result { + match dirs::cache_dir() { + Some(tmp) => Ok(Task::new(job.id, job.item, tmp, range)), + None => Err(TaskError::CacheError), } } - // this could be async? we'll see. - + // TODO: Instead /// The behaviour of this function returns the percentage of the remaining jobs in poll. - /// E.g. 102 (80%) of 120 remaining would return 96 end frames. + /// E.g. 102 (out of 255- 80%) of 120 remaining would return 96 end frames. /// TODO: Allow other node or host to fetch end frames from this task and distribute to other requesting workers. - pub fn fetch_end_frames(&mut self, percentage: i8) -> Option> { + pub fn fetch_end_frames(&mut self, percentage: u8) -> Option> { // Here we'll determine how many franes left, and then pass out percentage of that frames back. - let perc = percentage as f32 / i8::MAX as f32; + let perc = percentage as f32 / u8::MAX as f32; let end = self.range.end; let delta = (end - self.range.start) as f32; let trunc = (perc * (delta.powf(2.0)).sqrt()).floor() as usize; - if trunc.le(&2) { + if trunc <= MIN_THRESHOLD_FETCH { return None; } @@ -93,13 +80,9 @@ impl Task { Some(range) } - pub fn get_peer_id(&self) -> PeerId { - PeerId::from_bytes(&self.peer_id).expect("Peer Id was posioned!") - } - fn get_next_frame(&mut self) -> Option { // we will use this to generate a temporary frame record on database for now. - if self.range.start < self.range.end { + if self.range.start < (self.range.end + 1) { let value = Some(self.range.start); self.range.start = self.range.start + 1; value @@ -110,15 +93,20 @@ impl Task { // Invoke blender to run the job // how do I stop this? Will this be another async container? - pub async fn run( + pub async fn run>( self, - blend_file: PathBuf, + blend_file: T, // output is used to create local path storage to save frame path to - output: PathBuf, + output: T, // reference to the blender executable path to run this task. blender: &Blender, - ) -> Result, TaskError> { - let args = Args::new(blend_file, output); + ) -> Result, TaskError> { + let args = Args::new( + blend_file.as_ref().to_path_buf(), + output.as_ref().to_path_buf(), + Engine::CYCLES, + ); + let arc_task = Arc::new(RwLock::new(self)).clone(); // TODO: How can I adjust blender jobs? @@ -135,3 +123,60 @@ impl Task { Ok(receiver) } } + +impl AsRef for Task { + fn as_ref(&self) -> &Uuid { + &self.job_id + } +} + +impl AsRef for Task { + fn as_ref(&self) -> &Job { + &self.job + } +} + +#[cfg(test)] +mod test { + use super::*; + use crate::models::job::test::scaffold_job; + use uuid::Uuid; + + fn scaffold_task(start: i32, end: i32) -> Task { + let data = WithId { + id: Uuid::new_v4(), + item: scaffold_job(), + }; + let range = Range { start, end }; + Task::from(data, range).expect("Should have valid task") + } + + #[test] + fn fetch_end_frame_success() { + // we should run two scenario, one with actual frames, and another with limited or no frames left. + // if we tried to call with enough buffer pending, we should expect Some(value) back + // otherwise if the node is almost done and it was called, None should return. + let mut task = scaffold_task(0, 50); + let data = task.fetch_end_frames(255); + assert!(data.is_some()); + + let data = task.fetch_end_frames(5); + assert!(data.is_none()); + } + + #[test] + fn get_next_frame_success() { + // We should expect two successful result + // one result is that we should have remaining frames, so we should expect to get Some(value) + // otherwise None should return that we've completed the job. + let mut task = scaffold_task(0, 1); + let data = task.get_next_frame(); + assert!(data.is_some()); + + let data = task.get_next_frame(); + assert!(data.is_some()); + + let data = task.get_next_frame(); + assert!(data.is_none()); + } +} diff --git a/src-tauri/src/models/with_id.rs b/src-tauri/src/models/with_id.rs new file mode 100644 index 00000000..1f397acb --- /dev/null +++ b/src-tauri/src/models/with_id.rs @@ -0,0 +1,27 @@ +use serde::Serialize; +use sqlx::prelude::*; +use uuid::Uuid; + +#[derive(Debug, Serialize, FromRow, Clone)] +pub struct WithId { + pub id: ID, + pub item: T, +} + +impl AsRef for WithId +where + T: Serialize, +{ + fn as_ref(&self) -> &Uuid { + &self.id + } +} + +impl PartialEq for WithId +where + T: Serialize, +{ + fn eq(&self, other: &Uuid) -> bool { + self.id.eq(other) + } +} diff --git a/src-tauri/src/models/worker.rs b/src-tauri/src/models/worker.rs index b768037d..ca6873f7 100644 --- a/src-tauri/src/models/worker.rs +++ b/src-tauri/src/models/worker.rs @@ -1,22 +1,21 @@ use super::computer_spec::ComputerSpec; -use serde::{Deserialize, Serialize}; +use libp2p::PeerId; use thiserror::Error; +#[derive(Debug)] +pub struct Worker { + pub id: PeerId, + pub spec: ComputerSpec, +} + #[derive(Debug, Error)] pub enum WorkerError { #[error("Received error from database: {0}")] Database(String), } -// we will use this to store data into database at some point. -#[derive(Serialize, Deserialize)] -pub struct Worker { - pub machine_id: String, - pub spec: ComputerSpec, -} - impl Worker { - pub fn new(machine_id: String, spec: ComputerSpec) -> Self { - Self { machine_id, spec } + pub fn new(id: PeerId, spec: ComputerSpec) -> Self { + Self { id, spec } } } diff --git a/src-tauri/src/network/controller.rs b/src-tauri/src/network/controller.rs new file mode 100644 index 00000000..9767eb6c --- /dev/null +++ b/src-tauri/src/network/controller.rs @@ -0,0 +1,199 @@ +use std::error::Error; +use std::{ + collections::HashSet, + path::{Path, PathBuf}, +}; + +use crate::models::behaviour::FileResponse; +use crate::models::job::JobEvent; +use crate::network::message::{Command, FileCommand, NetworkError, NodeEvent}; +use crate::network::provider_rule::ProviderRule; +use futures::channel::oneshot::{self}; +use libp2p::{Multiaddr, PeerId}; +use libp2p_request_response::ResponseChannel; +use tokio::sync::mpsc::Sender; + +// Network Controller interfaces network service. +#[derive(Clone)] +pub struct Controller { + sender: Sender, // send net commands + pub public_id: PeerId, + pub hostname: String, +} + +impl Controller { + pub(crate) fn new(sender: Sender, peer_id: PeerId, hostname: String) -> Self { + Self { + sender, + public_id: peer_id, + hostname, + } + } + + pub(crate) async fn start_listening(&mut self, addr: Multiaddr) { + let (sender, receiver) = oneshot::channel(); + self.sender + .send(Command::StartListening { addr, sender }) + .await + .expect("Command receiver should never be dropped"); + if let Err(e) = receiver.await { + eprintln!("Fail to listen? {e:?}"); + } + } + + pub(crate) async fn subscribe(&mut self, topic: &str) -> Result<(), Box> { + // TODO: find a better way to get around to_owned(), but for now focus on getting this application to work. + let cmd = Command::Subscribe { + topic: topic.to_owned(), + }; + if let Err(e) = self.sender.send(cmd).await { + eprintln!("Fail to subscribe? {e:}"); + } + Ok(()) + } + + #[allow(dead_code)] + pub(crate) async fn send_node_status(&mut self, status: NodeEvent) { + if let Err(e) = self.sender.send(Command::NodeStatus(status)).await { + eprintln!("Failed to send node status to network service: {e:?}"); + } + } + + pub(crate) async fn dial( + &mut self, + peer_id: &PeerId, + peer_addr: &Multiaddr, + ) -> Result<(), Box> { + let (sender, receiver) = oneshot::channel(); + self.sender + .send(Command::Dial { + peer_id: peer_id.clone(), + peer_addr: peer_addr.clone(), + sender, + }) + .await + .expect("Should not drop"); + + // so at this point we're waiting for connection established. + if let Err(e) = receiver.await { + eprintln!("Should not error? {e:?}"); + } + Ok(()) + } + + // send job event to all connected node + pub async fn send_job_event(&mut self, event: JobEvent) { + self.sender + .send(Command::JobStatus(event)) + .await + .expect("Command should not be dropped"); + } + + pub(crate) async fn file_service(&mut self, command: FileCommand) { + self.sender + .send(Command::FileService(command)) + .await + .expect("Command should not have been dropped!"); + } + + /// file_name are broadcasted with the extensions included, but not the directory it's located in. E.g. "test.blend" + // I need to use some kind of enumeration to help make this process flexible with rules.. + pub(crate) async fn start_providing( + &mut self, + provider: &ProviderRule, + ) -> Result<(), NetworkError> { + let cmd = match provider { + ProviderRule::Default(path_buf) => { + // TODO: remove .expect(), .to_str(), and .to_owned() + match path_buf.file_name() { + Some(file_name) => { + let keyword = file_name + .to_str() + .expect("Must be able to convert OsStr to Str!"); + + FileCommand::StartProviding(keyword.into(), path_buf.into()) + } + None => return Err(NetworkError::BadInput), + } + } + ProviderRule::Custom(keyword, path_buf) => { + FileCommand::StartProviding(keyword.to_owned(), path_buf.to_owned()) + } + }; + + if let Err(e) = self.sender.send(Command::FileService(cmd)).await { + eprintln!("How did this happen? {e:?}"); + } + Ok(()) + } + + pub async fn get_providers(&mut self, file_name: &str) -> HashSet { + let (sender, receiver) = oneshot::channel(); + let cmd = Command::FileService(FileCommand::GetProviders { + file_name: file_name.to_string(), + sender, + }); + self.sender + .send(cmd) + .await + .expect("Command receiver should not be dropped"); + receiver.await.unwrap_or(HashSet::new()) + } + + // client request file from peers. + // I feel like we should make this as fetching data from network? Some sort of stream? + pub async fn get_file_from_peers>( + &mut self, + file_name: &str, + destination: T, + ) -> Result { + let providers = self.get_providers(&file_name).await; + match providers.iter().next() { + Some(peer_id) => { + self.request_file(peer_id, file_name, destination.as_ref()) + .await + } + None => Err(NetworkError::NoPeerProviderFound), + } + } + + async fn request_file( + &mut self, + peer_id: &PeerId, + file_name: &str, + destination: &Path, + ) -> Result { + let (sender, receiver) = oneshot::channel(); + let cmd = Command::FileService(FileCommand::RequestFile { + peer_id: *peer_id, + file_name: file_name.into(), + sender, + }); + self.sender + .send(cmd) + .await + .expect("Command should not be dropped"); + let content = receiver + .await + .expect("Should not be closed?") + .or_else(|e| Err(NetworkError::UnableToSave(e.to_string())))?; + + let file_path = destination.join(file_name); + match async_std::fs::write(file_path.clone(), content).await { + Ok(_) => Ok(file_path), + Err(e) => Err(NetworkError::UnableToSave(e.to_string())), + } + } + + // TODO: Come back to this one and see how this one gets invoked. + pub(crate) async fn respond_file( + &mut self, + file: Vec, + channel: ResponseChannel, + ) { + let cmd = Command::FileService(FileCommand::RespondFile { file, channel }); + if let Err(e) = self.sender.send(cmd).await { + println!("Command should not be dropped: {e:?}"); + } + } +} diff --git a/src-tauri/src/network/message.rs b/src-tauri/src/network/message.rs new file mode 100644 index 00000000..8cad4920 --- /dev/null +++ b/src-tauri/src/network/message.rs @@ -0,0 +1,139 @@ +use blender::models::event::BlenderEvent; +use futures::channel::oneshot::{self}; +use libp2p::{Multiaddr, PeerId}; +use libp2p_request_response::{OutboundRequestId, ResponseChannel}; +use serde::{Deserialize, Serialize}; +use std::path::PathBuf; +use std::{collections::HashSet, error::Error}; +use thiserror::Error; + +use crate::models::behaviour::FileResponse; +use crate::models::computer_spec::ComputerSpec; +use crate::models::job::JobEvent; +use crate::network::PeerIdString; + +#[derive(Debug, Error)] +pub enum NetworkError { + #[error("Unable to listen: {0}")] + UnableToListen(String), + #[error("Not Connected")] + NotConnected, + #[error("Invalid connection")] + Invalid, + #[error("Bad Input")] + BadInput, + #[error("Send Error: {0}")] + SendError(String), + #[error("No peers on network have this file available to download!")] + NoPeerProviderFound, + #[error("Unable to save download file: {0}")] + UnableToSave(String), + #[error("Timeout, unable to connect peer")] + Timeout, +} + +pub type KeywordSearch = String; + +// to make things simple, we'll create a file service command to handle file service. +#[derive(Debug)] +pub enum FileCommand { + StartProviding(KeywordSearch, PathBuf), // update kademlia service to provide a new file. Must have a file name and a extension! Cannot be a directory! + StopProviding(KeywordSearch), // update kademlia service to stop providing the file. + GetProviders { + file_name: String, + sender: oneshot::Sender>, + }, + RequestFile { + peer_id: PeerId, + file_name: String, + sender: oneshot::Sender, Box>>, + }, + RespondFile { + file: Vec, + channel: ResponseChannel, + }, + RequestFilePath { + keyword: KeywordSearch, + sender: oneshot::Sender>, + }, +} + +// Send commands to network. +#[derive(Debug)] +pub enum Command { + Dial { + peer_id: PeerId, + peer_addr: Multiaddr, + sender: oneshot::Sender>>, + }, + Subscribe { + topic: String, + }, + // TODO: figure out a way to get around the Box traits! + StartListening { + addr: Multiaddr, + sender: oneshot::Sender>>, + }, + // TODO: Find a way to get around the string type! This expects a copy! + StartProviding { + file_name: String, + sender: oneshot::Sender<()>, + }, + + GetProviders { + file_name: String, + sender: oneshot::Sender>, + }, + RequestFile { + file_name: String, + peer: PeerId, + sender: oneshot::Sender, Box>>, + }, + RespondFile { + file: Vec, + channel: ResponseChannel, + }, + + // TODO: More documentation to explain below + // These are signal to use to send out message and forget. + // May expect a respoonse back potentially requesting this node to work new jobs. + NodeStatus(NodeEvent), // broadcast node activity changed + JobStatus(JobEvent), + FileService(FileCommand), +} + +// Must be serializable to send data across network +// issue with this is that this cannot be convert into Encode,Decode by bincode. Instead we'll have to +#[derive(Debug, Serialize, Deserialize)] +pub enum NodeEvent { + Hello(PeerIdString, ComputerSpec), + Disconnected { + peer_id: PeerIdString, + reason: Option, + }, + BlenderStatus(BlenderEvent), +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum StatusEvent { + Offline, + Online, + Busy, + Error(String), + Signal(String), +} + +// Received network events. +#[derive(Debug)] +pub enum Event { + // Don't think I need this anymore, trying to rely on DHT for node availability somehow? + // TODO: See about utilizing DHT instead of this? How can I get event from DHT? + Discovered(PeerId, Multiaddr), + NodeStatus(NodeEvent), + InboundRequest { + request: String, + channel: ResponseChannel, + }, + JobUpdate(JobEvent), + ReceivedFileData(OutboundRequestId, Vec), +} diff --git a/src-tauri/src/network/mod.rs b/src-tauri/src/network/mod.rs new file mode 100644 index 00000000..a125cade --- /dev/null +++ b/src-tauri/src/network/mod.rs @@ -0,0 +1,127 @@ +use crate::{ + constant::TRANSFER, + models::behaviour::BlendFarmBehaviour, + network::{ + controller::Controller, + message::{Command, Event, NetworkError}, + service::Service, + }, +}; +use libp2p::{StreamProtocol, SwarmBuilder, gossipsub, identity, kad, mdns, noise, tcp, yamux}; +use libp2p_request_response::ProtocolSupport; +use machine_info::Machine; +use std::{/*hash::DefaultHasher,*/ time::Duration}; +use tokio::io; +use tokio::sync::mpsc::{self, Receiver}; +pub mod controller; +pub mod message; +pub(crate) mod provider_rule; +pub mod service; + +// type is locally contained +pub type PeerIdString = String; + +// the tuples return two objects +// Network Controller to interface network service +// Receiver receive network events +pub async fn new( + secret_key_seed: Option, +) -> Result<(Controller, Receiver, Service), NetworkError> { + // wonder why we have a connection timeout of 60 seconds? Why not uint::MAX? + + let duration = Duration::from_secs(60); + // is there a reason for the secret key seed? + let id_keys = match secret_key_seed { + Some(seed) => { + let mut bytes = [0u8; 32]; + bytes[0] = seed; + identity::Keypair::ed25519_from_bytes(bytes).unwrap() + } + None => identity::Keypair::generate_ed25519(), + }; + + let mut swarm = SwarmBuilder::with_existing_identity(id_keys) + // let mut swarm = SwarmBuilder::with_new_identity() + .with_tokio() + .with_tcp( + tcp::Config::default(), + noise::Config::new, + yamux::Config::default, + ) + .expect("Should be able to build with tcp configuration?") + .with_quic() + .with_behaviour(|key| { + // seems like we need to content-address message. We'll use the hash of the message as the ID. + // let message_id_fn = |message: &gossipsub::Message| { + // let mut s = DefaultHasher::new(); + // message.data.hash(&mut s); + // gossipsub::MessageId::from(s.finish().to_string()) + // }; + + let gossipsub_config = gossipsub::ConfigBuilder::default() + .heartbeat_interval(Duration::from_secs(10)) + .validation_mode(gossipsub::ValidationMode::Strict) + // .message_id_fn(message_id_fn) + .build() + .map_err(|msg| io::Error::new(io::ErrorKind::Other, msg))?; + + // p2p communication + let gossipsub = gossipsub::Behaviour::new( + gossipsub::MessageAuthenticity::Signed(key.clone()), + gossipsub_config, + ) + .expect("Fail to create gossipsub behaviour"); + + // network discovery usage + // TODO: replace expect with error handling + let mdns = + mdns::tokio::Behaviour::new(mdns::Config::default(), key.public().to_peer_id()) + .expect("Fail to create mdns behaviour!"); + + // Used to provide file provision list + let kad = kad::Behaviour::new( + key.public().to_peer_id(), + kad::store::MemoryStore::new(key.public().to_peer_id()), + ); + + let rr_config = libp2p_request_response::Config::default(); + // Learn more about this and see if we need the transfer keyword of some sort? + let protocol = [(StreamProtocol::new(TRANSFER), ProtocolSupport::Full)]; + let request_response = libp2p_request_response::Behaviour::new(protocol, rr_config); + + Ok(BlendFarmBehaviour { + request_response, + gossipsub, + mdns, + kademlia: kad, + }) + }) + // TODO remove/handle expect() + .expect("Expect to build behaviour") + .with_swarm_config(|cfg| cfg.with_idle_connection_timeout(duration)) + .build(); + + // set the kad as server mode + swarm + .behaviour_mut() + .kademlia + .set_mode(Some(kad::Mode::Server)); + + // the command sender is used for outside method to send message commands to network queue + let (sender, receiver) = mpsc::channel::(32); + + // the event sender is used to handle incoming network message. E.g. RunJob + let (event_sender, event_receiver) = mpsc::channel::(32); + + let public_id = swarm.local_peer_id().clone(); + + let controller = Controller::new(sender, public_id, Machine::new().system_info().hostname); + + let service = Service::new( + swarm, + receiver, + event_sender, // Here is where network service communicates out. + ); + + Ok((controller, event_receiver, service)) +} diff --git a/src-tauri/src/network/provider_rule.rs b/src-tauri/src/network/provider_rule.rs new file mode 100644 index 00000000..deb2fe7b --- /dev/null +++ b/src-tauri/src/network/provider_rule.rs @@ -0,0 +1,19 @@ +use crate::network::message::KeywordSearch; +use std::{ffi::OsStr, path::PathBuf}; + +#[allow(dead_code)] +pub enum ProviderRule { + // Use "file name.ext", Extracted from PathBuf. + Default(PathBuf), + // Custom keyword search for specific PathBuf. + Custom(KeywordSearch, PathBuf), +} + +impl ProviderRule { + pub fn get_file_name(&self) -> Option<&OsStr> { + match self { + ProviderRule::Default(path) => path.file_name(), + ProviderRule::Custom(_, path_buf) => path_buf.file_name(), + } + } +} diff --git a/src-tauri/src/network/service.rs b/src-tauri/src/network/service.rs new file mode 100644 index 00000000..4dd623d2 --- /dev/null +++ b/src-tauri/src/network/service.rs @@ -0,0 +1,579 @@ +use crate::constant::{JOB_TOPIC, NODE_TOPIC}; +use crate::models::behaviour::{BlendFarmBehaviourEvent, FileRequest, FileResponse}; +use crate::models::job::JobEvent; +use crate::network::message::{FileCommand, NodeEvent}; +use crate::{ + models::behaviour::BlendFarmBehaviour, + network::message::{Command, Event}, +}; +use futures::StreamExt; +use futures::channel::oneshot; +use libp2p::gossipsub::{self, IdentTopic}; +use libp2p::kad::RecordKey; +use libp2p::mdns; +use libp2p::multiaddr::Protocol; +use libp2p::swarm::SwarmEvent; +use libp2p::{ + Multiaddr, PeerId, Swarm, + kad::{self, QueryId}, +}; +use libp2p_request_response::OutboundRequestId; +use std::collections::{HashMap, HashSet, hash_map}; +use std::error::Error; +use std::path::PathBuf; +use tokio::select; +use tokio::sync::mpsc::{Receiver, Sender}; + +// Network service module to handle invocation commands to send to network service, +// as well as handling network event from other peers +pub struct Service { + // swarm behaviour - interface to the network + swarm: Swarm, + + // receive Network command + receiver: Receiver, + + // Send Network event to subscribers. + sender: Sender, + + // connection established + dialers: HashMap, + + pending_start_providing: HashMap>, + pending_dial: HashMap>>>, + providing_files: HashMap, + pending_get_providers: HashMap>>, + pending_request_file: + HashMap, Box>>>, +} + +// network service will be used to handle and receive network signal. It will also transmit network package over lan +impl Service { + pub fn new( + swarm: Swarm, + receiver: Receiver, + sender: Sender, + ) -> Self { + Self { + swarm, + receiver, + sender, + dialers: Default::default(), + pending_start_providing: Default::default(), + pending_dial: Default::default(), + providing_files: Default::default(), + pending_get_providers: Default::default(), + pending_request_file: Default::default(), + } + } + + /* + From my understanding about this method implementation is that we wanted to be able to broadcast + all of the potential files out there and sponsor what's available. + I think this methodology will change because we wanted the host to ask the client if there's any files available + or completed by this machine, and then reply back to the host. + + I need to setup a network diagram to make this network layer protocol clear and understand, + as well as easy to debug, test, and identify potential issues. + + From the host side. the host will broadcast asking for job updates. + This update will include job id. + + On the client side, the client will receive the notification from the host, + and check the database to see if the job id exist. + + if it does exist, then the client will broadcast list of completed images. + The host will receive this list and compare to the host machine to see if they have the image + + If the host does not have the image, it will initiate a file transfer between the host and the client machine + In this case, we should not have to make all of the files available, but instead make the target image + available for the host to transfer over the network protocol. + + This is recognized as a tcp handshake connection, asking for the image from the node + and the node will send the image via channel request. + */ + + // here we will deviate handling the file service command. + async fn process_file_service(&mut self, cmd: FileCommand) { + match cmd { + FileCommand::RequestFile { + peer_id, + file_name, + sender, + } => { + let request_id = self + .swarm + .behaviour_mut() + .request_response + .send_request(&peer_id, FileRequest(file_name.into())); + self.pending_request_file.insert(request_id, sender); + } + FileCommand::RespondFile { file, channel } => { + // somehow the send_response errored out? How come? + // Seems like this function got timed out? + if let Err(e) = self + .swarm + .behaviour_mut() + .request_response + .send_response(channel, FileResponse(file)) + { + // why am I'm getting error message here? + eprintln!("Error received on sending response! {e:?}"); + } + } + FileCommand::GetProviders { file_name, sender } => { + let key = file_name.into_bytes().into(); + let query_id = self.swarm.behaviour_mut().kademlia.get_providers(key); + self.pending_get_providers.insert(query_id, sender); + } + FileCommand::StartProviding(keyword, file_path) => { + let key = keyword.clone().into_bytes().into(); + // could we make use of this query ID? + let _query_id = self + .swarm + .behaviour_mut() + .kademlia + .start_providing(key) + .expect("No store error."); + self.providing_files.insert(keyword, file_path); + } + FileCommand::StopProviding(keyword) => { + let key = RecordKey::new(&keyword.as_bytes()); + self.swarm.behaviour_mut().kademlia.stop_providing(&key); + self.providing_files.remove(&keyword); + } + FileCommand::RequestFilePath { keyword, sender } => { + let result = self + .providing_files + .get(&keyword) + .and_then(|f| Some(f.to_owned())); + println!("{keyword:?} | {result:?}"); + sender.send(result).expect("Receiver should not be dropped"); + } + }; + } + + // send command + // Receive commands from foreign invocation. + async fn handle_command(&mut self, cmd: Command) { + match cmd { + Command::Subscribe { topic } => { + let identity = IdentTopic::new(topic); + if let Err(e) = self.swarm.behaviour_mut().gossipsub.subscribe(&identity) { + eprintln!("Fail to subscribe! {e:}"); + } + } + Command::StartListening { addr, sender } => { + let _result = match self.swarm.listen_on(addr) { + Err(e) => match e.source() { + Some(err) => Err(Box::new(err.to_string())), + None => Ok(()), + }, + _ => Ok(()), + }; + // TODO, figure out how to get this situation straighten? Why + // sender.send(result); + if let Err(e) = sender.send(Ok(())) { + eprintln!("Fail to send! {e:?}"); + } + } + + Command::Dial { + peer_id, + peer_addr, + sender, + } => { + if let hash_map::Entry::Vacant(e) = self.pending_dial.entry(peer_id) { + self.swarm + .behaviour_mut() + .kademlia + .add_address(&peer_id, peer_addr.clone()); + + match self.swarm.dial(peer_addr.with(Protocol::P2p(peer_id))) { + Ok(()) => { + e.insert(sender); + } + Err(e) => { + sender.send(Err(Box::new(e))).expect("Should not drop"); + } + } + } else { + eprintln!("Already dialing the peer!"); + } + } + + // use this to advertise files. On app startup we should broadcast blender apps as well. + Command::StartProviding { file_name, sender } => { + // TODO: Find a way to get around expect()! + let query_id = self + .swarm + .behaviour_mut() + .kademlia + .start_providing(file_name.into_bytes().into()) + .expect("No store value"); + self.pending_start_providing.insert(query_id, sender); + } + + Command::GetProviders { file_name, sender } => { + let query_id = self + .swarm + .behaviour_mut() + .kademlia + .get_providers(file_name.into_bytes().into()); + self.pending_get_providers.insert(query_id, sender); + } + Command::RequestFile { + file_name, + peer, + sender, + } => { + let request_id = self + .swarm + .behaviour_mut() + .request_response + .send_request(&peer, FileRequest(file_name)); + self.pending_request_file.insert(request_id, sender); + } + Command::RespondFile { file, channel } => { + self.swarm + .behaviour_mut() + .request_response + .send_response(channel, FileResponse(file)) + .expect("Connection to peer should be still open"); + } + Command::FileService(service) => self.process_file_service(service).await, + + // received job status. invoke commands + Command::JobStatus(event) => { + // convert data into json format. + let data = serde_json::to_string(&event).unwrap(); + let topic = IdentTopic::new(JOB_TOPIC); + match self.swarm.behaviour_mut().gossipsub.publish(topic, data) { + Ok(_) => println!("Job Status Sent!\n{event:?}"), + Err(e) => eprintln!("Fail to send message! {e:?}"), + }; + } + Command::NodeStatus(status) => { + // we want to send this info across broadcast network. We do not care who is listening the network. Only the fact that we want our hosts to keep notify for availability. + let data = serde_json::to_string(&status).unwrap(); + let topic = IdentTopic::new(NODE_TOPIC); + if let Err(e) = self.swarm.behaviour_mut().gossipsub.publish(topic, data) { + eprintln!("Fail to publish gossip message: {e:?}"); + } + } + } + } + + // TODO: Where is this method calling from? + async fn process_response_event( + &mut self, + event: libp2p_request_response::Event, + ) { + match event { + libp2p_request_response::Event::Message { message, .. } => match message { + libp2p_request_response::Message::Request { + request, channel, .. + } => { + self.sender + .send(Event::InboundRequest { + request: request.0, + channel, + }) + .await + .expect("Event receiver should not be dropped!"); + } + libp2p_request_response::Message::Response { + request_id, + response, + } => { + let _ = self + .pending_request_file + .remove(&request_id) + .expect("Request to still be pending") + .send(Ok(response.0)); + } + }, + libp2p_request_response::Event::OutboundFailure { + request_id, error, .. + } => { + let _ = self + .pending_request_file + .remove(&request_id) + .expect("Request to still be pending") + .send(Err(Box::new(error))); + } + libp2p_request_response::Event::ResponseSent { .. } => {} + _ => {} + } + } + + async fn process_mdns_event(&mut self, event: mdns::Event) { + match event { + mdns::Event::Discovered(peers) => { + for (peer_id, address) in peers { + println!("Discovered [{peer_id:?}] {address:?}"); + + // when I process this, how do I know where dialers is used? + let event = Event::Discovered(peer_id, address); + if let Err(e) = self.sender.send(event).await { + eprintln!("sender should not drop! {e:?}"); + } + + // if I have already discovered this address, then I need to skip it. Otherwise I will produce garbage log input for duplicated peer id already exist. + // it seems that I do need to explicitly add the peers to the list. + // self.swarm + // .behaviour_mut() + // .gossipsub + // .add_explicit_peer(&peer_id); + + // // add the discover node to kademlia list. + // why would I want to do this? + // self.swarm + // .behaviour_mut() + // .kad + // .add_address(&peer_id, address.clone()); + } + } + mdns::Event::Expired(..) => { + // for (peer_id, ..) in peers { + // self.swarm + // .behaviour_mut() + // .gossipsub + // .remove_explicit_peer(&peer_id); + // } + } + }; + } + + async fn process_gossip_event(&mut self, event: gossipsub::Event) { + match event { + // what is propagation source? can we use this somehow? + gossipsub::Event::Message { message, .. } => match message.topic.as_str() { + // if the topic is JOB related, assume data as JobEvent + JOB_TOPIC => match serde_json::from_slice::(&message.data) { + Ok(job_event) => { + if let Err(e) = self.sender.send(Event::JobUpdate(job_event)).await { + eprintln!("Something failed? {e:?}"); + } + } + Err(e) => { + eprintln!("Fail to parse Job topic data! {e:?}"); + } + }, + // Node based event awareness + NODE_TOPIC => match serde_json::from_slice::(&message.data) { + Ok(node_event) => { + if let Err(e) = self.sender.send(Event::NodeStatus(node_event)).await { + eprintln!("Something failed? {e:?}"); + } + } + Err(e) => eprintln!("fail to parse Node topic data! {e:?}"), + }, + + // Garbage collector - Treat this as a grain of salt. Do not execute any data from this scope + // should only be used to display logs and info, things for us to identify unusual activity going on outside our domain specification. + _ => { + // I received Mac.lan from message.topic? + let topic = message.topic.as_str(); + eprintln!("Intercepted unhandled signal here: {topic}"); + } + }, + // I should be logging info from other event from gossip... wonder what they got to say? + // TODO: Log and verify if we need to handle other gossip events. + _ => {} + } + } + + // async fn process_outbound_query(&mut ) + + // Handle kademila events (Used for file sharing) + // can we use this same DHT to make node spec publicly available? + async fn process_kademlia_event(&mut self, kad_event: kad::Event) { + match kad_event { + kad::Event::OutboundQueryProgressed { + id: query_id, + result: query_result, + .. + } => { + match query_result { + kad::QueryResult::StartProviding(..) => { + let sender: oneshot::Sender<()> = self + .pending_start_providing + .remove(&query_id) + .expect("Completed query to be previously pending."); + let _ = sender.send(()); + } + kad::QueryResult::GetProviders(Ok(kad::GetProvidersOk::FoundProviders { + providers, + .. + })) => { + if let Some(sender) = self.pending_get_providers.remove(&query_id) { + sender.send(providers).expect("Receiver not to be dropped"); + + if let Some(mut node) = + self.swarm.behaviour_mut().kademlia.query_mut(&query_id) + { + node.finish(); + } + } + } + kad::QueryResult::GetProviders(Ok( + kad::GetProvidersOk::FinishedWithNoAdditionalRecord { .. }, + )) => { + // yeah this looks wrong? + if let Some(sender) = self.pending_get_providers.remove(&query_id) { + sender + .send(HashSet::new()) + .expect("Sender not to be dropped"); + } + + if let Some(mut node) = + self.swarm.behaviour_mut().kademlia.query_mut(&query_id) + { + node.finish(); + } + // This piece of code means that there's nobody advertising this on the network? + // what was suppose to happen here? + // TODO: I am once again stopped here. This message appeared from the CLI side. Not the host. + + // let outbound_request_id = id; + // let event = Event::PendingRequestFiled(outbound_request_id, None); + // self.sender.send(event).await; + } + kad::QueryResult::PutRecord(result) => match result { + Ok(value) => println!("Successfully append the record! {value:?}"), + Err(e) => eprintln!("Error putting record in! {e:?}"), + }, + // suppressed + _ => {} + } + } + + kad::Event::InboundRequest { .. } => {} // suppressed + kad::Event::RoutingUpdated { .. } => {} // suppressed + // TODO: Find out what cause this to happen and see if we need to handle anything for this invocation exception + kad::Event::UnroutablePeer { peer } => { + eprintln!("Unroutable Peer? {peer}"); + } // suppressed + _ => { + // oh mah gawd. What am I'm suppose to do here? + eprintln!("Unhandled Kademila event: {kad_event:?}"); + } + } + } + + // Process incoming network events - Treat this as receiving new orders. + async fn handle_event(&mut self, event: SwarmEvent) { + match event { + SwarmEvent::Behaviour(behaviour) => match behaviour { + BlendFarmBehaviourEvent::RequestResponse(event) => { + self.process_response_event(event).await; + } + BlendFarmBehaviourEvent::Gossipsub(event) => { + self.process_gossip_event(event).await; + } + BlendFarmBehaviourEvent::Mdns(event) => { + self.process_mdns_event(event).await; + } + BlendFarmBehaviourEvent::Kademlia(event) => { + self.process_kademlia_event(event).await; + } + }, + // So how does the established works? + SwarmEvent::ConnectionEstablished { + peer_id, endpoint, .. + } => { + println!("Connection Established: {peer_id:?}\n{endpoint:?}"); + + if endpoint.is_dialer() { + if let Some(sender) = self.pending_dial.remove(&peer_id) { + self.dialers + .entry(peer_id) + .and_modify(|f| *f = endpoint.get_remote_address().clone()); + let _ = sender.send(Ok(())); + } + } + } + // This was called when client starts while manager is running. "Connection error: I/O error: closed by peer: 0" + // TODO: Read what ConnectionClosed does? + SwarmEvent::ConnectionClosed { peer_id, cause, .. } => { + let reason = cause.and_then(|f| Some(f.to_string())); + let node = NodeEvent::Disconnected { + peer_id: peer_id.to_base58(), + reason, + }; + let event = Event::NodeStatus(node); + if let Err(e) = self.sender.send(event).await { + eprintln!("Fail to send event on connection closed! {e:?}"); + } + } + SwarmEvent::OutgoingConnectionError { + peer_id: Some(peer_id), + error, + .. + } => { + if let Some(sender) = self.pending_dial.remove(&peer_id) { + let _ = sender.send(Err(Box::new(error))); + } + } + // TODO: Figure out what these events are, and see if they're any use for us to play with or delete them. Unnecessary comment codeblocks + // SwarmEvent::ListenerClosed { .. } => todo!(), + // SwarmEvent::ListenerError { listener_id, error } => todo!(), + + // FEATURE: Display verbose info using argument switch + /* #region vv verbose events vv */ + SwarmEvent::OutgoingConnectionError { peer_id: None, .. } => {} + + SwarmEvent::NewListenAddr { address, .. } => { + // println!("[New Listener Address]: {address}"); + let local_peer_id = *self.swarm.local_peer_id(); + eprintln!( + "Listening @ {:?}", + address.with(Protocol::P2p(local_peer_id)) + ); + } + + SwarmEvent::Dialing { .. } => {} + + SwarmEvent::IncomingConnection { + connection_id, + local_addr, + send_back_addr, + } => { + // Incoming connection? How do I accept? + eprintln!("Incoming connection: {connection_id} | {local_addr} | {send_back_addr}"); + + // I'm assuming this is reply from dial? + // what does it mean to have incoming connection here? + // self.dialers.entry() + } // Suppressing logs + + // Suppressing logs + SwarmEvent::NewExternalAddrOfPeer { .. } => {} + SwarmEvent::IncomingConnectionError { .. } => {} // I recognize this and do want to display result below. + SwarmEvent::ExpiredListenAddr { .. } => {} + + /* #endregion ^^eof ignore^^ */ + // Must fully exhaust all condition types as possible! + // Add to the ignore list with description why we're suppressing logs. They must be visible under verbose mode. + e => panic!("{e:?}"), + }; + } + + pub(crate) async fn run(mut self) { + loop { + select! { + event = self.swarm.select_next_some() => self.handle_event(event).await, + command = self.receiver.recv() => match command { + Some(c) => self.handle_command(c).await, + None => return, + }, + } + } + } +} + +#[cfg(test)] +pub mod test { + // TODO: perform some service test. How can I get the service up and running for this? +} diff --git a/src-tauri/src/routes/index.rs b/src-tauri/src/routes/index.rs new file mode 100644 index 00000000..639bd80b --- /dev/null +++ b/src-tauri/src/routes/index.rs @@ -0,0 +1,45 @@ +use maud::html; +use tauri::command; +use crate::constant::WORKPLACE; + +// separate this? +#[command] +pub fn index() -> String { + html! ( + div { + div class="sidebar" { + nav { + ul class="nav-menu-items" { + + // li key="manager" class="nav-bar" tauri-invoke="remote_render_page" hx-target=(format!("#{WORKPLACE}")) { + // span { "Remote Render" } + // }; + + li key="setting" class="nav-bar" tauri-invoke="setting_page" hx-target=(format!("#{WORKPLACE}")) { + span { "Setting" } + }; + }; + }; + div { + h3 { "Jobs" } + + button tauri-invoke="open_dialog_for_blend_file" hx-target="body" hx-swap="beforeend" { + "Import" + }; + + // Is there a way to select the first item on the list by default? + // TODO: Take a look into hx-swap-oob on how we can refresh when a record is deleted or added + div class="group" id="joblist" tauri-invoke="list_jobs" hx-trigger="load" hx-target="this"; + } + + // div { + // h2 { "Computer Nodes" }; + // // hx-trigger="every 10s" - omitting this as this was spamming console log + // div class="group" id="workers" tauri-invoke="list_workers" hx-target="this" {}; + // }; + }; + + } + main id=(WORKPLACE); + ).0 +} \ No newline at end of file diff --git a/src-tauri/src/routes/job.rs b/src-tauri/src/routes/job.rs index a0392107..f4b5c404 100644 --- a/src-tauri/src/routes/job.rs +++ b/src-tauri/src/routes/job.rs @@ -1,19 +1,141 @@ -use blender::models::mode::Mode; -use maud::html; +use crate::constant::WORKPLACE; +use crate::domains::job_store::JobError; +use crate::models::job::{CreatedJobDto, Output}; +use crate::models::project_file::ProjectFile; +use crate::models::{app_state::AppState, job::{Job, JobAction}}; +use crate::services::tauri_app::UiCommand; +use blender::models::mode::RenderMode; +use futures::channel::mpsc; +use futures::{SinkExt, StreamExt}; +use maud::{html, PreEscaped}; use semver::Version; use serde_json::json; -use std::path::PathBuf; -use std::{ops::Range, str::FromStr}; -use tauri::{command, State}; +use std::{path::PathBuf, str::FromStr}; +use tauri::{State, command}; use tokio::sync::Mutex; use uuid::Uuid; -use crate::{ - models::{app_state::AppState, job::Job}, - services::tauri_app::UiCommand, -}; +/* + private method to call the function and return the objects, but not the actual renders. +*/ +async fn cmd_create_job(state: &mut AppState, job: Job) -> Result { + let (sender, mut receiver) = mpsc::channel(1); + let add = UiCommand::Job(JobAction::Create(job, sender)); + state + .invoke + .send(add) + .await.map_err(|e| JobError::Send(e.to_string()))?; -use super::remote_render::remote_render_page; + receiver.select_next_some().await +} + +/// used to send command to backend service to fetch for the job list. +async fn cmd_list_jobs(state: &mut AppState) -> Option> { + let (sender, mut receiver) = mpsc::channel(0); + let cmd = UiCommand::Job(JobAction::All(sender)); + if let Err(e) = state.invoke.send(cmd).await { + eprintln!("Fail to send command to server! {e:?}"); + return None; + } + receiver.select_next_some().await +} + +/// command to fetch the job from backend service. +async fn cmd_fetch_job(state: &mut AppState, job_id: Uuid) -> Option { + let (sender, mut receiver) = mpsc::channel(0); + let cmd = UiCommand::Job(JobAction::Find(job_id, sender)); + if let Err(e) = state.invoke.send(cmd).await { + eprintln!("Fail to send job action: {e:?}"); + return None + }; + receiver.select_next_some().await +} + +/// Used to render the job list on teh side of the app. +fn render_list_job(collection: &Option>) -> String { + match collection { + Some(list) => { + html! { + @for job in list { + div { + table { + tbody { + tr tauri-invoke="get_job_detail" hx-vals=(json!({"jobId":job.id.to_string()})) hx-target={"#" (WORKPLACE) } { + td style="width:100%" { + (job.item.get_file_name_expected()) + }; + }; + }; + }; + }; + }; + } + } + None => { + html! { + div { + "No job found!" + } + } + } + }.0 +} + +/// Render the full job description and detail page. +fn render_job_detail_page(job: &Option) -> String { + match job { + Some(job) => { + let result = fetch_img_result(&job.item.as_ref()); + + // TODO: it would be nice to provide ffmpeg gif result of the completed render image. + // Something to add for immediate preview and feedback from render result + // this is to fetch the render collection + // if let Some(imgs) = result { + // let preview = fetch_img_preview(&job.item.output, &imgs); + // } + + let project_file = AsRef::::as_ref(&job.item); + let output = AsRef::::as_ref(&job.item); + let version = AsRef::::as_ref(&job.item); + + html!( + div class="content" { + h2 { "Job Detail" }; + + button tauri-invoke="open_dir" hx-vals=(json!({"path": project_file.to_str().unwrap()})) { ( project_file.to_str().unwrap() ) }; + + div { ( output.to_str().unwrap() ) }; + + div { ( version.to_string() ) }; + + button tauri-invoke="delete_job" hx-vals=(json!({"jobId":job.id})) hx-target="#workplace" { "Delete Job" }; + + p; + + @if let Some(list) = result { + @for img in list { + tr { + td { + img width="120px" src=(convert_file_src(&img)); + } + } + } + } + @else { + div { + "No image found in output directory..." + } + } + }; + ) + } + None => html!( + div { + p { "Job do not exist.. How did you get here?" }; + }; + ), + }.0 +} // input values are always string type. I need to validate input on backend instead of front end. // return invalidation if the value are not accepted. @@ -26,107 +148,247 @@ pub async fn create_job( path: PathBuf, output: PathBuf, ) -> Result { - // first thing first, parse the string into number - let start = start.parse::().map_err(|e| e.to_string())?; - let end = end.parse::().map_err(|e| e.to_string())?; - // stop if the parse fail to parse. - - let mode = Mode::Animation(Range { start, end }); - let job = Job::from(path, output, version, mode); - let app_state = state.lock().await; - let mut jobs = app_state.job_db.write().await; - - // use this to send the job over to database instead of command to network directly. - // We're splitting this apart to rely on database collection instead of forcing to send command over. - if let Err(e) = jobs.add_job(job.clone()).await { - eprintln!("{:?}", e); - } - - // send job to server - if let Err(e) = app_state - .to_network - .send(UiCommand::StartJob(job.clone())) - .await - { - eprintln!("Fail to send command to the server! \n{e:?}"); - } + let mode = RenderMode::try_new(&start, &end).map_err(|e| e.to_string())?; + let job = Job::from(mode, path, version, output).map_err(|e| e.to_string())?; + let mut app_state = state.lock().await; + let job_created = cmd_create_job(&mut app_state, job).await.map_err(|e| e.to_string())?; + let list = cmd_list_jobs(&mut app_state).await; - remote_render_page().await + // TODO: Utilize hx-swap-oob to update the list, then we'll update the portal to display selected job. + let list = render_list_job(&list); + let detail = render_job_detail_page(&Some(job_created)); + + Ok(html!( + div hx-target={ "#" (WORKPLACE) }{ + (PreEscaped(detail)) + } + div id="joblist" hx-swap-oob="true" { + (PreEscaped(list)) + } + ) + .0) } #[command(async)] -pub async fn list_jobs(state: State<'_, Mutex>) -> Result { - let server = state.lock().await; - let jobs = server.job_db.read().await; - let job_list = jobs.list_all().await.unwrap(); - - Ok(html! { - @for job in job_list { - div { - table { - tbody { - tr tauri-invoke="get_job" hx-vals=(json!({"jobId":job.id.to_string()})) hx-target="#detail" { - td style="width:100%" { - (job.get_file_name()) - }; - }; - }; - }; - }; - }; +pub async fn list_jobs(state: State<'_, Mutex>) -> Result { + let mut server = state.lock().await; + let content = cmd_list_jobs(&mut server).await; + Ok(render_list_job(&content)) +} + +fn fetch_img_result(path: &PathBuf) -> Option> { + match path.read_dir() { + // read the directory content + Ok(dir) => { + let mut list = dir + .filter_map(|res| res.ok()) // collect valid result + .map(|ent| ent.path()) // collect path from Directory entry result + .filter(|path| path.extension().map_or(false, |ext| ext == "png")) + .collect::>(); // collect the result into array list + list.sort(); // the list is not organzied, sort the list after collecting data + Some(list) + } + Err(e) => { + eprintln!("Unable to find any image stored in the directory:\nPath:{path:?}\nError:{e:?}"); + None + } } - .0) +} + +/* +fn fetch_img_preview(path: &PathBuf, imgs: &Vec) -> PathBuf { + // ffmpeg command usage + // ffmpeg -y -framerate 10 -i %02d.png -s 426x240 preview.gif + + let output = Command::new("ffmpeg").arg("-y -framerate 10 -i 02d.png -s 426x240 preview.gif").output(); + + + PathBuf::new() +} +*/ + +fn convert_file_src(path: &PathBuf) -> String { + #[cfg(any(windows, target_os = "android"))] + let base = "http://asset.localhost/"; + #[cfg(not(any(windows, target_os = "android")))] + let base = "asset://localhost/"; + // Consider about removing dunce lib for less dependencies involve for this case? + let path = dunce::canonicalize(path).expect("Should be able to canonicalize path!"); + let binding = path.to_string_lossy(); + let encoded = urlencoding::encode(&binding); + + format!("{base}{encoded}") } #[command(async)] -pub async fn get_job(state: State<'_, Mutex>, job_id: &str) -> Result { - // TODO: ask for the key to fetch the job details. - let job_id = Uuid::from_str(job_id).map_err(|e| { - eprintln!("Unable to parse uuid? \n{e:?}"); - () - })?; - let app_state = state.lock().await; - let jobs = app_state.job_db.read().await; - - match jobs.get_job(&job_id).await { - Ok(job) => Ok(html!( - div { - p { "Job Detail" }; - div { ( job.project_file.to_str().unwrap() ) }; - div { ( job.output.to_str().unwrap() ) }; - div { ( job.blender_version.to_string() ) }; - button tauri-invoke="delete_job" hx-vals=(json!({"jobId":job_id})) hx-target="#workplace" { "Delete Job" }; - }; - ) - .0), - Err(e) => Ok(html!( - div { - p { "Job do not exist.. How did you get here?" }; - input type="hidden" value=(e.to_string()); - }; - ) - .0), - } +pub async fn get_job_detail( + state: State<'_, Mutex>, + job_id: &str, +) -> Result { + let job_id = Uuid::from_str(job_id).map_err(|e| format!("Unable to parse uuid? \n{e:?}"))?; + let mut app_state = state.lock().await; + let result = cmd_fetch_job(&mut app_state, job_id).await; + Ok(render_job_detail_page(&result)) } // we'll need to figure out more about this? How exactly are we going to update the job? -// #[command(async)] -// pub fn update_job() +#[command(async)] +pub async fn update_job(state: State<'_, Mutex>, job_id: Uuid) -> Result<(), String> { + let mut app_state = state.lock().await; + if let Err(e) = app_state.invoke.send(UiCommand::Job(JobAction::Kill(job_id))).await { + return Err(format!("Fail to send command to host! Are you sure this app is responsive? {e:?}").into()); + } + + // TODO: call list_jobs and perform hx-swap-oob here to trigger job list refresh. + Ok(()) +} /// just delete the job from database. Notify peers to abandon task matches job_id #[command(async)] pub async fn delete_job(state: State<'_, Mutex>, job_id: &str) -> Result { + // here we're deleting it from the database { - let id = Uuid::from_str(job_id).unwrap(); - let server = state.lock().await; - let mut jobs = server.job_db.write().await; - let _ = jobs.delete_job(&id).await; - // TODO: Figure out what suppose to be done and handle here? - // let msg = UiCommand::StopJob(id); - // if let Err(e) = server.to_network.send(msg).await { - // eprintln!("Fail to send stop job command! {e:?}"); - // } + let mut app_state = state.lock().await; + let id = Uuid::from_str(job_id).map_err(|e| format!("{e:?}"))?; + let cmd = UiCommand::Job(JobAction::Kill(id)); + if let Err(e) = app_state.invoke.send(cmd).await { + eprintln!("{e:?}"); + } + } + + // now here we need to refresh the list + let list = list_jobs(state).await?; + + // TODO: do not send back Ok() response if there's an error, consider handling this separately. + // use a match condition to avoid sending error to the list + Ok(html!( + div class="group" id="joblist" hx-swap-oob="true" { + (PreEscaped(list)); + } + ) + .0) +} + +#[cfg(test)] +mod test { + /* + In this test suite, we are going to simply invoke all of the api function that are exposed to the UI. + Each API should have at least a minimum 1 passing test and 4 expect failures on certain edge cases + (malform input entry, wrong json syntax, incomplete form, etc) + + TODO: See about how we can get test coverage that handle all possible cases + */ + + use super::*; + use crate::{services::tauri_app::TauriApp}; + // use crate::models::constant::test::{EXAMPLE_FILE, EXAMPLE_OUTPUT}; + use anyhow::Error; + use futures::channel::mpsc::Receiver; + use ntest::timeout; + use tauri::{ + test::{mock_builder, MockRuntime}, + // webview::InvokeRequest + }; + + // TODO: Fix this so that I can get unit test working again + #[allow(dead_code)] + async fn scaffold_app() -> Result<(tauri::App, Receiver), Error> { + let (_invoke, receiver) = mpsc::channel(1); + // let conn = config_sqlite_db().await?; + // let app = TauriApp::new(&conn).await; + // TODO: Find a better way to get around this approach. Seems like I may not need to have an actual tauri app builder? + // error: symbol `_EMBED_INFO_PLIST` is already defined + let context = tauri::generate_context!("tauri.conf.json"); + let app = TauriApp::init_tauri_plugins(mock_builder()).build(context).expect("Should be able to build"); + Ok((app, receiver)) + } + + #[tokio::test] + #[timeout(5000)] + async fn create_job_successfully() { + // For now I'm going to let this pass, until I figure out how/why mockup tauri app dead-lock on initialization. + /* + let (app, mut receiver) = scaffold_app().await.unwrap(); + let webview = tauri::WebviewWindowBuilder::new(&app, "main", Default::default()) + .build() + .unwrap(); + let start = "1".to_owned(); + let end = "2".to_owned(); + let blender_version = Version::new(4, 1, 0); + let project_file = PathBuf::from(EXAMPLE_FILE); + let output = PathBuf::from(EXAMPLE_OUTPUT); + + let body = json!({ + "start": start, + "end": end, + "version": blender_version, + "path": project_file, + "output": output, + }); + + let res = tauri::test::get_ipc_response( + &webview, + InvokeRequest { + cmd: "create_job".into(), + callback: tauri::ipc::CallbackFn(0), + error: tauri::ipc::CallbackFn(1), + url: "tauri://localhost".parse().unwrap(), + body: tauri::ipc::InvokeBody::Json(body), + headers: Default::default(), + invoke_key: tauri::test::INVOKE_KEY.to_string(), + }, + ) + .map(|b| b.deserialize::().unwrap()); + + assert!(res.is_ok()); + + let expected_mode = RenderMode::Frame(1); + let job = Job::from(expected_mode, project_file, blender_version, output).expect("Should not fail"); + + let event = receiver.select_next_some().await; + let (mock_sender, _) = mpsc::channel(0); + assert_eq!(event, UiCommand::Job(JobAction::Create(job, mock_sender))); + */ + + assert!(true); + } + + #[tokio::test] + #[timeout(5000)] + async fn create_job_malform_fail() { + // For now I'm going to let this pass, until I figure out how/why mockup tauri app dead-lock on initialization. + // let (app, _) = scaffold_app().await.unwrap(); + // let webview = tauri::WebviewWindowBuilder::new(&app, "main", Default::default()); + // let start = "1".to_owned(); + // let end = "2".to_owned(); + // let project_file = PathBuf::from("./blender_rs/examples/assets/test.blend".to_owned()); + // let output = PathBuf::from("./blender_rs/examples/assets/".to_owned()); + + // let body = json!({ + // "start": start, + // "end": end, + // "version": "1a2b3c", + // "path": project_file, + // "output": output, + // }); + + // let res = tauri::test::get_ipc_response( + // &webview, + // InvokeRequest { + // cmd: "create_job".into(), + // callback: tauri::ipc::CallbackFn(0), + // error: tauri::ipc::CallbackFn(1), + // url: "tauri://localhost".parse().unwrap(), + // body: tauri::ipc::InvokeBody::Json(body), + // headers: Default::default(), + // invoke_key: tauri::test::INVOKE_KEY.to_string(), + // }, + // ) + // .map(|b| b.deserialize::().unwrap()); + + // assert!(res.is_err()); + assert!(true); } - remote_render_page().await + //#endregion } diff --git a/src-tauri/src/routes/live_view.rs b/src-tauri/src/routes/live_view.rs deleted file mode 100644 index 6c92ae09..00000000 --- a/src-tauri/src/routes/live_view.rs +++ /dev/null @@ -1,25 +0,0 @@ -use std::fs::File; -use tauri::{command, AppHandle}; - -/* - The idea behind this is to allow a scene you're working on to refresh and render from remote computer parts of the viewport render. - Almost like linescan rendering. - - TODO: Find a way to pipe render preview from Blender's .so/.a/.dll? - TODO: Find a way to receive and send data across network -*/ - -#[allow(dead_code)] -pub struct LiveView { - file: File, -} - -#[allow(dead_code)] -#[command] -pub fn load_file(_app: AppHandle) { - // load the project file - // spin up render_node to send the files over - // then have it prepare to render section of it - // and return the result to this view - todo!("impl this later!"); -} diff --git a/src-tauri/src/routes/mod.rs b/src-tauri/src/routes/mod.rs index 9ed25657..b525e528 100644 --- a/src-tauri/src/routes/mod.rs +++ b/src-tauri/src/routes/mod.rs @@ -1,7 +1,7 @@ pub mod job; -pub(crate) mod live_view; pub(crate) mod remote_render; pub mod server_settings; pub(crate) mod settings; pub(crate) mod util; pub(crate) mod worker; +pub(crate) mod index; diff --git a/src-tauri/src/routes/remote_render.rs b/src-tauri/src/routes/remote_render.rs index 683047b8..2cd3d9a2 100644 --- a/src-tauri/src/routes/remote_render.rs +++ b/src-tauri/src/routes/remote_render.rs @@ -1,51 +1,84 @@ /* Dev blog: - I really need to draw things out and make sense of the workflow for using this application. -I wonder why initially I thought of importing the files over and then selecting the files again to begin the render job? - -For now - Let's go ahead and save the changes we have so far. -Next update - Remove Project list, and instead just allow user to create a new job. -when you create a new job, it immediately sends a new job to the server farm for future features impl: Get a preview window that show the user current job progress - this includes last frame render, node status, (and time duration?) */ -use crate::AppState; +use super::util::select_directory; +use crate::{ + models::{app_state::AppState, project_file::ProjectFile}, + services::tauri_app::{QueryMode, UiCommand}, +}; +use crate::models::blender_action::BlenderAction; use blender::blender::Blender; +use futures::{SinkExt, StreamExt, channel::mpsc}; use maud::html; use semver::Version; use std::path::PathBuf; -use tauri::{command, AppHandle, State}; +use tauri::{AppHandle, State, command}; use tauri_plugin_dialog::DialogExt; use tauri_plugin_fs::FilePath; use tokio::sync::Mutex; -// todo break commands apart, find a way to get the list of versions -async fn list_versions(app_state: &AppState) -> Vec { - let manager = app_state.manager.read().await; - let mut versions = Vec::new(); - - let _ = manager.home.as_ref().iter().for_each(|b| { - let version = match b.fetch_latest() { - Ok(download_link) => download_link.get_version().clone(), - Err(_) => Version::new(b.major, b.minor, 0), - }; - versions.push(version); - }); - - // let manager = server.manager.read().await; - let _ = manager - .get_blenders() - .iter() - .for_each(|b| versions.push(b.get_version().clone())); - - versions +// TODO: where is this function called? +async fn list_versions(app_state: &mut AppState) -> Vec { + // TODO: see if there's a better way to get around this problematic function + /* + Issues: I'm noticing a significant delay of behaviour event happening here when connected online. + When connected online, BlenderManager seems to hold up to approximately 2-3 seconds before the remaining content fills in. + Offline loads instant, which is exactly the kind of behaviour I expect to see from this application. + */ + let (sender, mut receiver) = mpsc::channel(1); + let event = UiCommand::Blender(BlenderAction::List( + sender, + QueryMode::ONLINE | QueryMode::LOCAL, + )); + if let Err(e) = app_state.invoke.send(event).await { + eprintln!("Fail to send event! {e:?}"); + return Vec::new(); + } + + let res = receiver.select_next_some().await; + match res { + // Clone operation used here. might be expensive? See if there's another way to get aorund this. + Some(list) => list + .iter() + .map(|f| f.version.clone()) + .collect::>(), + None => Vec::new(), + } + + // let mut versions = Vec::new(); + + // // fetch local installation first. + // let mut local = manager + // .get_blenders() + // .iter() + // .map(|b| b.get_version().clone()) + // .collect::>(); + + // if !local.is_empty() { + // versions.append(&mut local); + // } + + // // then display the rest of the download list + // if let Some(downloads) = manager.fetch_download_list() { + // let mut item = downloads + // .iter() + // .map(|d| d.get_version().clone()) + // .collect::>(); + // versions.append(&mut item); + // }; + + // versions } /// List all of the available blender version. +// TODO: not used in the function yet? #[command(async)] pub async fn available_versions(state: State<'_, Mutex>) -> Result { - let server = state.lock().await; - let versions = list_versions(&server).await; + let mut server = state.lock().await; + let versions = list_versions(&mut server).await; Ok(html!( div { @@ -59,44 +92,52 @@ pub async fn available_versions(state: State<'_, Mutex>) -> Result>, +// This function must be async to avoid ui thread lock. Without async, no dialog will appear and app will freeze +/// Display dialog and return file path to blender. +/// This function will read the file and display another dialog prompt for additional detail before continue to display the result from import_blend() +#[command] +pub async fn open_dialog_for_blend_file( app: AppHandle, + state: State<'_, Mutex>, ) -> Result { - // tell tauri to open file dialog - // with that file path we will run import_blend function. - // else return nothing. - let result = match app + let given_path = app .dialog() .file() .add_filter("Blender", &["blend"]) .blocking_pick_file() - { - Some(file_path) => match file_path { - FilePath::Path(path) => import_blend(state, path).await.unwrap(), - FilePath::Url(uri) => import_blend(state, uri.as_str().into()).await.unwrap(), - }, - None => "".to_owned(), - }; - - Ok(result) + .and_then(|f| match f { + // TODO - see about converting PathBuf into &str, to reduce .into() for Url + FilePath::Path(f) => Some(f), + FilePath::Url(u) => Some(u.as_str().into()), + }); + + if let Some(path) = given_path { + return import_blend(&state, path).await; + } + Err("No file selected!".into()) } -// change this to return HTML content of the info back. -#[command(async)] -pub async fn import_blend( - state: State<'_, Mutex>, - path: PathBuf, -) -> Result { - let server = state.lock().await; - let versions = list_versions(&server).await; - - if path.file_name() == None { - return Err("Should be a valid file!".to_owned()); +#[command] +pub async fn update_output_field(app: AppHandle) -> Result { + match select_directory(app).await { + Ok(path) => Ok(html!( + input type="text" class="form-input" placeholder="Output Path" name="output" value=(path) readonly={true}; + ).0), + Err(_) => Err(()), } +} - let data = match Blender::peek(&path).await { +// TODO: Rename this function to "read_blend_file_content" - return info about this file. +// we can multi-purpose this for drag and drop feature +pub async fn import_blend(state: &Mutex, path: PathBuf) -> Result { + // for some reason this function takes longer online than it does offline? + // TODO: set unit test to make sure this function doesn't repetitively call blender.org everytime it's called. + let mut app_state = state.lock().await; + let versions = list_versions(&mut app_state).await; + + // validate file path. + let project_file = ProjectFile::from(path).map_err(|e| e.to_string())?; + let data = match Blender::peek(&project_file.to_path_buf()).await { Ok(data) => data, Err(e) => return Err(e.to_string()), }; @@ -108,12 +149,13 @@ pub async fn import_blend( form method="dialog" tauri-invoke="create_job" hx-target="#workplace" _="on submit trigger closeModal" { h1 { "Create new Render Job" }; label { "Project File Path:" }; - input type="text" class="form-input" name="path" value=(path.to_str().unwrap()) placeholder="Project path" readonly={true}; - // add a button here to let the user search by directory path. Let them edit the form. + input type="text" class="form-input" name="path" value=(project_file.to_str().unwrap()) placeholder="Project path" readonly={true}; br; label { "Output destination:" }; - input type="text" tauri-invoke="select_directory" hx-target="this" class="form-input" placeholder="Output Path" name="output" value=(data.output.to_str().unwrap()) readonly={true}; + div tauri-invoke="update_output_field" hx-target="this" { + input type="text" class="form-input" placeholder="Output Path" name="output" value=(data.current.render_setting.get_output().to_str().unwrap()) readonly={true}; + } br; div name="mode" { @@ -159,24 +201,7 @@ pub async fn import_blend( Ok(content.into_string()) } -#[command(async)] -pub async fn remote_render_page() -> Result { - let content = html! { - div class="content" { - h1 { "Remote Jobs" }; - - button tauri-invoke="create_new_job" hx-target="body" hx-indicator="#spinner" hx-swap="beforeend" { - "Import" - }; - - img id="spinner" class="htmx-indicator" src="/assets/svg-loaders/tail-spin.svg"; - - div class="group" id="joblist" tauri-invoke="list_jobs" hx-trigger="load" hx-target="this" { - }; - - div id="detail"; - }; - }; - - Ok(content.0) +#[cfg(test)] +mod test { + // TODO: fill testing suite for this route } diff --git a/src-tauri/src/routes/server_settings.rs b/src-tauri/src/routes/server_settings.rs index 935ed6e3..72623f57 100644 --- a/src-tauri/src/routes/server_settings.rs +++ b/src-tauri/src/routes/server_settings.rs @@ -1,13 +1,12 @@ -use tauri::{command, State}; -// TODO: Double verify that this is the correct Mutex usage throughout the application +use crate::models::{app_state::AppState, server_setting::ServerSetting, setting_action::SettingsAction}; +use crate::services::tauri_app::UiCommand; +use futures::SinkExt; +use tauri::{State, command}; use tokio::sync::Mutex; -use crate::models::{app_state::AppState, server_setting::ServerSetting}; - - #[command(async)] pub async fn get_server_settings() -> Result { - Ok( "".to_owned() ) + Ok("".to_owned()) } #[command(async)] @@ -15,11 +14,10 @@ pub async fn set_server_settings( state: State<'_, Mutex>, new_settings: ServerSetting, ) -> Result<(), String> { - // maybe I'm a bit confused here? - let app_state = state.lock().await; - let mut old_setting = app_state.setting.write().await; - new_settings.save(); - *old_setting = new_settings; - + let mut app_state = state.lock().await; + let event = UiCommand::Settings(SettingsAction::Update(new_settings)); + if let Err(e) = app_state.invoke.send(event).await { + return Err(e.to_string()); + } Ok(()) -} \ No newline at end of file +} diff --git a/src-tauri/src/routes/settings.rs b/src-tauri/src/routes/settings.rs index 7922d75c..1015cb5b 100644 --- a/src-tauri/src/routes/settings.rs +++ b/src-tauri/src/routes/settings.rs @@ -1,41 +1,69 @@ -use std::{path::PathBuf, sync::Arc}; - -// this is the settings controller section that will handle input from the setting page. use crate::models::{app_state::AppState, server_setting::ServerSetting}; +use crate::models::blender_action::BlenderAction; +use crate::models::setting_action::SettingsAction; +use crate::services::tauri_app::{QueryMode, UiCommand}; +use std::{env, path::PathBuf, str::FromStr, process::Command}; use blender::blender::Blender; +use futures::{channel::mpsc, SinkExt, StreamExt}; use maud::html; use semver::Version; use serde_json::json; -use tauri::{command, AppHandle, Error, State}; +use tauri::{command, AppHandle, State}; use tauri_plugin_dialog::DialogExt; use tauri_plugin_fs::FilePath; -use tokio::{ - join, - sync::{Mutex, RwLock}, -}; +use tokio::sync::Mutex; const SETTING: &str= "settings"; -/* - Because blender installation path is not store in server setting, it is infact store under blender manager, - we will need to create a new custom response message to provide all of the information needed to display on screen properly -*/ +#[command] +pub fn open_dir(path: &str) -> Result<(),()> { + // macos is special, the path link inside app bundle, but cannot access via file explore/finder + let path = PathBuf::from_str(path).unwrap(); + let result = match env::consts::OS { + "windows" => Ok("explorer"), + "macos" => Ok("open"), + "linux" => Ok("xdg-open"), + _ => Err(()) + }; + if let Ok(program) = result { + Command::new(program) + .arg(path) + .spawn() + .unwrap(); + } + Ok(()) +} #[command(async)] pub async fn list_blender_installed(state: State<'_, Mutex>) -> Result { - let app_state = state.lock().await; - let manager = app_state.manager.read().await; - let localblenders = manager.get_blenders(); + let (sender, mut receiver) = mpsc::channel(0); + let mut app_state = state.lock().await; + + let event = UiCommand::Blender(BlenderAction::List(sender, QueryMode::LOCAL)); + if let Err(e) = app_state.invoke.send(event).await { + eprintln!("fail to send mpsc to event! {e:?}"); + return Err(()) + } + + let list = receiver.select_next_some().await.expect("Should expect data back!"); Ok(html! { - @for blend in localblenders { + @for blend in list { tr { td { - (blend.get_version().to_string()) + label title=(blend.link()) { + (blend.version.to_string()) + } }; td { - (blend.get_executable().to_str().unwrap()) - }; + button tauri-invoke="open_dir" hx-vals=(json!({"path":blend.link()})) { + r"📁" + } + button tauri-invoke="delete_blender" hx-vals=(json!({"path":blend.link() })) + { + r"🗑︎" + } + } }; }; } @@ -45,11 +73,10 @@ pub async fn list_blender_installed(state: State<'_, Mutex>) -> Result /// Add a new blender entry to the system, but validate it first! #[command(async)] pub async fn add_blender_installation( - app: AppHandle, - state: State<'_, Mutex>, // TODO: Need to change this to string, string? -) -> Result { - // TODO: include behaviour to search for file that contains blender. - // so here's where + handle: State<'_, Mutex>, + state: State<'_, Mutex>, +) -> Result<(), ()> { // TODO: Need to change this to string, string? + let app = handle.lock().await; let path = match app.dialog().file().blocking_pick_file() { Some(file_path) => match file_path { FilePath::Path(path) => path, @@ -58,15 +85,22 @@ pub async fn add_blender_installation( None => return Err(()), }; - let app_state = state.lock().await; - let mut manager = app_state.manager.write().await; - match manager.add_blender_path(&path) { - Ok(_blender) => Ok(html! { - // HX-trigger="newBlender" - } - .0), - Err(_) => Err(()), + let mut app_state = state.lock().await; + if let Err(e) = app_state.invoke.send(UiCommand::Blender(BlenderAction::Add(path))).await { + eprintln!("Fail to send data back! {e:?}"); } + Ok(()) +} + +// Somehow I was missing this function where it was used in this class? +#[command(async)] +pub async fn install_from_internet( + _handle: State<'_, Mutex>, + _state: State<'_, Mutex> +) -> Result<(), ()>{ + print!("Show me what the internet still have?"); + // in this case, I need to return a maud layout of the dialog pop up using htmx + Err(()) } // So this can no longer be a valid api call? @@ -75,88 +109,120 @@ pub async fn add_blender_installation( pub async fn fetch_blender_installation( state: State<'_, Mutex>, version: &str, -) -> Result { - let app_state = state.lock().await; - let mut manager = app_state.manager.write().await; - let version = Version::parse(version).map_err(|e| e.to_string())?; - let blender = manager.fetch_blender(&version).map_err(|e| match e { - blender::manager::ManagerError::DownloadNotFound { arch, os, url } => { - format!("Download link not found! {arch} {os} {url}") - } - blender::manager::ManagerError::RequestError(request) => { - format!("Request error: {request}") - } - blender::manager::ManagerError::FetchError(fetch) => format!("Fetch error: {fetch}"), - blender::manager::ManagerError::IoError(io) => format!("IoError: {io}"), - blender::manager::ManagerError::UnsupportedOS(os) => format!("Unsupported OS {os}"), - blender::manager::ManagerError::UnsupportedArch(arch) => { - format!("Unsupported architecture! {arch}") - } - blender::manager::ManagerError::UnableToExtract(ctx) => { - format!("Unable to extract content! {ctx}") - } - blender::manager::ManagerError::UrlParseError(url) => format!("Url parse error: {url}"), - blender::manager::ManagerError::PageCacheError(cache) => { - format!("Page cache error! {cache}") - } - blender::manager::ManagerError::BlenderError { source } => { - format!("Blender error: {source}") - } - })?; - Ok(blender) +) -> Result { + let version = Version::parse(version).map_err(|_| ())?; + let (sender, mut receiver) = mpsc::channel(1); + let event = UiCommand::Blender(BlenderAction::Get(version, sender)); + let mut app_state = state.lock().await; + app_state.invoke.send(event).await.unwrap(); + let result = receiver.select_next_some().await; + + // let blender = manager.fetch_blender(&version).map_err(|e| match e { + // blender::manager::ManagerError::DownloadNotFound { arch, os, url } => { + // format!("Download link not found! {arch} {os} {url}") + // } + // blender::manager::ManagerError::RequestError(request) => { + // format!("Request error: {request}") + // } + // blender::manager::ManagerError::FetchError(fetch) => format!("Fetch error: {fetch}"), + // blender::manager::ManagerError::IoError(io) => format!("IoError: {io}"), + // blender::manager::ManagerError::UnsupportedOS(os) => format!("Unsupported OS {os}"), + // blender::manager::ManagerError::UnsupportedArch(arch) => { + // format!("Unsupported architecture! {arch}") + // } + // blender::manager::ManagerError::UnableToExtract(ctx) => { + // format!("Unable to extract content! {ctx}") + // } + // blender::manager::ManagerError::UrlParseError(url) => format!("Url parse error: {url}"), + // blender::manager::ManagerError::PageCacheError(cache) => { + // format!("Page cache error! {cache}") + // } + // blender::manager::ManagerError::BlenderError { source } => { + // format!("Blender error: {source}") + // } + // })?; + + match result { + Some(blend) => Ok(blend), + None => Err(()) + } +} + +/// Permanently delete blender from the system using the file path given +#[command(async)] +pub async fn delete_blender(state: State<'_, Mutex>, path: &str) -> Result<(), String> { + let mut app_state = state.lock().await; + let blender = match Blender::from_executable(path) { + Ok(blend) => blend, + Err(e) => return Err(e.to_string()) + }; + + let event = UiCommand::Blender(BlenderAction::Remove(blender)); + if let Err(e) = app_state.invoke.send(event).await { + eprintln!("Fail to send blender action event! {e:?}"); + return Err(e.to_string()) + } + + Ok(()) } -// TODO: Ambiguous name - Change this so that we have two methods, -// - Severe local path to blender from registry (Orphan on disk/not touched) -// - Delete blender content completely (erasing from disk) +/// - Severe local path to blender from registry (Orphan on disk/not touched) #[command(async)] -pub async fn remove_blender_installation( +pub async fn disconnect_blender_installation( state: State<'_, Mutex>, blender: Blender, -) -> Result<(), Error> { - let app_state = state.lock().await; - let mut manager = app_state.manager.write().await; - manager.remove_blender(&blender); +) -> Result<(), String> { + let mut app_state = state.lock().await; + + let event = UiCommand::Blender(BlenderAction::Disconnect(blender)); + if let Err(e) = app_state.invoke.send(event).await { + eprintln!("Fail to send blender action event! {e:?}"); + return Err(e.to_string()) + } + Ok(()) } +// I am a little confused about this function. #[command(async)] pub async fn update_settings( state: State<'_, Mutex>, install_path: String, cache_path: String, render_path: String, -) -> Result { - let install_path = PathBuf::from(install_path); +) -> Result<(), ()> { + let _install_path = PathBuf::from(install_path); let blend_dir = PathBuf::from(cache_path); let render_dir = PathBuf::from(render_path); - { - let mut server = state.lock().await; - server.setting = Arc::new(RwLock::new(ServerSetting { - blend_dir, - render_dir, - })); - let mut manager = server.manager.write().await; - manager.set_install_path(&install_path); + let mut state = state.lock().await; + let new_setting = ServerSetting { + blend_dir, + render_dir, + }; + + let command = UiCommand::Settings(SettingsAction::Update(new_setting)); + if let Err(e) = state.invoke.send(command).await { + eprintln!("{e:?}"); } - Ok(get_settings(state).await.unwrap()) + Ok(()) } // change this so that this is returning the html layout to let the client edit the settings. #[command(async)] pub async fn edit_settings(state: State<'_, Mutex>) -> Result { - let app_state = state.lock().await; - let (settings, manager) = join!(app_state.setting.read(), app_state.manager.read()); - let install_path = manager.get_install_path(); + let mut app_state = state.lock().await; + let settings = app_state.get_settings().await.map_err(|e| e.to_string())?; + + // let install_path = manager.get_install_path(); let cache_path = &settings.blend_dir; let render_path = &settings.render_dir; Ok(html!( form tauri-invoke="update_settings" hx-target="this" hx-swap="outerHTML" { - h3 { "Blender Installation Path:" }; - input name="installPath" class="form-input" readonly="true" tauri-invoke="select_directory" hx-trigger="click" hx-target="this" value=(install_path.to_str().unwrap() ); + // h3 { "Blender Installation Path:" }; + // input name="installPath" class="form-input" readonly="true" tauri-invoke="select_directory" hx-trigger="click" hx-target="this" value=(install_path.to_str().unwrap() ); h3 { "Blender File Cache Path:" }; input name="cachePath" class="form-input" readonly="true" tauri-invoke="select_directory" hx-trigger="click" hx-target="this" value=(cache_path.to_str().unwrap()); @@ -174,23 +240,26 @@ pub async fn edit_settings(state: State<'_, Mutex>) -> Result>) -> Result { - let app_state = state.lock().await; - let (settings, manager) = join!(app_state.setting.read(), app_state.manager.read()); + let mut app_state = state.lock().await; + let settings = app_state.get_settings().await.map_err(|e| e.to_string())?; - let install_path = manager.get_install_path().to_str().unwrap(); let cache_path = &settings.blend_dir.to_str().unwrap(); let render_path = &settings.render_dir.to_str().unwrap(); Ok(html!( div tauri-invoke="open_path" hx-target="this" hx-swap="outerHTML" { - h3 { "Blender Installation Path:" }; - label hx-info=(json!( { "path": install_path } )) { (install_path) }; - + // TODO: Could we make a factory to build buttons for this? h3 { "Blender File Cache Path:" }; - label hx-info=(json!( { "path": cache_path } )) { (cache_path) }; + button tauri-invoke="open_dir" hx-vals=(json!({"path":cache_path})) { + r"📁" + } + label word-wrap="break-word" hx-info=(json!( { "path": cache_path } )) { (cache_path) }; h3 { "Render cache directory:" }; - label hx-info=(json!( { "path": render_path } )) { (render_path) }; + button tauri-invoke="open_dir" hx-vals=(json!({"path":render_path})) { + r"📁" + } + label word-wrap="break-word" hx-info=(json!( { "path": render_path } )) { (render_path) }; br; button tauri-invoke="edit_settings" { "Edit" }; diff --git a/src-tauri/src/routes/worker.rs b/src-tauri/src/routes/worker.rs index f4c18a51..1aff6b69 100644 --- a/src-tauri/src/routes/worker.rs +++ b/src-tauri/src/routes/worker.rs @@ -1,37 +1,53 @@ +use std::str::FromStr; + +use futures::channel::mpsc; +use futures::{SinkExt, StreamExt}; +use libp2p::PeerId; use maud::html; use serde_json::json; use tauri::{command, State}; use tokio::sync::Mutex; +use crate::constant::WORKPLACE; use crate::models::app_state::AppState; -use crate::services::tauri_app::WORKPLACE; +use crate::services::tauri_app::{UiCommand, WorkerAction}; #[command(async)] pub async fn list_workers(state: State<'_, Mutex>) -> Result { - let server = state.lock().await; - let workers = server.worker_db.read().await; - match &workers.list_worker().await { - Ok(data) => Ok(html! { - @for worker in data { - div tauri-invoke="get_worker" hx-vals=(json!({ "machineId": worker.machine_id })) hx-target=(format!("#{WORKPLACE}")) { - table { - tbody { - tr { - td style="width:100%" { - div { (worker.spec.host) } - div { (worker.spec.os) " | " (worker.spec.arch) } + let mut server = state.lock().await; + let (sender, mut receiver) = mpsc::channel(1); + let cmd = UiCommand::Worker(WorkerAction::List(sender)); + if let Err(e) = server.invoke.send(cmd).await { + eprintln!("Fail to send command to fetch workers{e:?}"); + } + + match receiver.select_next_some().await { + Some(data) => { + let content = match data.len() { + 0 => html! { div { } }, + _ => html! { + @for worker in data { + div { + table tauri-invoke="get_worker" hx-vals=(json!({ "machineId": worker.id.to_base58() })) hx-target=(format!("#{WORKPLACE}")) { + tbody { + tr { + td style="width:100%" { + div { (worker.spec.host) } + div { (worker.spec.os) " | " (worker.spec.arch) } + } + } } } } } - } - } + }, + }; + Ok(content.0) } - .0), - Err(e) => { - eprintln!("Received error on list workers: \n{e:?}"); + None => { + eprintln!("No workers found"); Ok(html!( div { }; ).0) - }, + } } } @@ -56,21 +72,78 @@ pub async fn list_workers(state: State<'_, Mutex>) -> Result>, machine_id: &str) -> Result { - let app_state = state.lock().await; - let workers = app_state.worker_db.read().await; - match workers.get_worker(machine_id).await { + let mut app_state = state.lock().await; + let (mut sender, mut receiver) = mpsc::channel(0); + match PeerId::from_str(machine_id) { + Ok(peer_id) => { + let cmd = UiCommand::Worker(WorkerAction::Get(peer_id, sender)); + if let Err(e) = app_state.invoke.send(cmd).await { + eprintln!("{e:?}"); + } + } + Err(e) => { + eprintln!("Fail to parse machine id from input! {e:?}"); + sender + .send(None) + .await + .expect("Sender/Receiver should not be closed"); + } + }; + + match receiver.select_next_some().await { Some(worker) => Ok(html! { - div { - h1 { (format!("Computer: {}", worker.machine_id)) }; + div class="content" { + h1 { (format!("Computer: {}", &worker.spec.host)) }; h3 { "Hardware Info:" }; - p { (format!("System: {} | {}", worker.spec.os, worker.spec.arch))} - p { (format!("CPU: {} | ({} threads)", worker.spec.cpu, worker.spec.cores)) }; - p { (format!("Ram: {} GB", worker.spec.memory / ( 1024 * 1024 )))} - @if let Some(gpu) = worker.spec.gpu { - p { (format!("GPU: {gpu}")) }; - } @else { - p { "GPU: N/A" }; - }; + table { + tr { + th { + "System" + } + th { + "CPU" + } + th { + "Memory" + } + th { + "GPU" + } + } + tr { + td { + p { (worker.spec.os) } + span { (worker.spec.arch) } + } + td { + p { (worker.spec.cpu) } + span { (format!("({} cores)",worker.spec.cores)) } + } + td { + (format!("{}GB", worker.spec.memory / ( 1024 * 1024 * 1024 ))) + } + td { + @if let Some(gpu) = &worker.spec.gpu { + label { (gpu) }; + } @else { + label { "N/A" }; + }; + } + } + } + + h3 { "Task List" } + table { + tr { + th { + "Project Name" + } + th { + "Progresss" + } + } + // TODO: Fill in the info from the worker machine here. + } }; } .0), diff --git a/src-tauri/src/services/blend_farm.rs b/src-tauri/src/services/blend_farm.rs index 10fa4e1b..206f72c0 100644 --- a/src-tauri/src/services/blend_farm.rs +++ b/src-tauri/src/services/blend_farm.rs @@ -1,8 +1,9 @@ -use crate::models::{ - message::{NetEvent, NetworkError}, - network::NetworkController, - }; +use crate::models::behaviour::FileResponse; +use crate::network::controller::Controller as NetworkController; +use crate::network::message::{Event, FileCommand, NetworkError}; use async_trait::async_trait; +use futures::channel::oneshot; +use libp2p_request_response::ResponseChannel; use tokio::sync::mpsc::Receiver; #[async_trait] @@ -10,6 +11,31 @@ pub trait BlendFarm { async fn run( mut self, client: NetworkController, - event_receiver: Receiver, + event_receiver: Receiver, ) -> Result<(), NetworkError>; + + // could we use this inside the blendfarm as a base class? + async fn handle_inbound_request( + &mut self, + client: &mut NetworkController, + request: String, + channel: ResponseChannel, + ) { + let (sender, receiver) = oneshot::channel(); + let cmd = FileCommand::RequestFilePath { + keyword: request, + sender, + }; + client.file_service(cmd).await; + + // once we received the data signal - process the remaining with the information obtained. + if let Some(path) = receiver.await.expect("Sender should not be dropped") { + let file = async_std::fs::read(path).await.unwrap(); + client.respond_file(file, channel).await; + } else { + eprintln!( + "This local service does not have any matching request providing! Do something about the ResponseChannel?" + ); + } + } } diff --git a/src-tauri/src/services/cli_app.rs b/src-tauri/src/services/cli_app.rs index 54ef9843..873e8527 100644 --- a/src-tauri/src/services/cli_app.rs +++ b/src-tauri/src/services/cli_app.rs @@ -1,5 +1,3 @@ -use std::sync::Arc; - /* Have a look into TUI for CLI status display window to show user entertainment on screen https://docs.rs/tui/latest/tui/ @@ -9,235 +7,610 @@ Feature request: - receive command to properly reboot computer when possible? */ use super::blend_farm::BlendFarm; +use crate::domains::render_store::RenderStore; +use crate::models::render_info::NewRenderInfoDto; +use crate::network::message::{self, Event, NetworkError, NodeEvent}; +use crate::network::provider_rule::ProviderRule; use crate::{ domains::{job_store::JobError, task_store::TaskStore}, models::{ - job::JobEvent, - message::{NetEvent, NetworkError}, - network::{NetworkController, JOB}, + job::{Job, JobEvent}, + project_file::ProjectFile, + server_setting::ServerSetting, task::Task, }, + network::controller::Controller, }; -use blender::blender::Manager as BlenderManager; -use blender::models::status::Status; -use libp2p::PeerId; -use tokio::{ - select, - sync::{mpsc::Receiver, RwLock}, - // task::JoinHandle, -}; +use blender::blender::{Manager as BlenderManager, ManagerError}; +use blender::models::event::BlenderEvent; +use libp2p::{Multiaddr, PeerId}; +use std::time::Duration; +use std::{path::PathBuf, str::FromStr, sync::Arc}; +use thiserror::Error; +use tokio::spawn; +use tokio::sync::mpsc::{self, Receiver, Sender}; +use tokio::time::sleep; +use tokio::{select, sync::RwLock}; +use uuid::Uuid; + +// TODO: What was this for? +#[allow(dead_code)] +enum CmdCommand { + // TODO: See where this can be used? + Render(Task, Sender), + Dial(PeerId, Multiaddr), + RequestTask, // calls to host for more task. +} + +#[derive(Debug, Error)] +enum CliError { + #[error("Encounter an network error! \n{0:}")] + NetworkError(#[from] message::NetworkError), + #[error("Encounter an IO error! \n{0}")] + Io(#[from] async_std::io::Error), + #[error("Manager Error: {0}")] + ManagerError(#[from] ManagerError), +} pub struct CliApp { manager: BlenderManager, - task_store: Arc>, - // Hmm not sure if I need this but we'll see! - // task_handle: Option>, // isntead of this, we should hold task_handler. That way, we can abort it when we receive the invocation to do so. + + // database + task_store: Arc>, + render_store: Arc>, + + // config + settings: ServerSetting, + + // The idea behind this is to let the network manager aware that the client side of the app is busy working on current task. + // it would be nice to receive information and notification about this current client status somehow. + // Could I use PhantomData to hold Task Object type? + host: Option<(PeerId, Multiaddr)>, // isntead of this, we should hold task_handler. That way, we can abort it when we receive the invocation to do so. } impl CliApp { - pub fn new(task_store: Arc>) -> Self { + // we could simplify this design by just asking for the database info? + pub fn new( + task_store: Arc>, + render_store: Arc>, + ) -> Self { let manager = BlenderManager::load(); Self { + settings: ServerSetting::load(), manager, task_store, - // task_handle: None, + render_store, + host: None, // no task assigned yet } } -} - -impl CliApp { - // TODO: May have to refactor this to take consideration of Job Storage - // How do I abort the job? - // Invokes the render job. The task needs to be mutable for frame deque. - async fn render_task( - &mut self, - request_id: PeerId, - client: &mut NetworkController, - task: &mut Task, - ) { - let status = format!("Receive task from peer [{:?}]", task); - client.send_status(status).await; - let id = task.job_id; + // This function will ensure the directory will exist, and return the path to that given directory. + // It will remain valid unless directory or parent above is removed during runtime. + async fn generate_temp_project_task_directory( + settings: &ServerSetting, + task: &Task, + id: &str, + ) -> Result { // create a path link where we think the file should be - let blend_dir = client.settings.blend_dir.join(id.to_string()); - if let Err(e) = async_std::fs::create_dir_all(&blend_dir).await { - eprintln!("Error creating blend directory! {e:?}"); + let job = AsRef::::as_ref(&task); + let project_path = settings + .blend_dir + .join(id.to_string()) + .join(&job.get_file_name_expected()); + + // we only want the parent directory to exist. + match async_std::fs::create_dir_all(&project_path.parent().expect("I wouldn't think we'd be trying to check files in root? Please write a bug report and replicate step by step to reproduce the issue")).await { + Ok(_) => Ok(project_path), + Err(e) => { + Err(e) + } } + } - // assume project file is located inside this directory. - let project_file = blend_dir.join(&task.blend_file_name); // append the file name here instead. + #[allow(dead_code)] + async fn validate_project_file( + &self, + client: &mut Controller, + task: &Task, + ) -> Result { + let id = AsRef::::as_ref(&task); + let project_file_path = + CliApp::generate_temp_project_task_directory(&self.settings, &task, &id.to_string()) + .await + .expect("Should have permission!"); - client - .send_status(format!("Checking for project file {:?}", &project_file)) - .await; + // assume project file is located inside this directory. + println!("Checking for {:?}", &project_file_path); + let job = AsRef::::as_ref(&task); // Fetch the project from peer if we don't have it. - if !project_file.exists() { + if !project_file_path.exists() { println!( - "Project file do not exist, asking to download from host: {:?}", - &task.blend_file_name + "calling network for project file, asking to download from DHT: {:?}", + &job.get_file_name_expected() ); - let file_name = task.blend_file_name.to_str().unwrap(); + let search_directory = project_file_path + .parent() + .expect("Shouldn't be anywhere near root level?"); + + // so I need to figure out something about this... + // TODO - find a way to break out of this if we can't fetch the project file. + let job = AsRef::::as_ref(&task); + let file_name = job.get_file_name_expected(); + // TODO: To receive the path or not to modify existing project_file value? I expect both would have the same value? - match client.get_file_from_peers(&file_name, &blend_dir).await { - Ok(path) => println!("File successfully download from peers! path: {path:?}"), - Err(e) => match e { - NetworkError::UnableToListen(_) => todo!(), - NetworkError::NotConnected => todo!(), - NetworkError::SendError(_) => {} - NetworkError::NoPeerProviderFound => { - // I was timed out here? - client - .send_status("No peer provider found on the network?".to_owned()) - .await - } - NetworkError::UnableToSave(e) => { - client - .send_status(format!("Fail to save file to disk: {e}")) - .await - } - NetworkError::Timeout => { - // somehow we lost connection, try to establish connection again? - // client.dial(request_id, client.public_addr).await; - dbg!("Timed out?"); - } - _ => println!("Unhandle error received {e:?}"), // shouldn't be covered? - }, - } + let path = client + .get_file_from_peers(&file_name, search_directory) + .await + .map_err(CliError::NetworkError)?; + return Ok(path); } - // here we'll ask if we have blender installed before usage - let blender = self - .manager - .fetch_blender(&task.blender_version) - .expect("Fail to download blender"); - - // TODO: Call other network on specific topics to see if there's a version available. - // match manager.have_blender(job.as_ref()) { - // Some(exe) => exe.clone(), - // None => { - // // try to fetch from other peers with matching os / arch. - // // question is, how do I make them publicly available with the right blender version? or do I just find it by the executable name instead? - // } - // } + Ok(project_file_path) + } + async fn verify_and_check_render_output_path( + &self, + id: &Uuid, + ) -> Result { // create a output destination for the render image - let output = client.settings.render_dir.join(id.to_string()); - if let Err(e) = async_std::fs::create_dir_all(&output).await { - eprintln!("Error creating render directory: {e:?}"); - } + let output = self.settings.render_dir.join(&id.to_string()); + async_std::fs::create_dir_all(&output).await?; + Ok(output) + } + + // TODO: Refactor this! + // TODO: Rewrite this to meet Single responsibility principle. + // How do I abort the job? -> That's the neat part! You don't! Delete the job+task entry from the database, and notify client to halt if running deleted jobs. + /// Invokes the render job. The task needs to be mutable for frame deque. + async fn render_task( + &mut self, + client: &mut Controller, + task: &mut Task, + sender: &mut Sender, + ) -> Result<(), CliError> { + // for now, let's skip this part and continue on. We don't have DHT setup, but I want to make sure cli does actually render once we get the file share situation straighten out. + // TODO: Find a way to get the file share working across network. + // let project_file = self.validate_project_file(client, &task).await?; + + let job = AsRef::::as_ref(&task); + let project_file = AsRef::::as_ref(&job); + let version = job.as_ref(); + + /* + this script below was our internal implementation of handling DHT fallback mode + save this for future feature updates + let blender = match self.manager.have_blender(version) { + Some(blend) => blend, + None => { + // when I do not have task blender version installed - two things will happen here before an error is thrown + // First, check our internal DHT services to see if any other client on the network have matching version - then fetch it. Install after completion + // Secondly, download the file online. + // If we reach here - it is because no other node have matching version, and unable to connect to download url (Internet connectivity most likely). + // TODO: It would be nice to broadcast everyone else "Hey! I'm download this version, could you wait until I'm done to distribute?" + let link_name = &self + .manager + .get_blender_link_by_version(version) + .expect(&format!( + "Invalid Blender version used. Not found anywhere! Version {:?}", + &version + )) + .name; + let destination = self.manager.get_install_path(); + + // should also use this to send CmdCommands for network stuff. + let latest = client.get_file_from_peers(&link_name, destination).await; + + match latest { + Ok(path) => { + // assumed the file I downloaded is already zipped, proceed with caution on installing. + let folder_name = self.manager.get_install_path(); + let exe = + DownloadLink::extract_content(path, folder_name.to_str().unwrap()) + .expect( + "Unable to extract content, More likely a permission issue?", + ); + &Blender::from_executable(exe).expect("Received invalid blender copy!") + } + Err(e) => { + println!( + "No client on network is advertising target blender installation! {e:?}" + ); + &self + .manager + .fetch_blender(&version) + .expect("Fail to download blender") + } + } + } + }; + */ + + let blender = match self.manager.fetch_blender(version) { + Ok(blender) => blender, + Err(e) => { + return Err(CliError::ManagerError(e)); + } + }; + + let id = AsRef::::as_ref(&task); + let output = self + .verify_and_check_render_output_path(id) + .await + .map_err(|e| CliError::Io(e))?; // run the job! - match task.clone().run(project_file, output, &blender).await { + // TODO: is there a better way to get around clone? + match task + .clone() + .run(project_file.to_path_buf(), output, &blender) + .await + { Ok(rx) => loop { - if let Ok(status) = rx.recv() { - match status { - Status::Idle => client.send_status("[Idle]".to_owned()).await, - Status::Running { status } => { - client.send_status(format!("[Running] {status}")).await + match rx.recv() { + Ok(status) => { + // SHould look into a better way to write this so that we can handle loop better for blender process.... + // Somehow, receiver was closed? + match &status { + BlenderEvent::Error(..) => { + sender + .send(status) + .await + .expect("Channel should not be closed"); + // make sure to break out of this loop! + break; + } + _ => sender + .send(status) + .await + .expect("Channel should not be closed"), + } + } + Err(e) => { + let event = BlenderEvent::Error(e.to_string()); + if let Err(c) = sender.send(event).await { + eprintln!( + "Unable to send error event over clseod channel: {c:?}\n{e:?}" + ); } - Status::Log { status } => { - client.send_status(format!("[Log] {status}")).await + break; + } + } + }, + Err(e) => { + let err = JobError::TaskError(e); + client.send_job_event(JobEvent::Error(err)).await; + } + }; + + Ok(()) + } + + async fn handle_job_from_network(&mut self, client: &mut Controller, event: JobEvent) { + match event { + // on render task received, we should store this in the database. + JobEvent::Render(peer_id_str, mut task) => { + let peer_id = match PeerId::from_str(&peer_id_str) { + Ok(peer_id) => peer_id, + Err(e) => { + eprintln!("Not a valid peer id! {e:?}"); + return; + } + }; + + if client.public_id.ne(&peer_id) { + return; + } + + // Skip this for now. We'll work on DHT at another time. + // let project_file = match self.validate_project_file(client, &task).await { + // Ok(path) => path, + // Err(e) => { + // eprintln!("Fail to validate project file! {e:?}"); + // return; + // } + // }; + // let project_file = task.get_job().get_project_path(); + + // scope containing using self. Need to close at the end of the scope for other method to use it as mutable state. + // do we need this right now? + { + let db = self.task_store.write().await; + // Need to make sure no other node work the same job here. + if let Err(e) = db.add_task(task.clone()).await { + println!("Unable to add task! {e:?}"); + } + } + + // println!("Begin printing task at this level!"); + // let blend = match &self.manager.fetch_blender(&task.get_job().get_version()) { + // Ok(result) => result, + // Err(e) => { + // eprintln!("problem downloading blender! {e:?}"); + // return; + // } + // }; + + let (mut sender, mut receiver) = mpsc::channel(32); + let job_id = AsRef::::as_ref(&task).clone(); + + match self.render_task(client, &mut task, &mut sender).await { + Ok(()) => { + println!("task completed!"); + } + Err(e) => { + eprintln!("Error rendering task! {e:?}"); + } + }; + + loop { + match receiver.blocking_recv().unwrap_or(BlenderEvent::Error( + "Client receiver was closed. Perhaps something happen to the host?" + .to_owned(), + )) { + BlenderEvent::Log(log) => { + println!("[LOG] {log}"); } - Status::Warning { message } => { - client.send_status(format!("[Warning] {message}")).await + BlenderEvent::Warning(warn) => { + eprintln!("[WARN] {warn}"); } - Status::Error(blender_error) => { - client.send_status(format!("[ERR] {blender_error:?}")).await + BlenderEvent::Rendering { current, total } => { + println!("[LOG] Rendering {current} out of {total}..."); } - Status::Completed { frame, result, .. } => { - // Use PathBuf as this helps enforce type intention of using OsString - // Why don't I create it like a directory instead? = - let file_name = result.file_name().unwrap().to_string_lossy(); - let file_name = format!("/{}/{}", id, file_name); - let event = JobEvent::ImageCompleted { - job_id: id, - frame, - file_name: file_name.clone(), + BlenderEvent::Completed { frame, result } => { + println!("Image completed!"); + let provider_rule = ProviderRule::Default(result); + if let Err(e) = client.start_providing(&provider_rule).await { + eprintln!("Unable to provide completed render image! {e:?}"); + } + + match provider_rule.get_file_name() { + Some(file_name) => { + let job_event = JobEvent::ImageCompleted { + job_id, + frame, + file_name: file_name.to_str().unwrap().to_string(), + }; + client.send_job_event(job_event).await; + } + None => { + eprintln!( + "Fail to get file name from provider rule - Did we get the file name incorrectly somehow?" + ); + } }; - client.start_providing(file_name, result).await; - client.send_job_message(request_id, event).await; } - Status::Exit => { - client - .send_job_message(request_id, JobEvent::JobComplete) - .await; - break; + BlenderEvent::Unhandled(unk) => { + eprintln!("An unhandled blender event received: {unk}") + } + BlenderEvent::Exit => break, + BlenderEvent::Error(e) => { + eprintln!("Blender error event received! \n{e}"); } - }; + } } - }, - Err(e) => { - let err = JobError::TaskError(e); - client - .send_job_message(request_id, JobEvent::Error(err)) - .await; } - }; + + JobEvent::ImageCompleted { .. } => {} // ignored since we do not want to capture image? + // For future impl. we can take advantage about how we can allieve existing job load. E.g. if I'm still rendering 50%, try to send this node the remaining parts? + JobEvent::TaskComplete => {} // Ignored, we're treated as a client node, waiting for new job request. + // Remove all task with matching job id. + JobEvent::Remove(job_id) => { + let db = self.task_store.write().await; + if let Err(e) = db.delete_job_task(&job_id).await { + eprintln!("Unable to remove all task with matching job id! {e:?}"); + } + // Find a way to check and see if we are running any task that matches target job_id and stop the blender sequence immediately. + } + _ => println!("Unhandle Job Event: {event:?}"), + } } - async fn handle_message(&mut self, client: &mut NetworkController, event: NetEvent) { + // Handle network event (From network as user to operate this) + async fn handle_net_event(&mut self, client: &mut Controller, event: Event) { match event { - NetEvent::OnConnected(peer_id) => client.share_computer_info(peer_id).await, - NetEvent::NodeDiscovered(..) => {} // Ignored - NetEvent::NodeDisconnected(_) => {} // ignored - NetEvent::JobUpdate(peer_id, job_event) => match job_event { - // on render task received, we should store this in the database. - JobEvent::Render(mut task) => self.render_task(peer_id, client, &mut task).await, - JobEvent::ImageCompleted { .. } => {} // ignored since we do not want to capture image? - // For future impl. we can take advantage about how we can allieve existing job load. E.g. if I'm still rendering 50%, try to send this node the remaining parts? - JobEvent::JobComplete => {} // Ignored, we're treated as a client node, waiting for new job request. - JobEvent::Remove(id) => { - let mut db = self.task_store.write().await; - let _ = db.delete_job_task(id).await; - // let mut db = self.job_store.write().await; - // if let Err(e) = db.delete_job(id).await { - // eprintln!("Fail to remove job from database! {e:?}"); - // } else { - // println!("Successfully remove job from database!"); - // } + // once we discover a peer, let's dial that peer. + Event::Discovered(peer_id, multiaddr) => { + if self.host.is_none() { + if let Err(e) = client.dial(&peer_id, &multiaddr).await { + eprintln!("Fail to dial! {e:?}"); + } + + self.host = Some((peer_id, multiaddr)); } - _ => println!("Unhandle Job Event: {job_event:?}"), - }, - // maybe move this inside Network code? Seems repeative in both cli and Tauri side of application here. - NetEvent::InboundRequest { request, channel } => { - if let Some(path) = client.providing_files.get(&request) { - println!("Sending file {path:?}"); - client - .respond_file(std::fs::read(path).unwrap(), channel) - .await; + } + Event::JobUpdate(job_event) => self.handle_job_from_network(client, job_event).await, + Event::InboundRequest { request, channel } => { + self.handle_inbound_request(client, request, channel).await + } + Event::NodeStatus(event) => { + match event { + NodeEvent::Hello(peer_id, spec) => { + // peer connected with specs. + println!("Peer connected with specs provided : {peer_id:?}\n{spec:?}"); + // if we are not connected to host, connect to this one. await further instructions. + // TODO: See where my multiaddr went? + // self.host = Some((PeerIdStr::from(peer_id), multiaddr)); + todo!("assign host, figure out where my multiaddr went"); + + // let public_ip = client.public_id.to_base58(); + // let mut machine = Machine::new(); + // let computer_spec = ComputerSpec::new(&mut machine); + // let status = NodeEvent::Hello(public_ip, computer_spec); + // client.send_node_status(status).await; + } + NodeEvent::Disconnected { peer_id, reason } => match reason { + Some(err) => { + println!("Peer Disconnected with reason [{peer_id:?}] {err}"); + } + None => println!("Peer Disconnected without reason! [{peer_id:?}]"), + }, + NodeEvent::BlenderStatus(_blender_event) => { + // println!("[Blender Status] {blender_event:?}"); + // probably doesn't matter, but shouldn't spam the network with this info yet... + } } } _ => println!("[CLI] Unhandled event from network: {event:?}"), } } + + async fn handle_command(&mut self, client: &mut Controller, cmd: CmdCommand) { + match cmd { + CmdCommand::Dial(peer_id, addr) => match client.dial(&peer_id, &addr).await { + Ok(_) => self.host = Some((peer_id, addr)), + Err(e) => eprintln!("{e:?}"), + }, + + CmdCommand::Render(mut task, mut sender) => { + // TODO: We should find a way to mark this node currently busy so we should unsubscribe any pending new jobs if possible? + // mutate this struct to skip listening for any new jobs. + // proceed to render the task. + match self.render_task(client, &mut task, &mut sender).await { + Ok(_) => { + // here we should send successful result? + println!("Successfully rendered task!"); + } + Err(e) => { + let event = JobEvent::Failed(e.to_string()); + client.send_job_event(event).await; + } + } + } + + CmdCommand::RequestTask => { + // or at least have this node look into job history and start working on jobs that are not completed yet. + let peer_id = client.public_id.to_base58(); + let event = JobEvent::RequestTask(peer_id); + client.send_job_event(event).await; + } + } + } } #[async_trait::async_trait] impl BlendFarm for CliApp { async fn run( mut self, - mut client: NetworkController, - mut event_receiver: Receiver, + mut client: Controller, + mut event_receiver: Receiver, ) -> Result<(), NetworkError> { - // Future Impl. Make this machine available to other peers that share the same operating system and arch - // - so that we can distribute blender across network rather than download blender per each peers. - // let system = self.machine.system_info(); - // let system_info = format!("blendfarm/{}{}", consts::OS, &system.processor.brand); - // client.subscribe_to_topic(system_info).await; - client.subscribe_to_topic(JOB.to_string()).await; - - // let current_job: Option = None; + // I need to find a way to safely notify the background to stop in case the job was deleted from host machine. + // we will have one thread to process blender and queue, but I must have access to database. + + let (event, mut command) = mpsc::channel(32); + + // TODO: move this inside on discovery call + // let cmd = CmdCommand::RequestTask; + // event.send(cmd).await.expect("Should not be free?"); + + let taskdb = self.task_store.clone(); + let render_db = self.render_store.clone(); + + // background thread to handle blender invocation + spawn(async move { + loop { + let db = taskdb.write().await; + + // think I have too many nested conditions here? Is it possible to break apart this component into smaller s + match db.poll_task().await { + Ok(result) => { + match result { + Some(task) => { + // why did this method get invoked twice? + println!("Begin some task!"); + let (sender, mut receiver) = mpsc::channel(32); + let job_id_ref: &Uuid = AsRef::as_ref(&task.item); + let job_id = job_id_ref.to_owned(); + let cmd = CmdCommand::Render(task.item, sender); + if let Err(e) = event.send(cmd).await { + eprintln!("Fail to send backend service render request! {e:?}"); + } + + loop { + select! { + event = receiver.recv() => match event { + Some(event) => { + match event { + BlenderEvent::Log(log) => println!("{log}"), + BlenderEvent::Warning(warn) => println!("{warn}"), + BlenderEvent::Rendering { current, total } => println!("Rendering {current} out of {total}"), + BlenderEvent::Completed { result, frame } => { + let render_info = NewRenderInfoDto::new(job_id.clone(), frame, result ); + let render_db = render_db.write().await; + if let Err(e) = render_db.create_renders(render_info).await { + eprintln!("Fail to create a new render entry to the database! {e:?}"); + } + }, + // receiving unhandled event for getting blender version and commit hash value? + BlenderEvent::Unhandled(e) => { + // Blender 4.3.2 (hash 32f5fdce0a0a built 2024-12-17 02:14:25) + eprintln!("{e:?}"); + }, + BlenderEvent::Exit => { + println!("Blender exit!"); + // so once the render is done, we somehow deleted the task afterward? + // How do I store the final render image result? + if let Err(e) = db.delete_task(&task.id).await { + // if the task doesn't exist + eprintln!( + "Fail to delete task entry from database! {e:?}" + ); + } + break; + }, + BlenderEvent::Error(e) => { + eprintln!("Received Blender Error: {e:?}"); + break + }, + } + }, + None => { + eprintln!("Received None from Blender loop! Breaking"); + break + } + } + } + } + } + None => match event.send(CmdCommand::RequestTask).await { + Ok(_) => { + sleep(Duration::from_secs(5u64)).await; + } + Err(e) => { + eprintln!("Error fail to send command to backend! {e:?}"); + sleep(Duration::from_secs(5u64)).await; + } + }, + } + } + Err(e) => { + eprintln!("Issue polling task from db: {e:?}"); + match event.send(CmdCommand::RequestTask).await { + Ok(_) => { + sleep(Duration::from_secs(5u64)).await; + } + Err(e) => { + eprintln!("Fail to send command to network! {e:?}"); + } + } + } + }; + } + }); + + // run cli mode in loop loop { select! { - // here we can insert job_db here to receive event invocation from Tauri_app - Some(event) = event_receiver.recv() => self.handle_message(&mut client, event).await, - // how do I poll database here? - // Some(task) = db.poll_task().await => self.handle_poll(&) - - // how do I poll the machine specs in certain intervals? + net_event = event_receiver.recv() => match net_event { + Some(event) => self.handle_net_event(&mut client, event).await, + None => return Err(NetworkError::Invalid), + }, + msg = command.recv() => match msg { + Some(cmd) => self.handle_command(&mut client, cmd).await, + _ => (), + } } } } diff --git a/src-tauri/src/services/data_store/mod.rs b/src-tauri/src/services/data_store/mod.rs index 35aab06e..fd50db86 100644 --- a/src-tauri/src/services/data_store/mod.rs +++ b/src-tauri/src/services/data_store/mod.rs @@ -1,3 +1,5 @@ +pub mod sqlite_advertise_store; pub mod sqlite_job_store; +pub mod sqlite_renders_store; pub mod sqlite_task_store; pub mod sqlite_worker_store; diff --git a/src-tauri/src/services/data_store/sqlite_advertise_store.rs b/src-tauri/src/services/data_store/sqlite_advertise_store.rs new file mode 100644 index 00000000..aafc4af6 --- /dev/null +++ b/src-tauri/src/services/data_store/sqlite_advertise_store.rs @@ -0,0 +1,99 @@ +use std::{path::PathBuf, str::FromStr}; + +use serde::{Deserialize, Serialize}; +use sqlx::{query, query_as, FromRow, SqlitePool}; +use uuid::Uuid; + +use crate::{ + domains::advertise_store::{AdvertiseError, AdvertiseStore}, + models::advertise::Advertise, +}; + +pub struct SqliteAdvertiseStore { + conn: SqlitePool, +} + +#[derive(Debug, FromRow, Serialize, Deserialize)] +struct AdvertiseDAO { + id: String, + ad_name: String, + file_path: String, +} + +impl AdvertiseDAO { + pub fn dto_to_obj(self) -> Advertise { + let id = Uuid::from_str(&self.id).expect("ID was mutated!"); + let file_path = PathBuf::from_str(&self.file_path).expect("File path was mutated!"); + Advertise { + id, + ad_name: self.ad_name, + file_path, + } + } +} + +#[async_trait::async_trait] +impl AdvertiseStore for SqliteAdvertiseStore { + async fn find(&self, id: Uuid) -> Result, AdvertiseError> { + let id = id.to_string(); + match query_as!( + AdvertiseDAO, + r"SELECT id, ad_name, file_path FROM advertise WHERE id=$1", + id + ) + .fetch_optional(&self.conn) + .await + { + Ok(dto) => Ok(dto.map(|d| d.dto_to_obj())), + Err(e) => Err(AdvertiseError::DatabaseError(e.to_string())), + } + } + + async fn update(&self, advertise: Advertise) -> Result<(), AdvertiseError> { + let id = advertise.id.to_string(); + let file_path = advertise.file_path.to_str(); + query!( + "UPDATE advertise SET ad_name=$2, file_path=$3 WHERE id=$1", + id, + advertise.ad_name, + file_path + ) + .execute(&self.conn) + .await + .map_err(|e| AdvertiseError::DatabaseError(e.to_string()))?; + Ok(()) + } + + async fn create(&self, advertise: Advertise) -> Result<(), AdvertiseError> { + let id = advertise.id.to_string(); + let file_path = advertise.file_path.to_str(); + if let Err(e) = query!( + r" + INSERT INTO advertise (id, ad_name, file_path) + VALUES($1, $2, $3); + ", + id, + advertise.ad_name, + file_path + ) + .execute(&self.conn) + .await + { + return Err(AdvertiseError::DatabaseError(e.to_string())); + } + + Ok(()) + } + + async fn kill(&self, id: Uuid) -> Result<(), AdvertiseError> { + let id = id.to_string(); + let _ = query!(r"DELETE FROM advertise WHERE id=$1", id) + .execute(&self.conn) + .await + .map_err(|e| AdvertiseError::DatabaseError(e.to_string()))?; + Ok(()) + } + async fn all(&self) -> Result>, AdvertiseError> { + Ok(None) + } +} diff --git a/src-tauri/src/services/data_store/sqlite_job_store.rs b/src-tauri/src/services/data_store/sqlite_job_store.rs index bf938871..b6908277 100644 --- a/src-tauri/src/services/data_store/sqlite_job_store.rs +++ b/src-tauri/src/services/data_store/sqlite_job_store.rs @@ -2,11 +2,13 @@ use std::{path::PathBuf, str::FromStr}; use crate::{ domains::job_store::{JobError, JobStore}, - models::job::Job, + models::{ + job::{CreatedJobDto, Job, NewJobDto, Output}, project_file::ProjectFile, with_id::WithId + }, }; -use blender::models::mode::Mode; +use blender::models::mode::RenderMode; use semver::Version; -use sqlx::{FromRow, SqlitePool}; +use sqlx::{FromRow, SqlitePool, query_as}; use uuid::Uuid; pub struct SqliteJobStore { @@ -19,8 +21,9 @@ impl SqliteJobStore { } } -#[derive(FromRow)] -struct JobDb { +// this information is used to help transpose data into database format. +#[derive(Debug, Clone, FromRow)] +struct JobDAO { id: String, mode: String, project_file: String, @@ -28,76 +31,126 @@ struct JobDb { output_path: String, } +impl JobDAO { + pub fn dto_to_obj(self) -> Result, JobError> { + let id = Uuid::from_str(&self.id).expect("id malformed"); + let mode = serde_json::from_str(&self.mode).expect("mode malformed"); + let project_file = PathBuf::from_str(&self.project_file).expect("Project path malformed"); + let blender_version = + Version::from_str(&self.blender_version).expect("Blender version malformed"); + let output = PathBuf::from_str(&self.output_path).expect("Output path malformed"); + match Job::from(mode, project_file, blender_version, output) { + Ok(item) => Ok(WithId { id, item }), + Err(e) => Err(JobError::InvalidFile(e.to_string())), + } + } +} + #[async_trait::async_trait] impl JobStore for SqliteJobStore { - async fn add_job(&mut self, job: Job) -> Result<(), JobError> { - let id = job.id.to_string(); - let mode = serde_json::to_string(&job.mode).unwrap(); - let project_file = job.project_file.to_str().unwrap().to_owned(); - let blender_version = job.blender_version.to_string(); - let output = job.output.to_str().unwrap().to_owned(); + async fn add_job(&mut self, job: NewJobDto) -> Result { + let id = Uuid::new_v4(); + let id_str = id.to_string(); + let mode = serde_json::to_string::(job.as_ref()).unwrap(); + let project_file = AsRef::::as_ref(&job).to_str().unwrap().to_owned(); + let blender_version = AsRef::::as_ref(&job).to_string(); + let output = AsRef::::as_ref(&job).to_str().unwrap().to_owned(); - sqlx::query( + sqlx::query!( r" INSERT INTO jobs (id, mode, project_file, blender_version, output_path) VALUES($1, $2, $3, $4, $5); ", + id_str, + mode, + project_file, + blender_version, + output ) - .bind(id) - .bind(mode) - .bind(project_file) - .bind(blender_version) - .bind(output) .execute(&self.conn) .await .map_err(|e| JobError::DatabaseError(e.to_string()))?; - Ok(()) + Ok(CreatedJobDto { id, item: job }) } - async fn get_job(&self, job_id: &Uuid) -> Result { - let sql = - "SELECT id, mode, project_file, blender_version, output_path FROM Jobs WHERE id=$1"; - match sqlx::query_as::<_, JobDb>(sql) - .bind(job_id.to_string()) - .fetch_one(&self.conn) - .await + async fn get_job(&self, job_id: &Uuid) -> Result, JobError> { + let id_str = job_id.to_string(); + match sqlx::query_as!( + JobDAO, + r"SELECT id, mode, project_file, blender_version, output_path FROM Jobs WHERE id=$1", + id_str + ) + .fetch_optional(&self.conn) + .await { - Ok(r) => { - let id = Uuid::parse_str(&r.id).unwrap(); - let mode: Mode = serde_json::from_str(&r.mode).unwrap(); - let project = PathBuf::from(r.project_file); - let version = Version::from_str(&r.blender_version).unwrap(); - let output = PathBuf::from(r.output_path); - let job = Job::new(id, mode, project, version, output, Default::default()); - Ok(job) - } + Ok(record) => match record { + Some(r) => { + let id = Uuid::parse_str(&r.id).unwrap(); + let mode: RenderMode = serde_json::from_str(&r.mode).unwrap(); + let project = PathBuf::from(r.project_file); + let version = Version::from_str(&r.blender_version).unwrap(); + let output = PathBuf::from(r.output_path); + match Job::from(mode, project, version, output) { + Ok(job) => Ok(Some(WithId { id, item: job })), + Err(e) => Err(JobError::InvalidFile(e.to_string())), + } + } + None => Ok(None), + }, Err(e) => Err(JobError::DatabaseError(e.to_string())), } } - async fn update_job(&mut self, _job: Job) -> Result<(), JobError> { - todo!("Update job to database"); + async fn update_job(&mut self, job: CreatedJobDto) -> Result<(), JobError> { + let id = job.id.to_string(); + let item = &job.item; + let mode = serde_json::to_string(item.into()).unwrap(); + let project = AsRef::::as_ref(&item) + .to_str() + .expect("Must have valid path!"); + let version = AsRef::::as_ref(&item).to_string(); + let output = AsRef::::as_ref(&item).to_str().expect("Must have valid path!"); + + match sqlx::query!( + r"UPDATE Jobs SET mode=$2, project_file=$3, blender_version=$4, output_path=$5 + WHERE id=$1", + id, + mode, + project, + version, + output + ) + .execute(&self.conn) + .await + { + Ok(record) => match record.rows_affected() { + 0 => Err(JobError::DatabaseError( + "Unable to find record! No record was affected!".into(), + )), + 1 => Ok(()), + _ => Err(JobError::DatabaseError(format!( + "More than one records was affected! {}", + record.rows_affected() + ))), + }, + Err(e) => Err(JobError::DatabaseError(e.to_string())), + } } - async fn list_all(&self) -> Result, JobError> { - let sql = r"SELECT id, mode, project_file, blender_version, output_path FROM jobs"; - let mut data: Vec = Vec::new(); - let results = sqlx::query_as::<_, JobDb>(sql).fetch_all(&self.conn).await; - match results { - Ok(records) => { - for r in records { - let id = Uuid::parse_str(&r.id).unwrap(); - let mode: Mode = serde_json::from_str(&r.mode).unwrap(); - let project = PathBuf::from(r.project_file); - let version = Version::from_str(&r.blender_version).unwrap(); - let output = PathBuf::from(r.output_path); - let job = Job::new(id, mode, project, version, output, Default::default()); - data.push(job); - } - } - Err(e) => return Err(JobError::DatabaseError(e.to_string())), + async fn list_all(&self) -> Result, JobError> { + let query = query_as!( + JobDAO, + r"SELECT id, mode, project_file, blender_version, output_path FROM jobs LIMIT 20" + ); + + let result = query.fetch_all(&self.conn).await; + match result { + Ok(records) => Ok(records + .iter() + .map(|r| r.clone().dto_to_obj().expect("Must have valid job")) + .collect()), + Err(e) => Err(JobError::DatabaseError(e.to_string())), } - Ok(data) } async fn delete_job(&mut self, id: &Uuid) -> Result<(), JobError> { @@ -111,3 +164,61 @@ impl JobStore for SqliteJobStore { Ok(()) } } + +#[cfg(test)] +mod tests { + use crate::{config_sqlite_db, constant::DATABASE_FILE_NAME, models::job::test::scaffold_job}; + + use super::*; + + async fn get_sqlite_pool() -> SqlitePool { + let pool = config_sqlite_db(DATABASE_FILE_NAME).await; + assert!(pool.is_ok()); + pool.expect("Should be ok") + } + + async fn scaffold_job_store() -> SqliteJobStore { + let conn = get_sqlite_pool().await; + SqliteJobStore::new(conn) + } + + #[tokio::test] + async fn can_create_worker_success() { + let mut job_store = scaffold_job_store().await; + let job = scaffold_job(); + + let result = job_store.add_job(job).await; + assert!(result.is_ok()); + } + + #[tokio::test] + async fn fetch_job_success() { + let mut job_store = scaffold_job_store().await; + let job = scaffold_job(); + + // append a job to the database first + let result = job_store.add_job(job).await; + assert!(result.is_ok()); + + // retrieve the ID from the created job we inserted + let id = result.expect("Should be safe").id; + + // test and see if we can fetch it. + let fetch_result = job_store.get_job(&id).await; + assert!(fetch_result.is_ok()); + } + + #[tokio::test] + async fn fetch_job_fail_no_record_found() { + let job_store = scaffold_job_store().await; + + // generate random uuid that doesn't exist in the databset yet + let fake_id = Uuid::new_v4(); + + // query the result + let result = job_store.get_job(&fake_id).await; + + // Query should be successful, but should return none + assert!(result.is_ok_and(|e| e.is_none())); + } +} diff --git a/src-tauri/src/services/data_store/sqlite_renders_store.rs b/src-tauri/src/services/data_store/sqlite_renders_store.rs new file mode 100644 index 00000000..b3dda9df --- /dev/null +++ b/src-tauri/src/services/data_store/sqlite_renders_store.rs @@ -0,0 +1,87 @@ +use std::path::PathBuf; + +use crate::{ + domains::render_store::{RenderError, RenderStore}, + models::render_info::{CreatedRenderInfoDto, NewRenderInfoDto, RenderInfo}, +}; +use sqlx::{sqlite::SqliteRow, Row, SqlitePool}; +use uuid::Uuid; + +pub struct SqliteRenderStore { + conn: SqlitePool, +} + +impl SqliteRenderStore { + pub fn new(conn: SqlitePool) -> Self { + Self { conn } + } +} + +#[async_trait::async_trait] +impl RenderStore for SqliteRenderStore { + async fn list_renders(&self) -> Result, RenderError> { + // query all and list the renders + let sql = "SELECT id, job_id, frame, render_path FROM renders"; + // TODO: For future impl, Consider looking into Stream and see how we can take advantage of streaming realtime data? + let col = sqlx::query(sql) + .map(|row: SqliteRow| { + let id = row.try_get(0).expect("Missing id column data"); + let job_id = row.try_get(1).expect("Missing job_id column data"); + let frame = row.try_get(2).expect("Missing frame column"); + let render_path: String = row.try_get(3).expect("Missing render_path column"); + let render_path = PathBuf::from(render_path); + + let item = RenderInfo { + job_id, + frame, + render_path, + }; + + CreatedRenderInfoDto { id, item } + }) + .fetch_all(&self.conn) + .await + .map_err(|e| RenderError::DatabaseError(e.to_string()))?; + + Ok(col) + } + + async fn create_renders( + &self, + render_info: NewRenderInfoDto, + ) -> Result { + let sql = + r#"INSERT INTO renders (id, job_id, frame, render_path) VALUES( $1, $2, $3, $4, $5);"#; + let id = Uuid::new_v4(); + if let Err(e) = sqlx::query(sql) + .bind(id.to_string()) + .bind(render_info.job_id.to_string()) + .bind(render_info.frame.to_string()) + .bind(render_info.render_path.to_str()) + .execute(&self.conn) + .await + { + eprintln!("Fail to save data to database! {e:?}"); + } + + Ok(CreatedRenderInfoDto { + id, + item: render_info, + }) + } + + async fn read_renders(&self, id: &Uuid) -> Result { + dbg!(id); + todo!("Impl missing implementations here") + } + + async fn update_renders(&mut self, render_info: RenderInfo) -> Result<(), RenderError> { + dbg!(render_info); + todo!("Impl. missing implementations here") + } + + async fn delete_renders(&mut self, id: &Uuid) -> Result<(), RenderError> { + dbg!(id); + Ok(()) + } +} diff --git a/src-tauri/src/services/data_store/sqlite_task_store.rs b/src-tauri/src/services/data_store/sqlite_task_store.rs index 74f8493b..563fccd2 100644 --- a/src-tauri/src/services/data_store/sqlite_task_store.rs +++ b/src-tauri/src/services/data_store/sqlite_task_store.rs @@ -1,11 +1,16 @@ -use sqlx::SqlitePool; -use uuid::Uuid; - -use crate::{domains::task_store::{TaskError, TaskStore}, models::task::Task}; - +use crate::{ + domains::task_store::{TaskError, TaskStore}, + models::{ + job::Job, + task::{CreatedTaskDto, Task}, + with_id::WithId, + }, +}; +use sqlx::{FromRow, SqlitePool, types::Uuid}; +use std::{ops::Range, str::FromStr}; pub struct SqliteTaskStore { - conn: SqlitePool + conn: SqlitePool, } impl SqliteTaskStore { @@ -14,43 +19,114 @@ impl SqliteTaskStore { } } +#[derive(Debug, Clone, FromRow)] +struct TaskDAO { + id: String, + job_id: String, + job: String, + start: i64, + end: i64, +} + +impl TaskDAO { + fn dto_to_task(self) -> WithId { + let id = Uuid::from_str(&self.id).expect("id was mutated"); + let job_id = Uuid::from_str(&self.job_id).expect("job_id was mutated"); + let job = serde_json::from_str::(&self.job).expect("job record was malformed!"); + let range = Range { + start: self.start as i32, + end: self.end as i32, + }; + + // at this point here, we shouldn't have to worry about Job's original rendering mode, + let job_record = WithId { + id: job_id, + item: job, + }; + // TODO: Find a way to handle expect() + let item = Task::from(job_record, range).expect("Malformed data detected!"); + WithId { id, item } + } +} + #[async_trait::async_trait] impl TaskStore for SqliteTaskStore { - async fn add_task(&mut self, task: Task) -> Result<(), TaskError> { - let id = task.id.to_string(); - let peer_id = task.get_peer_id().to_base58(); - let job_id = task.job_id.to_string(); - let blend_file_name = task.blend_file_name.to_str().unwrap().to_string(); - let blender_version = task.blender_version.to_string(); - let range = serde_json::to_string(&task.range).unwrap(); - let _ = sqlx::query(r"INSERT INTO tasks(id, peer_id, job_id, blend_file_name, blender_version, range) - VALUES($1, $2, $3, $4, $5, $6)") - .bind(id) - .bind(peer_id) - .bind(job_id) - .bind(blend_file_name) - .bind(blender_version) - .bind(range) - .execute(&self.conn); - Ok(()) + async fn add_task(&self, task: Task) -> Result { + let sql = r"INSERT INTO tasks(id, job_id, job, start, end) + VALUES($1, $2, $3, $4, $5)"; + let id = Uuid::new_v4(); + let job = serde_json::to_string::(task.as_ref()) + .expect("Should be able to convert job into json"); + + let job_id = AsRef::::as_ref(&task).to_string(); + let _ = sqlx::query(sql) + .bind(id.to_string()) + .bind(job_id) + .bind(job) + .bind(&task.range.start) + .bind(&task.range.end) + .execute(&self.conn) + .await + .map_err(|e| TaskError::DatabaseError(e.to_string()))?; + + Ok(WithId { id, item: task }) } - async fn poll_task(&mut self) -> Result { - todo!("poll pending task?"); + // Poll next available task if there any. + async fn poll_task(&self) -> Result, TaskError> { + // fetch next available task to work on + // TODO: Implement creation date to order by + let query = sqlx::query_as!( + TaskDAO, + r" + SELECT id, job_id, job, start, end + FROM tasks + LIMIT 1 + " + ); + + let result = query + .fetch_optional(&self.conn) + .await + .map_err(|e| TaskError::DatabaseError(e.to_string()))?; + + match result { + Some(data) => Ok(Some(data.dto_to_task())), + None => Ok(None), + } } - - async fn delete_task(&mut self, task: Task) -> Result<(), TaskError> { - let id = task.id.to_string(); - let _ = sqlx::query(r"DELETE * FROM tasks WHERE id = $1") - .bind(id) - .execute(&self.conn).await; + + async fn list_tasks(&self) -> Result>, TaskError> { + let result = sqlx::query_as!( + TaskDAO, + r" + SELECT id, job_id, job, start, end + FROM tasks + LIMIT 10 + " + ) + .fetch_all(&self.conn) + .await; + + match result { + Ok(list) => Ok(Some(list.iter().map(|d| d.clone().dto_to_task()).collect())), + Err(e) => Err(TaskError::DatabaseError(e.to_string())), + } + } + + async fn delete_task(&self, id: &Uuid) -> Result<(), TaskError> { + let _ = sqlx::query(r"DELETE FROM tasks WHERE id = $1") + .bind(id.to_string()) + .execute(&self.conn) + .await; Ok(()) } - - async fn delete_job_task(&mut self, job_id: Uuid) -> Result<(), TaskError> { - let _ = sqlx::query(r"DELETE * FROM tasks WHERE job_id = $1") + + async fn delete_job_task(&self, job_id: &Uuid) -> Result<(), TaskError> { + let _ = sqlx::query(r"DELETE FROM tasks WHERE job_id = $1") .bind(job_id.to_string()) - .execute(&self.conn).await; + .execute(&self.conn) + .await; Ok(()) } -} \ No newline at end of file +} diff --git a/src-tauri/src/services/data_store/sqlite_worker_store.rs b/src-tauri/src/services/data_store/sqlite_worker_store.rs index fc429f24..b29618e2 100644 --- a/src-tauri/src/services/data_store/sqlite_worker_store.rs +++ b/src-tauri/src/services/data_store/sqlite_worker_store.rs @@ -1,3 +1,9 @@ +use std::str::FromStr; + +use libp2p::PeerId; +use serde::{Deserialize, Serialize}; +use sqlx::{FromRow, SqlitePool}; + use crate::{ domains::worker_store::WorkerStore, models::{ @@ -5,83 +11,85 @@ use crate::{ worker::{Worker, WorkerError}, }, }; -use sqlx::{prelude::FromRow, query, SqlitePool}; pub struct SqliteWorkerStore { conn: SqlitePool, } +#[derive(FromRow, Serialize, Deserialize, Debug)] +struct WorkerDTO { + machine_id: String, + // TODO: find a way to use #[sqlx(json)]? + spec: String, // deserialize/serialize as json +} + +impl WorkerDTO { + pub fn dto_to_obj(&self) -> Worker { + let id = PeerId::from_str(&self.machine_id).expect("ID was mutated!"); + let spec = serde_json::from_str::(&self.spec).expect("spec was mutated!"); + Worker { id, spec } + } +} + impl SqliteWorkerStore { pub fn new(conn: SqlitePool) -> Self { Self { conn } } } -#[derive(FromRow)] -struct WorkerDb { - machine_id: String, - spec: String, -} - #[async_trait::async_trait] impl WorkerStore for SqliteWorkerStore { // List async fn list_worker(&self) -> Result, WorkerError> { // we'll add a limit here for now. - let sql = r"SELECT machine_id, spec FROM workers LIMIT 255"; - sqlx::query_as(sql) - .fetch_all(&self.conn) - .await - .map_err(|e| WorkerError::Database(e.to_string())) - .and_then(|r: Vec| { - Ok(r.into_iter() - .map(|r: WorkerDb| { - let spec: ComputerSpec = serde_json::from_str(&r.spec).unwrap(); - Worker::new(r.machine_id, spec) - }) - .collect::>()) - }) + let result: Vec = + sqlx::query_as!(WorkerDTO, r"SELECT machine_id, spec FROM workers") + .fetch_all(&self.conn) + .await + .map_err(|e| WorkerError::Database(e.to_string()))?; + + Ok(result.iter().map(|e| e.dto_to_obj()).collect()) } // Create async fn add_worker(&mut self, worker: Worker) -> Result<(), WorkerError> { - let spec = serde_json::to_string(&worker.spec).unwrap(); - + let id = worker.id.to_base58(); + let spec = serde_json::to_string(&worker.spec).expect("Fail to parse specs"); + // TODO: Update the record if it exist by marking it status "Active", relearn SQL again? if let Err(e) = sqlx::query( r" INSERT INTO workers (machine_id, spec) VALUES($1, $2); ", ) - .bind(worker.machine_id) + .bind(id) .bind(spec) .execute(&self.conn) .await { - eprintln!("{e}"); + eprintln!("Fail to insert new worker: {e}"); } Ok(()) } // Read - async fn get_worker(&self, id: &str) -> Option { - match query!( + async fn get_worker(&self, id: &PeerId) -> Option { + // so this panic when there's no record? + let peer_id = id.to_base58(); + let result: Result = sqlx::query_as!( + WorkerDTO, r#"SELECT machine_id, spec FROM workers WHERE machine_id=$1"#, - id, + peer_id ) .fetch_one(&self.conn) - .await - { - Ok(worker) => { - let spec = - serde_json::from_str::(&String::from_utf8(worker.spec).unwrap()) - .unwrap(); - Some(Worker::new(worker.machine_id, spec)) - } + .await; + + match result { + Ok(data) => Some(data.dto_to_obj()), Err(e) => { - eprintln!("{:?}", e.to_string()); - return None; + eprintln!("SQLx generated an error: {e:?}"); + None } } } @@ -89,11 +97,25 @@ impl WorkerStore for SqliteWorkerStore { // no update? // Delete - async fn delete_worker(&mut self, machine_id: &str) -> Result<(), WorkerError> { - let _ = sqlx::query(r"DELETE FROM workers WHERE machine_id = $1") - .bind(machine_id) + async fn delete_worker(&mut self, id: &PeerId) -> Result<(), WorkerError> { + let peer_id = id.to_base58(); + // TODO: mark the worker inactive instead. + let _ = sqlx::query!(r"DELETE FROM workers WHERE machine_id = $1", peer_id) + // my mind goes on a brainfart moment overcomplicating simplification and data requirement. + // should status be a enum type, then should it be a string instead? + // let _ = sqlx::query!("UPDATE workers SET status=false, ") + // .bind(peer_id) .execute(&self.conn) .await; Ok(()) } + + // Clear worker table + async fn clear_worker(&mut self) -> Result<(), WorkerError> { + let _ = sqlx::query(r"DELETE FROM workers") + .execute(&self.conn) + .await + .map_err(|e| WorkerError::Database(e.to_string()))?; + Ok(()) + } } diff --git a/src-tauri/src/services/tauri_app.rs b/src-tauri/src/services/tauri_app.rs index 212a122f..be2a1359 100644 --- a/src-tauri/src/services/tauri_app.rs +++ b/src-tauri/src/services/tauri_app.rs @@ -1,179 +1,173 @@ -use super::blend_farm::BlendFarm; +/* DEV Blog + + Issue: files provider are stored in memory, and do not recover after application restart. + - mitigate this by using a persistent storage solution instead of memory storage. + + Issue: Cannot debug this application unless it is built completely. See if there's a way to run debug mode without building the app entirely. +*/ + +use super::{ + blend_farm::BlendFarm, + data_store::{sqlite_job_store::SqliteJobStore, sqlite_worker_store::SqliteWorkerStore}, +}; +use crate::network::controller::Controller as NetworkController; +use crate::network::message::{Event, NetworkError, NodeEvent}; +use crate::network::provider_rule::ProviderRule; use crate::{ - domains::{job_store::JobStore, worker_store::WorkerStore}, + domains::{ + job_store::{JobError, JobStore}, + worker_store::WorkerStore, + }, models::{ app_state::AppState, + blender_action::BlenderAction, computer_spec::ComputerSpec, - job::{Job, JobEvent}, - message::{NetEvent, NetworkError}, - network::{NetworkController, HEARTBEAT, JOB, SPEC, STATUS}, + job::{CreatedJobDto, JobAction, JobEvent}, + project_file::ProjectFile, server_setting::ServerSetting, + setting_action::SettingsAction, task::Task, worker::Worker, }, - routes::{job::*, remote_render::*, settings::*, util::*, worker::*}, + routes::{index::*, job::*, remote_render::*, settings::*, util::*, worker::*}, }; -use blender::manager::Manager as BlenderManager; -use blender::models::mode::Mode; -use libp2p::PeerId; -use maud::html; -use serde::Serialize; -use std::{collections::HashMap, ops::Range, sync::Arc}; -use std::{path::PathBuf, thread::sleep, time::Duration}; -use tauri::{self, command, App, AppHandle, Emitter, Manager}; -use tokio::{ - select, spawn, - sync::{ - mpsc::{self, Receiver, Sender}, - Mutex, RwLock, - }, +use bitflags; +use blender::{manager::Manager as BlenderManager, models::mode::RenderMode}; +use futures::{ + SinkExt, StreamExt, + channel::mpsc::{self, Sender}, }; -use uuid::Uuid; +use libp2p::PeerId; +use semver::Version; +use sqlx::{Pool, Sqlite}; +use std::{collections::HashMap, ops::Range, path::PathBuf, str::FromStr}; +use tauri::{self, Url}; +use tokio::sync::mpsc::Receiver; +use tokio::{select, spawn, sync::Mutex}; + +bitflags::bitflags! { + #[derive(Debug, PartialEq)] + pub struct QueryMode: u8 { + const LOCAL = 0x1; + const ONLINE = 0x2; + } +} -pub const WORKPLACE: &str = "workplace"; +#[derive(Debug, PartialEq)] +pub enum Origin { + Local(PathBuf), + Online(Url), +} -// This UI Command represent the top level UI that user clicks and interface with. #[derive(Debug)] -pub enum UiCommand { - StartJob(Job), - StopJob(Uuid), - UploadFile(PathBuf, String), - RemoveJob(Uuid), +pub struct BlenderQuery { + pub version: Version, + pub origin: Origin, } -// TODO: make this user adjustable. -const MAX_BLOCK_SIZE: i32 = 30; +impl BlenderQuery { + pub fn is_install_locally(&self) -> bool { + match self.origin { + Origin::Local(_) => true, + _ => false, + } + } -pub struct TauriApp { - // I need the peer's address? - peers: HashMap, - worker_store: Arc>, - job_store: Arc>, + pub fn link(&self) -> String { + match &self.origin { + // TODO: Find a way to resolve expect() + Origin::Local(path) => path.to_str().expect("Should be valid").to_owned(), + Origin::Online(url) => url.to_string().to_owned(), + } + } } -#[derive(Clone, Serialize)] -struct FrameUpdatePayload { - id: Uuid, - frame: i32, - file_name: String, +#[derive(Debug)] +pub enum WorkerAction { + Get(PeerId, Sender>), + List(Sender>>), } -#[command] -pub fn index() -> String { - html! ( - div { - div class="sidebar" { - nav { - ul class="nav-menu-items" { - li key="manager" class="nav-bar" tauri-invoke="remote_render_page" hx-target=(format!("#{WORKPLACE}")) { - span { "Remote Render" } - }; - li key="setting" class="nav-bar" tauri-invoke="setting_page" hx-target=(format!("#{WORKPLACE}")) { - span { "Setting" } - }; - }; - }; - div { - h2 { "Computer Nodes" }; - div class="group" id="workers" tauri-invoke="list_workers" hx-trigger="every 2s" hx-target="this" {}; - }; - }; - - main tauri-invoke="remote_render_page" hx-trigger="load" hx-target="this" id=(WORKPLACE) {}; +impl PartialEq for WorkerAction { + fn eq(&self, other: &Self) -> bool { + match (self, other) { + (Self::Get(l0, ..), Self::Get(r0, ..)) => l0 == r0, + (Self::List(..), Self::List(..)) => true, + _ => false, } - ).0 + } +} + +#[derive(Debug, PartialEq)] +pub enum UiCommand { + Job(JobAction), + UploadFile(PathBuf), + Worker(WorkerAction), + Settings(SettingsAction), + Blender(BlenderAction), +} + +pub struct TauriApp { + // I need the peer's address? I don't think I need the PeerId, but will hold onto it just in case. + // we may ultimately change this to rely on the computer name instead of PeerId? + peers: HashMap, + worker_store: SqliteWorkerStore, + job_store: SqliteJobStore, + settings: ServerSetting, + manager: BlenderManager, } impl TauriApp { - pub async fn new( - worker_store: Arc>, - job_store: Arc>, - ) -> Self { + // Clear worker database before usage! + pub async fn clear_workers_collection(mut self) -> Self { + if let Err(e) = self.worker_store.clear_worker().await { + eprintln!("Error clearing worker database! {e:?}"); + } + self + } + + pub async fn new(pool: &Pool) -> Self { Self { peers: Default::default(), - worker_store, - job_store, + worker_store: SqliteWorkerStore::new(pool.clone()), + job_store: SqliteJobStore::new(pool.clone()), + settings: ServerSetting::load(), + manager: BlenderManager::load(), } } // Create a builder to make Tauri application - fn config_tauri_builder(&self, to_network: Sender) -> Result { - // I would like to find a better way to update or append data to render_nodes, - // "Do not communicate with shared memory" - let builder = tauri::Builder::default() + // Let's just use the controller in here anyway. + pub fn init_tauri_plugins(builder: tauri::Builder) -> tauri::Builder { + builder .plugin(tauri_plugin_cli::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_fs::init()) - .plugin(tauri_plugin_sql::Builder::default().build()) .plugin(tauri_plugin_persisted_scope::init()) .plugin(tauri_plugin_shell::init()) .plugin(tauri_plugin_dialog::init()) - .setup(|_| Ok(())); - - let manager = Arc::new(RwLock::new(BlenderManager::load())); - let setting = Arc::new(RwLock::new(ServerSetting::load())); - - // here we're setting the sender command to app state before the builder. - let app_state = AppState { - manager, - to_network, - setting, - job_db: self.job_store.clone(), - worker_db: self.worker_store.clone(), - }; - - let mut_app_state = Mutex::new(app_state); - - builder - .manage(mut_app_state) - .invoke_handler(tauri::generate_handler![ - index, - open_path, - select_directory, - select_file, - create_job, - delete_job, - get_job, - setting_page, - edit_settings, - get_settings, - update_settings, - create_new_job, - available_versions, - remote_render_page, - list_workers, - list_jobs, - get_worker, - import_blend, - add_blender_installation, - list_blender_installed, - remove_blender_installation, - fetch_blender_installation, - ]) - .build(tauri::generate_context!()) } - // because this is async, we can make our function wait for a new peers available. - async fn get_idle_peers(&self) -> PeerId { - // this will destroy the vector anyway. - // TODO: Impl. Round Robin or pick first idle worker, whichever have the most common hardware first in query? - // This code doesn't quite make sense, at least not yet? - loop { - if let Some((peer, ..)) = self.peers.clone().into_iter().nth(0) { - return peer; - } - sleep(Duration::from_secs(1)); - } + // This design implement doesn't fit the concept of decentralized network situation setup. + // We shouldn't have to rely on finding node availability, instead other node should ping out to other node and offer help instead of relying the host to do the work. + /* + async fn get_idle_peers(&self) -> String { + // see comment above, this method is no longer in use. } + */ - fn generate_tasks(job: &Job, file_name: PathBuf, chunks: i32, requestor: PeerId) -> Vec { + // The idea here is to generate new task based on job creation. + // TODO: Explain the expect behaviour for this method before reference it. + #[allow(dead_code)] + fn generate_tasks(job: &CreatedJobDto, chunks: i32) -> Vec { // mode may be removed soon, we'll see? - let (time_start, time_end) = match &job.mode { - Mode::Animation(anim) => (anim.start, anim.end), - Mode::Frame(frame) => (frame.clone(), frame.clone()), + let (time_start, time_end) = match AsRef::::as_ref(&job.item) { + RenderMode::Animation(anim) => (anim.start, anim.end), + RenderMode::Frame(frame) => (frame.clone(), frame.clone()), }; // What if it's in the negative? e.g. [-200, 2 ] ? would this result to -180 and what happen to the equation? + // ^^^^ TODO: This is a good example for unit test! let step = time_end - time_start; let max_step = step / chunks; let mut tasks = Vec::with_capacity(max_step as usize); @@ -195,122 +189,356 @@ impl TauriApp { }; let range = Range { start, end }; - let task = Task::new( - requestor, - job.id, - file_name.clone(), - job.get_version().clone(), - range, - ); + // TODO: Find a way to handle this error. + // It should only error if we don't have permission to temp cache storage location + let task = Task::from(job.clone(), range).expect("Should be able to create task!"); tasks.push(task); } tasks } - // command received from UI - async fn handle_command(&mut self, client: &mut NetworkController, cmd: UiCommand) { - match cmd { - // TODO: This may subject to change. - // Issue: What if the app restarts? We no longer provide the file after reboot. - UiCommand::StartJob(job) => { + async fn handle_job_command(&mut self, job_action: JobAction, client: &mut NetworkController) { + match job_action { + JobAction::Find(job_id, mut sender) => { + let result = self.job_store.get_job(&job_id).await; + match result { + Ok(record) => { + if let Err(e) = sender.send(record).await { + eprintln!("unable to send record back! \n{e:?}"); + } + } + Err(e) => eprintln!("Job store reported an error: {e:?}"), + }; + } + JobAction::Update(job) => { + // as long as the uuid exist in the database, we should be fine to update the job entry. + let result = self.job_store.update_job(job).await; + if let Err(e) = result { + eprintln!("Fail to update job! {e:?}"); + } + } + JobAction::Create(job, mut sender) => { + let result = self.job_store.add_job(job).await; + + match result { + Ok(job) => { + sender.send(Ok(job)).await.expect("Should not drop"); + } + Err(e) => { + sender + .send(Err(JobError::DatabaseError(e.to_string()))) + .await + .expect("Should not drop"); + } + }; + } + JobAction::Kill(job_id) => { + if let Err(e) = self.job_store.delete_job(&job_id).await { + eprintln!("Receiver/sender should not be dropped! {e:?}"); + } + client.send_job_event(JobEvent::Remove(job_id)).await; + } + JobAction::AskForCompletedList(job_id) => { + // here we will try and send out network node asking for any available client for the list of completed frame images. + let event = JobEvent::AskForCompletedJobFrameList(job_id); + client.send_job_event(event).await; + } + JobAction::All(mut sender) => { + /* + There's something wrong with this datastructure. + On first call, this command works as expected, + however additional call afterward does not let this function continue or invoke? + I must be waiting for something here? + */ + let result = match self.job_store.list_all().await { + Ok(jobs) => { + if jobs.is_empty() { + None + } else { + Some(jobs) + } + } + Err(e) => { + eprintln!("Unable to send list of jobs: {e:?}"); + None + } + }; + + if let Err(e) = sender.send(result).await { + eprintln!("Fail to send data back! {e:?}"); + } + } + + // Nothing is calling this yet??? + JobAction::Advertise(job_id) => + // Here we will simply add the job to the database, and let client poll them! + { + let result = match self.job_store.get_job(&job_id).await { + Ok(job) => job, + Err(e) => { + eprintln!("No Job record found! Skipping! {e:?}"); + return (); + } + }; + // first make the file available on the network - let file_name = job.project_file.file_name().unwrap(); - let path = job.project_file.clone(); + if let Some(job) = result { + let project_file: &ProjectFile = job.item.as_ref(); + let file_name = project_file.file_name().unwrap(); // this is &OsStr + let path: &PathBuf = job.item.as_ref(); + + println!("Reached to this point of code {file_name:?}"); + + // Once job is initiated, we need to be able to provide the files for network distribution. + let _provider = ProviderRule::Default(path.to_path_buf()); + // this is where I'm confused? + // if let Err(e) = client.start_providing(&provider).await { + // eprintln!("Fail to provide file! {e:?}"); + // return; + // } + + // let tasks = Self::generate_tasks( + // &job, + // MAX_FRAME_CHUNK_SIZE + // ); + + // // so here's the culprit. We're waiting for a peer to become idle and inactive waiting for the next job + // for task in tasks { + // // problem here - I'm getting one client to do all of the rendering jobs, not the inactive one. + // // Perform a round-robin selection instead. + + // println!("Sending task to {:?} \nRange( {} - {} )\n", &host, &task.range.start, &task.range.end); + // client.send_job_event(Some(host.clone()), JobEvent::Render(task)).await; + // } + } + } + } + } + + async fn handle_blender_command(&mut self, blender_action: BlenderAction) { + match blender_action { + BlenderAction::Add(_blender) => { + todo!("impl adding blender?"); + } + BlenderAction::List(mut sender, flags) => { + let mut versions = Vec::new(); + + if flags.contains(QueryMode::LOCAL) { + let mut localblenders = self + .manager + .get_blenders() + .iter() + .map(|b| BlenderQuery { + version: b.get_version().to_owned(), + origin: Origin::Local(b.get_executable().into()), + }) + .collect::>(); + versions.append(&mut localblenders); + } + + // then display the rest of the download list + // TODO: Figure out why fetch_download_list() takes awhile to query the data. + // I expect the cache should fetch the info and provide that information rather than querying the internet + // everytime this function is called. + if flags.contains(QueryMode::ONLINE) { + if let Some(downloads) = self.manager.fetch_download_list() { + let mut item = downloads + .iter() + .map(|d| BlenderQuery { + version: d.get_version().clone(), + origin: Origin::Online(d.get_url().clone()), + }) + .collect::>(); + versions.append(&mut item); + }; + } + + // send the collective list result back + if let Err(e) = sender.send(Some(versions)).await { + eprintln!("Fail to send back list of blenders to caller! {e:?}"); + } + } + BlenderAction::Get(version, mut sender) => { + let result = self.manager.fetch_blender(&version); + match result { + Ok(blender) => { + if let Err(e) = sender.send(Some(blender)).await { + eprintln!("Fail to send result back to caller! {e:?}"); + } + } + Err(e) => { + eprintln!("Fail to fetch blender! {e:?}"); + let _ = sender.send(None); + } + }; + } + // severe connection - remove the entry from database, but do not touch the installation + BlenderAction::Disconnect(blender) => { + self.manager.remove_blender(&blender); + } + // uninstall blender from local machine + BlenderAction::Remove(blender) => { + self.manager.delete_blender(&blender); + } + } + } - client - .start_providing(file_name.to_str().unwrap().to_string(), path) + async fn handle_worker_command(&mut self, worker_action: WorkerAction) { + match worker_action { + WorkerAction::Get(peer_id, mut sender) => { + let result = sender + .send(self.worker_store.get_worker(&peer_id).await) + .await; + if let Err(e) = result { + eprintln!("Unable to get worker!: {e:?}"); + } + } + WorkerAction::List(mut sender) => { + let result = sender + .send(self.worker_store.list_worker().await.ok()) .await; + if let Err(e) = result { + eprintln!("Unable to send list of workers: {e:?}"); + } + } + } + } - let tasks = Self::generate_tasks( - &job, - PathBuf::from(file_name), - MAX_BLOCK_SIZE, - client.public_id.clone(), - ); - - // so here's the culprit. We're waiting for a peer to become idle and inactive waiting for the next job - for task in tasks { - let peer = self.get_idle_peers().await; // this means I must wait for an active peers to become available? - let event = JobEvent::Render(task); - client.send_job_message(peer, event).await; + async fn handle_setting_command(&mut self, setting_action: SettingsAction) { + match setting_action { + SettingsAction::Get(mut sender) => { + if let Err(e) = sender.send(self.settings.clone()).await { + eprintln!("Fail to send to invoker! {e:?}"); } } - UiCommand::UploadFile(path, file_name) => { - client.start_providing(file_name, path).await; + SettingsAction::Update(new_settings) => { + self.settings = new_settings; + self.settings.save(); } - UiCommand::StopJob(id) => { - println!( - "Impl how to send a stop signal to stop the job and remove the job from queue {id:?}" - ); + } + } + + // command received from UI + async fn handle_command(&mut self, client: &mut NetworkController, cmd: UiCommand) { + // println!("Received command from UI: {cmd:?}"); + match cmd { + // could this be used as a trait? + UiCommand::Blender(blender_action) => self.handle_blender_command(blender_action).await, + UiCommand::Settings(setting_action) => { + self.handle_setting_command(setting_action).await } - UiCommand::RemoveJob(id) => { - for (peer, _) in self.peers.clone() { - client.send_job_message(peer, JobEvent::Remove(id)).await; + UiCommand::Job(job_action) => self.handle_job_command(job_action, client).await, + UiCommand::Worker(worker_action) => self.handle_worker_command(worker_action).await, + UiCommand::UploadFile(path) => { + // this is design to notify the network controller to start advertise provided file path + let provider = ProviderRule::Default(path); + if let Err(e) = client.start_providing(&provider).await { + eprintln!("Network issue on providing file! {e:?}"); } } } } // commands received from network - async fn handle_net_event( - &mut self, - client: &mut NetworkController, - event: NetEvent, - // This is currently used to receive worker's status update. We do not want to store this information in the database, instead it should be sent only when the application is available. - app_handle: Arc>, - ) { + async fn handle_net_event(&mut self, client: &mut NetworkController, event: Event) { match event { - NetEvent::Status(peer_id, msg) => { - // this may soon change. - let handle = app_handle.read().await; - handle - .emit("node_status", (peer_id.to_base58(), msg)) - .unwrap(); - } - NetEvent::NodeDiscovered(peer_id, spec) => { - let worker = Worker::new(peer_id.to_base58(), spec.clone()); - let mut db = self.worker_store.write().await; - if let Err(e) = db.add_worker(worker).await { - eprintln!("Error adding worker to database! {e:?}"); - } + Event::NodeStatus(node_status) => match node_status { + NodeEvent::Hello(peer_id_string, spec) => { + // a new node acknowledge your greets. + // this node now listens to you, and has provided info to communicate back + let peer_id = + PeerId::from_str(&peer_id_string).expect("Peer id should be valid"); + + // We'll tag this node as a worker. + let worker = Worker::new(peer_id.clone(), spec.clone()); + + // append new worker to database store + if let Err(e) = self.worker_store.add_worker(worker).await { + eprintln!("Error adding worker to database! {e:?}"); + } - self.peers.insert(peer_id, spec); - // let handle = app_handle.write().await; - // emit a signal to query the data. - // TODO: See how this can be done: https://github.com/ChristianPavilonis/tauri-htmx-extension - // let _ = handle.emit("worker_update"); - } - NetEvent::NodeDisconnected(peer_id) => { - let mut db = self.worker_store.write().await; - if let Err(e) = db.delete_worker(&peer_id.to_base58()).await { - eprintln!("Error deleting worker from database! {e:?}"); + println!("New worker added!"); + self.peers.insert(peer_id, spec); + + // let handle = app_handle.write().await; + // emit a signal to query the data. + // TODO: See how this can be done: https://github.com/ChristianPavilonis/tauri-htmx-extension + // let _ = handle.emit("worker_update"); } + // concerning - this String could be anything? + // TODO: Find a better way to get around this. + NodeEvent::Disconnected { peer_id, reason } => { + if let Some(msg) = reason { + eprintln!("Node disconnected with reason!\n {msg}"); + } - self.peers.remove(&peer_id); - // let handle = app_handle.write().await; - // let _ = handle.emit("worker_update", ()); - } - NetEvent::InboundRequest { request, channel } => { - if let Some(path) = client.providing_files.get(&request) { - client - .respond_file(std::fs::read(path).unwrap(), channel) - .await + // So the main issue is that there's no way to identify by the machine id? + let peer_id = + PeerId::from_str(&peer_id).expect("Received invalid peer_id string!"); + + // probably best to mark the node "inactive" instead? + if let Err(e) = self.worker_store.delete_worker(&peer_id).await { + eprintln!("Error deleting worker from database! {e:?}"); + } + + self.peers.remove(&peer_id); + } + // this is the same as saying down in the garbage disposal. Anything goes here. Do not trust data source here! + NodeEvent::BlenderStatus(blend_event) => { + println!("Blender Status Received: {blend_event:?}") } + }, + + // let me figure out what's going on here. + // a network sent us a inbound request - reply back with the file data in channel. + // yeah I wonder why we can't move this inside network class? + Event::InboundRequest { request, channel } => { + self.handle_inbound_request(client, request, channel).await; } - NetEvent::JobUpdate(.., job_event) => match job_event { + + Event::JobUpdate(job_event) => match job_event { // when we receive a completed image, send a notification to the host and update job index to obtain the latest render image. + JobEvent::AskForCompletedJobFrameList(_) => { + // this is reserved for the host side of the app to send out. We do not process this data here. + // only client should receive this notification, host will ignore this. + } + JobEvent::ImageCompletedList { job_id, files } => { + // first thing first, check and see if this job id matches what we have in our database. + // if it doesn't then we ignore this request and move on. + let result = self.job_store.get_job(&job_id).await; + + if result.is_err() { + return; // stop here. do not proceed forward. We do not care. + } + + // not that we have the job, we need to fetch for our existing files that we have completed + // We received a list of files from the client. We will run and compare this list to our local machine + // let local = + + // if we do not have the file locally, we will ask for the image from the provided node. + // In this case, we do not care who have the node, we will send out a signal stating I need this file. + // the node that receive the signal will message back. + + for file in files { + println!("file: {file}"); + } + } + // we received a job event that a node have finish rendering an image. + // We now need to make sure our output destination exist and valid. + // Afterward, we should try to fetch the file from that caller. JobEvent::ImageCompleted { - job_id: id, - frame, + job_id, + frame: _, file_name, } => { // create a destination with respective job id path. - let destination = client.settings.render_dir.join(id.to_string()); + let destination = self.settings.render_dir.join(job_id.to_string()); if let Err(e) = async_std::fs::create_dir_all(destination.clone()).await { println!("Issue creating temp job directory! {e:?}"); } + /* send update to ui let handle = app_handle.write().await; if let Err(e) = handle.emit( "frame_update", @@ -322,18 +550,26 @@ impl TauriApp { ) { eprintln!("Unable to send emit to app handler\n{e:?}"); } + */ // Fetch the completed image file from the network - if let Ok(file) = client.get_file_from_peers(&file_name, &destination).await { - let handle = app_handle.write().await; - if let Err(e) = handle.emit("job_image_complete", (id, frame, file)) { - eprintln!("Fail to publish image completion emit to front end! {e:?}"); + match client.get_file_from_peers(&file_name, &destination).await { + Ok(file) => { + println!("File stored at {file:?}"); + // let handle = app_handle.write().await; + // if let Err(e) = handle.emit("job_image_complete", (job_id, frame, file)) { + // eprintln!("Fail to publish image completion emit to front end! {e:?}"); + // } + } + Err(e) => { + eprintln!("Failed to fetch the file from peers!\n{:?}", e); } } } - - // when a job is complete, check the poll for next available job queue? - JobEvent::JobComplete => {} // Hmm how do I go about handling this one? + // when a task is complete, check the poll for next available job queue? + JobEvent::TaskComplete => { + println!("Received Task Completed! Do something about this!"); + } // TODO: how do we handle error from node? What kind of errors are we expecting here and what can the host do about it? JobEvent::Error(job_error) => { @@ -341,16 +577,44 @@ impl TauriApp { } // send a render job - // this will soon go away - host should not be receiving render jobs. - JobEvent::Render(..) => {} - // this will soon go away - host should not receive request job. - JobEvent::RequestJob => {} + JobEvent::Render(..) => { + // if we have a local client up and running, we should just communicate it directly. This will help setup the output correctly. + // TODO: Host should try to communicate local client + println!( + "Host received a Render Job - Contact client and provide info about this job. Read on how Rust micromange services?" + ); + } + JobEvent::RequestTask(peer_id_str) => { + // a node is requesting task. + + let jobs = self.job_store.list_all().await.expect("Should have jobs?"); + if let Some(job) = jobs.first() { + // how do I reply back for this task then? + // use the peer_id_string. + match job.item.clone().generate_task(job.id) { + Some(task) => { + let event = JobEvent::Render(peer_id_str, task); + client.send_job_event(event).await; + } + None => return, + } + } + } // this will soon go away + JobEvent::Failed(msg) => { + eprintln!("Job failed! {msg}"); + } JobEvent::Remove(_) => { // Should I do anything on the manager side? Shouldn't matter at this point? } }, - _ => println!("{:?}", event), + Event::Discovered(..) => { + // from this level, we have discovered other potential client on the network. + // at this level, we do absolutely nothing. We only respond to client incoming request. + } + _ => { + println!("[TauriApp]: {:?}", event); + } } } } @@ -360,32 +624,59 @@ impl BlendFarm for TauriApp { async fn run( mut self, mut client: NetworkController, - mut event_receiver: Receiver, + mut event_receiver: Receiver, ) -> Result<(), NetworkError> { - // for application side, we will subscribe to message event that's important to us to intercept. - client.subscribe_to_topic(SPEC.to_owned()).await; - client.subscribe_to_topic(HEARTBEAT.to_owned()).await; - client.subscribe_to_topic(STATUS.to_owned()).await; - client.subscribe_to_topic(JOB.to_owned()).await; // This might get changed? we'll see. - // this channel is used to send command to the network, and receive network notification back. + // ok where is this used? let (event, mut command) = mpsc::channel(32); + let app_state = AppState::new(event); + let mut_app_state = Mutex::new(app_state); + + // at the start of this program, I need to broadcast existing project file before the rest of the command hooks. + // This way, any job pending would have the file already available to distribute across the network. + // we send the sender to the tauri builder - which will send commands to "from_ui". - let app = self - .config_tauri_builder(event) + let app = Self::init_tauri_plugins(tauri::Builder::default()) + .invoke_handler(tauri::generate_handler![ + index, + open_path, + open_dir, + select_directory, + select_file, + create_job, + delete_job, + get_job_detail, + setting_page, + edit_settings, + get_settings, + update_settings, + open_dialog_for_blend_file, + available_versions, + list_workers, + list_jobs, + get_worker, + update_output_field, + add_blender_installation, + install_from_internet, + list_blender_installed, + disconnect_blender_installation, + delete_blender, + fetch_blender_installation, + ]) + .manage(mut_app_state) + .build(tauri::generate_context!("tauri.conf.json")) .expect("Fail to build tauri app - Is there an active display session running?"); - // create a safe and mutable way to pass application handler to send notification from network event. - // TODO: Get rid of this. - let app_handle = Arc::new(RwLock::new(app.app_handle().clone())); - - // create a background loop to send and process network event + // background thread to handle network process spawn(async move { loop { select! { - Some(msg) = command.recv() => self.handle_command(&mut client, msg).await, - Some(event) = event_receiver.recv() => self.handle_net_event(&mut client, event, app_handle.clone()).await, + msg = command.select_next_some() => self.handle_command(&mut client, msg).await, + event = event_receiver.recv() => match event { + Some(net_event) => self.handle_net_event(&mut client, net_event).await, + _ => () + } } } }); @@ -394,3 +685,31 @@ impl BlendFarm for TauriApp { Ok(()) } } + +#[cfg(test)] +mod test { + use super::*; + use crate::{config_sqlite_db, constant::DATABASE_FILE_NAME}; + + async fn get_sqlite_conn() -> Pool { + let pool = config_sqlite_db(DATABASE_FILE_NAME).await; + assert!(pool.is_ok()); + pool.expect("Assert above should force this to be ok()") + } + + #[tokio::test] + async fn clear_workers_success() { + let pool = get_sqlite_conn().await; + let app = TauriApp::new(&pool).await; + + let app = app.clear_workers_collection().await; + assert!( + app.worker_store + .list_worker() + .await + .is_ok_and(|f| f.iter().count() == 0) + ); + } + + // todo: identify other part of this code that I can run unit test and list out potential edge cases +} diff --git a/src-tauri/tauri.conf.json b/src-tauri/tauri.conf.json index a534b747..b59811af 100644 --- a/src-tauri/tauri.conf.json +++ b/src-tauri/tauri.conf.json @@ -19,11 +19,16 @@ "security": { "assetProtocol": { "scope": [ - "*/**" + "**" ], "enable": true }, - "csp": "default-src 'self'; img-src 'self' asset: https://asset.localhost; connect-src ipc: http://ipc.localhost" + "csp": { + "default-src": "'self'", + "ipc": "http://ipc.localhost", + "img-src": "'self'", + "asset": "http://asset.localhost" + } } }, "bundle": { diff --git a/src/htmx.js b/src/htmx.js index 9da48daa..93865749 100644 --- a/src/htmx.js +++ b/src/htmx.js @@ -1077,13 +1077,6 @@ var htmx = (function() { if (elt && elt.closest) { return elt.closest(selector) } else { - // TODO remove when IE goes away - do { - if (elt == null || matches(elt, selector)) { - return elt - } - } - while (elt = elt && asElement(parentElt(elt))) return null } } @@ -2962,7 +2955,7 @@ var htmx = (function() { function makeEvent(eventName, detail) { let evt if (window.CustomEvent && typeof window.CustomEvent === 'function') { - // TODO: `composed: true` here is a hack to make global event handlers work with events in shadow DOM + // `composed: true` here is a hack to make global event handlers work with events in shadow DOM // This breaks expected encapsulation but needs to be here until decided otherwise by core devs evt = new CustomEvent(eventName, { bubbles: true, cancelable: true, composed: true, detail }) } else { diff --git a/src/styles.css b/src/styles.css index 3e605ca3..f60c649e 100644 --- a/src/styles.css +++ b/src/styles.css @@ -23,12 +23,6 @@ body { height: 100%; } -/* TODO: Where is this used? what is this? */ -.imgbox { - display: grid; - height: 100%; -} - .center-fit { max-width: 100%; max-height: 100%; @@ -103,7 +97,10 @@ button { margin-right: 5px; } -/* TODO: Do we still use this anymore anywhere? */ +/* + Q: Do we still use this anymore anywhere? + A: Yes we are using nav-bar style in tauri_app.rs +*/ .nav-bar { font-weight: 500; color: #646cff; diff --git a/src/todo.txt b/src/todo.txt new file mode 100644 index 00000000..7942937f --- /dev/null +++ b/src/todo.txt @@ -0,0 +1,33 @@ +[todo] + Make the GUI app run in client mode? + - test fully through, see if it can render the job. + - Working on impl. Unit test. Few scripts have basic unit test coverage. + - Need to research about ideal unit test coverage. + Go through TODO list and see if there's any that can be done in five minutes. Work on that first. + Then come back to Network protocol + + +[issues] + - Client is not receiving network event from host. It receives connection established, but no network data exchanged yet? +E.g. +%> Sending task Task { job_id: f5f3af8b-4a74-4729-84e1-d25c4da4f4dc, blender_version: Version { major: 4, minor: 1, patch: 0 }, blend_file_name: "test.blend", range: 1..10 } to the gossip channel + - Deleting job does not clear entry from the job list + - Unable to open import_blend dialog + + - client does not send message while the job is running, I thought this was done async? what's going on? + - only at the end of the task does it ever notify host? + +[features] + provide the menu context to allow user to start or end local client mode session + Got image screen to display + Further separate Ui commands into event registration. + +Progress: + Still having problem with network code. + - read more into libp2p and run examples. + Got UI working again + - Provided buttons to open directory on setting page + - Job now display render image from output directory. + - See about how we can customize view from image tile to list + - [Feature] See about ffmpeg integration. Blender doesn't have ffmpeg + \ No newline at end of file diff --git a/system_arch.excalidraw b/system_arch.excalidraw new file mode 100644 index 00000000..7d20b946 --- /dev/null +++ b/system_arch.excalidraw @@ -0,0 +1,2811 @@ +{ + "type": "excalidraw", + "version": 2, + "source": "https://excalidraw.com", + "elements": [ + { + "id": "hcNmKG6RNHlIZluTwlCyr", + "type": "rectangle", + "x": 29.181233835953634, + "y": -46.169481312454536, + "width": 187.42223968426126, + "height": 467.0168265896209, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0d", + "roundness": { + "type": 3 + }, + "seed": 924372424, + "version": 586, + "versionNonce": 574350024, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "lpX5o3RMN0SfJAdP1sp3J" + }, + { + "id": "3JcNs_ceXa04q4Fy8M3AQ", + "type": "arrow" + }, + { + "id": "VNBuMuhrBdk6-qyRSJScX", + "type": "arrow" + }, + { + "id": "VF2PQtfx_p4AoqPL252S-", + "type": "arrow" + }, + { + "id": "ffTH8nDNfJXaJdW5_qSo7", + "type": "arrow" + }, + { + "id": "C6HTF3smGfNHQai67ofiv", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "lpX5o3RMN0SfJAdP1sp3J", + "type": "text", + "x": 72.52568879160965, + "y": -41.169481312454536, + "width": 100.73332977294922, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "a0l", + "roundness": null, + "seed": 1431033032, + "version": 464, + "versionNonce": 865521864, + "isDeleted": false, + "boundElements": null, + "updated": 1768791969315, + "link": null, + "locked": false, + "text": "libp2p", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "hcNmKG6RNHlIZluTwlCyr", + "originalText": "libp2p", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "F6pKEwbBLGwpNddWJxrY0", + "type": "rectangle", + "x": 846.9707326469716, + "y": 68.94869616275065, + "width": 704, + "height": 375.0815261182259, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a2", + "roundness": { + "type": 3 + }, + "seed": 2051888072, + "version": 775, + "versionNonce": 1299986632, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "utA6V4gprvoo5FzydQuWD" + } + ], + "updated": 1768793986688, + "link": null, + "locked": false + }, + { + "id": "utA6V4gprvoo5FzydQuWD", + "type": "text", + "x": 1104.8624028740223, + "y": 73.94869616275065, + "width": 188.21665954589844, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a3", + "roundness": null, + "seed": 201300680, + "version": 673, + "versionNonce": 1107943368, + "isDeleted": false, + "boundElements": [], + "updated": 1768793986688, + "link": null, + "locked": false, + "text": "Blender_rs", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "F6pKEwbBLGwpNddWJxrY0", + "originalText": "Blender_rs", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "rSoDP6s_fSonPZvameC56", + "type": "rectangle", + "x": 868.9962095589171, + "y": 128.46907769230745, + "width": 221.99999999999997, + "height": 277, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a4", + "roundness": { + "type": 3 + }, + "seed": 489176520, + "version": 624, + "versionNonce": 1145743048, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "zO-urD1mxa2c4mJ_NKAu6" + }, + { + "id": "0jZKIDlgSFE-48ZpcBnFN", + "type": "arrow" + }, + { + "id": "N_AsJslh4bXSQZrUxC5ai", + "type": "arrow" + }, + { + "id": "B9Uks7msNdSBTALz_Ibu6", + "type": "arrow" + }, + { + "id": "wxkhOnEba572OmVshH7Us", + "type": "arrow" + } + ], + "updated": 1768793986688, + "link": null, + "locked": false + }, + { + "id": "zO-urD1mxa2c4mJ_NKAu6", + "type": "text", + "x": 908.9295439095031, + "y": 244.46907769230745, + "width": 142.13333129882812, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a5", + "roundness": null, + "seed": 1370571192, + "version": 604, + "versionNonce": 1893371336, + "isDeleted": false, + "boundElements": null, + "updated": 1768793986688, + "link": null, + "locked": false, + "text": "Manager", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "rSoDP6s_fSonPZvameC56", + "originalText": "Manager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "L4I697w34meinXEVNHdJi", + "type": "rectangle", + "x": 1252.4962095589171, + "y": 228.96907769230745, + "width": 279, + "height": 55, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a6", + "roundness": { + "type": 3 + }, + "seed": 973692872, + "version": 560, + "versionNonce": 1775745992, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "dgwAEG0Gv5FXgrM75MLvc" + }, + { + "id": "N_AsJslh4bXSQZrUxC5ai", + "type": "arrow" + } + ], + "updated": 1768793986688, + "link": null, + "locked": false + }, + { + "id": "dgwAEG0Gv5FXgrM75MLvc", + "type": "text", + "x": 1327.137879785968, + "y": 233.96907769230745, + "width": 129.71665954589844, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a7", + "roundness": null, + "seed": 274343624, + "version": 555, + "versionNonce": 1845297864, + "isDeleted": false, + "boundElements": [], + "updated": 1768793986688, + "link": null, + "locked": false, + "text": "Blender", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "L4I697w34meinXEVNHdJi", + "originalText": "Blender", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "tgzqc3vn0nAmbzeYlUbvQ", + "type": "rectangle", + "x": 1245.4962095589171, + "y": 147.46907769230745, + "width": 279, + "height": 55, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a8", + "roundness": { + "type": 3 + }, + "seed": 1229728968, + "version": 566, + "versionNonce": 1809934792, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "fkTPONrIgzUmXfzOF8aIL" + }, + { + "id": "0jZKIDlgSFE-48ZpcBnFN", + "type": "arrow" + } + ], + "updated": 1768793986688, + "link": null, + "locked": false + }, + { + "id": "fkTPONrIgzUmXfzOF8aIL", + "type": "text", + "x": 1277.079545435382, + "y": 152.46907769230745, + "width": 215.8333282470703, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "a9", + "roundness": null, + "seed": 666588104, + "version": 577, + "versionNonce": 513863880, + "isDeleted": false, + "boundElements": [], + "updated": 1768793986688, + "link": null, + "locked": false, + "text": "Online Check", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "tgzqc3vn0nAmbzeYlUbvQ", + "originalText": "Online Check", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "IyIWOuIx444nBo5vkfGno", + "type": "rectangle", + "x": 1252.4962095589171, + "y": 312.46907769230745, + "width": 279, + "height": 100, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "aA", + "roundness": { + "type": 3 + }, + "seed": 2080394424, + "version": 613, + "versionNonce": 2111281096, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "FM3NpPyA64mkABPT8oB6D" + }, + { + "id": "B9Uks7msNdSBTALz_Ibu6", + "type": "arrow" + } + ], + "updated": 1768793986688, + "link": null, + "locked": false + }, + { + "id": "FM3NpPyA64mkABPT8oB6D", + "type": "text", + "x": 1273.012879785968, + "y": 317.46907769230745, + "width": 237.96665954589844, + "height": 90, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "aB", + "roundness": null, + "seed": 12973496, + "version": 654, + "versionNonce": 765550280, + "isDeleted": false, + "boundElements": [], + "updated": 1768793986688, + "link": null, + "locked": false, + "text": "Database\n(Persist info)", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "IyIWOuIx444nBo5vkfGno", + "originalText": "Database\n(Persist info)", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "0jZKIDlgSFE-48ZpcBnFN", + "type": "arrow", + "x": 1101.9962095589171, + "y": 170.1325822651816, + "width": 145, + "height": 3.33649542712584, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "aC", + "roundness": { + "type": 2 + }, + "seed": 1687379384, + "version": 354, + "versionNonce": 1855604168, + "isDeleted": false, + "boundElements": null, + "updated": 1768793986688, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 145, + 3.33649542712584 + ] + ], + "startBinding": { + "elementId": "rSoDP6s_fSonPZvameC56", + "mode": "orbit", + "fixedPoint": [ + 0.8532110091743116, + 0.14678899082568825 + ] + }, + "endBinding": { + "elementId": "tgzqc3vn0nAmbzeYlUbvQ", + "mode": "inside", + "fixedPoint": [ + 0.005376344086021506, + 0.4727272727272727 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "N_AsJslh4bXSQZrUxC5ai", + "type": "arrow", + "x": 1101.9962095589171, + "y": 260.0255573519698, + "width": 139.50000000000023, + "height": 0.529671133356203, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "aD", + "roundness": { + "type": 2 + }, + "seed": 1219497656, + "version": 337, + "versionNonce": 232672456, + "isDeleted": false, + "boundElements": null, + "updated": 1768793986688, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 139.50000000000023, + -0.529671133356203 + ] + ], + "startBinding": { + "elementId": "rSoDP6s_fSonPZvameC56", + "mode": "orbit", + "fixedPoint": [ + 0.5234657039711198, + 0.47653429602888125 + ] + }, + "endBinding": { + "elementId": "L4I697w34meinXEVNHdJi", + "mode": "orbit", + "fixedPoint": [ + 0.4544801395909596, + 0.5455198604090439 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "B9Uks7msNdSBTALz_Ibu6", + "type": "arrow", + "x": 1101.9962095589171, + "y": 367.5102394966007, + "width": 154, + "height": 0.9588381957067327, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "RRmMeFmAMpiKag069kOmt" + ], + "frameId": null, + "index": "aE", + "roundness": { + "type": 2 + }, + "seed": 2036746936, + "version": 373, + "versionNonce": 1426682824, + "isDeleted": false, + "boundElements": null, + "updated": 1768793986688, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 154, + 0.9588381957067327 + ] + ], + "startBinding": { + "elementId": "rSoDP6s_fSonPZvameC56", + "mode": "orbit", + "fixedPoint": [ + 0.8620287602020997, + 0.862028760202099 + ] + }, + "endBinding": { + "elementId": "IyIWOuIx444nBo5vkfGno", + "mode": "inside", + "fixedPoint": [ + 0.012544802867383513, + 0.56 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "D4wC3cc2HWhwasVdehc-3", + "type": "text", + "x": -128.06856200524857, + "y": 17.314420298960613, + "width": 107.56666564941406, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aUV", + "roundness": null, + "seed": 465195720, + "version": 45, + "versionNonce": 221359288, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Announcement", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ko3rqWr3W74NUQbRi0SKz", + "originalText": "Announcement", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "5rBQ_ArF4Ru8VhORGSZVE", + "type": "rectangle", + "x": -452.2465467713236, + "y": -385.83725137258574, + "width": 1232.7673231428405, + "height": 107.66984856524238, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aZ", + "roundness": { + "type": 3 + }, + "seed": 1082713784, + "version": 286, + "versionNonce": 581511880, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "7fbDfKLlo-OxqNpuR7wU5" + }, + { + "id": "hKMlEQJyuB1XLOdlxBq16", + "type": "arrow" + }, + { + "id": "dhhjZ1q6N_9xmmb-_TsHv", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "7fbDfKLlo-OxqNpuR7wU5", + "type": "text", + "x": 17.295455254198203, + "y": -354.5023270899645, + "width": 293.6833190917969, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "aZV", + "roundness": null, + "seed": 1695228600, + "version": 312, + "versionNonce": 722754488, + "isDeleted": false, + "boundElements": null, + "updated": 1768793830785, + "link": null, + "locked": false, + "text": "User Interaction", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "5rBQ_ArF4Ru8VhORGSZVE", + "originalText": "User Interaction", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "hKMlEQJyuB1XLOdlxBq16", + "type": "arrow", + "x": -288.55129045741756, + "y": -267.1674028073433, + "width": 3.8967975700685997, + "height": 207.64611822472807, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ab", + "roundness": { + "type": 2 + }, + "seed": 807097784, + "version": 399, + "versionNonce": 126537144, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "VfbdOENlkUBklbTh0OwzH" + } + ], + "updated": 1768793830785, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 3.8967975700685997, + 207.64611822472807 + ] + ], + "startBinding": { + "elementId": "5rBQ_ArF4Ru8VhORGSZVE", + "mode": "orbit", + "fixedPoint": [ + 0.13240235246973892, + 0.8675976475302611 + ] + }, + "endBinding": null, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "VfbdOENlkUBklbTh0OwzH", + "type": "text", + "x": -332.62789319826214, + "y": -185.84434369497924, + "width": 92.05000305175781, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ab4", + "roundness": null, + "seed": 181283512, + "version": 17, + "versionNonce": 1502155464, + "isDeleted": false, + "boundElements": null, + "updated": 1768793837752, + "link": null, + "locked": false, + "text": "Tauri", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hKMlEQJyuB1XLOdlxBq16", + "originalText": "Tauri", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "dhhjZ1q6N_9xmmb-_TsHv", + "type": "arrow", + "x": 587.5165188855618, + "y": -267.16740280734336, + "width": 2.0963570765007944, + "height": 195.9088950818114, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "af", + "roundness": { + "type": 2 + }, + "seed": 1622059464, + "version": 448, + "versionNonce": 1314352824, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "MSRQtzdfaETACS9uSflzh" + } + ], + "updated": 1768793935604, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 2.0963570765007944, + 195.9088950818114 + ] + ], + "startBinding": { + "elementId": "5rBQ_ArF4Ru8VhORGSZVE", + "mode": "orbit", + "fixedPoint": [ + 0.8431961791115165, + 0.8431961791115163 + ] + }, + "endBinding": { + "elementId": "u4UP0pE9dcRSdKQE-6Mii", + "mode": "orbit", + "fixedPoint": [ + 0.4774547624963938, + 0.477454762496394 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "MSRQtzdfaETACS9uSflzh", + "type": "text", + "x": 557.6563970436093, + "y": -191.7129552664376, + "width": 61.81666564941406, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "afV", + "roundness": null, + "seed": 1640085704, + "version": 38, + "versionNonce": 1296555704, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "CLI", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "dhhjZ1q6N_9xmmb-_TsHv", + "originalText": "CLI", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "wxkhOnEba572OmVshH7Us", + "type": "arrow", + "x": 712.1087181997963, + "y": 269.71828718934364, + "width": 158.42114516827542, + "height": 6.317928634050304, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ag", + "roundness": { + "type": 2 + }, + "seed": 717714616, + "version": 344, + "versionNonce": 1893045448, + "isDeleted": false, + "boundElements": null, + "updated": 1768793986688, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 158.42114516827542, + -6.317928634050304 + ] + ], + "startBinding": { + "elementId": "LNIk5qXErRLGTE77O-C8E", + "mode": "orbit", + "fixedPoint": [ + 0.62620400724163, + 0.6262040072416295 + ] + }, + "endBinding": { + "elementId": "rSoDP6s_fSonPZvameC56", + "mode": "inside", + "fixedPoint": [ + 0.0069083504916871335, + 0.4871165374115014 + ] + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "BLJxrLFWWrZcd5wgN-8jl", + "type": "text", + "x": 290.8963716338197, + "y": 16.954638276974045, + "width": 107.56666564941406, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "al", + "roundness": null, + "seed": 2131486152, + "version": 48, + "versionNonce": 519671992, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Announcement", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VNBuMuhrBdk6-qyRSJScX", + "originalText": "Announcement", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "C6HTF3smGfNHQai67ofiv", + "type": "arrow", + "x": 227.60347352021492, + "y": 132.0044861068664, + "width": 228.20474256768514, + "height": 3.6359821395237475, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "am", + "roundness": { + "type": 2 + }, + "seed": 1568181960, + "version": 291, + "versionNonce": 761602232, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "42ML-8zyTqNqNJgcmQi9o" + } + ], + "updated": 1768793935604, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 228.20474256768514, + 3.6359821395237475 + ] + ], + "startBinding": { + "elementId": "hcNmKG6RNHlIZluTwlCyr", + "mode": "orbit", + "fixedPoint": [ + 0.6212821888451624, + 0.3787178111548376 + ] + }, + "endBinding": { + "elementId": "6GLKwsy__oleqFg_DKe0C", + "mode": "orbit", + "fixedPoint": [ + 0.4094413058545063, + 0.590558694145495 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "42ML-8zyTqNqNJgcmQi9o", + "type": "text", + "x": 296.39751045347157, + "y": 123.82575617927861, + "width": 90.61666870117188, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "an", + "roundness": null, + "seed": 2081125832, + "version": 40, + "versionNonce": 2079766968, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Job Update", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "C6HTF3smGfNHQai67ofiv", + "originalText": "Job Update", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "3JcNs_ceXa04q4Fy8M3AQ", + "type": "arrow", + "x": 457.5819585280833, + "y": 269.9608246207511, + "width": 239.38646446618048, + "height": 1.263441072312844, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "ap", + "roundness": { + "type": 2 + }, + "seed": 754606792, + "version": 301, + "versionNonce": 404964024, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "sDL28GYsrqP0ui--5Wl8Q" + } + ], + "updated": 1768793963371, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -239.38646446618048, + 1.263441072312844 + ] + ], + "startBinding": { + "elementId": "LNIk5qXErRLGTE77O-C8E", + "mode": "orbit", + "fixedPoint": [ + 0.3784059354670282, + 0.6215940645329713 + ] + }, + "endBinding": { + "elementId": "PGvAIRHl59laFP1wJL99F", + "mode": "orbit", + "fixedPoint": [ + 0.6504670680418346, + 0.6504670680418341 + ] + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "sDL28GYsrqP0ui--5Wl8Q", + "type": "text", + "x": 285.0553942332255, + "y": 260.5923901977289, + "width": 105.66666412353516, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "apV", + "roundness": null, + "seed": 551842744, + "version": 28, + "versionNonce": 659244984, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Render Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "3JcNs_ceXa04q4Fy8M3AQ", + "originalText": "Render Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "VF2PQtfx_p4AoqPL252S-", + "type": "arrow", + "x": -175.18035794839324, + "y": 119.57511811623904, + "width": 204.2902908694808, + "height": 4.82742165401099, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "arG", + "roundness": { + "type": 2 + }, + "seed": 551797960, + "version": 424, + "versionNonce": 418286264, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "DDb_EC26v6QUf60EGXo6s" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 204.2902908694808, + 4.82742165401099 + ] + ], + "startBinding": { + "elementId": "hSITs0-BXLZEuSLyckysl", + "mode": "orbit", + "fixedPoint": [ + 0.7714395175744546, + 0.22856048242554547 + ] + }, + "endBinding": { + "elementId": "1C-V6FBRBZt1Bd4DqrDq9", + "mode": "orbit", + "fixedPoint": [ + 0.4847453803674703, + 0.51525461963253 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "DDb_EC26v6QUf60EGXo6s", + "type": "text", + "x": -118.34354686423877, + "y": 111.9897128912447, + "width": 90.61666870117188, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "arV", + "roundness": null, + "seed": 1357812680, + "version": 61, + "versionNonce": 617305528, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Job Update", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "VF2PQtfx_p4AoqPL252S-", + "originalText": "Job Update", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ffTH8nDNfJXaJdW5_qSo7", + "type": "arrow", + "x": 35.52653847582883, + "y": 265.1939164618733, + "width": 210.70689642422207, + "height": 9.230724989191827, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "as", + "roundness": { + "type": 2 + }, + "seed": 1656221128, + "version": 399, + "versionNonce": 746037944, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "1pHqNXY7pBQJWRBqMdPIG" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + -210.70689642422207, + -9.230724989191827 + ] + ], + "startBinding": { + "elementId": "PGvAIRHl59laFP1wJL99F", + "mode": "inside", + "fixedPoint": [ + 0.004830917874398955, + 0.5555555555555534 + ] + }, + "endBinding": { + "elementId": "hSITs0-BXLZEuSLyckysl", + "mode": "orbit", + "fixedPoint": [ + 0.8173631318365124, + 0.8173631318365125 + ] + }, + "startArrowhead": null, + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "1pHqNXY7pBQJWRBqMdPIG", + "type": "text", + "x": -122.66024179804978, + "y": 250.5785539672774, + "width": 105.66666412353516, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "at", + "roundness": null, + "seed": 455880, + "version": 30, + "versionNonce": 1152694200, + "isDeleted": false, + "boundElements": [], + "updated": 1768793926537, + "link": null, + "locked": false, + "text": "Render Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "ffTH8nDNfJXaJdW5_qSo7", + "originalText": "Render Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "ko3rqWr3W74NUQbRi0SKz", + "type": "arrow", + "x": -175.18035794839312, + "y": 12.996709587404492, + "width": 201.7902575357032, + "height": 28.630782726012743, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "auG", + "roundness": { + "type": 2 + }, + "seed": 166747848, + "version": 342, + "versionNonce": 557609912, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "D4wC3cc2HWhwasVdehc-3" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 201.7902575357032, + 28.630782726012743 + ] + ], + "startBinding": { + "elementId": "iJTtIjuF5BPf2AA9OiSM-", + "mode": "orbit", + "fixedPoint": [ + 0.5587649555455054, + 0.44123504445449463 + ] + }, + "endBinding": { + "elementId": "AXSYFY_GFdM-eU0etWSJs", + "mode": "orbit", + "fixedPoint": [ + 0.3660714819234105, + 0.6339285180765895 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "VNBuMuhrBdk6-qyRSJScX", + "type": "arrow", + "x": 221.11219961797718, + "y": 47.38982705537615, + "width": 247.135009681099, + "height": 40.85896525828861, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "auV", + "roundness": { + "type": 2 + }, + "seed": 1405550280, + "version": 401, + "versionNonce": 434453688, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "BLJxrLFWWrZcd5wgN-8jl" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 247.135009681099, + -40.85896525828861 + ] + ], + "startBinding": { + "elementId": "AXSYFY_GFdM-eU0etWSJs", + "mode": "orbit", + "fixedPoint": [ + 0.7120533599662096, + 0.7120533599662096 + ] + }, + "endBinding": { + "elementId": "u4UP0pE9dcRSdKQE-6Mii", + "mode": "orbit", + "fixedPoint": [ + 0.45220610543257905, + 0.4522061054325794 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "uIDdeV3qmKiKJc0gO9Rga", + "type": "rectangle", + "x": -475.81934004920953, + "y": -128.06759963124705, + "width": 1237.6393746927079, + "height": 588.7548056237531, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "auX", + "roundness": { + "type": 3 + }, + "seed": 880272584, + "version": 630, + "versionNonce": 1919515592, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Xak1IXlswugj0h2f2rDrX" + }, + { + "id": "Gcrk54RqYVHVP6UjqS5GG", + "type": "arrow" + }, + { + "id": "z79o6U2j5getfDGscbkjt", + "type": "arrow" + }, + { + "id": "hKMlEQJyuB1XLOdlxBq16", + "type": "arrow" + }, + { + "id": "dhhjZ1q6N_9xmmb-_TsHv", + "type": "arrow" + } + ], + "updated": 1768793936654, + "link": null, + "locked": false + }, + { + "id": "Xak1IXlswugj0h2f2rDrX", + "type": "text", + "x": 54.508678595972526, + "y": -123.06759963124705, + "width": 176.98333740234375, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "auZ", + "roundness": null, + "seed": 313080776, + "version": 524, + "versionNonce": 1607664824, + "isDeleted": false, + "boundElements": null, + "updated": 1768793936654, + "link": null, + "locked": false, + "text": "BlendFarm", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": "uIDdeV3qmKiKJc0gO9Rga", + "originalText": "BlendFarm", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "iJTtIjuF5BPf2AA9OiSM-", + "type": "rectangle", + "x": -418.70711762010615, + "y": -49.55334129619581, + "width": 232.52675967171302, + "height": 105.23108831858542, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aub", + "roundness": { + "type": 3 + }, + "seed": 233121976, + "version": 151, + "versionNonce": 1850396360, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "FZuLAXlCHhgaSwbnqeq-R" + }, + { + "id": "ko3rqWr3W74NUQbRi0SKz", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "FZuLAXlCHhgaSwbnqeq-R", + "type": "text", + "x": -392.0937393101285, + "y": -19.4377971369031, + "width": 179.3000030517578, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aud", + "roundness": null, + "seed": 1053505976, + "version": 164, + "versionNonce": 738240696, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Supervisor", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "iJTtIjuF5BPf2AA9OiSM-", + "originalText": "Supervisor", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "u4UP0pE9dcRSdKQE-6Mii", + "type": "rectangle", + "x": 479.2472092990762, + "y": -60.258507725531956, + "width": 232.52675967171302, + "height": 105.23108831858542, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "auf", + "roundness": { + "type": 3 + }, + "seed": 748774840, + "version": 177, + "versionNonce": 863509432, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "SaTGVviYKVtxUtNesULpD" + }, + { + "id": "dhhjZ1q6N_9xmmb-_TsHv", + "type": "arrow" + }, + { + "id": "VNBuMuhrBdk6-qyRSJScX", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "SaTGVviYKVtxUtNesULpD", + "type": "text", + "x": 537.0105891349327, + "y": -30.142963566239246, + "width": 117, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "auh", + "roundness": null, + "seed": 266071736, + "version": 188, + "versionNonce": 140169656, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Worker", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "u4UP0pE9dcRSdKQE-6Mii", + "originalText": "Worker", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "hSITs0-BXLZEuSLyckysl", + "type": "rectangle", + "x": -418.70711762010626, + "y": 65.43708182612954, + "width": 232.52675967171302, + "height": 230.23275500747474, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aul", + "roundness": { + "type": 3 + }, + "seed": 2050118584, + "version": 260, + "versionNonce": 2104831176, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Xq3pK4WcjMe9kDzA9L8RF" + }, + { + "id": "VF2PQtfx_p4AoqPL252S-", + "type": "arrow" + }, + { + "id": "ffTH8nDNfJXaJdW5_qSo7", + "type": "arrow" + }, + { + "id": "xLiK-jHXVGxJoxXujP5EK", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "Xq3pK4WcjMe9kDzA9L8RF", + "type": "text", + "x": -404.5437362583708, + "y": 158.0534593298669, + "width": 204.1999969482422, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aun", + "roundness": null, + "seed": 421469368, + "version": 288, + "versionNonce": 159898296, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "JobManager", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "hSITs0-BXLZEuSLyckysl", + "originalText": "JobManager", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "6GLKwsy__oleqFg_DKe0C", + "type": "rectangle", + "x": 466.80821608790006, + "y": 75.18708770038239, + "width": 232.52675967171302, + "height": 105.23108831858542, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aup", + "roundness": { + "type": 3 + }, + "seed": 1837513672, + "version": 251, + "versionNonce": 436112824, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "SxbeBknFLTgcxtsDDaqhn" + }, + { + "id": "VF2PQtfx_p4AoqPL252S-", + "type": "arrow" + }, + { + "id": "C6HTF3smGfNHQai67ofiv", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "SxbeBknFLTgcxtsDDaqhn", + "type": "text", + "x": 477.4132630990496, + "y": 105.3026318596751, + "width": 211.31666564941406, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aut", + "roundness": null, + "seed": 2010874568, + "version": 313, + "versionNonce": 1378599864, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "JobSchedule", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "6GLKwsy__oleqFg_DKe0C", + "originalText": "JobSchedule", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "LNIk5qXErRLGTE77O-C8E", + "type": "rectangle", + "x": 468.5819585280833, + "y": 204.02738886076358, + "width": 232.52675967171302, + "height": 105.23108831858542, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "auv", + "roundness": { + "type": 3 + }, + "seed": 28821192, + "version": 235, + "versionNonce": 633342152, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "jLala9XQT2GHCQh9pgdvp" + }, + { + "id": "3JcNs_ceXa04q4Fy8M3AQ", + "type": "arrow" + }, + { + "id": "wxkhOnEba572OmVshH7Us", + "type": "arrow" + } + ], + "updated": 1768793963371, + "link": null, + "locked": false + }, + { + "id": "jLala9XQT2GHCQh9pgdvp", + "type": "text", + "x": 553.8120055392328, + "y": 234.1429330200563, + "width": 62.06666564941406, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aux", + "roundness": null, + "seed": 1134800328, + "version": 254, + "versionNonce": 275418296, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Job", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "LNIk5qXErRLGTE77O-C8E", + "originalText": "Job", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "PGvAIRHl59laFP1wJL99F", + "type": "rectangle", + "x": 34.69319403123575, + "y": 227.6934164552067, + "width": 172.50230003066707, + "height": 67.50090001200022, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "av", + "roundness": { + "type": 3 + }, + "seed": 622740920, + "version": 141, + "versionNonce": 950522056, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "2NMG9x8eeSBS7dARiFw0I" + }, + { + "id": "3JcNs_ceXa04q4Fy8M3AQ", + "type": "arrow" + }, + { + "id": "ffTH8nDNfJXaJdW5_qSo7", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "2NMG9x8eeSBS7dARiFw0I", + "type": "text", + "x": 68.1110119848017, + "y": 251.4438664612068, + "width": 105.66666412353516, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "av8", + "roundness": null, + "seed": 1715303352, + "version": 167, + "versionNonce": 1231296200, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Render Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "PGvAIRHl59laFP1wJL99F", + "originalText": "Render Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "HO5XzQNUlewF1NeJ8b9MQ", + "type": "rectangle", + "x": 37.5657980504497, + "y": 327.36947243008046, + "width": 172.50230003066707, + "height": 67.50090001200022, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "avG", + "roundness": { + "type": 3 + }, + "seed": 43763144, + "version": 188, + "versionNonce": 559342520, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "76fndoOHH8pamjTEAJNQF" + }, + { + "id": "M9zqCB7wcQlPUwpQcDaja", + "type": "arrow" + }, + { + "id": "xLiK-jHXVGxJoxXujP5EK", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "76fndoOHH8pamjTEAJNQF", + "type": "text", + "x": 84.48361600401566, + "y": 351.11992243608057, + "width": 78.66666412353516, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "avV", + "roundness": null, + "seed": 632278216, + "version": 217, + "versionNonce": 680822456, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "File Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "HO5XzQNUlewF1NeJ8b9MQ", + "originalText": "File Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "1C-V6FBRBZt1Bd4DqrDq9", + "type": "rectangle", + "x": 40.10993292108756, + "y": 91.85827198661383, + "width": 172.50230003066707, + "height": 67.50090001200022, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "avd", + "roundness": { + "type": 3 + }, + "seed": 1221418168, + "version": 117, + "versionNonce": 475091896, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "7OryM90mKgFF8Q0Hl6TTZ" + }, + { + "id": "VF2PQtfx_p4AoqPL252S-", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "7OryM90mKgFF8Q0Hl6TTZ", + "type": "text", + "x": 86.57775011171407, + "y": 115.60872199261394, + "width": 79.56666564941406, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "avl", + "roundness": null, + "seed": 1366361528, + "version": 116, + "versionNonce": 1900412360, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Job Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "1C-V6FBRBZt1Bd4DqrDq9", + "originalText": "Job Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "AXSYFY_GFdM-eU0etWSJs", + "type": "rectangle", + "x": 37.60989958731008, + "y": 9.357171971946741, + "width": 172.50230003066707, + "height": 67.50090001200022, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "aw", + "roundness": { + "type": 3 + }, + "seed": 1760130248, + "version": 143, + "versionNonce": 1833727176, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "Z-V_YiDmv-063k_XeYytO" + }, + { + "id": "ko3rqWr3W74NUQbRi0SKz", + "type": "arrow" + }, + { + "id": "VNBuMuhrBdk6-qyRSJScX", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "Z-V_YiDmv-063k_XeYytO", + "type": "text", + "x": 78.86938471616901, + "y": 33.10762197794685, + "width": 89.98332977294922, + "height": 20, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "awG", + "roundness": null, + "seed": 1953471432, + "version": 169, + "versionNonce": 333614008, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Node Event", + "fontSize": 16, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "AXSYFY_GFdM-eU0etWSJs", + "originalText": "Node Event", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "I66YaUTkYLMJ0_adpa6Pf", + "type": "rectangle", + "x": 465.73840202372617, + "y": 328.19529014764294, + "width": 232.52675967171302, + "height": 89.1671222282964, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "awV", + "roundness": { + "type": 3 + }, + "seed": 714130360, + "version": 270, + "versionNonce": 30273976, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "SV5mB5HHfnUyfxpGJ_5HZ" + }, + { + "id": "M9zqCB7wcQlPUwpQcDaja", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "SV5mB5HHfnUyfxpGJ_5HZ", + "type": "text", + "x": 497.4767803337038, + "y": 350.27885126179115, + "width": 169.0500030517578, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "ax", + "roundness": null, + "seed": 338660536, + "version": 333, + "versionNonce": 545976, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Database", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "I66YaUTkYLMJ0_adpa6Pf", + "originalText": "Database", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "iqANu3qYKnTm6PYrsGqFc", + "type": "rectangle", + "x": -411.5745858632879, + "y": 321.02836092113284, + "width": 232.52675967171302, + "height": 89.1671222282964, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "axV", + "roundness": { + "type": 3 + }, + "seed": 582908856, + "version": 365, + "versionNonce": 585003720, + "isDeleted": false, + "boundElements": [ + { + "type": "text", + "id": "8oN273RVW_2jaM_3sXhiO" + }, + { + "id": "xLiK-jHXVGxJoxXujP5EK", + "type": "arrow" + } + ], + "updated": 1768793935587, + "link": null, + "locked": false + }, + { + "id": "8oN273RVW_2jaM_3sXhiO", + "type": "text", + "x": -379.83620755331026, + "y": 343.11192203528105, + "width": 169.0500030517578, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [ + "mUv5IzxC9f215gBQyFtLt" + ], + "frameId": null, + "index": "ay", + "roundness": null, + "seed": 770065592, + "version": 432, + "versionNonce": 1452080568, + "isDeleted": false, + "boundElements": [], + "updated": 1768793935587, + "link": null, + "locked": false, + "text": "Database", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "middle", + "containerId": "iqANu3qYKnTm6PYrsGqFc", + "originalText": "Database", + "autoResize": true, + "lineHeight": 1.25 + }, + { + "id": "M9zqCB7wcQlPUwpQcDaja", + "type": "arrow", + "x": 207.56819808245018, + "y": 364.0367613272657, + "width": 247.170203941276, + "height": 2.635801826420959, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b02", + "roundness": { + "type": 2 + }, + "seed": 510879688, + "version": 215, + "versionNonce": 1038508232, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935587, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 247.170203941276, + -2.635801826420959 + ] + ], + "startBinding": { + "elementId": "HO5XzQNUlewF1NeJ8b9MQ", + "mode": "inside", + "fixedPoint": [ + 0.9855080193236718, + 0.5432118518518506 + ] + }, + "endBinding": { + "elementId": "I66YaUTkYLMJ0_adpa6Pf", + "mode": "orbit", + "fixedPoint": [ + 0.3610423712287407, + 0.3610423712287408 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "xLiK-jHXVGxJoxXujP5EK", + "type": "arrow", + "x": -175.18035794839324, + "y": 249.90780254270828, + "width": 202.1160955076612, + "height": 88.85635524737768, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b03", + "roundness": { + "type": 2 + }, + "seed": 40061880, + "version": 231, + "versionNonce": 1955390904, + "isDeleted": false, + "boundElements": null, + "updated": 1768793935604, + "link": null, + "locked": false, + "points": [ + [ + 0, + 0 + ], + [ + 202.1160955076612, + 88.85635524737768 + ] + ], + "startBinding": { + "elementId": "hSITs0-BXLZEuSLyckysl", + "mode": "orbit", + "fixedPoint": [ + 0.6047248309576458, + 0.6047248309576458 + ] + }, + "endBinding": { + "elementId": "HO5XzQNUlewF1NeJ8b9MQ", + "mode": "orbit", + "fixedPoint": [ + 0.3588233676159732, + 0.6411766323840272 + ] + }, + "startArrowhead": "arrow", + "endArrowhead": "arrow", + "elbowed": false, + "moveMidPointsWithElement": false + }, + { + "id": "J3AjrcTauZgXaqEBAoi6P", + "type": "text", + "x": 1136.1382866776255, + "y": 434.3976449696289, + "width": 14.399999618530273, + "height": 45, + "angle": 0, + "strokeColor": "#1e1e1e", + "backgroundColor": "transparent", + "fillStyle": "solid", + "strokeWidth": 2, + "strokeStyle": "solid", + "roughness": 1, + "opacity": 100, + "groupIds": [], + "frameId": null, + "index": "b04", + "roundness": null, + "seed": 276506056, + "version": 3, + "versionNonce": 1973660856, + "isDeleted": true, + "boundElements": null, + "updated": 1768793975138, + "link": null, + "locked": false, + "text": "", + "fontSize": 36, + "fontFamily": 5, + "textAlign": "center", + "verticalAlign": "top", + "containerId": null, + "originalText": "", + "autoResize": true, + "lineHeight": 1.25 + } + ], + "appState": { + "gridSize": 20, + "gridStep": 5, + "gridModeEnabled": false, + "viewBackgroundColor": "#ffffff", + "lockedMultiSelections": {} + }, + "files": {} +} \ No newline at end of file