this repo has no description

buh

phaz.uk b7deb18e 8303945b

verified
+1
package.json
··· 13 13 "license": "MIT", 14 14 "dependencies": { 15 15 "@tauri-apps/api": "^2", 16 + "@tauri-apps/plugin-dialog": "~2", 16 17 "@tauri-apps/plugin-opener": "^2", 17 18 "animejs": "^4.1.2", 18 19 "solid-js": "^1.9.3"
+15
pnpm-lock.yaml
··· 11 11 '@tauri-apps/api': 12 12 specifier: ^2 13 13 version: 2.7.0 14 + '@tauri-apps/plugin-dialog': 15 + specifier: ~2 16 + version: 2.4.0 14 17 '@tauri-apps/plugin-opener': 15 18 specifier: ^2 16 19 version: 2.4.0 ··· 396 399 '@tauri-apps/api@2.7.0': 397 400 resolution: {integrity: sha512-v7fVE8jqBl8xJFOcBafDzXFc8FnicoH3j8o8DNNs0tHuEBmXUDqrCOAzMRX0UkfpwqZLqvrvK0GNQ45DfnoVDg==} 398 401 402 + '@tauri-apps/api@2.8.0': 403 + resolution: {integrity: sha512-ga7zdhbS2GXOMTIZRT0mYjKJtR9fivsXzsyq5U3vjDL0s6DTMwYRm0UHNjzTY5dh4+LSC68Sm/7WEiimbQNYlw==} 404 + 399 405 '@tauri-apps/cli-darwin-arm64@2.7.1': 400 406 resolution: {integrity: sha512-j2NXQN6+08G03xYiyKDKqbCV2Txt+hUKg0a8hYr92AmoCU8fgCjHyva/p16lGFGUG3P2Yu0xiNe1hXL9ZuRMzA==} 401 407 engines: {node: '>= 10'} ··· 466 472 resolution: {integrity: sha512-RcGWR4jOUEl92w3uvI0h61Llkfj9lwGD1iwvDRD2isMrDhOzjeeeVn9aGzeW1jubQ/kAbMYfydcA4BA0Cy733Q==} 467 473 engines: {node: '>= 10'} 468 474 hasBin: true 475 + 476 + '@tauri-apps/plugin-dialog@2.4.0': 477 + resolution: {integrity: sha512-OvXkrEBfWwtd8tzVCEXIvRfNEX87qs2jv6SqmVPiHcJjBhSF/GUvjqUNIDmKByb5N8nvDqVUM7+g1sXwdC/S9w==} 469 478 470 479 '@tauri-apps/plugin-opener@2.4.0': 471 480 resolution: {integrity: sha512-43VyN8JJtvKWJY72WI/KNZszTpDpzHULFxQs0CJBIYUdCRowQ6Q1feWTDb979N7nldqSuDOaBupZ6wz2nvuWwQ==} ··· 991 1000 992 1001 '@tauri-apps/api@2.7.0': {} 993 1002 1003 + '@tauri-apps/api@2.8.0': {} 1004 + 994 1005 '@tauri-apps/cli-darwin-arm64@2.7.1': 995 1006 optional: true 996 1007 ··· 1037 1048 '@tauri-apps/cli-win32-arm64-msvc': 2.7.1 1038 1049 '@tauri-apps/cli-win32-ia32-msvc': 2.7.1 1039 1050 '@tauri-apps/cli-win32-x64-msvc': 2.7.1 1051 + 1052 + '@tauri-apps/plugin-dialog@2.4.0': 1053 + dependencies: 1054 + '@tauri-apps/api': 2.8.0 1040 1055 1041 1056 '@tauri-apps/plugin-opener@2.4.0': 1042 1057 dependencies:
+1
public/assets/icons/floppy-disk-solid-full.svg
··· 1 + <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="#fff" d="M160 96C124.7 96 96 124.7 96 160L96 480C96 515.3 124.7 544 160 544L480 544C515.3 544 544 515.3 544 480L544 237.3C544 220.3 537.3 204 525.3 192L448 114.7C436 102.7 419.7 96 402.7 96L160 96zM192 192C192 174.3 206.3 160 224 160L384 160C401.7 160 416 174.3 416 192L416 256C416 273.7 401.7 288 384 288L224 288C206.3 288 192 273.7 192 256L192 192zM320 352C355.3 352 384 380.7 384 416C384 451.3 355.3 480 320 480C284.7 480 256 451.3 256 416C256 380.7 284.7 352 320 352z"/></svg>
+213 -1
src-tauri/Cargo.lock
··· 13 13 "serde_json", 14 14 "tauri", 15 15 "tauri-build", 16 + "tauri-plugin-dialog", 16 17 "tauri-plugin-opener", 17 18 "tokio", 18 19 ] ··· 76 77 version = "1.0.99" 77 78 source = "registry+https://github.com/rust-lang/crates.io-index" 78 79 checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 80 + 81 + [[package]] 82 + name = "ashpd" 83 + version = "0.11.0" 84 + source = "registry+https://github.com/rust-lang/crates.io-index" 85 + checksum = "6cbdf310d77fd3aaee6ea2093db7011dc2d35d2eb3481e5607f1f8d942ed99df" 86 + dependencies = [ 87 + "enumflags2", 88 + "futures-channel", 89 + "futures-util", 90 + "rand 0.9.2", 91 + "raw-window-handle", 92 + "serde", 93 + "serde_repr", 94 + "tokio", 95 + "url", 96 + "wayland-backend", 97 + "wayland-client", 98 + "wayland-protocols", 99 + "zbus", 100 + ] 79 101 80 102 [[package]] 81 103 name = "async-broadcast" ··· 758 780 checksum = "89a09f22a6c6069a18470eb92d2298acf25463f14256d24778e1230d789a2aec" 759 781 dependencies = [ 760 782 "bitflags 2.9.1", 783 + "block2 0.6.1", 784 + "libc", 761 785 "objc2 0.6.1", 762 786 ] 763 787 ··· 773 797 ] 774 798 775 799 [[package]] 800 + name = "dlib" 801 + version = "0.5.2" 802 + source = "registry+https://github.com/rust-lang/crates.io-index" 803 + checksum = "330c60081dcc4c72131f8eb70510f1ac07223e5d4163db481a04a0befcffa412" 804 + dependencies = [ 805 + "libloading", 806 + ] 807 + 808 + [[package]] 776 809 name = "dlopen2" 777 810 version = "0.7.0" 778 811 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 794 827 "quote", 795 828 "syn 2.0.104", 796 829 ] 830 + 831 + [[package]] 832 + name = "downcast-rs" 833 + version = "1.2.1" 834 + source = "registry+https://github.com/rust-lang/crates.io-index" 835 + checksum = "75b325c5dbd37f80359721ad39aca5a29fb04c89279657cffdda8736d0c0b9d2" 797 836 798 837 [[package]] 799 838 name = "dpi" ··· 2634 2673 dependencies = [ 2635 2674 "base64 0.22.1", 2636 2675 "indexmap 2.10.0", 2637 - "quick-xml", 2676 + "quick-xml 0.38.0", 2638 2677 "serde", 2639 2678 "time", 2640 2679 ] ··· 2765 2804 2766 2805 [[package]] 2767 2806 name = "quick-xml" 2807 + version = "0.37.5" 2808 + source = "registry+https://github.com/rust-lang/crates.io-index" 2809 + checksum = "331e97a1af0bf59823e6eadffe373d7b27f485be8748f71471c662c1f269b7fb" 2810 + dependencies = [ 2811 + "memchr", 2812 + ] 2813 + 2814 + [[package]] 2815 + name = "quick-xml" 2768 2816 version = "0.38.0" 2769 2817 source = "registry+https://github.com/rust-lang/crates.io-index" 2770 2818 checksum = "8927b0664f5c5a98265138b7e3f90aa19a6b21353182469ace36d4ac527b7b1b" ··· 2813 2861 ] 2814 2862 2815 2863 [[package]] 2864 + name = "rand" 2865 + version = "0.9.2" 2866 + source = "registry+https://github.com/rust-lang/crates.io-index" 2867 + checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" 2868 + dependencies = [ 2869 + "rand_chacha 0.9.0", 2870 + "rand_core 0.9.3", 2871 + ] 2872 + 2873 + [[package]] 2816 2874 name = "rand_chacha" 2817 2875 version = "0.2.2" 2818 2876 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2833 2891 ] 2834 2892 2835 2893 [[package]] 2894 + name = "rand_chacha" 2895 + version = "0.9.0" 2896 + source = "registry+https://github.com/rust-lang/crates.io-index" 2897 + checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" 2898 + dependencies = [ 2899 + "ppv-lite86", 2900 + "rand_core 0.9.3", 2901 + ] 2902 + 2903 + [[package]] 2836 2904 name = "rand_core" 2837 2905 version = "0.5.1" 2838 2906 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2851 2919 ] 2852 2920 2853 2921 [[package]] 2922 + name = "rand_core" 2923 + version = "0.9.3" 2924 + source = "registry+https://github.com/rust-lang/crates.io-index" 2925 + checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" 2926 + dependencies = [ 2927 + "getrandom 0.3.3", 2928 + ] 2929 + 2930 + [[package]] 2854 2931 name = "rand_hc" 2855 2932 version = "0.2.0" 2856 2933 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2979 3056 ] 2980 3057 2981 3058 [[package]] 3059 + name = "rfd" 3060 + version = "0.15.4" 3061 + source = "registry+https://github.com/rust-lang/crates.io-index" 3062 + checksum = "ef2bee61e6cffa4635c72d7d81a84294e28f0930db0ddcb0f66d10244674ebed" 3063 + dependencies = [ 3064 + "ashpd", 3065 + "block2 0.6.1", 3066 + "dispatch2", 3067 + "glib-sys", 3068 + "gobject-sys", 3069 + "gtk-sys", 3070 + "js-sys", 3071 + "log", 3072 + "objc2 0.6.1", 3073 + "objc2-app-kit", 3074 + "objc2-core-foundation", 3075 + "objc2-foundation 0.3.1", 3076 + "raw-window-handle", 3077 + "wasm-bindgen", 3078 + "wasm-bindgen-futures", 3079 + "web-sys", 3080 + "windows-sys 0.59.0", 3081 + ] 3082 + 3083 + [[package]] 2982 3084 name = "rustc-demangle" 2983 3085 version = "0.1.26" 2984 3086 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3079 3181 ] 3080 3182 3081 3183 [[package]] 3184 + name = "scoped-tls" 3185 + version = "1.0.1" 3186 + source = "registry+https://github.com/rust-lang/crates.io-index" 3187 + checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294" 3188 + 3189 + [[package]] 3082 3190 name = "scopeguard" 3083 3191 version = "1.2.0" 3084 3192 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3680 3788 ] 3681 3789 3682 3790 [[package]] 3791 + name = "tauri-plugin-dialog" 3792 + version = "2.3.2" 3793 + source = "registry+https://github.com/rust-lang/crates.io-index" 3794 + checksum = "37e5858cc7b455a73ab4ea2ebc08b5be33682c00ff1bf4cad5537d4fb62499d9" 3795 + dependencies = [ 3796 + "log", 3797 + "raw-window-handle", 3798 + "rfd", 3799 + "serde", 3800 + "serde_json", 3801 + "tauri", 3802 + "tauri-plugin", 3803 + "tauri-plugin-fs", 3804 + "thiserror 2.0.12", 3805 + "url", 3806 + ] 3807 + 3808 + [[package]] 3809 + name = "tauri-plugin-fs" 3810 + version = "2.4.1" 3811 + source = "registry+https://github.com/rust-lang/crates.io-index" 3812 + checksum = "8c6ef84ee2f2094ce093e55106d90d763ba343fad57566992962e8f76d113f99" 3813 + dependencies = [ 3814 + "anyhow", 3815 + "dunce", 3816 + "glob", 3817 + "percent-encoding", 3818 + "schemars 0.8.22", 3819 + "serde", 3820 + "serde_json", 3821 + "serde_repr", 3822 + "tauri", 3823 + "tauri-plugin", 3824 + "tauri-utils", 3825 + "thiserror 2.0.12", 3826 + "toml 0.8.23", 3827 + "url", 3828 + ] 3829 + 3830 + [[package]] 3683 3831 name = "tauri-plugin-opener" 3684 3832 version = "2.4.0" 3685 3833 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3916 4064 "libc", 3917 4065 "mio", 3918 4066 "pin-project-lite", 4067 + "signal-hook-registry", 3919 4068 "slab", 3920 4069 "socket2", 3921 4070 "tokio-macros", 4071 + "tracing", 3922 4072 "windows-sys 0.59.0", 3923 4073 ] 3924 4074 ··· 4433 4583 ] 4434 4584 4435 4585 [[package]] 4586 + name = "wayland-backend" 4587 + version = "0.3.11" 4588 + source = "registry+https://github.com/rust-lang/crates.io-index" 4589 + checksum = "673a33c33048a5ade91a6b139580fa174e19fb0d23f396dca9fa15f2e1e49b35" 4590 + dependencies = [ 4591 + "cc", 4592 + "downcast-rs", 4593 + "rustix", 4594 + "scoped-tls", 4595 + "smallvec", 4596 + "wayland-sys", 4597 + ] 4598 + 4599 + [[package]] 4600 + name = "wayland-client" 4601 + version = "0.31.11" 4602 + source = "registry+https://github.com/rust-lang/crates.io-index" 4603 + checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" 4604 + dependencies = [ 4605 + "bitflags 2.9.1", 4606 + "rustix", 4607 + "wayland-backend", 4608 + "wayland-scanner", 4609 + ] 4610 + 4611 + [[package]] 4612 + name = "wayland-protocols" 4613 + version = "0.32.9" 4614 + source = "registry+https://github.com/rust-lang/crates.io-index" 4615 + checksum = "efa790ed75fbfd71283bd2521a1cfdc022aabcc28bdcff00851f9e4ae88d9901" 4616 + dependencies = [ 4617 + "bitflags 2.9.1", 4618 + "wayland-backend", 4619 + "wayland-client", 4620 + "wayland-scanner", 4621 + ] 4622 + 4623 + [[package]] 4624 + name = "wayland-scanner" 4625 + version = "0.31.7" 4626 + source = "registry+https://github.com/rust-lang/crates.io-index" 4627 + checksum = "54cb1e9dc49da91950bdfd8b848c49330536d9d1fb03d4bfec8cae50caa50ae3" 4628 + dependencies = [ 4629 + "proc-macro2", 4630 + "quick-xml 0.37.5", 4631 + "quote", 4632 + ] 4633 + 4634 + [[package]] 4635 + name = "wayland-sys" 4636 + version = "0.31.7" 4637 + source = "registry+https://github.com/rust-lang/crates.io-index" 4638 + checksum = "34949b42822155826b41db8e5d0c1be3a2bd296c747577a43a3e6daefc296142" 4639 + dependencies = [ 4640 + "dlib", 4641 + "log", 4642 + "pkg-config", 4643 + ] 4644 + 4645 + [[package]] 4436 4646 name = "web-sys" 4437 4647 version = "0.3.77" 4438 4648 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5057 5267 "ordered-stream", 5058 5268 "serde", 5059 5269 "serde_repr", 5270 + "tokio", 5060 5271 "tracing", 5061 5272 "uds_windows", 5062 5273 "windows-sys 0.59.0", ··· 5176 5387 "endi", 5177 5388 "enumflags2", 5178 5389 "serde", 5390 + "url", 5179 5391 "winnow 0.7.12", 5180 5392 "zvariant_derive", 5181 5393 "zvariant_utils",
+1
src-tauri/Cargo.toml
··· 26 26 dirs = "6.0.0" 27 27 anyhow = "1.0.99" 28 28 flate2 = "1.1.2" 29 + tauri-plugin-dialog = "2" 29 30
+6 -3
src-tauri/capabilities/default.json
··· 2 2 "$schema": "../gen/schemas/desktop-schema.json", 3 3 "identifier": "default", 4 4 "description": "Capability for the main window", 5 - "windows": ["main"], 5 + "windows": [ 6 + "main" 7 + ], 6 8 "permissions": [ 7 9 "core:default", 8 - "opener:default" 10 + "opener:default", 11 + "dialog:default" 9 12 ] 10 - } 13 + }
+4 -4
src-tauri/src/frontend_calls/get_addresses.rs
··· 5 5 use crate::osc::OSCMessage; 6 6 7 7 #[tauri::command] 8 - pub fn get_addresses( addresses: State<&Mutex<Vec<OSCMessage>>> ) -> Vec<OSCMessage> { 9 - let addresses = addresses.lock().unwrap(); 10 - addresses.clone() 11 - } 8 + pub fn get_addresses(addresses: State<&Mutex<Vec<OSCMessage>>>) -> Vec<OSCMessage> { 9 + let addresses = addresses.lock().unwrap(); 10 + addresses.clone() 11 + }
+1 -1
src-tauri/src/frontend_calls/mod.rs
··· 1 1 pub mod get_addresses; 2 - pub mod save_graph; 2 + pub mod save_graph;
+13 -10
src-tauri/src/frontend_calls/save_graph.rs
··· 1 - use std::{ fs::File, io::Write }; 1 + use std::{fs::File, io::Write}; 2 2 3 - use flate2::{ write::GzEncoder, Compression }; 3 + use flate2::{write::GzEncoder, Compression}; 4 4 5 5 #[tauri::command] 6 - pub fn save_graph( tab_name: String, graph: String ) { 7 - dbg!(&graph); 6 + pub fn save_graph(tab_name: String, graph: String) { 7 + dbg!(&graph); 8 8 9 - let path = dirs::config_dir().unwrap().join("VRCMacros").join(format!("{}.macro", tab_name)); 9 + let path = dirs::config_dir() 10 + .unwrap() 11 + .join("VRCMacros") 12 + .join(format!("{}.macro", tab_name)); 10 13 11 - let file = File::create(&path).unwrap(); 12 - let mut encoder = GzEncoder::new(file, Compression::default()); 14 + let file = File::create(&path).unwrap(); 15 + let mut encoder = GzEncoder::new(file, Compression::default()); 13 16 14 - encoder.write_all(graph.as_bytes()).unwrap(); 15 - encoder.finish().unwrap(); 16 - } 17 + encoder.write_all(graph.as_bytes()).unwrap(); 18 + encoder.finish().unwrap(); 19 + }
+33 -33
src-tauri/src/lib.rs
··· 1 - use std::{ fs, sync::Mutex }; 1 + use std::{fs, sync::Mutex}; 2 2 3 3 use frontend_calls::*; 4 4 5 - use crate::{ osc::OSCMessage, setup::setup, utils::config::Config }; 5 + use crate::{osc::OSCMessage, setup::setup, utils::config::Config}; 6 6 7 7 mod frontend_calls; 8 - mod structs; 8 + mod osc; 9 9 mod setup; 10 + mod structs; 10 11 mod utils; 11 - mod osc; 12 12 13 13 #[cfg_attr(mobile, tauri::mobile_entry_point)] 14 14 #[tokio::main] 15 15 pub async fn run() { 16 - let container_folder = dirs::config_dir().unwrap().join("VRCMacros"); 16 + let container_folder = dirs::config_dir().unwrap().join("VRCMacros"); 17 17 18 - match fs ::metadata(&container_folder){ 19 - Ok(meta) => { 20 - if meta.is_file(){ 21 - panic!("document.write('Cannot launch app as the container path is a file not a directory')"); 22 - } 23 - } 24 - Err(_) => { 25 - fs::create_dir(&container_folder).unwrap(); 18 + match fs::metadata(&container_folder) { 19 + Ok(meta) => { 20 + if meta.is_file() { 21 + panic!("document.write('Cannot launch app as the container path is a file not a directory')"); 22 + } 23 + } 24 + Err(_) => { 25 + fs::create_dir(&container_folder).unwrap(); 26 + } 26 27 } 27 - } 28 28 29 - let conf_file = container_folder.join("conf"); 30 - let conf = Config::new(conf_file); 31 - 32 - static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new()); 29 + let conf_file = container_folder.join("conf"); 30 + let conf = Config::new(conf_file); 33 31 34 - tauri::Builder::default() 35 - .plugin(tauri_plugin_opener::init()) 36 - .invoke_handler(tauri::generate_handler![ 37 - get_addresses::get_addresses, 32 + static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new()); 38 33 39 - save_graph::save_graph 40 - ]) 41 - .manage(conf) 42 - .manage(&ADDRESSES) 43 - .setup(| app | { 44 - setup(app, &ADDRESSES); 45 - Ok(()) 46 - }) 47 - .run(tauri::generate_context!()) 48 - .expect("error while running tauri application"); 49 - } 34 + tauri::Builder::default() 35 + .plugin(tauri_plugin_dialog::init()) 36 + .plugin(tauri_plugin_opener::init()) 37 + .invoke_handler(tauri::generate_handler![ 38 + get_addresses::get_addresses, 39 + save_graph::save_graph 40 + ]) 41 + .manage(conf) 42 + .manage(&ADDRESSES) 43 + .setup(|app| { 44 + setup(app, &ADDRESSES); 45 + Ok(()) 46 + }) 47 + .run(tauri::generate_context!()) 48 + .expect("error while running tauri application"); 49 + }
+3 -3
src-tauri/src/main.rs
··· 2 2 #![cfg_attr(not(debug_assertions), windows_subsystem = "windows")] 3 3 4 4 fn main() { 5 - #[cfg(target_os = "linux")] 6 - std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); // Fix webkit being shit 5 + #[cfg(target_os = "linux")] 6 + std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); // Fix webkit being shit 7 7 8 - vrcmacros_lib::run() 8 + vrcmacros_lib::run() 9 9 }
+117 -109
src-tauri/src/osc.rs
··· 1 1 // https://gist.github.com/phaze-the-dumb/634daacb5141eae2f846e20987dba7a8 2 2 3 - use std::{ net::UdpSocket, sync::mpsc::Sender }; 3 + use std::{net::UdpSocket, sync::mpsc::Sender}; 4 4 5 5 use serde::Serialize; 6 6 7 7 use crate::structs::parameter_types::ParameterType; 8 8 9 9 #[derive(Debug, Clone, Serialize)] 10 - pub struct OSCMessage{ 11 - pub address: String, 12 - pub values: Vec<ParameterType> 10 + pub struct OSCMessage { 11 + pub address: String, 12 + pub values: Vec<ParameterType>, 13 13 } 14 14 15 - impl PartialEq for OSCMessage{ 16 - // Technically this isn't exactly equal, but the only time i'm checking if OSCMessage's are equal 17 - // Is when i'm checking for the "address" value 15 + impl PartialEq for OSCMessage { 16 + // Technically this isn't exactly equal, but the only time i'm checking if OSCMessage's are equal 17 + // Is when i'm checking for the "address" value 18 18 19 - fn eq(&self, other: &Self) -> bool { 20 - self.address == other.address 21 - } 19 + fn eq(&self, other: &Self) -> bool { 20 + self.address == other.address 21 + } 22 22 23 - fn ne(&self, other: &Self) -> bool { 24 - self.address != other.address 25 - } 23 + fn ne(&self, other: &Self) -> bool { 24 + self.address != other.address 25 + } 26 26 } 27 27 28 28 // TODO: Implement osc bundles 29 - pub fn start_server( sender: Sender<OSCMessage>, addr: &str ) { 30 - let socket = UdpSocket::bind(addr).unwrap(); 29 + pub fn start_server(sender: Sender<OSCMessage>, addr: &str) { 30 + let socket = UdpSocket::bind(addr).unwrap(); 31 31 32 - loop { 33 - let mut buf = [0; 1024]; 34 - let (amt, _src) = socket.recv_from(&mut buf).unwrap(); 32 + loop { 33 + let mut buf = [0; 1024]; 34 + let (amt, _src) = socket.recv_from(&mut buf).unwrap(); 35 35 36 - let buf = &mut buf[..amt]; 37 - if buf[0] != 0x2F { panic!("Packet is not an OSC Message"); } 36 + let buf = &mut buf[..amt]; 37 + if buf[0] != 0x2F { 38 + panic!("Packet is not an OSC Message"); 39 + } 38 40 39 - let mut addr: Vec<u8> = Vec::new(); 40 - let mut value_start = 0; 41 + let mut addr: Vec<u8> = Vec::new(); 42 + let mut value_start = 0; 41 43 42 - loop{ 43 - let byte = buf[value_start]; 44 - if byte == 0x00{ break; } 44 + loop { 45 + let byte = buf[value_start]; 46 + if byte == 0x00 { 47 + break; 48 + } 45 49 46 - value_start += 1; 47 - addr.push(byte); 48 - } 50 + value_start += 1; 51 + addr.push(byte); 52 + } 49 53 50 - loop{ 51 - let byte = buf[value_start]; 52 - value_start += 1; 54 + loop { 55 + let byte = buf[value_start]; 56 + value_start += 1; 53 57 54 - if byte == 0x2C{ break; } 55 - } 58 + if byte == 0x2C { 59 + break; 60 + } 61 + } 56 62 57 - let mut types: Vec<u8> = Vec::new(); 63 + let mut types: Vec<u8> = Vec::new(); 58 64 59 - loop{ 60 - let byte = buf[value_start]; 61 - if byte == 0x00{ break; } 65 + loop { 66 + let byte = buf[value_start]; 67 + if byte == 0x00 { 68 + break; 69 + } 62 70 63 - types.push(byte); 64 - value_start += 1; 65 - } 71 + types.push(byte); 72 + value_start += 1; 73 + } 66 74 67 - value_start = ((value_start as f32 / 4.0).ceil() * 4.0) as usize; 68 - let mut values = Vec::new(); 75 + value_start = ((value_start as f32 / 4.0).ceil() * 4.0) as usize; 76 + let mut values = Vec::new(); 69 77 70 - for tp in types{ 71 - match tp{ 72 - 0x69 => { 73 - let val_buf = &buf[value_start..value_start + 4]; 78 + for tp in types { 79 + match tp { 80 + 0x69 => { 81 + let val_buf = &buf[value_start..value_start + 4]; 74 82 75 - let bytes = <&[u8; 4]>::try_from(val_buf).unwrap().clone(); 76 - let int = i32::from_be_bytes(bytes); 83 + let bytes = <&[u8; 4]>::try_from(val_buf).unwrap().clone(); 84 + let int = i32::from_be_bytes(bytes); 77 85 78 - values.push(ParameterType::Int(int)); 79 - value_start += 4; 80 - }, 81 - 0x66 => { 82 - let val_buf = &buf[value_start..value_start + 4]; 86 + values.push(ParameterType::Int(int)); 87 + value_start += 4; 88 + } 89 + 0x66 => { 90 + let val_buf = &buf[value_start..value_start + 4]; 83 91 84 - let bytes = <&[u8; 4]>::try_from(val_buf).unwrap().clone(); 85 - let float = f32::from_be_bytes(bytes); 92 + let bytes = <&[u8; 4]>::try_from(val_buf).unwrap().clone(); 93 + let float = f32::from_be_bytes(bytes); 86 94 87 - values.push(ParameterType::Float(float)); 88 - value_start += 4; 89 - }, 90 - 0x54 => values.push(ParameterType::Boolean(true)), 91 - 0x46 => values.push(ParameterType::Boolean(false)), 92 - _ => {} 93 - } 94 - } 95 + values.push(ParameterType::Float(float)); 96 + value_start += 4; 97 + } 98 + 0x54 => values.push(ParameterType::Boolean(true)), 99 + 0x46 => values.push(ParameterType::Boolean(false)), 100 + _ => {} 101 + } 102 + } 95 103 96 - let message = OSCMessage { 97 - address: String::from_utf8(addr.clone()).unwrap(), 98 - values: values 99 - }; 104 + let message = OSCMessage { 105 + address: String::from_utf8(addr.clone()).unwrap(), 106 + values: values, 107 + }; 100 108 101 - sender.send(message).unwrap(); 102 - } 109 + sender.send(message).unwrap(); 110 + } 103 111 } 104 112 105 - pub fn send_message( address: &str, values: Vec<ParameterType>, ip_addr: &str ){ 106 - let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); 107 - let mut buf: Vec<u8> = Vec::new(); 113 + pub fn send_message(address: &str, values: Vec<ParameterType>, ip_addr: &str) { 114 + let socket = UdpSocket::bind("127.0.0.1:0").unwrap(); 115 + let mut buf: Vec<u8> = Vec::new(); 108 116 109 - buf.append(&mut address.as_bytes().to_vec()); 117 + buf.append(&mut address.as_bytes().to_vec()); 110 118 111 - let rounded_len = ((((buf.len() as f64) + 1.0) / 4.0).ceil() * 4.0) as usize; 112 - let original_len = buf.len(); 119 + let rounded_len = ((((buf.len() as f64) + 1.0) / 4.0).ceil() * 4.0) as usize; 120 + let original_len = buf.len(); 113 121 114 - for _i in original_len..rounded_len { 115 - buf.push(0); 116 - } 122 + for _i in original_len..rounded_len { 123 + buf.push(0); 124 + } 117 125 118 - buf.push(0x2C); 126 + buf.push(0x2C); 119 127 120 - let mut value_count = 1; 121 - for value in values.clone() { 122 - match value { 123 - ParameterType::Boolean( val ) => buf.push(if val { 0x54 } else { 0x46 }), 124 - ParameterType::Float(_) => buf.push(0x66), 125 - ParameterType::Int(_) => buf.push(0x69), 126 - ParameterType::String(_) => buf.push(0x73), 127 - _ => {} 128 - }; 128 + let mut value_count = 1; 129 + for value in values.clone() { 130 + match value { 131 + ParameterType::Boolean(val) => buf.push(if val { 0x54 } else { 0x46 }), 132 + ParameterType::Float(_) => buf.push(0x66), 133 + ParameterType::Int(_) => buf.push(0x69), 134 + ParameterType::String(_) => buf.push(0x73), 135 + _ => {} 136 + }; 129 137 130 - value_count += 1; 131 - } 138 + value_count += 1; 139 + } 132 140 133 - for _i in 0..4 - (value_count % 4) { 134 - buf.push(0); 135 - } 141 + for _i in 0..4 - (value_count % 4) { 142 + buf.push(0); 143 + } 136 144 137 - for value in values{ 138 - match value{ 139 - ParameterType::Float( val ) => buf.append(&mut val.to_be_bytes().to_vec()), 140 - ParameterType::Int( val ) => buf.append(&mut val.to_be_bytes().to_vec()), 141 - ParameterType::String( val ) => { 142 - let mut str_buf = val.as_bytes().to_vec(); 143 - let buf_len = str_buf.len().clone(); 145 + for value in values { 146 + match value { 147 + ParameterType::Float(val) => buf.append(&mut val.to_be_bytes().to_vec()), 148 + ParameterType::Int(val) => buf.append(&mut val.to_be_bytes().to_vec()), 149 + ParameterType::String(val) => { 150 + let mut str_buf = val.as_bytes().to_vec(); 151 + let buf_len = str_buf.len().clone(); 144 152 145 - buf.append(&mut str_buf); 153 + buf.append(&mut str_buf); 146 154 147 - for _i in 0..4 - (buf_len % 4) { 148 - buf.push(0); 155 + for _i in 0..4 - (buf_len % 4) { 156 + buf.push(0); 157 + } 158 + } 159 + _ => {} 149 160 } 150 - } 151 - _ => {} 152 161 } 153 - } 154 162 155 - println!("{:X?}", &buf); 156 - socket.send_to(&buf, ip_addr).unwrap(); 157 - } 163 + println!("{:X?}", &buf); 164 + socket.send_to(&buf, ip_addr).unwrap(); 165 + }
+35 -29
src-tauri/src/setup.rs
··· 1 - use std::{ fs::File, io::Read, sync::{ self, Mutex } }; 1 + use std::{ 2 + fs::File, 3 + io::Read, 4 + sync::{self, Mutex}, 5 + }; 2 6 3 7 use flate2::read::GzDecoder; 4 8 use serde_json::Value; 5 - use tauri::{ App, Emitter, Listener, Manager }; 9 + use tauri::{App, Emitter, Listener, Manager}; 6 10 7 - use crate::osc::{ self, OSCMessage }; 11 + use crate::osc::{self, OSCMessage}; 8 12 9 - pub fn setup( app: &mut App, addresses: &'static Mutex<Vec<OSCMessage>> ){ 10 - let window = app.get_webview_window("main").unwrap(); 13 + pub fn setup(app: &mut App, addresses: &'static Mutex<Vec<OSCMessage>>) { 14 + let window = app.get_webview_window("main").unwrap(); 11 15 12 - let handle = window.clone(); 13 - window.listen("tauri://drag-drop", move | ev | { 14 - let path: Value = serde_json::from_str(ev.payload()).unwrap(); 15 - let path = path["paths"][0].as_str().unwrap(); 16 + let handle = window.clone(); 17 + window.listen("tauri://drag-drop", move |ev| { 18 + let path: Value = serde_json::from_str(ev.payload()).unwrap(); 19 + let path = path["paths"][0].as_str().unwrap(); 16 20 17 - let file = File::open(path).unwrap(); 18 - let mut decoder = GzDecoder::new(file); 19 - let mut string = String::new(); 21 + let file = File::open(path).unwrap(); 22 + let mut decoder = GzDecoder::new(file); 23 + let mut string = String::new(); 20 24 21 - decoder.read_to_string(&mut string).unwrap(); 25 + decoder.read_to_string(&mut string).unwrap(); 22 26 23 - handle.emit("load_new_tab", Value::String(string)).unwrap(); 24 - }); 27 + handle.emit("load_new_tab", Value::String(string)).unwrap(); 28 + }); 25 29 26 - let ( sender, receiver ) = sync::mpsc::channel(); 30 + let (sender, receiver) = sync::mpsc::channel(); 27 31 28 - tokio::spawn(async move { 29 - osc::start_server(sender, "127.0.0.1:9001"); 30 - }); 32 + tokio::spawn(async move { 33 + osc::start_server(sender, "127.0.0.1:9001"); 34 + }); 31 35 32 - tokio::spawn(async move { 33 - loop { 34 - let message = receiver.recv().unwrap(); 36 + tokio::spawn(async move { 37 + loop { 38 + let message = receiver.recv().unwrap(); 35 39 36 - window.emit("osc-message", &message).unwrap(); 40 + window.emit("osc-message", &message).unwrap(); 37 41 38 - let msg = message.clone(); 39 - let mut addresses = addresses.lock().unwrap(); 40 - if !addresses.contains(&msg){ addresses.push(msg); } 41 - } 42 - }); 43 - } 42 + let msg = message.clone(); 43 + let mut addresses = addresses.lock().unwrap(); 44 + if !addresses.contains(&msg) { 45 + addresses.push(msg); 46 + } 47 + } 48 + }); 49 + }
+1 -1
src-tauri/src/structs/mod.rs
··· 1 - pub mod parameter_types; 1 + pub mod parameter_types;
+8 -8
src-tauri/src/structs/parameter_types.rs
··· 2 2 3 3 #[derive(Serialize, Clone, Debug)] 4 4 #[serde(tag = "key", content = "value")] 5 - pub enum ParameterType{ 6 - AnyType(String), 7 - Label(&'static str), 5 + pub enum ParameterType { 6 + AnyType(String), 7 + Label(&'static str), 8 8 9 - Int(i32), 10 - Float(f32), 11 - Boolean(bool), 12 - String(String), 13 - } 9 + Int(i32), 10 + Float(f32), 11 + Boolean(bool), 12 + String(String), 13 + }
+69 -63
src-tauri/src/utils/config.rs
··· 1 - use std::{ collections::HashMap, fs::File, io::{ Read, Write }, path::PathBuf, sync::Mutex }; 2 - 3 - use flate2::{ read::GzDecoder, write::GzEncoder, Compression }; 4 - use serde_json::Value; 5 - 6 - pub struct Config{ 7 - store: Mutex<HashMap<String, Value>>, 8 - path: PathBuf 9 - } 10 - 11 - impl Config{ 12 - pub fn new( path: PathBuf ) -> Self{ 13 - let json_string = if path.exists(){ 14 - let mut decoder = GzDecoder::new(File::open(&path).unwrap()); 15 - let mut string = String::new(); 16 - 17 - decoder.read_to_string(&mut string).unwrap(); 18 - string 19 - } else{ 20 - "{}".into() 21 - }; 22 - 23 - let json: Value = serde_json::from_str(&json_string).unwrap(); 24 - 25 - let mut hashmap = HashMap::new(); 26 - 27 - let obj = json.as_object().unwrap(); 28 - for ( key, val ) in obj{ 29 - hashmap.insert(key.clone(), val.clone()); 30 - } 31 - 32 - Config { 33 - store: Mutex::new(hashmap), 34 - path: path 35 - } 36 - } 37 - 38 - pub fn set( &self, key: &str, value: Value ){ 39 - self.store.lock().unwrap().insert(key.to_owned(), value); 40 - } 41 - 42 - pub fn get( &self, key: &str ) -> Option<Value>{ 43 - let store = self.store.lock().unwrap(); 44 - let val = store.get(&key.to_owned()); 45 - 46 - if val.is_none(){ 47 - None 48 - } else{ 49 - Some(val.unwrap().clone()) 50 - } 51 - } 52 - 53 - pub fn save( &self ){ 54 - let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap(); 55 - dbg!(&dat); 56 - 57 - let file = File::create(&self.path).unwrap(); 58 - let mut encoder = GzEncoder::new(file, Compression::default()); 59 - 60 - encoder.write_all(dat.as_bytes()).unwrap(); 61 - encoder.finish().unwrap(); 62 - } 63 - } 1 + use std::{ 2 + collections::HashMap, 3 + fs::File, 4 + io::{Read, Write}, 5 + path::PathBuf, 6 + sync::Mutex, 7 + }; 8 + 9 + use flate2::{read::GzDecoder, write::GzEncoder, Compression}; 10 + use serde_json::Value; 11 + 12 + pub struct Config { 13 + store: Mutex<HashMap<String, Value>>, 14 + path: PathBuf, 15 + } 16 + 17 + impl Config { 18 + pub fn new(path: PathBuf) -> Self { 19 + let json_string = if path.exists() { 20 + let mut decoder = GzDecoder::new(File::open(&path).unwrap()); 21 + let mut string = String::new(); 22 + 23 + decoder.read_to_string(&mut string).unwrap(); 24 + string 25 + } else { 26 + "{}".into() 27 + }; 28 + 29 + let json: Value = serde_json::from_str(&json_string).unwrap(); 30 + 31 + let mut hashmap = HashMap::new(); 32 + 33 + let obj = json.as_object().unwrap(); 34 + for (key, val) in obj { 35 + hashmap.insert(key.clone(), val.clone()); 36 + } 37 + 38 + Config { 39 + store: Mutex::new(hashmap), 40 + path: path, 41 + } 42 + } 43 + 44 + pub fn set(&self, key: &str, value: Value) { 45 + self.store.lock().unwrap().insert(key.to_owned(), value); 46 + } 47 + 48 + pub fn get(&self, key: &str) -> Option<Value> { 49 + let store = self.store.lock().unwrap(); 50 + let val = store.get(&key.to_owned()); 51 + 52 + if val.is_none() { 53 + None 54 + } else { 55 + Some(val.unwrap().clone()) 56 + } 57 + } 58 + 59 + pub fn save(&self) { 60 + let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap(); 61 + dbg!(&dat); 62 + 63 + let file = File::create(&self.path).unwrap(); 64 + let mut encoder = GzEncoder::new(file, Compression::default()); 65 + 66 + encoder.write_all(dat.as_bytes()).unwrap(); 67 + encoder.finish().unwrap(); 68 + } 69 + }
+1 -1
src-tauri/src/utils/mod.rs
··· 1 - pub mod config; 1 + pub mod config;
+22 -10
src/Mangers/NodeManager.tsx
··· 5 5 import { listen } from "@tauri-apps/api/event"; 6 6 import { getVersion } from "@tauri-apps/api/app"; 7 7 import { NodesByID } from "../Nodes/Nodes"; 8 + import { save } from "@tauri-apps/plugin-dialog"; 8 9 9 10 export interface TabHashMap { 10 11 [details: string] : Tab; ··· 17 18 private _tabs: TabHashMap = {}; 18 19 19 20 private _nodes: Node[] = []; 20 - private _needsSave = false; 21 21 22 22 constructor(){ 23 23 NodeManager.Instance = this; 24 24 25 - setInterval(() => { 26 - // Save config every 1 second 27 - if(this._needsSave)this._saveConfigToDisk(); 28 - }, 1_000); 29 - 30 25 listen('load_new_tab', ( ev: any ) => { 31 26 this._loadFromConfig(ev.payload); 32 27 }) ··· 38 33 39 34 public async AddTab( name: string ){ 40 35 let [ selected, setSelected ] = createSignal(false); 36 + let [ needsSave, setNeedsSave ] = createSignal(false); 41 37 42 38 let tab: Tab = { 43 39 name: name, 44 40 id: await NodeManager.Instance.GetNewNodeId(), 45 41 nodes: [], 46 - selected: selected, 47 - setSelected: setSelected 42 + 43 + selected, 44 + setSelected, 45 + 46 + needsSave, 47 + setNeedsSave 48 48 }; 49 49 50 50 this._tabs[tab.id] = tab; ··· 110 110 } 111 111 } 112 112 113 + public async SaveTab( tab: Tab ){ 114 + let path = await save({ defaultPath: tab.name + '.macro', filters: [ { name: 'Macro Files', extensions: [ 'macro' ] } ] }); 115 + 116 + console.log(path); 117 + 118 + // TODO: Add location metadata to tab interface so it knows where to save 119 + // TODO: store file 120 + } 121 + 113 122 public HookTabUpdate( cb: ( tabs: TabHashMap ) => void ){ 114 123 this._tabUpdateHook = cb; 115 124 } ··· 150 159 151 160 152 161 public UpdateConfig(){ 153 - this._needsSave = true; 162 + if(!this._selectedTab)return; 163 + let tab = this._tabs[this._selectedTab]; 164 + if(!tab)return; 165 + 166 + tab.setNeedsSave(true); 154 167 } 155 168 156 169 private async _loadFromConfig( config: string ){ ··· 215 228 } 216 229 217 230 private async _saveConfigToDisk(){ 218 - this._needsSave = false; 219 231 // Convert it into a structure we can actually save... 220 232 221 233 if(!this._selectedTab)return;
-2
src/components/TabMenu.css
··· 88 88 } 89 89 90 90 .tab-new-dropdown{ 91 - opacity: 0; 92 91 position: absolute; 93 92 width: 180px; 94 93 transform: translate(-10px, 35px); 95 94 border-radius: 10px; 96 95 background: #fff1; 97 - transition: 0.1s; 98 96 }
+21 -10
src/components/TabMenu.tsx
··· 1 - import { createSignal, For, onMount } from 'solid-js'; 1 + import { createSignal, For, onMount, Show } from 'solid-js'; 2 2 import './TabMenu.css'; 3 3 import { NodeManager } from '../Mangers/NodeManager'; 4 4 import { Tab } from '../structs/Tab'; ··· 21 21 <For each={Object.values(tabs())}> 22 22 { 23 23 tab => 24 - <div class={ tab.selected() ? 'tab-selected ' : 'tab' }> 25 - <div class="tab-icon" onClick={() => { 26 - NodeManager.Instance.SelectTab(tab.id); 27 - }}><img src="/assets/icons/pen-to-square-regular-full.svg" width="15" /></div> 28 - <div class="tab-meta" onClick={() => { 29 - NodeManager.Instance.SelectTab(tab.id); 30 - }} onDblClick={( e ) => { 24 + <div class={ tab.selected() ? 'tab-selected ' : 'tab' } onClick={() => { 25 + NodeManager.Instance.SelectTab(tab.id); 26 + }}> 27 + <div class="tab-icon" onClick={async () => { 28 + if(tab.selected()){ 29 + NodeManager.Instance.SaveTab(tab); 30 + } 31 + }}> 32 + <Show when={tab.selected() && tab.needsSave()} fallback={ 33 + <img src="/assets/icons/pen-to-square-regular-full.svg" width="15" /> 34 + }> 35 + <img src="/assets/icons/floppy-disk-solid-full.svg" width="15" /> 36 + </Show> 37 + 38 + </div> 39 + <div class="tab-meta" onDblClick={( e ) => { 31 40 let input = <input class="tab-meta-input" value={ e.target.innerHTML } /> as HTMLInputElement; 32 41 33 42 e.target.innerHTML = ''; ··· 40 49 } 41 50 }}>{ tab.name }</div> 42 51 <div class="tab-close" onClick={() => { 43 - NodeManager.Instance.CloseTab(tab.id); 52 + setTimeout(() => { 53 + NodeManager.Instance.CloseTab(tab.id); 54 + }, 50) 44 55 }}><img src="/assets/icons/xmark-solid-full.svg" width="12" /></div> 45 56 </div> 46 57 } ··· 54 65 55 66 window.addEventListener('click', closeTabImportMenu); 56 67 }}> 57 - <div class="tab-new-dropdown" style={{ opacity: tabImportOpen() ? 1 : 0 }}> 68 + <div class="tab-new-dropdown" style={{ display: tabImportOpen() ? 'block' : 'none' }}> 58 69 <div class="tab">Import from file</div> 59 70 <div class="tab">Import from URL</div> 60 71 </div>
+4
src/structs/Tab.ts
··· 5 5 name: string, 6 6 id: string, 7 7 nodes: Node[], 8 + 8 9 selected: Accessor<boolean>, 9 10 setSelected: Setter<boolean> 11 + 12 + needsSave: Accessor<boolean>, 13 + setNeedsSave: Setter<boolean> 10 14 }