this repo has no description

basic app outline

phaz.uk 4631ebd1 779e78ea

verified
-7
README.md
··· 1 - # Tauri + Solid + Typescript 2 - 3 - This template should help get you started developing with Tauri, Solid and Typescript in Vite. 4 - 5 - ## Recommended IDE Setup 6 - 7 - - [VS Code](https://code.visualstudio.com/) + [Tauri](https://marketplace.visualstudio.com/items?itemName=tauri-apps.tauri-vscode) + [rust-analyzer](https://marketplace.visualstudio.com/items?itemName=rust-lang.rust-analyzer)
+3 -2
src-tauri/Cargo.lock
··· 6 6 name = "VRCMacros" 7 7 version = "0.1.0" 8 8 dependencies = [ 9 + "anyhow", 9 10 "dirs", 10 11 "serde", 11 12 "serde_json", ··· 78 79 79 80 [[package]] 80 81 name = "anyhow" 81 - version = "1.0.98" 82 + version = "1.0.99" 82 83 source = "registry+https://github.com/rust-lang/crates.io-index" 83 - checksum = "e16d2d3311acee920a9eb8d33b8cbc1787ce4a264e85f964c2404b969bdcd487" 84 + checksum = "b0674a1ddeecb70197781e945de4b3b8ffb61fa939a5597bcf48503737663100" 84 85 85 86 [[package]] 86 87 name = "async-broadcast"
+1
src-tauri/Cargo.toml
··· 25 25 sqlx = { version = "0.8.6", features = [ "sqlite", "runtime-tokio" ] } 26 26 tokio = { version = "1.47.0", features = [ "macros" ] } 27 27 dirs = "6.0.0" 28 + anyhow = "1.0.99" 28 29
+10 -3
src-tauri/src/lib.rs
··· 1 1 use std::fs; 2 2 3 - use sqlx::{migrate::MigrateDatabase, Sqlite, SqlitePool}; 3 + use sqlx::{ migrate::MigrateDatabase, Sqlite, SqlitePool }; 4 4 5 - use crate::utils::config::Config; 5 + use crate::{ setup::setup, utils::config::Config }; 6 6 7 + mod setup; 7 8 mod utils; 9 + mod osc; 8 10 9 11 #[cfg_attr(mobile, tauri::mobile_entry_point)] 10 12 #[tokio::main] ··· 26 28 if !db_file.exists(){ Sqlite::create_database(db_file.to_str().unwrap()).await.unwrap(); } 27 29 28 30 let conf_file = container_folder.join("VRCMacros.json"); 29 - if !conf_file.exists(){ fs::write(&conf_file, "{ \"setup_complete\": false }").unwrap() } 31 + if !conf_file.exists(){ fs::write(&conf_file, "{}").unwrap() } 30 32 31 33 let pool = SqlitePool::connect(db_file.to_str().unwrap()).await.unwrap(); 32 34 let conf = Config::new(conf_file); ··· 36 38 .invoke_handler(tauri::generate_handler![]) 37 39 .manage(pool) 38 40 .manage(conf) 41 + .setup(| app | { 42 + setup(app); 43 + 44 + Ok(()) 45 + }) 39 46 .run(tauri::generate_context!()) 40 47 .expect("error while running tauri application"); 41 48 }
+5 -2
src-tauri/src/osc.rs
··· 2 2 3 3 use std::{ net::UdpSocket, sync::mpsc::Sender }; 4 4 5 - #[derive(Debug, Clone)] 5 + use serde::Serialize; 6 + 7 + #[derive(Debug, Clone, Serialize)] 6 8 pub enum OSCValue{ 7 9 Int(i32), 8 10 Float(f32), ··· 10 12 String(String), 11 13 } 12 14 13 - #[derive(Debug)] 15 + #[derive(Debug, Clone, Serialize)] 14 16 pub struct OSCMessage{ 15 17 pub address: String, 16 18 pub values: Vec<OSCValue> 17 19 } 18 20 21 + // TODO: implement osc bundles 19 22 pub fn start_server( sender: Sender<OSCMessage>, addr: &str ) { 20 23 let socket = UdpSocket::bind(addr).unwrap(); 21 24
+25
src-tauri/src/setup.rs
··· 1 + use std::sync; 2 + 3 + use tauri::{ App, Emitter, Manager }; 4 + 5 + use crate::osc; 6 + 7 + pub fn setup( app: &mut App ){ 8 + let window = app.get_webview_window("main").unwrap(); 9 + 10 + let ( sender, receiver ) = sync::mpsc::channel(); 11 + 12 + tokio::spawn(async move { 13 + osc::start_server(sender, "127.0.0.1:9001"); 14 + }); 15 + 16 + tokio::spawn(async move { 17 + loop { 18 + let message = receiver.recv().unwrap(); 19 + 20 + 21 + 22 + window.emit("osc-message", message).unwrap(); 23 + } 24 + }); 25 + }
+1 -1
src-tauri/src/utils/config.rs
··· 1 1 use std::{ collections::HashMap, fs, path::PathBuf, sync::Mutex }; 2 2 3 3 use serde::{ Deserialize, Serialize }; 4 - use serde_json::{json, Value}; 4 + use serde_json::Value; 5 5 6 6 #[derive(Clone, Deserialize, Serialize)] 7 7 pub enum ConfigValue{
+18 -7
src/App.css
··· 22 22 23 23 h1, h2, h3, h4, h5, h6, p{ 24 24 margin: 0; 25 + font-weight: 500; 25 26 } 26 27 27 - div[app-centre]{ 28 + div[app-carousel]{ 28 29 position: fixed; 29 - top: 50%; 30 - left: 50%; 31 - transform: translate(-50%, -50%); 32 - text-align: center; 30 + top: 10px; 31 + left: 220px; 32 + width: calc(100vw - 230px); 33 + height: calc(100vh - 20px); 34 + overflow: hidden; 35 + } 36 + 37 + div[app-page]{ 38 + width: calc(100vw - 230px); 39 + height: calc(100vh - 20px); 40 + overflow-y: auto; 41 + overflow-x: hidden; 33 42 } 34 43 35 44 div[app-button]{ ··· 67 76 68 77 div[app-col]{ 69 78 display: flex; 70 - justify-content: center; 71 - align-items: center; 79 + } 80 + 81 + div[app-col] > div{ 82 + width: 50%; 72 83 }
+26 -2
src/App.tsx
··· 1 - import { onMount } from "solid-js"; 2 1 import "./App.css"; 3 2 3 + import { createEffect, createSignal, onCleanup, onMount } from "solid-js"; 4 + import { listen } from "@tauri-apps/api/event"; 5 + 6 + import { Sidebar } from "./Components/Sidebar"; 7 + import { Actions } from "./Components/Actions"; 8 + import { Relays } from "./Components/Relays"; 9 + import { animate } from "animejs"; 10 + import { Settings } from "./Components/Settings"; 11 + import { Debug } from "./Components/Debug"; 12 + 4 13 let App = () => { 5 - onMount(() => { 14 + let [ page, setPage ] = createSignal(0); 15 + let carousel!: HTMLDivElement; 16 + 17 + onMount(async () => { 6 18 7 19 }); 20 + 21 + createEffect(() => { 22 + let pagenum = page(); 23 + animate(carousel.children, { translateY: '-' + ( 100 * pagenum ) + '%', ease: 'outElastic(.1, .7)', duration: 500 }); 24 + }) 8 25 9 26 return ( 10 27 <> 28 + <Sidebar setPage={setPage} /> 11 29 30 + <div app-carousel ref={carousel}> 31 + <Actions /> 32 + <Relays /> 33 + <Debug page={page} /> 34 + <Settings /> 35 + </div> 12 36 </> 13 37 ); 14 38 }
src/Components/Actions.css

This is a binary file and will not be displayed.

+16
src/Components/Actions.tsx
··· 1 + import './Actions.css'; 2 + 3 + export let Actions = () => { 4 + return ( 5 + <div app-page> 6 + <div app-col> 7 + <div><h1>Actions</h1></div> 8 + <div app-button style={{ width: 'fit-content', "margin-left": '50%' }}>+</div> 9 + </div> 10 + 11 + <div> 12 + 13 + </div> 14 + </div> 15 + ) 16 + }
+8
src/Components/Debug.css
··· 1 + div[app-debug-el]{ 2 + padding: 10px; 3 + background: #272e44; 4 + margin: 10px; 5 + border-radius: 5px; 6 + box-shadow: #00ccff 0 0 10px; 7 + transition: 0.5s; 8 + }
+89
src/Components/Debug.tsx
··· 1 + import './Debug.css'; 2 + 3 + import { createEffect, onCleanup, onMount } from 'solid-js'; 4 + import { listen, UnlistenFn } from '@tauri-apps/api/event'; 5 + import { OSCMessage, OSCValue } from '../Structs/OSCMessage'; 6 + 7 + let formatValuesForDebug = ( values: OSCValue[] ): string => { 8 + let text = ''; 9 + 10 + for(let value of values){ 11 + if(value.Boolean !== undefined) 12 + text += ' Boolean: ' + value.Boolean; 13 + else if(value.Float !== undefined) 14 + text += ' Float: ' + value.Float.toFixed(6); 15 + else if(value.Int !== undefined) 16 + text += ' Int: ' + value.Int; 17 + else if(value.String !== undefined) 18 + text += ' String: ' + value.String; 19 + } 20 + 21 + return text.trimStart(); 22 + } 23 + 24 + export interface DebugProps{ 25 + page: () => number 26 + } 27 + 28 + export let Debug = ( props: DebugProps ) => { 29 + let debugContainer!: HTMLDivElement; 30 + 31 + let debugEls: any = {}; 32 + 33 + let isListening = false; 34 + let unlisten: UnlistenFn; 35 + 36 + let stopListening = () => { 37 + if(!isListening)return; 38 + isListening = false; 39 + 40 + unlisten(); 41 + } 42 + 43 + let startListening = async () => { 44 + if(isListening)return; 45 + isListening = true; 46 + 47 + unlisten = await listen<OSCMessage>('osc-message', ( ev ) => { 48 + let el = debugEls[ev.payload.address]; 49 + if(el){ 50 + el.style.boxShadow = '#00ccff 0 0 10px'; 51 + debugContainer.insertBefore(el, debugContainer.firstChild); 52 + 53 + el.innerHTML = `<div>${ ev.payload.address }</div><div>${ formatValuesForDebug(ev.payload.values) }</div>`; 54 + setTimeout(() => { el.style.boxShadow = '#00ccff 0 0 0px'; }) 55 + } else{ 56 + el = <div app-debug-el app-col><div>{ ev.payload.address }</div><div>{ formatValuesForDebug(ev.payload.values) }</div></div> as Node; 57 + 58 + el.style.boxShadow = '#00ccff 0 0 10px'; 59 + debugContainer.insertBefore(el, debugContainer.firstChild); 60 + 61 + setTimeout(() => { el.style.boxShadow = '#00ccff 0 0 0px'; }) 62 + debugEls[ev.payload.address] = el; 63 + } 64 + }) 65 + } 66 + 67 + onMount(() => { 68 + createEffect(() => { 69 + if(props.page() === 2) 70 + startListening(); 71 + else 72 + stopListening(); 73 + }); 74 + }); 75 + 76 + onCleanup(() => { 77 + stopListening(); 78 + }); 79 + 80 + return ( 81 + <div app-page> 82 + <h1>Debug</h1> 83 + 84 + <div ref={debugContainer}> 85 + 86 + </div> 87 + </div> 88 + ) 89 + }
src/Components/Relays.css

This is a binary file and will not be displayed.

+9
src/Components/Relays.tsx
··· 1 + import './Relays.css'; 2 + 3 + export let Relays = () => { 4 + return ( 5 + <div app-page> 6 + <h1>Relays</h1> 7 + </div> 8 + ) 9 + }
src/Components/Settings.css

This is a binary file and will not be displayed.

+9
src/Components/Settings.tsx
··· 1 + import './Settings.css'; 2 + 3 + export let Settings = () => { 4 + return ( 5 + <div app-page> 6 + <h1>Settings</h1> 7 + </div> 8 + ) 9 + }
+32
src/Components/Sidebar.css
··· 1 + div[app-sidebar]{ 2 + position: fixed; 3 + top: 10px; 4 + left: 10px; 5 + 6 + height: calc(100vh - 20px); 7 + width: 200px; 8 + padding: 0px; 9 + 10 + background: #272e44; 11 + border-radius: 5px; 12 + } 13 + 14 + div[app-sidebar-tab]{ 15 + padding: 10px; 16 + cursor: pointer; 17 + user-select: none; 18 + -webkit-user-select: none; 19 + transition: 0.1s; 20 + margin: 10px; 21 + border-radius: 5px; 22 + } 23 + 24 + div[app-sidebar-tab]:hover{ 25 + background: #5b6ca5; 26 + } 27 + 28 + div[app-sidebar-tab-dropped]{ 29 + position: absolute; 30 + width: calc(100% - 20px); 31 + bottom: 0px; 32 + }
+19
src/Components/Sidebar.tsx
··· 1 + import './Sidebar.css' 2 + 3 + export interface SidebarProps{ 4 + setPage: ( page: number ) => number 5 + } 6 + 7 + export let Sidebar = ( props: SidebarProps ) => { 8 + return ( 9 + <> 10 + <div app-sidebar> 11 + <div app-sidebar-tab onClick={() => props.setPage(0)}>Actions</div> 12 + <div app-sidebar-tab onClick={() => props.setPage(1)}>Relays</div> 13 + <div app-sidebar-tab onClick={() => props.setPage(2)}>Debug</div> 14 + 15 + <div app-sidebar-tab app-sidebar-tab-dropped onClick={() => props.setPage(3)}>Settings</div> 16 + </div> 17 + </> 18 + ) 19 + }
+11
src/Structs/OSCMessage.ts
··· 1 + export interface OSCMessage{ 2 + address: string, 3 + values: OSCValue[] 4 + } 5 + 6 + export interface OSCValue{ 7 + Float?: number, 8 + Int?: number, 9 + Boolean?: boolean, 10 + String?: string 11 + }