this repo has no description

Compare changes

Choose any two refs to compare.

+4 -1
.gitignore
··· 1 1 node_modules 2 2 dist 3 3 builds 4 - target 4 + target 5 + 6 + .vscode/ 7 + .idea/
-3
.vscode/extensions.json
··· 1 - { 2 - "recommendations": ["tauri-apps.tauri-vscode", "rust-lang.rust-analyzer"] 3 - }
+2
package.json
··· 13 13 "license": "MIT", 14 14 "dependencies": { 15 15 "@tauri-apps/api": "^2.9.0", 16 + "@tauri-apps/plugin-clipboard-manager": "~2", 16 17 "@tauri-apps/plugin-dialog": "~2.4.2", 17 18 "@tauri-apps/plugin-opener": "^2.5.2", 19 + "@tauri-apps/plugin-os": "~2", 18 20 "animejs": "^4.2.2", 19 21 "solid-js": "^1.9.10" 20 22 },
+20
pnpm-lock.yaml
··· 11 11 '@tauri-apps/api': 12 12 specifier: ^2.9.0 13 13 version: 2.9.0 14 + '@tauri-apps/plugin-clipboard-manager': 15 + specifier: ~2 16 + version: 2.3.2 14 17 '@tauri-apps/plugin-dialog': 15 18 specifier: ~2.4.2 16 19 version: 2.4.2 17 20 '@tauri-apps/plugin-opener': 18 21 specifier: ^2.5.2 19 22 version: 2.5.2 23 + '@tauri-apps/plugin-os': 24 + specifier: ~2 25 + version: 2.3.2 20 26 animejs: 21 27 specifier: ^4.2.2 22 28 version: 4.2.2 ··· 479 485 engines: {node: '>= 10'} 480 486 hasBin: true 481 487 488 + '@tauri-apps/plugin-clipboard-manager@2.3.2': 489 + resolution: {integrity: sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==} 490 + 482 491 '@tauri-apps/plugin-dialog@2.4.2': 483 492 resolution: {integrity: sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==} 484 493 485 494 '@tauri-apps/plugin-opener@2.5.2': 486 495 resolution: {integrity: sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==} 496 + 497 + '@tauri-apps/plugin-os@2.3.2': 498 + resolution: {integrity: sha512-n+nXWeuSeF9wcEsSPmRnBEGrRgOy6jjkSU+UVCOV8YUGKb2erhDOxis7IqRXiRVHhY8XMKks00BJ0OAdkpf6+A==} 487 499 488 500 '@types/animejs@3.1.13': 489 501 resolution: {integrity: sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==} ··· 1065 1077 '@tauri-apps/cli-win32-ia32-msvc': 2.9.3 1066 1078 '@tauri-apps/cli-win32-x64-msvc': 2.9.3 1067 1079 1080 + '@tauri-apps/plugin-clipboard-manager@2.3.2': 1081 + dependencies: 1082 + '@tauri-apps/api': 2.9.0 1083 + 1068 1084 '@tauri-apps/plugin-dialog@2.4.2': 1069 1085 dependencies: 1070 1086 '@tauri-apps/api': 2.9.0 1071 1087 1072 1088 '@tauri-apps/plugin-opener@2.5.2': 1089 + dependencies: 1090 + '@tauri-apps/api': 2.9.0 1091 + 1092 + '@tauri-apps/plugin-os@2.3.2': 1073 1093 dependencies: 1074 1094 '@tauri-apps/api': 2.9.0 1075 1095
+98 -54
src/App.tsx
··· 1 - import { createSignal, onCleanup, onMount } from "solid-js"; 1 + import { createEffect, createSignal, onCleanup, onMount } from "solid-js"; 2 2 import "./App.css"; 3 3 import { renderBackgroundGrid, renderContextMenu, renderNodes, renderNullTab, renderTempDrawing } from "./renderer"; 4 4 import { lerp } from "./utils/lerp"; ··· 10 10 import { ContextMenu } from "./structs/ContextMenu"; 11 11 import { NodeManager } from "./Mangers/NodeManager"; 12 12 import { TabMenu } from "./components/TabMenu"; 13 + import { ConfirmationPopup } from "./components/ConfirmationPopup"; 13 14 14 15 import * as keybinds from './keybinds'; 15 16 import { listen } from "@tauri-apps/api/event"; 16 - import { ConfirmationPopup } from "./components/ConfirmationPopup"; 17 + 18 + // TODO: Only allow one node to input on non-flow inputs 17 19 18 20 let App = () => { 19 - let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([]); 21 + let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([], { equals: false }); 22 + let [ mousePos, setMousePos ] = createSignal<[ number, number ]>([ 0, 0 ]); 20 23 21 24 let canvas!: HTMLCanvasElement; 22 25 let ctx: CanvasRenderingContext2D; ··· 56 59 size: [ 0, 0 ], 57 60 visible: false 58 61 } 62 + 63 + createEffect(() => { 64 + let snodes = selectedNodes(); 65 + 66 + let anodes = NodeManager.Instance.GetNodes(); 67 + if(!anodes)return; 68 + 69 + for(let node of anodes)node.selected = false; 70 + for(let node of snodes)node.selected = true; 71 + }) 59 72 60 73 onMount(async () => { 61 74 NodeManager.Instance.HookTabChange(() => setSelectedNodes([])); ··· 110 123 contextMenu.visible = true; 111 124 } 112 125 126 + let isShiftClick = false; 113 127 canvas.onmousedown = ( e ) => { 128 + isShiftClick = e.shiftKey; 129 + 114 130 if( 115 131 e.clientY < 60 || 116 132 e.clientX < 220 || ··· 156 172 157 173 if(nodes){ 158 174 nodes.map(node => { 159 - if(!e.shiftKey)node.selected = false; 160 - 161 175 if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 162 176 e.clientX, e.clientY, 163 177 node.x - 20, node.y, node.w + 40, node.h ··· 216 230 217 231 movingNode = clickedNode; 218 232 219 - if(!e.shiftKey){ 220 - if(clickedNode){ 221 - clickedNode.selected = true; 222 - setSelectedNodes([ clickedNode ]); 223 - } else{ 224 - setSelectedNodes([]); 225 - } 226 - } else{ 227 - clickedNode.selected = true; 228 - 229 - let snodes = selectedNodes(); 230 - if(!snodes.find(x => x.id === clickedNode!.id)){ 231 - snodes.push(clickedNode); 232 - clickedNode.selected = true; 233 - 234 - setSelectedNodes(snodes); 235 - } 236 - } 237 - 238 233 isMouseDown = true; 239 234 mouseStartPos = [ e.clientX, e.clientY ]; 235 + mouseMovePos = [ e.clientX, e.clientY ]; 240 236 } 241 237 242 238 canvas.onmousemove = ( e ) => { 239 + setMousePos([ e.clientX, e.clientY ]); 240 + 243 241 if(e.shiftKey && isMouseDown){ 244 242 let nodes = NodeManager.Instance.GetNodes(); 245 243 let hoveredNode: Node | null = null; ··· 262 260 snodes.push(hoveredNode); 263 261 264 262 // @ts-ignore 265 - hoveredNode.selected = true; 263 + hoveredNode.x = Math.round(hoveredNode.x / 10) * 10; 264 + // @ts-ignore 265 + hoveredNode.y = Math.round(hoveredNode.y / 10) * 10; 266 + 266 267 setSelectedNodes(snodes); 267 268 } 268 269 } 269 270 270 271 return; 271 - } 272 + } else if(isShiftClick)return; 272 273 273 274 if(isMouseDown){ 274 275 if(isDrawing){ 275 276 drawingTo = screenToWorldSpace(canvas, { x: offset[0], y: offset[1], scale }, e.clientX - 10 * scale, e.clientY - 10 * scale) as [ number, number ]; 276 277 } else if(movingNode){ 277 - movingNode.x = movingNode.x - (mouseStartPos[0] - e.clientX) / scale; 278 - movingNode.y = movingNode.y - (mouseStartPos[1] - e.clientY) / scale; 278 + let nodes = selectedNodes(); 279 279 280 - mouseStartPos = [ e.clientX, e.clientY ]; 280 + for(let node of nodes){ 281 + node.x = node.x - (mouseMovePos[0] - e.clientX) / scale; 282 + node.y = node.y - (mouseMovePos[1] - e.clientY) / scale; 283 + } 284 + 285 + mouseMovePos = [ e.clientX, e.clientY ]; 281 286 NodeManager.Instance.UpdateConfig(); 282 287 } else{ 283 - offsetTarget = [ offsetTarget[0] - (mouseStartPos[0] - e.clientX) / scale, offsetTarget[1] - (mouseStartPos[1] - e.clientY) / scale ]; 284 - mouseStartPos = [ e.clientX, e.clientY ]; 288 + offsetTarget = [ offsetTarget[0] - (mouseMovePos[0] - e.clientX) / scale, offsetTarget[1] - (mouseMovePos[1] - e.clientY) / scale ]; 289 + mouseMovePos = [ e.clientX, e.clientY ]; 285 290 286 291 screenMoved = true; 287 292 } ··· 325 330 326 331 canvas.onmouseup = ( e ) => { 327 332 let nodes = NodeManager.Instance.GetNodes(); 333 + let clickedNode; 328 334 329 335 if(nodes){ 330 336 nodes.map(node => { 331 - node.inputs.map(( input, i ) => { 332 - if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 333 - e.clientX, e.clientY, 334 - node.x - 10, 335 - node.y + 50 + (30 * i), 336 - 20, 20 337 - )){ 338 - if(isDrawing){ 339 - let fromType = NodeIOResolveAnyTypes(drawingFrom!); 340 - let toType = NodeIOResolveAnyTypes(input); 337 + if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 338 + e.clientX, e.clientY, 339 + node.x - 20, node.y, node.w + 40, node.h 340 + )){ 341 + clickedNode = node; 341 342 342 - if( 343 - drawingFrom!.connections.indexOf(input) === -1 && 344 - ( 345 - toType === null || 346 - NodeIOCanCast(fromType, toType) 347 - ) 348 - ){ 349 - drawingFrom!.connections.push(input); 350 - input.connections.push(drawingFrom!); 343 + node.inputs.map(( input, i ) => { 344 + if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale }, 345 + e.clientX, e.clientY, 346 + node.x - 10, 347 + node.y + 50 + (30 * i), 348 + 20, 20 349 + )){ 350 + if(isDrawing){ 351 + let fromType = NodeIOResolveAnyTypes(drawingFrom!); 352 + let toType = NodeIOResolveAnyTypes(input); 351 353 352 - NodeManager.Instance.UpdateConfig(); 354 + if( 355 + drawingFrom!.connections.indexOf(input) === -1 && 356 + ( 357 + toType === null || 358 + NodeIOCanCast(fromType, toType) 359 + ) 360 + ){ 361 + drawingFrom!.connections.push(input); 362 + input.connections.push(drawingFrom!); 363 + 364 + NodeManager.Instance.UpdateConfig(); 365 + } 353 366 } 354 367 } 355 - } 356 - }) 368 + }) 369 + } 357 370 }) 358 371 } 359 372 373 + let diffX = mouseStartPos[0] - e.clientX; 374 + let diffY = mouseStartPos[1] - e.clientY; 375 + 376 + let dist = Math.sqrt(diffX * diffX + diffY * diffY); 377 + 378 + if(dist < 10){ 379 + if(clickedNode){ 380 + if(e.shiftKey){ 381 + let snodes = selectedNodes(); 382 + if(snodes.indexOf(clickedNode) === -1)snodes.push(clickedNode); 383 + 384 + // @ts-ignore 385 + clickedNode.x = Math.round(clickedNode.x / 10) * 10; 386 + // @ts-ignore 387 + clickedNode.y = Math.round(clickedNode.y / 10) * 10; 388 + 389 + setSelectedNodes(snodes); 390 + } else{ 391 + // @ts-ignore 392 + clickedNode.x = Math.round(clickedNode.x / 10) * 10; 393 + // @ts-ignore 394 + clickedNode.y = Math.round(clickedNode.y / 10) * 10; 395 + 396 + setSelectedNodes([ clickedNode ]); 397 + } 398 + } else { 399 + setSelectedNodes([]); 400 + } 401 + } 402 + 360 403 isDrawing = false; 361 404 isMouseDown = false; 362 405 } 363 406 364 - keybinds.load(selectedNodes, setSelectedNodes); 407 + keybinds.load(canvas, mousePos, selectedNodes, setSelectedNodes); 365 408 requestAnimationFrame(update); 366 409 367 410 let unlisten_0 = await listen('hide-window', () => { ··· 407 450 408 451 let isMouseDown = false; 409 452 let mouseStartPos = [ 0, 0 ]; 453 + let mouseMovePos = [ 0, 0 ]; 410 454 411 455 let interval = setInterval(() => { 412 456 if(screenMoved){
+35 -2
src/Mangers/NodeManager.tsx
··· 7 7 import { NodesByID } from "../Nodes/Nodes"; 8 8 import { save } from "@tauri-apps/plugin-dialog"; 9 9 import { ConfirmationManager } from "./ConfirmationManager"; 10 + import { platform } from "@tauri-apps/plugin-os"; 10 11 11 12 export interface TabHashMap { 12 13 [details: string] : Tab; ··· 26 27 setInterval(() => { 27 28 let tabs = Object.values(this._tabs).filter(x => x.needSync); 28 29 for(let tab of tabs){ 29 - invoke('sync_tab', { graph: this._generateTabGraph(tab.id)[0], id: tab.id, name: tab.name, location: tab.saveLocation }); 30 + invoke('sync_tab', { 31 + graph: this._generateTabGraph(tab.id)[0], 32 + id: tab.id, 33 + name: tab.name, 34 + location: tab.saveLocation, 35 + saveState: tab.needsSave() 36 + }); 37 + 30 38 tab.needSync = false; 31 39 } 32 40 }, 1000); ··· 39 47 let version = await getVersion(); 40 48 41 49 for(let tab of Object.entries<any>(tabs)){ 42 - await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({ 50 + let loaded_tab = await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({ 43 51 tab_name: tab[1][1], 44 52 version, 45 53 graph: tab[1][0] 46 54 })); 55 + 56 + if(loaded_tab) 57 + loaded_tab.setNeedsSave(tab[1][3]); 47 58 }; 59 + 60 + this.UpdateConfig(); 48 61 }); 49 62 50 63 listen('prompt_to_close', async _ => { ··· 333 346 334 347 tab.needSync = false; 335 348 if(!id)this.UpdateConfig(false); 349 + 350 + return tab; 336 351 } 337 352 338 353 private _generateTabGraph( tabId: string | null ): [ any, Tab | null ]{ ··· 348 363 let node = tab.nodes[i]; 349 364 350 365 let nodeOutputs = []; 366 + let nodeInputs = []; 351 367 352 368 for (let j = 0; j < node.outputs.length; j++) { 353 369 let output = node.outputs[j]; ··· 364 380 }) 365 381 } 366 382 383 + for (let j = 0; j < node.inputs.length; j++) { 384 + let input = node.inputs[j]; 385 + 386 + nodeInputs.push({ 387 + name: input.name, 388 + type: input.type, 389 + connections: input.connections.map(x => { return { 390 + name: x.name, 391 + node: x.parent.id, 392 + index: x.index, 393 + type: x.type 394 + }}) 395 + }) 396 + } 397 + 367 398 nodesToSave.push({ 368 399 name: node.name, 369 400 id: node.id, 370 401 typeId: node.typeId, 371 402 pos: [ node.x, node.y ], 372 403 outputs: nodeOutputs, 404 + inputs: nodeInputs, 373 405 statics: node.statics 374 406 }) 375 407 } ··· 384 416 invoke('save_graph', { graph: JSON.stringify({ 385 417 tab_name: tab.name, 386 418 version: await getVersion(), 419 + platform: platform(), 387 420 graph: nodesToSave 388 421 }), path }); 389 422 }
+2 -1
src/Nodes/Conditional/IfEqual.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeConditionalIfEqual: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'If Equal', 7 9 typeId: 'ifequal', 8 10 9 11 w: 220, 10 - h: 150, 11 12 12 13 statics: [{ 13 14 type: NodeType.Label,
+2 -1
src/Nodes/Conditional/IfFalse.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeConditionalIfFalse: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'If False', 7 9 typeId: 'iffalse', 8 10 9 11 w: 220, 10 - h: 150, 11 12 12 13 statics: [{ 13 14 type: NodeType.Label,
+2 -1
src/Nodes/Conditional/IfTrue.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeConditionalIfTrue: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'If True', 7 9 typeId: 'iftrue', 8 10 9 11 w: 220, 10 - h: 150, 11 12 12 13 statics: [{ 13 14 type: NodeType.Label,
+2
src/Nodes/Conditional.tsx
··· 5 5 import { NodeConditionalIfFalse } from "./Conditional/IfFalse"; 6 6 7 7 export let NodeConditional: NodeDefinition = { 8 + os: 'any', 9 + 8 10 isSingle: false, 9 11 name: 'Conditional', 10 12 items: [
+2 -1
src/Nodes/Debug.tsx
··· 2 2 import { NodeDefinition } from "./Nodes"; 3 3 4 4 export let NodeDebug: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'Debug', 7 9 typeId: 'debug', 8 10 9 11 w: 200, 10 - h: 110, 11 12 12 13 statics: [ 13 14 {
+24 -3
src/Nodes/Nodes.tsx
··· 1 1 import { Node, NodeStatic, NodeType } from "../structs/node"; 2 + import { platform } from '@tauri-apps/plugin-os'; 2 3 3 4 import { NodeConditional } from "./Conditional"; 4 5 import { NodeDebug } from "./Debug"; 5 6 import { NodeOSCActions } from "./OSCActions"; 6 7 import { NodeOSCTrigger } from "./OSCTrigger"; 8 + import { NodePressKey } from "./PressKey"; 7 9 import { NodeStatics } from "./Statics"; 10 + import { NodeShellCommand } from "./Shell"; 8 11 9 12 export interface NodeDefinition{ 13 + os: string, 10 14 isSingle: boolean, 11 15 name: string, 12 16 typeId?: string, 13 17 onStaticsUpdate?: ( node: Node ) => Promise<void>, 14 18 // build?: ( pos: [ number, number ], onStaticsUpdate: ( node: Node ) => void ) => Promise<Node>, 15 19 w?: number, 16 - h?: number, 17 20 statics?: NodeStatic[], 18 21 inputs?: { name: string, type: NodeType }[], 19 22 outputs?: { name: string, type: NodeType }[], ··· 25 28 [details: string] : NodeDefinition; 26 29 } 27 30 28 - export let Nodes: NodeDefinition[] = [ 31 + // TODO: (Node Additions) Pressing keyboard keys (like to do linux, but has extra steps) 32 + // TODO: (Node Additions) Getting media state from os 33 + // TODO: (Node Additions) Sending custom OSC messages 34 + // TODO: (Node Additions) Sending HTTP requests? 35 + // TODO: (Node Additions) Voicemeeter integrations (win only) 36 + // TODO: (Node Additions) Voicemod integrations (win only) 37 + // TODO: (Node Additions) Executing shell commands? (probably need some kinda popup warning when these are imported about dangerous usage) 38 + 39 + export let Nodes: NodeDefinition[] = []; 40 + let nodes = [ 29 41 NodeOSCTrigger, 30 42 NodeConditional, 31 43 NodeStatics, 32 44 NodeOSCActions, 33 - NodeDebug 45 + NodeDebug, 46 + NodePressKey, 47 + NodeShellCommand 34 48 ] 49 + 50 + let os = platform(); 51 + 52 + for (let i = 0; i < nodes.length; i++) { 53 + let node = nodes[i]; 54 + if(node.os === 'any' || node.os === os)Nodes.push(node); 55 + } 35 56 36 57 export let NodesByID: NodeDefinitionHashMap = {} 37 58
+2 -1
src/Nodes/OSCActions/Send Chatbox.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeOSCActionsSendChatbox: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'Send Chatbox', 7 9 typeId: 'oscsendchatbox', 8 10 9 11 w: 200, 10 - h: 120, 11 12 12 13 statics: [{ 13 14 type: NodeType.Label,
+2
src/Nodes/OSCActions.tsx
··· 2 2 import { NodeOSCActionsSendChatbox } from "./OSCActions/Send Chatbox"; 3 3 4 4 export let NodeOSCActions: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: false, 6 8 name: 'OSC Actions', 7 9 items: [
+27 -10
src/Nodes/OSCTrigger.tsx
··· 5 5 import { NodeDefinition } from "./Nodes"; 6 6 7 7 export let NodeOSCTrigger: NodeDefinition = { 8 + os: 'any', 9 + 8 10 isSingle: true, 9 11 name: 'OSC Trigger', 10 12 typeId: 'osctrigger', 11 13 12 14 w: 200, 13 - h: 55, 14 15 15 16 statics: [ 16 17 { ··· 39 40 } 40 41 } 41 42 42 - node.outputs.map(output => { 43 - output.connections.map(partner => { 44 - partner.connections = partner.connections.filter(x => x != output); 45 - }) 46 - }) 47 - node.outputs = []; 43 + let tempOutputs = []; 48 44 49 - node.outputs.push({ 45 + tempOutputs.push({ 50 46 name: 'Flow', 51 47 type: NodeType.Flow, 52 48 connections: [], ··· 73 69 } 74 70 75 71 if(type){ 76 - node.outputs.push({ 72 + tempOutputs.push({ 77 73 name: dat.desc === '' ? dat.type : dat.desc, 78 74 type: type, 79 75 connections: [], ··· 83 79 } 84 80 }); 85 81 86 - node.h = 65 + (parameters.length + 1) * 30; 82 + let hasChanged = false; 83 + 84 + for(let i in tempOutputs){ 85 + if( 86 + node.outputs[i] === undefined || 87 + tempOutputs[i].type != node.outputs[i].type 88 + ){ 89 + hasChanged = true; 90 + } 91 + } 92 + 93 + if(hasChanged){ 94 + node.outputs.map(output => { 95 + output.connections.map(partner => { 96 + partner.connections = partner.connections.filter(x => x != output); 97 + }) 98 + }) 99 + 100 + node.outputs = tempOutputs; 101 + node.updateSize(); 102 + } 103 + 87 104 NodeManager.Instance.UpdateConfig(); 88 105 } 89 106 };
+29
src/Nodes/PressKey.tsx
··· 1 + import { Node, NodeType } from "../structs/node"; 2 + import { NodeDefinition } from "./Nodes"; 3 + 4 + export let NodePressKey: NodeDefinition = { 5 + os: 'windows', 6 + 7 + isSingle: true, 8 + name: 'Press Key', 9 + typeId: 'presskey', 10 + 11 + w: 200, 12 + 13 + statics: [ 14 + { 15 + name: "Key", 16 + type: NodeType.String, 17 + value: "" 18 + } 19 + ], 20 + 21 + inputs: [ 22 + { 23 + name: "Flow", 24 + type: NodeType.Flow, 25 + } 26 + ], 27 + 28 + onStaticsUpdate: async ( _node: Node ) => {} 29 + };
+38
src/Nodes/Shell.tsx
··· 1 + import { Node, NodeType } from "../structs/node"; 2 + import { NodeDefinition } from "./Nodes"; 3 + 4 + export let NodeShellCommand: NodeDefinition = { 5 + os: 'any', 6 + 7 + isSingle: true, 8 + name: 'Shell Command', 9 + typeId: 'shellcommand', 10 + 11 + w: 200, 12 + 13 + statics: [], 14 + 15 + inputs: [ 16 + { 17 + name: "Flow", 18 + type: NodeType.Flow, 19 + }, 20 + { 21 + name: "Command", 22 + type: NodeType.String, 23 + }, 24 + ], 25 + 26 + outputs: [ 27 + { 28 + name: "Flow", 29 + type: NodeType.Flow, 30 + }, 31 + { 32 + name: "Output", 33 + type: NodeType.String, 34 + }, 35 + ], 36 + 37 + onStaticsUpdate: async ( _node: Node ) => {} 38 + };
+2 -1
src/Nodes/Statics/Float.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeStaticsFloat: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'Float', 7 9 typeId: 'staticfloat', 8 10 9 11 w: 200, 10 - h: 85, 11 12 12 13 statics: [{ 13 14 type: NodeType.Float,
+2 -1
src/Nodes/Statics/Int.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeStaticsInt: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'Int', 7 9 typeId: 'staticint', 8 10 9 11 w: 200, 10 - h: 85, 11 12 12 13 statics: [{ 13 14 type: NodeType.Int,
+2 -1
src/Nodes/Statics/String.tsx
··· 2 2 import { NodeDefinition } from "../Nodes"; 3 3 4 4 export let NodeStaticsString: NodeDefinition = { 5 + os: 'any', 6 + 5 7 isSingle: true, 6 8 name: 'String', 7 9 typeId: 'staticstring', 8 10 9 11 w: 200, 10 - h: 85, 11 12 12 13 statics: [{ 13 14 type: NodeType.String,
+2
src/Nodes/Statics.tsx
··· 5 5 import { NodeStaticsString } from "./Statics/String"; 6 6 7 7 export let NodeStatics: NodeDefinition = { 8 + os: 'any', 9 + 8 10 isSingle: false, 9 11 name: 'Statics', 10 12 items: [
+1 -5
src/components/ControlBar.tsx
··· 1 1 import './ControlBar.css'; 2 2 3 - import { Accessor, createEffect, createSignal, For, Match, Show, Switch } from 'solid-js'; 3 + import { Accessor, createSignal, For, Match, Show, Switch } from 'solid-js'; 4 4 import { Node, NodeType } from '../structs/node'; 5 5 import { TextInput } from './TextInput'; 6 6 import { invoke } from '@tauri-apps/api/core'; ··· 14 14 } 15 15 16 16 export let ControlBar = ( props: ControlBarProps ) => { 17 - createEffect(() => { 18 - console.log(props.node()); 19 - }) 20 - 21 17 return ( 22 18 <div class="control-bar"> 23 19 <For each={props.node()[0]?.statics}>
+8 -8
src/components/SettingsMenu.css
··· 1 1 .settings-menu{ 2 - position: fixed; 3 - z-index: 110; 4 - top: 0; 5 - left: 0; 6 - width: 100vw; 7 - height: 100vh; 8 - background: rgba(0, 0, 0, 0.75); 9 - } 2 + position: fixed; 3 + z-index: 110; 4 + top: 0; 5 + left: 0; 6 + width: 100vw; 7 + height: 100vh; 8 + background: rgba(0, 0, 0, 0.75); 9 + } 10 10 11 11 .settings-menu-inner{ 12 12 position: fixed;
+3
src/components/SettingsMenu.tsx
··· 6 6 } 7 7 8 8 export let SettingsMenu = ( props: SettingsMenuProps ) => { 9 + // TODO: Changable OSC Ports 10 + // TODO: Changable keybinds 11 + 9 12 return ( 10 13 <> 11 14 <div class="settings-menu">
+40 -7
src/keybinds.ts
··· 1 1 import { Accessor, Setter } from "solid-js"; 2 2 import { NodeManager } from "./Mangers/NodeManager"; 3 3 import { Node } from "./structs/node"; 4 + import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager"; 5 + import { decodeNodeList, encodeNodeList } from "./utils/clipboard"; 4 6 5 7 let isKeyDown: any = {}; 6 8 7 - export let load = ( selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => { 8 - // TODO: Copy / paste 9 + export let load = ( canvas: HTMLCanvasElement, mousePos: Accessor<[ number, number ]>, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => { 9 10 // TODO: Add undo / redo -ing 10 11 11 - window.onkeydown = ( e ) => { 12 - isKeyDown[e.key] = true; 13 - 14 - console.log(e.key); 15 - 12 + canvas.onkeydown = async ( e ) => { 16 13 switch(e.key){ 17 14 case 'Delete': 18 15 let nodes = selectedNode(); ··· 34 31 35 32 setSelectedNode([]); 36 33 break; 34 + } 35 + } 36 + 37 + window.onkeydown = async ( e ) => { 38 + isKeyDown[e.key] = true; 39 + 40 + switch(e.key){ 37 41 case 's': 38 42 if(e.ctrlKey){ 39 43 let currentTab = NodeManager.Instance.CurrentTab(); ··· 50 54 51 55 // Save 52 56 NodeManager.Instance.SaveTab(currentTab, true); 57 + } 58 + break; 59 + case 'c': 60 + if(e.ctrlKey){ 61 + let nodes = selectedNode(); 62 + await writeText(encodeNodeList(nodes, mousePos())); 63 + } 64 + break; 65 + case 'v': 66 + if(e.ctrlKey){ 67 + let text = await readText(); 68 + 69 + let nodes = await decodeNodeList(text, mousePos()); 70 + if(!nodes)return; 71 + 72 + for(let node of nodes) 73 + NodeManager.Instance.AddNode(node); 74 + 75 + setSelectedNode(nodes); 76 + } 77 + break; 78 + case 'z': 79 + if(e.ctrlKey){ 80 + console.log('undo'); 81 + } 82 + break; 83 + case 'y': 84 + if(e.ctrlKey){ 85 + console.log('redo'); 53 86 } 54 87 break; 55 88 }
+7 -2
src/structs/node.ts
··· 20 20 this.typeId = node.typeId!; 21 21 this.x = pos[0]; 22 22 this.y = pos[1]; 23 - this.w = node.w!; 24 - this.h = node.h!; 25 23 26 24 this.inputs = node.inputs ? node.inputs.map(( x, indx ) => { 27 25 return { ··· 43 41 } 44 42 }) : []; 45 43 44 + this.w = node.w || 200; 45 + this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30; 46 + 46 47 this.selected = false; 47 48 this.statics = node.statics!, 48 49 this.onStaticsUpdate = node.onStaticsUpdate!; 50 + } 51 + 52 + updateSize(){ 53 + this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30; 49 54 } 50 55 } 51 56
+80
src/utils/clipboard.ts
··· 1 + import { NodeManager } from "../Mangers/NodeManager"; 2 + import { NodesByID } from "../Nodes/Nodes"; 3 + import { Node } from "../structs/node"; 4 + 5 + export let encodeNodeList = ( selectedNodes: Node[], mousePos: [ number, number ] ): string => { 6 + let arr: any[] = []; 7 + 8 + for(let node of selectedNodes){ 9 + arr.push({ 10 + id: node.id, 11 + type_id: node.typeId, 12 + statics: node.statics, 13 + x: node.x - mousePos[0], 14 + y: node.y - mousePos[1], 15 + outputs: node.outputs.map(x => { 16 + return x.connections.map(x => { 17 + return { node: x.parent.id, index: x.index } }) }) 18 + }) 19 + } 20 + 21 + for(let node of arr){ 22 + for(let output of node.outputs){ 23 + for(let i in output){ 24 + let indx = arr.findIndex(x => x.id === output[i].node); 25 + if(indx === -1) 26 + delete output[i]; 27 + else 28 + output[i].node = indx; 29 + } 30 + } 31 + } 32 + 33 + for(let node of arr)delete node.id; 34 + 35 + console.log(arr); 36 + return 'VRCMACRO' + btoa(JSON.stringify(arr)); 37 + } 38 + 39 + export let decodeNodeList = async ( text: string, mousePos: [ number, number ] ): Promise<Node[] | null> => { 40 + if(!text.startsWith("VRCMACRO"))return null; 41 + 42 + let data = text.slice(8); 43 + let json = JSON.parse(atob(data)); 44 + 45 + let nodes: Node[] = []; 46 + for(let node of json){ 47 + let n = new Node( 48 + [ node.x + mousePos[0] + 10, node.y + mousePos[1] + 10 ], 49 + NodesByID[node.type_id], 50 + await NodeManager.Instance.GetNewNodeId() 51 + ); 52 + 53 + n.statics = node.statics; 54 + await n.onStaticsUpdate(n); 55 + 56 + nodes.push(n); 57 + } 58 + 59 + for(let i in nodes){ 60 + let outputs: { node: number, index: number }[][] = json[i].outputs; 61 + let node = nodes[i]; 62 + 63 + for(let j in outputs){ 64 + let output = node.outputs[j]; 65 + 66 + for(let k in outputs[j]){ 67 + let connection = outputs[j][k]; 68 + if(!connection)continue; 69 + 70 + let peerNode = nodes[connection.node]; 71 + let input = peerNode.inputs[connection.index]; 72 + 73 + output.connections.push(input); 74 + input.connections.push(output); 75 + } 76 + } 77 + } 78 + 79 + return nodes; 80 + }
+435 -10
src-tauri/Cargo.lock
··· 7 7 version = "0.1.0" 8 8 dependencies = [ 9 9 "anyhow", 10 + "chrono", 10 11 "crossbeam-channel", 11 12 "dirs", 13 + "enigo", 12 14 "flate2", 13 15 "serde", 14 16 "serde_json", 15 17 "tauri", 16 18 "tauri-build", 19 + "tauri-plugin-clipboard-manager", 17 20 "tauri-plugin-dialog", 18 21 "tauri-plugin-opener", 22 + "tauri-plugin-os", 19 23 "tokio", 20 24 ] 21 25 ··· 65 69 checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" 66 70 67 71 [[package]] 72 + name = "arboard" 73 + version = "3.6.1" 74 + source = "registry+https://github.com/rust-lang/crates.io-index" 75 + checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" 76 + dependencies = [ 77 + "clipboard-win", 78 + "image", 79 + "log", 80 + "objc2 0.6.3", 81 + "objc2-app-kit", 82 + "objc2-core-foundation", 83 + "objc2-core-graphics", 84 + "objc2-foundation 0.3.2", 85 + "parking_lot", 86 + "percent-encoding", 87 + "windows-sys 0.60.2", 88 + "wl-clipboard-rs", 89 + "x11rb", 90 + ] 91 + 92 + [[package]] 68 93 name = "ashpd" 69 94 version = "0.11.0" 70 95 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 136 161 "futures-lite", 137 162 "parking", 138 163 "polling", 139 - "rustix", 164 + "rustix 1.1.2", 140 165 "slab", 141 166 "windows-sys 0.61.2", 142 167 ] ··· 167 192 "cfg-if", 168 193 "event-listener", 169 194 "futures-lite", 170 - "rustix", 195 + "rustix 1.1.2", 171 196 ] 172 197 173 198 [[package]] ··· 193 218 "cfg-if", 194 219 "futures-core", 195 220 "futures-io", 196 - "rustix", 221 + "rustix 1.1.2", 197 222 "signal-hook-registry", 198 223 "slab", 199 224 "windows-sys 0.61.2", ··· 495 520 checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" 496 521 dependencies = [ 497 522 "iana-time-zone", 523 + "js-sys", 498 524 "num-traits", 499 525 "serde", 526 + "wasm-bindgen", 500 527 "windows-link 0.2.1", 528 + ] 529 + 530 + [[package]] 531 + name = "clipboard-win" 532 + version = "5.4.1" 533 + source = "registry+https://github.com/rust-lang/crates.io-index" 534 + checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" 535 + dependencies = [ 536 + "error-code", 501 537 ] 502 538 503 539 [[package]] ··· 565 601 ] 566 602 567 603 [[package]] 604 + name = "core-graphics" 605 + version = "0.25.0" 606 + source = "registry+https://github.com/rust-lang/crates.io-index" 607 + checksum = "064badf302c3194842cf2c5d61f56cc88e54a759313879cdf03abdd27d0c3b97" 608 + dependencies = [ 609 + "bitflags 2.10.0", 610 + "core-foundation", 611 + "core-graphics-types", 612 + "foreign-types", 613 + "libc", 614 + ] 615 + 616 + [[package]] 568 617 name = "core-graphics-types" 569 618 version = "0.2.0" 570 619 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 609 658 checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" 610 659 611 660 [[package]] 661 + name = "crunchy" 662 + version = "0.2.4" 663 + source = "registry+https://github.com/rust-lang/crates.io-index" 664 + checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5" 665 + 666 + [[package]] 612 667 name = "crypto-common" 613 668 version = "0.1.6" 614 669 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 874 929 checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf" 875 930 876 931 [[package]] 932 + name = "enigo" 933 + version = "0.6.1" 934 + source = "registry+https://github.com/rust-lang/crates.io-index" 935 + checksum = "71c6c56e50f7acae2906a0dcbb34529ca647e40421119ad5d12e7f8ba6e50010" 936 + dependencies = [ 937 + "core-foundation", 938 + "core-graphics 0.25.0", 939 + "foreign-types-shared", 940 + "libc", 941 + "log", 942 + "nom 8.0.0", 943 + "objc2 0.6.3", 944 + "objc2-app-kit", 945 + "objc2-foundation 0.3.2", 946 + "windows", 947 + "x11rb", 948 + "xkbcommon", 949 + "xkeysym", 950 + ] 951 + 952 + [[package]] 877 953 name = "enumflags2" 878 954 version = "0.7.12" 879 955 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 922 998 ] 923 999 924 1000 [[package]] 1001 + name = "error-code" 1002 + version = "3.3.2" 1003 + source = "registry+https://github.com/rust-lang/crates.io-index" 1004 + checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" 1005 + 1006 + [[package]] 925 1007 name = "event-listener" 926 1008 version = "5.4.1" 927 1009 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 949 1031 checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" 950 1032 951 1033 [[package]] 1034 + name = "fax" 1035 + version = "0.2.6" 1036 + source = "registry+https://github.com/rust-lang/crates.io-index" 1037 + checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" 1038 + dependencies = [ 1039 + "fax_derive", 1040 + ] 1041 + 1042 + [[package]] 1043 + name = "fax_derive" 1044 + version = "0.2.0" 1045 + source = "registry+https://github.com/rust-lang/crates.io-index" 1046 + checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" 1047 + dependencies = [ 1048 + "proc-macro2", 1049 + "quote", 1050 + "syn 2.0.109", 1051 + ] 1052 + 1053 + [[package]] 952 1054 name = "fdeflate" 953 1055 version = "0.3.7" 954 1056 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 974 1076 checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127" 975 1077 976 1078 [[package]] 1079 + name = "fixedbitset" 1080 + version = "0.5.7" 1081 + source = "registry+https://github.com/rust-lang/crates.io-index" 1082 + checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" 1083 + 1084 + [[package]] 977 1085 name = "flate2" 978 1086 version = "1.1.5" 979 1087 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 988 1096 version = "1.0.7" 989 1097 source = "registry+https://github.com/rust-lang/crates.io-index" 990 1098 checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" 1099 + 1100 + [[package]] 1101 + name = "foldhash" 1102 + version = "0.1.5" 1103 + source = "registry+https://github.com/rust-lang/crates.io-index" 1104 + checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" 991 1105 992 1106 [[package]] 993 1107 name = "foreign-types" ··· 1239 1353 ] 1240 1354 1241 1355 [[package]] 1356 + name = "gethostname" 1357 + version = "1.1.0" 1358 + source = "registry+https://github.com/rust-lang/crates.io-index" 1359 + checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" 1360 + dependencies = [ 1361 + "rustix 1.1.2", 1362 + "windows-link 0.2.1", 1363 + ] 1364 + 1365 + [[package]] 1242 1366 name = "getrandom" 1243 1367 version = "0.1.16" 1244 1368 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 1421 1545 ] 1422 1546 1423 1547 [[package]] 1548 + name = "half" 1549 + version = "2.7.1" 1550 + source = "registry+https://github.com/rust-lang/crates.io-index" 1551 + checksum = "6ea2d84b969582b4b1864a92dc5d27cd2b77b622a8d79306834f1be5ba20d84b" 1552 + dependencies = [ 1553 + "cfg-if", 1554 + "crunchy", 1555 + "zerocopy", 1556 + ] 1557 + 1558 + [[package]] 1424 1559 name = "hashbrown" 1425 1560 version = "0.12.3" 1426 1561 source = "registry+https://github.com/rust-lang/crates.io-index" 1427 1562 checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" 1563 + 1564 + [[package]] 1565 + name = "hashbrown" 1566 + version = "0.15.5" 1567 + source = "registry+https://github.com/rust-lang/crates.io-index" 1568 + checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" 1569 + dependencies = [ 1570 + "foldhash", 1571 + ] 1428 1572 1429 1573 [[package]] 1430 1574 name = "hashbrown" ··· 1706 1850 "moxcms", 1707 1851 "num-traits", 1708 1852 "png 0.18.0", 1853 + "tiff", 1709 1854 ] 1710 1855 1711 1856 [[package]] ··· 1949 2094 1950 2095 [[package]] 1951 2096 name = "linux-raw-sys" 2097 + version = "0.4.15" 2098 + source = "registry+https://github.com/rust-lang/crates.io-index" 2099 + checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab" 2100 + 2101 + [[package]] 2102 + name = "linux-raw-sys" 1952 2103 version = "0.11.0" 1953 2104 source = "registry+https://github.com/rust-lang/crates.io-index" 1954 2105 checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" ··· 2018 2169 checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" 2019 2170 2020 2171 [[package]] 2172 + name = "memmap2" 2173 + version = "0.9.9" 2174 + source = "registry+https://github.com/rust-lang/crates.io-index" 2175 + checksum = "744133e4a0e0a658e1374cf3bf8e415c4052a15a111acd372764c55b4177d490" 2176 + dependencies = [ 2177 + "libc", 2178 + ] 2179 + 2180 + [[package]] 2021 2181 name = "memoffset" 2022 2182 version = "0.9.1" 2023 2183 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2033 2193 checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" 2034 2194 2035 2195 [[package]] 2196 + name = "minimal-lexical" 2197 + version = "0.2.1" 2198 + source = "registry+https://github.com/rust-lang/crates.io-index" 2199 + checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a" 2200 + 2201 + [[package]] 2036 2202 name = "miniz_oxide" 2037 2203 version = "0.8.9" 2038 2204 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2138 2304 version = "0.1.14" 2139 2305 source = "registry+https://github.com/rust-lang/crates.io-index" 2140 2306 checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb" 2307 + 2308 + [[package]] 2309 + name = "nom" 2310 + version = "7.1.3" 2311 + source = "registry+https://github.com/rust-lang/crates.io-index" 2312 + checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a" 2313 + dependencies = [ 2314 + "memchr", 2315 + "minimal-lexical", 2316 + ] 2317 + 2318 + [[package]] 2319 + name = "nom" 2320 + version = "8.0.0" 2321 + source = "registry+https://github.com/rust-lang/crates.io-index" 2322 + checksum = "df9761775871bdef83bee530e60050f7e54b1105350d6884eb0fb4f46c2f9405" 2323 + dependencies = [ 2324 + "memchr", 2325 + ] 2141 2326 2142 2327 [[package]] 2143 2328 name = "num-conv" ··· 2280 2465 ] 2281 2466 2282 2467 [[package]] 2468 + name = "objc2-core-location" 2469 + version = "0.3.2" 2470 + source = "registry+https://github.com/rust-lang/crates.io-index" 2471 + checksum = "ca347214e24bc973fc025fd0d36ebb179ff30536ed1f80252706db19ee452009" 2472 + dependencies = [ 2473 + "objc2 0.6.3", 2474 + "objc2-foundation 0.3.2", 2475 + ] 2476 + 2477 + [[package]] 2283 2478 name = "objc2-core-text" 2284 2479 version = "0.3.2" 2285 2480 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2398 2593 dependencies = [ 2399 2594 "bitflags 2.10.0", 2400 2595 "objc2 0.6.3", 2596 + "objc2-core-foundation", 2401 2597 "objc2-foundation 0.3.2", 2402 2598 ] 2403 2599 ··· 2419 2615 checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22" 2420 2616 dependencies = [ 2421 2617 "bitflags 2.10.0", 2618 + "block2 0.6.2", 2422 2619 "objc2 0.6.3", 2620 + "objc2-cloud-kit", 2621 + "objc2-core-data", 2423 2622 "objc2-core-foundation", 2623 + "objc2-core-graphics", 2624 + "objc2-core-image", 2625 + "objc2-core-location", 2626 + "objc2-core-text", 2627 + "objc2-foundation 0.3.2", 2628 + "objc2-quartz-core 0.3.2", 2629 + "objc2-user-notifications", 2630 + ] 2631 + 2632 + [[package]] 2633 + name = "objc2-user-notifications" 2634 + version = "0.3.2" 2635 + source = "registry+https://github.com/rust-lang/crates.io-index" 2636 + checksum = "9df9128cbbfef73cda168416ccf7f837b62737d748333bfe9ab71c245d76613e" 2637 + dependencies = [ 2638 + "objc2 0.6.3", 2424 2639 "objc2-foundation 0.3.2", 2425 2640 ] 2426 2641 ··· 2475 2690 ] 2476 2691 2477 2692 [[package]] 2693 + name = "os_info" 2694 + version = "3.13.0" 2695 + source = "registry+https://github.com/rust-lang/crates.io-index" 2696 + checksum = "7c39b5918402d564846d5aba164c09a66cc88d232179dfd3e3c619a25a268392" 2697 + dependencies = [ 2698 + "android_system_properties", 2699 + "log", 2700 + "nix", 2701 + "objc2 0.6.3", 2702 + "objc2-foundation 0.3.2", 2703 + "objc2-ui-kit", 2704 + "serde", 2705 + "windows-sys 0.61.2", 2706 + ] 2707 + 2708 + [[package]] 2709 + name = "os_pipe" 2710 + version = "1.2.3" 2711 + source = "registry+https://github.com/rust-lang/crates.io-index" 2712 + checksum = "7d8fae84b431384b68627d0f9b3b1245fcf9f46f6c0e3dc902e9dce64edd1967" 2713 + dependencies = [ 2714 + "libc", 2715 + "windows-sys 0.61.2", 2716 + ] 2717 + 2718 + [[package]] 2478 2719 name = "pango" 2479 2720 version = "0.18.3" 2480 2721 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2541 2782 checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" 2542 2783 2543 2784 [[package]] 2785 + name = "petgraph" 2786 + version = "0.8.3" 2787 + source = "registry+https://github.com/rust-lang/crates.io-index" 2788 + checksum = "8701b58ea97060d5e5b155d383a69952a60943f0e6dfe30b04c287beb0b27455" 2789 + dependencies = [ 2790 + "fixedbitset", 2791 + "hashbrown 0.15.5", 2792 + "indexmap 2.12.0", 2793 + ] 2794 + 2795 + [[package]] 2544 2796 name = "phf" 2545 2797 version = "0.8.0" 2546 2798 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 2752 3004 "concurrent-queue", 2753 3005 "hermit-abi", 2754 3006 "pin-project-lite", 2755 - "rustix", 3007 + "rustix 1.1.2", 2756 3008 "windows-sys 0.61.2", 2757 3009 ] 2758 3010 ··· 2864 3116 ] 2865 3117 2866 3118 [[package]] 3119 + name = "quick-error" 3120 + version = "2.0.1" 3121 + source = "registry+https://github.com/rust-lang/crates.io-index" 3122 + checksum = "a993555f31e5a609f617c12db6250dedcac1b0a85076912c436e6fc9b2c8e6a3" 3123 + 3124 + [[package]] 2867 3125 name = "quick-xml" 2868 3126 version = "0.37.5" 2869 3127 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3148 3406 checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92" 3149 3407 dependencies = [ 3150 3408 "semver", 3409 + ] 3410 + 3411 + [[package]] 3412 + name = "rustix" 3413 + version = "0.38.44" 3414 + source = "registry+https://github.com/rust-lang/crates.io-index" 3415 + checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" 3416 + dependencies = [ 3417 + "bitflags 2.10.0", 3418 + "errno", 3419 + "libc", 3420 + "linux-raw-sys 0.4.15", 3421 + "windows-sys 0.59.0", 3151 3422 ] 3152 3423 3153 3424 [[package]] ··· 3159 3430 "bitflags 2.10.0", 3160 3431 "errno", 3161 3432 "libc", 3162 - "linux-raw-sys", 3433 + "linux-raw-sys 0.11.0", 3163 3434 "windows-sys 0.61.2", 3164 3435 ] 3165 3436 ··· 3519 3790 dependencies = [ 3520 3791 "bytemuck", 3521 3792 "cfg_aliases", 3522 - "core-graphics", 3793 + "core-graphics 0.24.0", 3523 3794 "foreign-types", 3524 3795 "js-sys", 3525 3796 "log", ··· 3656 3927 ] 3657 3928 3658 3929 [[package]] 3930 + name = "sys-locale" 3931 + version = "0.3.2" 3932 + source = "registry+https://github.com/rust-lang/crates.io-index" 3933 + checksum = "8eab9a99a024a169fe8a903cf9d4a3b3601109bcc13bd9e3c6fff259138626c4" 3934 + dependencies = [ 3935 + "libc", 3936 + ] 3937 + 3938 + [[package]] 3659 3939 name = "system-deps" 3660 3940 version = "6.2.2" 3661 3941 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3677 3957 "bitflags 2.10.0", 3678 3958 "block2 0.6.2", 3679 3959 "core-foundation", 3680 - "core-graphics", 3960 + "core-graphics 0.24.0", 3681 3961 "crossbeam-channel", 3682 3962 "dispatch", 3683 3963 "dlopen2", ··· 3858 4138 ] 3859 4139 3860 4140 [[package]] 4141 + name = "tauri-plugin-clipboard-manager" 4142 + version = "2.3.2" 4143 + source = "registry+https://github.com/rust-lang/crates.io-index" 4144 + checksum = "206dc20af4ed210748ba945c2774e60fd0acd52b9a73a028402caf809e9b6ecf" 4145 + dependencies = [ 4146 + "arboard", 4147 + "log", 4148 + "serde", 4149 + "serde_json", 4150 + "tauri", 4151 + "tauri-plugin", 4152 + "thiserror 2.0.17", 4153 + ] 4154 + 4155 + [[package]] 3861 4156 name = "tauri-plugin-dialog" 3862 4157 version = "2.4.2" 3863 4158 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 3920 4215 ] 3921 4216 3922 4217 [[package]] 4218 + name = "tauri-plugin-os" 4219 + version = "2.3.2" 4220 + source = "registry+https://github.com/rust-lang/crates.io-index" 4221 + checksum = "d8f08346c8deb39e96f86973da0e2d76cbb933d7ac9b750f6dc4daf955a6f997" 4222 + dependencies = [ 4223 + "gethostname", 4224 + "log", 4225 + "os_info", 4226 + "serde", 4227 + "serde_json", 4228 + "serialize-to-javascript", 4229 + "sys-locale", 4230 + "tauri", 4231 + "tauri-plugin", 4232 + "thiserror 2.0.17", 4233 + ] 4234 + 4235 + [[package]] 3923 4236 name = "tauri-runtime" 3924 4237 version = "2.9.1" 3925 4238 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4028 4341 "fastrand", 4029 4342 "getrandom 0.3.4", 4030 4343 "once_cell", 4031 - "rustix", 4344 + "rustix 1.1.2", 4032 4345 "windows-sys 0.61.2", 4033 4346 ] 4034 4347 ··· 4084 4397 ] 4085 4398 4086 4399 [[package]] 4400 + name = "tiff" 4401 + version = "0.10.3" 4402 + source = "registry+https://github.com/rust-lang/crates.io-index" 4403 + checksum = "af9605de7fee8d9551863fd692cce7637f548dbd9db9180fcc07ccc6d26c336f" 4404 + dependencies = [ 4405 + "fax", 4406 + "flate2", 4407 + "half", 4408 + "quick-error", 4409 + "weezl", 4410 + "zune-jpeg", 4411 + ] 4412 + 4413 + [[package]] 4087 4414 name = "time" 4088 4415 version = "0.3.44" 4089 4416 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4360 4687 ] 4361 4688 4362 4689 [[package]] 4690 + name = "tree_magic_mini" 4691 + version = "3.2.1" 4692 + source = "registry+https://github.com/rust-lang/crates.io-index" 4693 + checksum = "52fac5f7d176f7f7f7e827299ead28ef98de642c5d93a97e0cd0816d17557f19" 4694 + dependencies = [ 4695 + "memchr", 4696 + "nom 7.1.3", 4697 + "petgraph", 4698 + ] 4699 + 4700 + [[package]] 4363 4701 name = "try-lock" 4364 4702 version = "0.2.5" 4365 4703 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4640 4978 dependencies = [ 4641 4979 "cc", 4642 4980 "downcast-rs", 4643 - "rustix", 4981 + "rustix 1.1.2", 4644 4982 "scoped-tls", 4645 4983 "smallvec", 4646 4984 "wayland-sys", ··· 4653 4991 checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d" 4654 4992 dependencies = [ 4655 4993 "bitflags 2.10.0", 4656 - "rustix", 4994 + "rustix 1.1.2", 4657 4995 "wayland-backend", 4658 4996 "wayland-scanner", 4659 4997 ] ··· 4671 5009 ] 4672 5010 4673 5011 [[package]] 5012 + name = "wayland-protocols-wlr" 5013 + version = "0.3.9" 5014 + source = "registry+https://github.com/rust-lang/crates.io-index" 5015 + checksum = "efd94963ed43cf9938a090ca4f7da58eb55325ec8200c3848963e98dc25b78ec" 5016 + dependencies = [ 5017 + "bitflags 2.10.0", 5018 + "wayland-backend", 5019 + "wayland-client", 5020 + "wayland-protocols", 5021 + "wayland-scanner", 5022 + ] 5023 + 5024 + [[package]] 4674 5025 name = "wayland-scanner" 4675 5026 version = "0.31.7" 4676 5027 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 4781 5132 "windows", 4782 5133 "windows-core 0.61.2", 4783 5134 ] 5135 + 5136 + [[package]] 5137 + name = "weezl" 5138 + version = "0.1.12" 5139 + source = "registry+https://github.com/rust-lang/crates.io-index" 5140 + checksum = "a28ac98ddc8b9274cb41bb4d9d4d5c425b6020c50c46f25559911905610b4a88" 4784 5141 4785 5142 [[package]] 4786 5143 name = "winapi" ··· 5242 5599 checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" 5243 5600 5244 5601 [[package]] 5602 + name = "wl-clipboard-rs" 5603 + version = "0.9.2" 5604 + source = "registry+https://github.com/rust-lang/crates.io-index" 5605 + checksum = "8e5ff8d0e60065f549fafd9d6cb626203ea64a798186c80d8e7df4f8af56baeb" 5606 + dependencies = [ 5607 + "libc", 5608 + "log", 5609 + "os_pipe", 5610 + "rustix 0.38.44", 5611 + "tempfile", 5612 + "thiserror 2.0.17", 5613 + "tree_magic_mini", 5614 + "wayland-backend", 5615 + "wayland-client", 5616 + "wayland-protocols", 5617 + "wayland-protocols-wlr", 5618 + ] 5619 + 5620 + [[package]] 5245 5621 name = "writeable" 5246 5622 version = "0.6.2" 5247 5623 source = "registry+https://github.com/rust-lang/crates.io-index" ··· 5312 5688 "once_cell", 5313 5689 "pkg-config", 5314 5690 ] 5691 + 5692 + [[package]] 5693 + name = "x11rb" 5694 + version = "0.13.2" 5695 + source = "registry+https://github.com/rust-lang/crates.io-index" 5696 + checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" 5697 + dependencies = [ 5698 + "gethostname", 5699 + "rustix 1.1.2", 5700 + "x11rb-protocol", 5701 + ] 5702 + 5703 + [[package]] 5704 + name = "x11rb-protocol" 5705 + version = "0.13.2" 5706 + source = "registry+https://github.com/rust-lang/crates.io-index" 5707 + checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" 5708 + 5709 + [[package]] 5710 + name = "xkbcommon" 5711 + version = "0.9.0" 5712 + source = "registry+https://github.com/rust-lang/crates.io-index" 5713 + checksum = "a7a974f48060a14e95705c01f24ad9c3345022f4d97441b8a36beb7ed5c4a02d" 5714 + dependencies = [ 5715 + "libc", 5716 + "memmap2", 5717 + "xkeysym", 5718 + ] 5719 + 5720 + [[package]] 5721 + name = "xkeysym" 5722 + version = "0.2.1" 5723 + source = "registry+https://github.com/rust-lang/crates.io-index" 5724 + checksum = "b9cc00251562a284751c9973bace760d86c0276c471b4be569fe6b068ee97a56" 5315 5725 5316 5726 [[package]] 5317 5727 name = "yoke" ··· 5470 5880 "proc-macro2", 5471 5881 "quote", 5472 5882 "syn 2.0.109", 5883 + ] 5884 + 5885 + [[package]] 5886 + name = "zune-core" 5887 + version = "0.4.12" 5888 + source = "registry+https://github.com/rust-lang/crates.io-index" 5889 + checksum = "3f423a2c17029964870cfaabb1f13dfab7d092a62a29a89264f4d36990ca414a" 5890 + 5891 + [[package]] 5892 + name = "zune-jpeg" 5893 + version = "0.4.21" 5894 + source = "registry+https://github.com/rust-lang/crates.io-index" 5895 + checksum = "29ce2c8a9384ad323cf564b67da86e21d3cfdff87908bc1223ed5c99bc792713" 5896 + dependencies = [ 5897 + "zune-core", 5473 5898 ] 5474 5899 5475 5900 [[package]]
+6
src-tauri/Cargo.toml
··· 28 28 flate2 = "1.1" 29 29 tauri-plugin-dialog = "2" 30 30 crossbeam-channel = "0.5" 31 + tauri-plugin-clipboard-manager = "2" 32 + tauri-plugin-os = "2" 33 + 34 + # [target.'cfg(windows)'.dependencies] 35 + enigo = { version = "0.6.1" } 36 + chrono = "0.4.42" 31 37
+4 -1
src-tauri/capabilities/default.json
··· 9 9 "core:default", 10 10 "core:window:allow-destroy", 11 11 "opener:default", 12 - "dialog:default" 12 + "dialog:default", 13 + "clipboard-manager:allow-read-text", 14 + "clipboard-manager:allow-write-text", 15 + "os:default" 13 16 ] 14 17 }
+2 -2
src-tauri/src/frontend_calls/close_app.rs
··· 3 3 use crate::utils::config::Config; 4 4 5 5 #[tauri::command] 6 - pub fn close_app( conf: State<Config> ){ 6 + pub fn close_app(conf: State<Config>) { 7 7 conf.save(); 8 8 std::process::exit(0); 9 - } 9 + }
+9 -4
src-tauri/src/frontend_calls/load_previous_tabs.rs
··· 2 2 3 3 use tauri::{State, Window}; 4 4 5 - use crate::{ structs::nodes::Node, utils::config::Config }; 5 + use crate::{structs::nodes::Node, utils::config::Config}; 6 6 7 7 #[tauri::command] 8 - pub fn load_previous_tabs( window: Window, conf: State<Config> ) -> HashMap<String, ( Vec<Node>, String, Option<String> )> { 8 + pub fn load_previous_tabs( 9 + window: Window, 10 + conf: State<Config>, 11 + ) -> HashMap<String, (Vec<Node>, String, Option<String>, bool)> { 9 12 let config = conf.store.lock().unwrap(); 10 13 11 - if !config.hide_editor_on_start { window.show().unwrap(); } 14 + if !config.hide_editor_on_start { 15 + window.show().unwrap(); 16 + } 12 17 13 18 let tabs = config.loaded_tabs.clone(); 14 19 tabs 15 - } 20 + }
+3 -3
src-tauri/src/frontend_calls/mod.rs
··· 1 + pub mod close_app; 1 2 pub mod get_addresses; 3 + pub mod load_previous_tabs; 2 4 pub mod save_graph; 5 + pub mod settings; 3 6 pub mod sync_tab; 4 - pub mod load_previous_tabs; 5 - pub mod close_app; 6 - pub mod settings;
+2 -2
src-tauri/src/frontend_calls/save_graph.rs
··· 1 1 use std::{fs::File, io::Write, path::PathBuf}; 2 2 3 - use flate2::{ write::GzEncoder, Compression }; 3 + use flate2::{write::GzEncoder, Compression}; 4 4 use tauri::State; 5 5 6 6 use crate::utils::config::Config; 7 7 8 8 #[tauri::command] 9 - pub fn save_graph( graph: String, path: PathBuf, conf: State<Config> ) { 9 + pub fn save_graph(graph: String, path: PathBuf, conf: State<Config>) { 10 10 let file = File::create(&path).unwrap(); 11 11 let mut encoder = GzEncoder::new(file, Compression::default()); 12 12
+3 -3
src-tauri/src/frontend_calls/settings.rs
··· 3 3 use crate::utils::config::Config; 4 4 5 5 #[tauri::command] 6 - pub fn set_hide_editor_on_app_start( value: bool, conf: State<Config> ){ 6 + pub fn set_hide_editor_on_app_start(value: bool, conf: State<Config>) { 7 7 let mut config = conf.store.lock().unwrap(); 8 8 config.hide_editor_on_start = value; 9 9 } 10 10 11 11 #[tauri::command] 12 - pub fn get_hide_editor_on_app_start( conf: State<Config> ) -> bool { 12 + pub fn get_hide_editor_on_app_start(conf: State<Config>) -> bool { 13 13 let config = conf.store.lock().unwrap(); 14 14 config.hide_editor_on_start 15 - } 15 + }
+19 -6
src-tauri/src/frontend_calls/sync_tab.rs
··· 1 + use chrono::Utc; 1 2 use crossbeam_channel::Sender; 2 3 3 4 use tauri::State; 4 5 5 - use crate::{ runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config }; 6 + use crate::{runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config}; 6 7 7 8 #[tauri::command] 8 - pub fn sync_tab( graph: Vec<Node>, id: String, name: String, location: Option<String>, cmd: State<Sender<RuntimeCommand>>, conf: State<Config> ){ 9 - cmd.send(RuntimeCommand::AddTab(graph.clone(), id.clone())).unwrap(); 9 + pub fn sync_tab( 10 + graph: Vec<Node>, 11 + id: String, 12 + name: String, 13 + save_state: bool, 14 + location: Option<String>, 15 + cmd: State<Sender<RuntimeCommand>>, 16 + conf: State<Config>, 17 + ) { 18 + cmd 19 + .send(RuntimeCommand::AddTab(graph.clone(), id.clone())) 20 + .unwrap(); 10 21 11 22 let mut config = conf.store.lock().unwrap(); 12 - config.loaded_tabs.insert(id, ( graph, name, location )); // TODO: When loading a tab into config, store the save state of it too 23 + config.loaded_tabs.insert(id, (graph, name, location, save_state)); 24 + 25 + conf.save_prelocked(config); 13 26 } 14 27 15 28 #[tauri::command] 16 - pub fn discard_tab( id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config> ){ 29 + pub fn discard_tab(id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config>) { 17 30 cmd.send(RuntimeCommand::RemoveTab(id.clone())).unwrap(); 18 31 19 32 let mut config = conf.store.lock().unwrap(); 20 33 config.loaded_tabs.remove(&id); 21 - } 34 + }
+10 -7
src-tauri/src/lib.rs
··· 1 - use std::{ fs, sync::Mutex }; 1 + use std::{fs, sync::Mutex}; 2 2 3 3 use crossbeam_channel::bounded; 4 4 use frontend_calls::*; 5 5 6 - use crate::{ osc::OSCMessage, setup::setup, utils::config::Config }; 6 + use crate::{osc::OSCMessage, setup::setup, utils::{config::Config, vrchat_builtin_parameters}}; 7 7 8 8 mod frontend_calls; 9 9 mod osc; 10 + mod runtime; 10 11 mod setup; 11 12 mod structs; 12 13 mod utils; 13 - mod runtime; 14 - 15 - // TODO: Add built-in OSC endpoints 16 14 17 15 #[cfg_attr(mobile, tauri::mobile_entry_point)] 18 16 #[tokio::main] ··· 35 33 36 34 static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new()); 37 35 38 - let ( runtime_sender, runtime_receiver ) = bounded(1024); 36 + let mut addresses = ADDRESSES.lock().unwrap(); 37 + addresses.append(&mut vrchat_builtin_parameters::get_read_addresses()); 38 + drop(addresses); 39 + 40 + let (runtime_sender, runtime_receiver) = bounded(1024); 39 41 40 42 tauri::Builder::default() 43 + .plugin(tauri_plugin_os::init()) 44 + .plugin(tauri_plugin_clipboard_manager::init()) 41 45 .plugin(tauri_plugin_dialog::init()) 42 46 .plugin(tauri_plugin_opener::init()) 43 47 .invoke_handler(tauri::generate_handler![ ··· 47 51 sync_tab::discard_tab, 48 52 load_previous_tabs::load_previous_tabs, 49 53 close_app::close_app, 50 - 51 54 settings::set_hide_editor_on_app_start, 52 55 settings::get_hide_editor_on_app_start, 53 56 ])
+3 -1
src-tauri/src/main.rs
··· 3 3 4 4 fn main() { 5 5 #[cfg(target_os = "linux")] 6 - std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); // Fix webkit being shit 6 + unsafe{ 7 + std::env::set_var("WEBKIT_DISABLE_DMABUF_RENDERER", "1"); // Fix webkit being shit 8 + } 7 9 8 10 vrcmacros_lib::run() 9 11 }
+4 -4
src-tauri/src/runtime/commands.rs
··· 1 - use crate::{ osc::OSCMessage, structs::nodes::Node }; 1 + use crate::{osc::OSCMessage, structs::nodes::Node}; 2 2 3 3 #[derive(Debug)] 4 - pub enum RuntimeCommand{ 4 + pub enum RuntimeCommand { 5 5 OSCMessage(OSCMessage), 6 6 7 7 AddTab(Vec<Node>, String), 8 - RemoveTab(String) 9 - } 8 + RemoveTab(String), 9 + }
+37 -36
src-tauri/src/runtime/nodes/conditional/ifequal.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 2 5 3 - pub struct ConditionalIfEqual{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - value1: ParameterType, 6 - value2: ParameterType 6 + pub struct ConditionalIfEqual { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 7 9 } 8 10 9 - impl ConditionalIfEqual{ 10 - pub fn new( node: Node ) -> Box<Self>{ 11 + impl ConditionalIfEqual { 12 + pub fn new(node: Node) -> Box<Self> { 11 13 Box::new(Self { 12 - outputs: node.outputs.iter() 13 - .map(| x | { 14 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 15 - }).collect(), 16 - value1: ParameterType::None, 17 - value2: ParameterType::None, 14 + outputs: node.outputs.iter().map(|x| { 15 + x.connections.iter() 16 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 17 + 18 + inputs: node.inputs.iter().map(|x| { 19 + let y = x.connections.get(0); 20 + if let Some(y) = y{ 21 + Some((y.node.clone(), y.index, y.value_type)) 22 + } else{ 23 + None 24 + } 25 + }).collect(), 18 26 }) 19 27 } 20 28 } 21 29 22 - impl RuntimeNode for ConditionalIfEqual{ 23 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() } 24 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) } 30 + impl RuntimeNode for ConditionalIfEqual { 31 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 32 + self.outputs.clone() 33 + } 25 34 26 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 27 - if self.value1 == ParameterType::None && self.value2 == ParameterType::None{ 28 - None 29 - } else{ 30 - let equal = self.value1 == self.value2; 31 - Some(vec![ ParameterType::Flow(equal), ParameterType::Flow(!equal) ]) 32 - } 35 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 36 + self.inputs.clone() 33 37 } 34 38 35 - fn update_arg( &mut self, index: usize, arg: ParameterType ) -> bool { 36 - match index{ 37 - 1 => { 38 - self.value1 = arg; 39 - } 40 - 2 => { 41 - self.value2 = arg; 42 - } 43 - _ => {} 44 - } 39 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 40 + let is_equal = args[1] == args[2]; 45 41 46 - false 42 + vec![ 43 + ParameterType::Flow(is_equal), 44 + ParameterType::Flow(!is_equal), 45 + ] 47 46 } 48 47 49 - fn is_entrypoint( &self ) -> bool { false } 50 - } 48 + fn is_entrypoint(&self) -> bool { 49 + false 50 + } 51 + }
+38 -26
src-tauri/src/runtime/nodes/conditional/iffalse.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 2 5 3 - pub struct ConditionalIfFalse{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - runtime_active: bool 6 + pub struct ConditionalIfFalse { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 6 9 } 7 10 8 - impl ConditionalIfFalse{ 9 - pub fn new( node: Node ) -> Box<Self>{ 11 + impl ConditionalIfFalse { 12 + pub fn new(node: Node) -> Box<Self> { 10 13 Box::new(Self { 11 - outputs: node.outputs.iter() 12 - .map(| x | { 13 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 14 - }).collect(), 15 - runtime_active: false 14 + outputs: node.outputs.iter().map(|x| { 15 + x.connections.iter() 16 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 17 + 18 + inputs: node.inputs.iter().map(|x| { 19 + let y = x.connections.get(0); 20 + if let Some(y) = y{ 21 + Some((y.node.clone(), y.index, y.value_type)) 22 + } else{ 23 + None 24 + } 25 + }).collect(), 16 26 }) 17 27 } 18 28 } 19 29 20 - impl RuntimeNode for ConditionalIfFalse{ 21 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() } 22 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) } 30 + impl RuntimeNode for ConditionalIfFalse { 31 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 32 + self.outputs.clone() 33 + } 23 34 24 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 25 - Some(vec![ ParameterType::Flow(!self.runtime_active), ParameterType::Flow(self.runtime_active) ]) 35 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 36 + self.inputs.clone() 26 37 } 27 38 28 - fn update_arg( &mut self, _: usize, arg: ParameterType ) -> bool { 29 - if arg.as_bool().unwrap(){ 30 - self.runtime_active = true; 31 - true 32 - } else{ 33 - self.runtime_active = false; 34 - false 35 - } 39 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 40 + let is_false = !args[1].as_bool().unwrap(); 41 + 42 + vec![ 43 + ParameterType::Flow(is_false), 44 + ParameterType::Flow(!is_false), 45 + ] 36 46 } 37 47 38 - fn is_entrypoint( &self ) -> bool { false } 39 - } 48 + fn is_entrypoint(&self) -> bool { 49 + false 50 + } 51 + }
+38 -26
src-tauri/src/runtime/nodes/conditional/iftrue.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 2 5 3 - pub struct ConditionalIfTrue{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - runtime_active: bool 6 + pub struct ConditionalIfTrue { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 6 9 } 7 10 8 - impl ConditionalIfTrue{ 9 - pub fn new( node: Node ) -> Box<Self>{ 11 + impl ConditionalIfTrue { 12 + pub fn new(node: Node) -> Box<Self> { 10 13 Box::new(Self { 11 - outputs: node.outputs.iter() 12 - .map(| x | { 13 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 14 - }).collect(), 15 - runtime_active: false 14 + outputs: node.outputs.iter().map(|x| { 15 + x.connections.iter() 16 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 17 + 18 + inputs: node.inputs.iter().map(|x| { 19 + let y = x.connections.get(0); 20 + if let Some(y) = y{ 21 + Some((y.node.clone(), y.index, y.value_type)) 22 + } else{ 23 + None 24 + } 25 + }).collect(), 16 26 }) 17 27 } 18 28 } 19 29 20 - impl RuntimeNode for ConditionalIfTrue{ 21 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { self.outputs.clone() } 22 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) } 30 + impl RuntimeNode for ConditionalIfTrue { 31 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 32 + self.outputs.clone() 33 + } 23 34 24 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 25 - Some(vec![ ParameterType::Flow(self.runtime_active), ParameterType::Flow(!self.runtime_active) ]) 35 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 36 + self.inputs.clone() 26 37 } 27 38 28 - fn update_arg( &mut self, _: usize, arg: ParameterType ) -> bool { 29 - if arg.as_bool().unwrap(){ 30 - self.runtime_active = true; 31 - true 32 - } else{ 33 - self.runtime_active = false; 34 - false 35 - } 39 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 40 + let is_true = args[1].as_bool().unwrap(); 41 + 42 + vec![ 43 + ParameterType::Flow(is_true), 44 + ParameterType::Flow(!is_true), 45 + ] 36 46 } 37 47 38 - fn is_entrypoint( &self ) -> bool { false } 39 - } 48 + fn is_entrypoint(&self) -> bool { 49 + false 50 + } 51 + }
+2 -2
src-tauri/src/runtime/nodes/conditional/mod.rs
··· 1 - pub mod iftrue; 1 + pub mod ifequal; 2 2 pub mod iffalse; 3 - pub mod ifequal; 3 + pub mod iftrue;
+36 -23
src-tauri/src/runtime/nodes/debug.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 2 5 3 - pub struct Debug{ 4 - to_log: Option<ParameterType> 6 + pub struct Debug { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>> 5 9 } 6 10 7 - impl Debug{ 8 - pub fn new( _: Node ) -> Box<Self>{ 9 - Box::new(Self { to_log: None }) 11 + impl Debug { 12 + pub fn new(node: Node) -> Box<Self> { 13 + Box::new(Self { 14 + outputs: node.outputs.iter().map(|x| { 15 + x.connections.iter() 16 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 17 + 18 + inputs: node.inputs.iter().map(|x| { 19 + let y = x.connections.get(0); 20 + if let Some(y) = y{ 21 + Some((y.node.clone(), y.index, y.value_type)) 22 + } else{ 23 + None 24 + } 25 + }).collect(), 26 + }) 10 27 } 11 28 } 12 29 13 - impl RuntimeNode for Debug{ 14 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { vec![] } 15 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) } 30 + impl RuntimeNode for Debug { 31 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 32 + self.outputs.clone() 33 + } 16 34 17 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 18 - dbg!(&self.to_log); 19 - self.to_log = None; 35 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 36 + self.inputs.clone() 37 + } 20 38 21 - None 39 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 40 + dbg!(&args); // TODO: Debug to actual UI instead of console 41 + vec![] 22 42 } 23 43 24 - fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool { 25 - if index == 1{ 26 - self.to_log = Some(value); 27 - true 28 - } else{ 29 - false 30 - } 44 + fn is_entrypoint(&self) -> bool { 45 + false 31 46 } 32 - 33 - fn is_entrypoint( &self ) -> bool { false } 34 - } 47 + }
+1 -1
src-tauri/src/runtime/nodes/oscactions/mod.rs
··· 1 - pub mod sendchatbox; 1 + pub mod sendchatbox;
+47 -25
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
··· 1 1 use std::vec; 2 2 3 - use crate::{ osc, runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 3 + use crate::{ 4 + osc, 5 + runtime::nodes::RuntimeNode, 6 + structs::{nodes::Node, parameter_types::ParameterType}, 7 + }; 4 8 5 - pub struct OSCActionsSendChatbox{ 6 - to_log: String 9 + pub struct OSCActionsSendChatbox { 10 + outputs: Vec<Vec<(String, isize, isize)>>, 11 + inputs: Vec<Option<(String, isize, isize)>>, 7 12 } 8 13 9 - impl OSCActionsSendChatbox{ 10 - pub fn new( _: Node ) -> Box<Self>{ 11 - Box::new(Self { to_log: "".into() }) 14 + impl OSCActionsSendChatbox { 15 + pub fn new(node: Node) -> Box<Self> { 16 + Box::new(Self { 17 + outputs: node.outputs.iter().map(|x| { 18 + x.connections.iter() 19 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 20 + 21 + inputs: node.inputs.iter().map(|x| { 22 + let y = x.connections.get(0); 23 + if let Some(y) = y{ 24 + Some((y.node.clone(), y.index, y.value_type)) 25 + } else{ 26 + None 27 + } 28 + }).collect(), 29 + }) 12 30 } 13 31 } 14 32 15 - impl RuntimeNode for OSCActionsSendChatbox{ 16 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { vec![] } 17 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { Some(vec![]) } 18 - 19 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 20 - osc::send_message("/chatbox/input", vec![ 21 - ParameterType::String(self.to_log.clone()), 22 - ParameterType::Boolean(true), 23 - ParameterType::Boolean(false) 24 - ], "127.0.0.1:9000"); 33 + impl RuntimeNode for OSCActionsSendChatbox { 34 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 35 + self.outputs.clone() 36 + } 25 37 26 - None 38 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 39 + self.inputs.clone() 27 40 } 28 41 29 - fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool { 30 - if index == 1{ 31 - self.to_log = value.as_string().unwrap(); 32 - true 33 - } else{ 34 - false 42 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 43 + if let Ok(msg) = args[1].as_string(){ 44 + osc::send_message( 45 + "/chatbox/input", 46 + vec![ 47 + ParameterType::String(msg.clone()), 48 + ParameterType::Boolean(true), 49 + ParameterType::Boolean(false), 50 + ], 51 + "127.0.0.1:9000", 52 + ); 35 53 } 54 + 55 + vec![] 36 56 } 37 57 38 - fn is_entrypoint( &self ) -> bool { false } 39 - } 58 + fn is_entrypoint(&self) -> bool { 59 + false 60 + } 61 + }
+49 -42
src-tauri/src/runtime/nodes/osctrigger.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 2 5 3 - pub struct OSCTrigger{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - address: Option<String>, 6 - runtime_active: bool 6 + pub struct OSCTrigger { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 9 + 10 + address: Option<String> 7 11 } 8 12 9 - impl OSCTrigger{ 10 - pub fn new( node: Node ) -> Box<Self>{ 13 + impl OSCTrigger { 14 + pub fn new(node: Node) -> Box<Self> { 11 15 let value = &node.statics[0].value; 12 16 13 17 Box::new(Self { 14 - address: if value.is_null(){ None } else { Some(value.as_str().unwrap().to_owned()) }, 15 - outputs: node.outputs.iter() 16 - .map(| x | { 17 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 18 - }).collect(), 19 - runtime_active: false 18 + outputs: node.outputs.iter().map(|x| { 19 + x.connections.iter() 20 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 21 + 22 + inputs: node.inputs.iter().map(|x| { 23 + let y = x.connections.get(0); 24 + if let Some(y) = y{ 25 + Some((y.node.clone(), y.index, y.value_type)) 26 + } else{ 27 + None 28 + } 29 + }).collect(), 30 + 31 + address: if value.is_null() { 32 + None 33 + } else { 34 + Some(value.as_str().unwrap().to_owned()) 35 + }, 20 36 }) 21 37 } 22 38 } 23 39 24 - impl RuntimeNode for OSCTrigger{ 25 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { 40 + impl RuntimeNode for OSCTrigger { 41 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 26 42 self.outputs.clone() 27 43 } 28 44 29 - fn execute_dry( &mut self, msg: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { 30 - if self.address.is_none(){ 31 - self.runtime_active = false; 32 - return None 33 - } 45 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 46 + self.inputs.clone() 47 + } 34 48 35 - if let ParameterType::String(address) = &msg[0]{ 36 - if *address == *self.address.as_ref().unwrap(){ 37 - self.runtime_active = true; 38 - Some(msg.clone()) 39 - // The first value is technically the address value, 40 - // but this value gets ignored as the first output of 41 - // the osctrigger node is a flow output which gets ignored 42 - // on dry runs. 49 + fn execute(&mut self, mut args: Vec<ParameterType>) -> Vec<ParameterType> { 50 + if args.len() == 0{ return args } 51 + 52 + let execute = if let Some(internal_address) = &self.address { 53 + if let Ok(address) = args[0].as_string() { 54 + address == *internal_address 43 55 } else{ 44 - self.runtime_active = false; 45 - None 56 + false 46 57 } 47 58 } else{ 48 - self.runtime_active = false; 49 - None 50 - } 51 - } 52 - 53 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { 54 - let execute = self.runtime_active; 55 - self.runtime_active = false; 59 + false 60 + }; 56 61 57 - Some(vec![ ParameterType::Flow(execute) ]) 62 + args[0] = ParameterType::Flow(execute); 63 + args 58 64 } 59 65 60 - fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false } 61 - fn is_entrypoint( &self ) -> bool { true } 62 - } 66 + fn is_entrypoint(&self) -> bool { 67 + true 68 + } 69 + }
+73
src-tauri/src/runtime/nodes/press_key.rs
··· 1 + use std::sync::{Arc, Mutex}; 2 + 3 + use enigo::{Direction, Enigo, Key, Keyboard}; 4 + 5 + use crate::{ 6 + runtime::nodes::RuntimeNode, 7 + structs::{nodes::Node, parameter_types::ParameterType}, 8 + }; 9 + 10 + pub struct PressKey { 11 + outputs: Vec<Vec<(String, isize, isize)>>, 12 + inputs: Vec<Option<(String, isize, isize)>>, 13 + 14 + key: Option<char>, 15 + enigo: Arc<Mutex<Enigo>>, 16 + } 17 + 18 + impl PressKey { 19 + pub fn new(node: Node, enigo: Arc<Mutex<Enigo>>) -> Box<Self> { 20 + let value = &node.statics[0].value; 21 + 22 + Box::new(Self { 23 + outputs: node.outputs.iter().map(|x| { 24 + x.connections.iter() 25 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 26 + 27 + inputs: node.inputs.iter().map(|x| { 28 + let y = x.connections.get(0); 29 + if let Some(y) = y{ 30 + Some((y.node.clone(), y.index, y.value_type)) 31 + } else{ 32 + None 33 + } 34 + }).collect(), 35 + 36 + enigo, 37 + key: if value.is_null() { 38 + None 39 + } else { 40 + let string = value.as_str().unwrap().to_owned(); 41 + 42 + if string.len() == 1 { 43 + Some(string.chars().nth(0).unwrap()) 44 + } else { 45 + None 46 + } 47 + }, 48 + }) 49 + } 50 + } 51 + 52 + impl RuntimeNode for PressKey { 53 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 54 + self.outputs.clone() 55 + } 56 + 57 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 58 + self.inputs.clone() 59 + } 60 + 61 + fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> { 62 + if self.key.is_some() { 63 + let mut enigo = self.enigo.lock().unwrap(); 64 + enigo.key(Key::MediaPlayPause, Direction::Click).unwrap(); 65 + } 66 + 67 + vec![] 68 + } 69 + 70 + fn is_entrypoint(&self) -> bool { 71 + false 72 + } 73 + }
+70
src-tauri/src/runtime/nodes/shell.rs
··· 1 + use std::{io::Stdin, process::{Command, Stdio}}; 2 + 3 + use crate::{ 4 + runtime::nodes::RuntimeNode, 5 + structs::{nodes::Node, parameter_types::ParameterType}, 6 + }; 7 + 8 + pub struct ShellCommand { 9 + outputs: Vec<Vec<(String, isize, isize)>>, 10 + inputs: Vec<Option<(String, isize, isize)>> 11 + } 12 + 13 + impl ShellCommand { 14 + pub fn new(node: Node) -> Box<Self> { 15 + Box::new(Self { 16 + outputs: node.outputs.iter().map(|x| { 17 + x.connections.iter() 18 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 19 + 20 + inputs: node.inputs.iter().map(|x| { 21 + let y = x.connections.get(0); 22 + if let Some(y) = y{ 23 + Some((y.node.clone(), y.index, y.value_type)) 24 + } else{ 25 + None 26 + } 27 + }).collect() 28 + }) 29 + } 30 + } 31 + 32 + impl RuntimeNode for ShellCommand { 33 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 34 + self.outputs.clone() 35 + } 36 + 37 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 38 + self.inputs.clone() 39 + } 40 + 41 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> { 42 + if let Ok(cmd) = args[1].as_string(){ 43 + if cmd != ""{ 44 + let mut split_cmd = cmd.split(" "); 45 + 46 + let mut cmd = Command::new(split_cmd.nth(0).unwrap()); 47 + if split_cmd.clone().count() > 0{ cmd.args(split_cmd); } 48 + 49 + cmd.stdout(Stdio::piped()); 50 + cmd.stderr(Stdio::piped()); 51 + 52 + let child = cmd.spawn().unwrap(); 53 + let output = child.wait_with_output().unwrap(); 54 + 55 + vec![ 56 + ParameterType::Flow(true), 57 + ParameterType::String(str::from_utf8(&output.stdout).unwrap().to_owned()) 58 + ] 59 + } else{ 60 + vec![ ParameterType::Flow(false) ] 61 + } 62 + } else{ 63 + vec![ ParameterType::Flow(false) ] 64 + } 65 + } 66 + 67 + fn is_entrypoint(&self) -> bool { 68 + false 69 + } 70 + }
+44 -23
src-tauri/src/runtime/nodes/statics/float.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 5 + 6 + pub struct StaticFloat { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 2 9 3 - pub struct StaticFloat{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - value: Option<f32> 10 + value: Option<f32>, 6 11 } 7 12 8 - impl StaticFloat{ 9 - pub fn new( node: Node ) -> Box<Self>{ 13 + impl StaticFloat { 14 + pub fn new(node: Node) -> Box<Self> { 10 15 let value = &node.statics[0].value; 11 16 12 17 Box::new(Self { 13 - value: if value.is_null(){ None } else { Some(value.as_f64().unwrap() as f32) }, 14 - outputs: node.outputs.iter() 15 - .map(| x | { 16 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 17 - }).collect(), 18 + outputs: node.outputs.iter().map(|x| { 19 + x.connections.iter() 20 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 21 + 22 + inputs: node.inputs.iter().map(|x| { 23 + let y = x.connections.get(0); 24 + if let Some(y) = y{ 25 + Some((y.node.clone(), y.index, y.value_type)) 26 + } else{ 27 + None 28 + } 29 + }).collect(), 30 + 31 + value: if value.is_null() { 32 + None 33 + } else { 34 + Some(value.as_f64().unwrap() as f32) 35 + } 18 36 }) 19 37 } 20 38 } 21 39 22 - impl RuntimeNode for StaticFloat{ 23 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { 40 + impl RuntimeNode for StaticFloat { 41 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 24 42 self.outputs.clone() 25 43 } 26 44 27 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { 28 - if self.value.is_some(){ 29 - Some(vec![ ParameterType::Float(self.value.clone().unwrap()) ]) 30 - } else{ 31 - None 45 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 46 + self.inputs.clone() 47 + } 48 + 49 + fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> { 50 + if self.value.is_some() { 51 + vec![ParameterType::Float(self.value.clone().unwrap())] 52 + } else { 53 + vec![ParameterType::Float(0.0)] 32 54 } 33 55 } 34 56 35 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { None } 36 - 37 - fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false } 38 - fn is_entrypoint( &self ) -> bool { true } 39 - } 57 + fn is_entrypoint(&self) -> bool { 58 + false 59 + } 60 + }
+44 -23
src-tauri/src/runtime/nodes/statics/int.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 5 + 6 + pub struct StaticInt { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 2 9 3 - pub struct StaticInt{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - value: Option<i32> 10 + value: Option<i32>, 6 11 } 7 12 8 - impl StaticInt{ 9 - pub fn new( node: Node ) -> Box<Self>{ 13 + impl StaticInt { 14 + pub fn new(node: Node) -> Box<Self> { 10 15 let value = &node.statics[0].value; 11 16 12 17 Box::new(Self { 13 - value: if value.is_null(){ None } else { Some(value.as_i64().unwrap() as i32) }, 14 - outputs: node.outputs.iter() 15 - .map(| x | { 16 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 17 - }).collect(), 18 + outputs: node.outputs.iter().map(|x| { 19 + x.connections.iter() 20 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 21 + 22 + inputs: node.inputs.iter().map(|x| { 23 + let y = x.connections.get(0); 24 + if let Some(y) = y{ 25 + Some((y.node.clone(), y.index, y.value_type)) 26 + } else{ 27 + None 28 + } 29 + }).collect(), 30 + 31 + value: if value.is_null() { 32 + None 33 + } else { 34 + Some(value.as_i64().unwrap() as i32) 35 + } 18 36 }) 19 37 } 20 38 } 21 39 22 - impl RuntimeNode for StaticInt{ 23 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { 40 + impl RuntimeNode for StaticInt { 41 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 24 42 self.outputs.clone() 25 43 } 26 44 27 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { 28 - if self.value.is_some(){ 29 - Some(vec![ ParameterType::Int(self.value.clone().unwrap()) ]) 30 - } else{ 31 - None 45 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 46 + self.inputs.clone() 47 + } 48 + 49 + fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> { 50 + if self.value.is_some() { 51 + vec![ParameterType::Int(self.value.clone().unwrap())] 52 + } else { 53 + vec![ParameterType::Int(0)] 32 54 } 33 55 } 34 56 35 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { None } 36 - 37 - fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false } 38 - fn is_entrypoint( &self ) -> bool { true } 39 - } 57 + fn is_entrypoint(&self) -> bool { 58 + false 59 + } 60 + }
+2 -2
src-tauri/src/runtime/nodes/statics/mod.rs
··· 1 - pub mod string; 1 + pub mod float; 2 2 pub mod int; 3 - pub mod float; 3 + pub mod string;
+44 -23
src-tauri/src/runtime/nodes/statics/string.rs
··· 1 - use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } }; 1 + use crate::{ 2 + runtime::nodes::RuntimeNode, 3 + structs::{nodes::Node, parameter_types::ParameterType}, 4 + }; 5 + 6 + pub struct StaticString { 7 + outputs: Vec<Vec<(String, isize, isize)>>, 8 + inputs: Vec<Option<(String, isize, isize)>>, 2 9 3 - pub struct StaticString{ 4 - outputs: Vec<Vec<( String, isize, isize )>>, 5 - value: Option<String> 10 + value: Option<String>, 6 11 } 7 12 8 - impl StaticString{ 9 - pub fn new( node: Node ) -> Box<Self>{ 13 + impl StaticString { 14 + pub fn new(node: Node) -> Box<Self> { 10 15 let value = &node.statics[0].value; 11 16 12 17 Box::new(Self { 13 - value: if value.is_null(){ None } else { Some(value.as_str().unwrap().to_owned()) }, 14 - outputs: node.outputs.iter() 15 - .map(| x | { 16 - x.connections.iter().map(| x | { ( x.node.clone(), x.index, x.value_type ) }).collect() 17 - }).collect(), 18 + outputs: node.outputs.iter().map(|x| { 19 + x.connections.iter() 20 + .map(|x| (x.node.clone(), x.index, x.value_type)).collect()}).collect(), 21 + 22 + inputs: node.inputs.iter().map(|x| { 23 + let y = x.connections.get(0); 24 + if let Some(y) = y{ 25 + Some((y.node.clone(), y.index, y.value_type)) 26 + } else{ 27 + None 28 + } 29 + }).collect(), 30 + 31 + value: if value.is_null() { 32 + None 33 + } else { 34 + Some(value.as_str().unwrap().to_owned()) 35 + } 18 36 }) 19 37 } 20 38 } 21 39 22 - impl RuntimeNode for StaticString{ 23 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> { 40 + impl RuntimeNode for StaticString { 41 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> { 24 42 self.outputs.clone() 25 43 } 26 44 27 - fn execute_dry( &mut self, _: &Vec<ParameterType> ) -> Option<Vec<ParameterType>> { 28 - if self.value.is_some(){ 29 - Some(vec![ ParameterType::String(self.value.clone().unwrap()) ]) 30 - } else{ 31 - None 45 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>> { 46 + self.inputs.clone() 47 + } 48 + 49 + fn execute(&mut self, _: Vec<ParameterType>) -> Vec<ParameterType> { 50 + if self.value.is_some() { 51 + vec![ParameterType::String(self.value.clone().unwrap())] 52 + } else { 53 + vec![ParameterType::String("".to_owned())] 32 54 } 33 55 } 34 56 35 - fn execute( &mut self ) -> Option<Vec<ParameterType>> { None } 36 - 37 - fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false } 38 - fn is_entrypoint( &self ) -> bool { true } 39 - } 57 + fn is_entrypoint(&self) -> bool { 58 + false 59 + } 60 + }
+78 -28
src-tauri/src/runtime/nodes.rs
··· 1 - use std::collections::HashMap; 1 + use std::{ 2 + collections::HashMap, 3 + sync::{Arc, Mutex}, 4 + }; 5 + 6 + // #[cfg(target_os = "windows")] 7 + use enigo::Enigo; 8 + 9 + use crate::{ 10 + runtime::nodes::{ 11 + conditional::{ 12 + ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue, 13 + }, debug::Debug, oscactions::sendchatbox::OSCActionsSendChatbox, osctrigger::OSCTrigger, shell::ShellCommand, statics::{float::StaticFloat, int::StaticInt, string::StaticString} 14 + }, 15 + structs::{nodes::Node, parameter_types::ParameterType}, 16 + }; 2 17 3 - use crate::{ runtime::nodes::{ conditional::{ ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue }, debug::Debug, oscactions::sendchatbox::OSCActionsSendChatbox, osctrigger::OSCTrigger, statics::{ float::StaticFloat, int::StaticInt, string::StaticString } }, structs::{ nodes::Node, parameter_types::ParameterType } }; 18 + // #[cfg(target_os = "windows")] 19 + use crate::runtime::nodes::press_key::PressKey; 4 20 21 + mod conditional; 22 + mod debug; 23 + mod oscactions; 5 24 mod osctrigger; 6 - mod debug; 7 25 mod statics; 8 - mod conditional; 9 - mod oscactions; 26 + mod shell; 27 + 28 + // #[cfg(target_os = "windows")] 29 + mod press_key; 10 30 11 - pub struct RuntimeNodeTree{ 12 - pub nodes: HashMap<String, Box<dyn RuntimeNode>> 31 + pub struct RuntimeNodeTree { 32 + pub nodes: HashMap<String, Box<dyn RuntimeNode>>, 13 33 } 14 34 15 - unsafe impl Send for RuntimeNodeTree{} 35 + unsafe impl Send for RuntimeNodeTree {} 16 36 17 - impl RuntimeNodeTree{ 18 - pub fn from( tree: Vec<Node> ) -> Self{ 37 + impl RuntimeNodeTree { 38 + pub fn from(tree: Vec<Node>, /*#[cfg(target_os = "windows")]*/ enigo: Arc<Mutex<Enigo>>) -> Self { 19 39 let mut runtime_nodes: HashMap<String, Box<dyn RuntimeNode>> = HashMap::new(); 20 - for node in tree{ 21 - match node.type_id.as_str(){ 22 - "osctrigger" => { runtime_nodes.insert(node.id.clone(), OSCTrigger::new(node)); } 40 + for node in tree { 41 + match node.type_id.as_str() { 42 + "osctrigger" => { 43 + runtime_nodes.insert(node.id.clone(), OSCTrigger::new(node)); 44 + } 23 45 24 - "staticstring" => { runtime_nodes.insert(node.id.clone(), StaticString::new(node)); } 25 - "staticint" => { runtime_nodes.insert(node.id.clone(), StaticInt::new(node)); } 26 - "staticfloat" => { runtime_nodes.insert(node.id.clone(), StaticFloat::new(node)); } 46 + "staticstring" => { 47 + runtime_nodes.insert(node.id.clone(), StaticString::new(node)); 48 + } 49 + "staticint" => { 50 + runtime_nodes.insert(node.id.clone(), StaticInt::new(node)); 51 + } 52 + "staticfloat" => { 53 + runtime_nodes.insert(node.id.clone(), StaticFloat::new(node)); 54 + } 27 55 28 - "iftrue" => { runtime_nodes.insert(node.id.clone(), ConditionalIfTrue::new(node)); } 29 - "iffalse" => { runtime_nodes.insert(node.id.clone(), ConditionalIfFalse::new(node)); } 30 - "ifequal" => { runtime_nodes.insert(node.id.clone(), ConditionalIfEqual::new(node)); } 56 + "iftrue" => { 57 + runtime_nodes.insert(node.id.clone(), ConditionalIfTrue::new(node)); 58 + } 59 + "iffalse" => { 60 + runtime_nodes.insert(node.id.clone(), ConditionalIfFalse::new(node)); 61 + } 62 + "ifequal" => { 63 + runtime_nodes.insert(node.id.clone(), ConditionalIfEqual::new(node)); 64 + } 65 + 66 + "oscsendchatbox" => { 67 + runtime_nodes.insert(node.id.clone(), OSCActionsSendChatbox::new(node)); 68 + } 69 + 70 + "debug" => { 71 + runtime_nodes.insert(node.id.clone(), Debug::new(node)); 72 + } 73 + 74 + // #[cfg(target_os = "windows")] 75 + "presskey" => { 76 + runtime_nodes.insert(node.id.clone(), PressKey::new(node, enigo.clone())); 77 + } 31 78 32 - "oscsendchatbox" => { runtime_nodes.insert(node.id.clone(), OSCActionsSendChatbox::new(node)); } 79 + "shellcommand" => { 80 + runtime_nodes.insert(node.id.clone(), ShellCommand::new(node)); 81 + } 33 82 34 - "debug" => { runtime_nodes.insert(node.id.clone(), Debug::new(node)); } 35 83 _ => {} 36 84 } 37 85 } 38 86 39 - Self { nodes: runtime_nodes } 87 + Self { 88 + nodes: runtime_nodes, 89 + } 40 90 } 41 91 } 42 92 43 - pub trait RuntimeNode{ 44 - fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>>; // Node ID, input index, output value type 45 - fn execute_dry( &mut self, msg: &Vec<ParameterType> ) -> Option<Vec<ParameterType>>; // Only update values on the first loop through 46 - fn execute( &mut self ) -> Option<Vec<ParameterType>>; // Then call functions on the second loop 47 - fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool; 48 - fn is_entrypoint( &self ) -> bool; 93 + pub trait RuntimeNode { 94 + fn outputs(&self) -> Vec<Vec<(String, isize, isize)>>; // Node ID, input index, output value type 95 + fn inputs(&self) -> Vec<Option<(String, isize, isize)>>; // Node ID, input index, output value type 96 + 97 + fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType>; // Then call functions on the second loop 98 + fn is_entrypoint(&self) -> bool; 49 99 }
+65 -40
src-tauri/src/runtime.rs
··· 1 - use anyhow::{ bail, Result }; 1 + use std::collections::HashMap; 2 + 3 + use anyhow::{bail, Result}; 2 4 3 - use crate::{ runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType }; 5 + use crate::{runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType}; 4 6 5 - pub mod nodes; 6 7 pub mod commands; 8 + pub mod nodes; 7 9 8 - // This is horrible. I know, I'm sorry. 10 + // TODO: Variables 9 11 10 - pub fn runtime_dry( entry: String, parameters: &Vec<ParameterType>, tab: &mut RuntimeNodeTree ) -> Result<()>{ 11 - let node = tab.nodes.get_mut(&entry); 12 - if node.is_none(){ bail!("Cannot find node"); } 12 + pub fn recurse_runtime(entry: String, tab: &mut RuntimeNodeTree, args: Vec<ParameterType>) -> Result<()>{ 13 + let ( out_args, output_map ) = runtime(entry, tab, args)?; 13 14 14 - let node = node.unwrap(); 15 + let mut next_node_args: HashMap<String, Vec<ParameterType>> = HashMap::new(); 15 16 16 - let output_map = node.outputs(); 17 - let args = node.execute_dry(parameters); 17 + for i in 0..out_args.len(){ 18 + if output_map.len() <= i { break; } 19 + let links = &output_map[i]; 18 20 19 - if args.is_some(){ 20 - let args = args.unwrap(); 21 + for ( id, link_index, _ ) in links{ 22 + let link_index = link_index.clone() as usize; 21 23 22 - for i in 0..args.len(){ 23 - let arg = &args[i]; 24 + if next_node_args.contains_key(id){ 25 + let args: &mut _ = next_node_args.get_mut(id).unwrap(); 26 + while args.len() < link_index{ args.push(ParameterType::None); } 24 27 25 - for output in &output_map[i]{ 26 - if output.2 == 5{ break; } // Ignore flow outputs 28 + args.push(out_args[i].clone()); 29 + } else{ 30 + let mut args = vec![ParameterType::None; link_index]; 31 + args.push(out_args[i].clone()); 27 32 28 - let next_node = tab.nodes.get_mut(&output.0); 29 - if next_node.is_none(){ bail!("Cannot find node {}", output.0) } 33 + next_node_args.insert(id.clone(), args); 34 + } 35 + } 36 + } 30 37 31 - let next_node = next_node.unwrap(); 32 - let can_update = next_node.update_arg(output.1 as usize, arg.clone()); 38 + for i in 0..out_args.len(){ 39 + if let ParameterType::Flow(next) = out_args[i]{ 40 + if next{ 41 + let links = &output_map[i]; 33 42 34 - if can_update{ 35 - runtime_dry(output.0.clone(), &vec![], tab)?; 43 + for ( id, _, _ ) in links{ 44 + let args = next_node_args.remove(id).unwrap(); 45 + recurse_runtime(id.clone(), tab, args)?; 36 46 } 37 47 } 38 48 } ··· 41 51 Ok(()) 42 52 } 43 53 44 - pub fn runtime( entry: String, tab: &mut RuntimeNodeTree ) -> Result<()>{ 54 + pub fn runtime(entry: String, tab: &mut RuntimeNodeTree, mut args: Vec<ParameterType>) -> Result<(Vec<ParameterType>, Vec<Vec<(String, isize, isize)>>)> { 45 55 let node = tab.nodes.get_mut(&entry); 46 - if node.is_none(){ bail!("Cannot find node"); } 56 + if node.is_none() { bail!("Cannot find node"); } 47 57 48 58 let node = node.unwrap(); 59 + let inputs = node.inputs(); 49 60 50 - let next = node.execute(); 51 - if next.is_some(){ 52 - let next = next.unwrap(); 61 + let mut needed_input_nodes = HashMap::new(); 53 62 54 - let outputs = node.outputs(); 63 + for i in 0..inputs.len(){ 64 + if i >= args.len() || args[i] == ParameterType::None{ 65 + if let Some(input) = &inputs[i]{ 66 + if !needed_input_nodes.contains_key(&input.0){ 67 + needed_input_nodes.insert(input.0.clone(), vec![(input.1.clone(), i.clone())]); 68 + } else{ 69 + needed_input_nodes.get_mut(&input.0).unwrap().push((input.1.clone(), i.clone())); 70 + } 71 + } 72 + } 73 + } 55 74 56 - for i in 0..next.len(){ 57 - let arg = &next[i]; 58 - if i >= outputs.len() { break; } 75 + for ( id, needed ) in needed_input_nodes{ 76 + let (out_args, _) = runtime(id, tab, vec![]).unwrap(); 77 + 78 + for ( output, input ) in needed{ 79 + let arg = &out_args[output as usize]; 59 80 60 - for output in &outputs[i]{ 61 - if let ParameterType::Flow(next) = arg{ 62 - if *next{ 63 - // This is a flow output, continue 64 - runtime(output.0.clone(), tab)?; 65 - } 66 - } 81 + if args.len() >= input{ 82 + while args.len() < input{ args.push(ParameterType::None); } 83 + args.push(arg.clone()); 84 + } else{ 85 + args[input] = arg.clone(); 67 86 } 68 87 } 69 88 } 70 89 71 - Ok(()) 72 - } 90 + let node = tab.nodes.get_mut(&entry); // TODO: Find a way to only do this lookup once 91 + if node.is_none() { bail!("Cannot find node"); } 92 + 93 + let node = node.unwrap(); 94 + 95 + let output = node.execute(args); 96 + Ok((output, node.outputs())) 97 + }
+62 -39
src-tauri/src/setup.rs
··· 1 - use std::{ collections::HashMap, fs::File, io::Read, sync::Mutex }; 2 - use crossbeam_channel::{ Receiver, bounded }; 1 + use crossbeam_channel::{bounded, Receiver}; 2 + use std::{ 3 + collections::HashMap, 4 + fs::File, 5 + io::Read, 6 + sync::{Arc, Mutex}, 7 + }; 3 8 4 9 use flate2::read::GzDecoder; 5 - use serde_json::{ Map, Value }; 6 - use tauri::{ App, Emitter, Listener, Manager, WindowEvent }; 10 + use serde_json::{Map, Value}; 11 + use tauri::{App, Emitter, Listener, Manager, WindowEvent}; 7 12 8 - use crate::{ osc::{ self, OSCMessage }, runtime::{ commands::RuntimeCommand, nodes::RuntimeNodeTree, runtime, runtime_dry }, structs::parameter_types::ParameterType, utils::setup_traymenu::setup_traymenu }; 13 + use crate::{ 14 + osc::{self, OSCMessage}, runtime::{commands::RuntimeCommand, nodes::RuntimeNodeTree, recurse_runtime}, structs::parameter_types::ParameterType, utils::{setup_traymenu::setup_traymenu, vrchat_builtin_parameters} 15 + }; 9 16 10 17 pub fn setup( 11 18 app: &mut App, 12 19 addresses: &'static Mutex<Vec<OSCMessage>>, 13 - runtime_command_receiver: Receiver<RuntimeCommand> 20 + runtime_command_receiver: Receiver<RuntimeCommand>, 14 21 ) { 15 22 let window = app.get_webview_window("main").unwrap(); 16 23 window.hide().unwrap(); 17 24 18 25 let win_handle = window.clone(); 19 - window.on_window_event(move | event | { 20 - match event{ 21 - WindowEvent::CloseRequested { api, .. } => { 22 - api.prevent_close(); 23 26 27 + window.on_window_event(move |event| match event { 28 + WindowEvent::CloseRequested { api, .. } => { 29 + api.prevent_close(); 30 + win_handle.emit("prompt_to_close", ()).unwrap(); 31 + } 32 + WindowEvent::Resized(_) => { 33 + let minimised = win_handle.is_minimized().unwrap(); 34 + if minimised{ 24 35 win_handle.hide().unwrap(); 25 36 win_handle.emit("hide-window", ()).unwrap(); 37 + win_handle.unminimize().unwrap(); 26 38 } 27 - _ => {} 28 39 } 40 + _ => {} 29 41 }); 30 42 31 43 setup_traymenu(app.handle()); ··· 49 61 handle.emit("load_new_tab", Value::Object(map)).unwrap(); 50 62 }); 51 63 52 - let ( sender, receiver ) = bounded(1024); 64 + let (sender, receiver) = bounded(1024); 53 65 54 66 tokio::spawn(async move { 55 67 osc::start_server(sender, "127.0.0.1:9001"); 56 68 }); 57 69 58 - let ( runtime_sender, runtime_receiver ) = bounded(1024); 70 + let (runtime_sender, runtime_receiver) = bounded(1024); 59 71 60 72 let runtime_sender_1 = runtime_sender.clone(); 61 73 tokio::spawn(async move { ··· 73 85 74 86 let msg = message.clone(); 75 87 let mut addrs = addresses.lock().unwrap(); 76 - if !addrs.contains(&msg) { addrs.push(msg); } 88 + if !addrs.contains(&msg) { 89 + addrs.push(msg); 90 + } 91 + 92 + if message.address == "/avatar/change".to_owned(){ 93 + *addrs = vrchat_builtin_parameters::get_read_addresses(); 77 94 78 - runtime_sender.send(RuntimeCommand::OSCMessage(message)).unwrap(); 95 + // TODO: Read avatar paramaters from file 96 + } 97 + 98 + runtime_sender 99 + .send(RuntimeCommand::OSCMessage(message)) 100 + .unwrap(); 79 101 } 80 102 }); 81 103 104 + // TODO: Run tabs in seperate threads (really not looking forward to this... thanks rust) 105 + 82 106 tokio::spawn(async move { 83 107 let mut tabs: HashMap<String, RuntimeNodeTree> = HashMap::new(); 84 108 109 + // #[cfg(target_os = "windows")] 110 + let enigo = Arc::new(Mutex::new(enigo::Enigo::new(&enigo::Settings::default()).unwrap())); 111 + 85 112 loop { 86 113 let cmd = runtime_receiver.recv().unwrap(); 87 114 88 - match cmd{ 89 - RuntimeCommand::OSCMessage( msg ) => { 90 - for ( _, mut tab ) in &mut tabs{ 91 - let keys: Vec<String> = tab.nodes.keys().map(| x | { x.clone() }).collect(); 115 + match cmd { 116 + RuntimeCommand::OSCMessage(msg) => { 117 + for (_, mut tab) in &mut tabs { 118 + let keys: Vec<String> = tab.nodes.keys().map(|x| x.clone()).collect(); 92 119 93 - for id in keys.clone(){ 120 + for id in keys { 94 121 let entry = tab.nodes[&id].is_entrypoint(); 95 122 96 - if entry{ 97 - let args = vec![ 98 - vec![ ParameterType::String(msg.address.clone()) ], msg.values.clone() 99 - ].concat(); 100 - 101 - runtime_dry(id.clone(), &args, &mut tab).unwrap(); 102 - } 103 - } 104 - 105 - for id in keys{ 106 - let entry = tab.nodes[&id].is_entrypoint(); 123 + if entry { 124 + let mut args = vec![ ParameterType::String(msg.address.clone())]; 125 + let mut values = msg.values.clone(); 107 126 108 - if entry{ 109 - let _ = runtime(id.clone(), &mut tab); 127 + args.append(&mut values); 128 + let _ = recurse_runtime(id.clone(), &mut tab, args); 110 129 } 111 130 } 112 131 } 113 - }, 132 + } 133 + 134 + RuntimeCommand::AddTab(graph, id) => { 135 + // #[cfg(target_os = "windows")] 136 + tabs.insert(id, RuntimeNodeTree::from(graph, enigo.clone())); 114 137 115 - RuntimeCommand::AddTab( graph, id ) => { 116 - tabs.insert(id, RuntimeNodeTree::from(graph)); 117 - }, 118 - RuntimeCommand::RemoveTab( id ) => { 138 + // #[cfg(target_os = "linux")] 139 + // tabs.insert(id, RuntimeNodeTree::from(graph)); 140 + } 141 + RuntimeCommand::RemoveTab(id) => { 119 142 tabs.remove(&id); 120 143 } 121 144 } 122 145 } 123 146 }); 124 - } 147 + }
+1 -1
src-tauri/src/structs/mod.rs
··· 1 + pub mod nodes; 1 2 pub mod parameter_types; 2 - pub mod nodes;
+13 -12
src-tauri/src/structs/nodes.rs
··· 1 - use serde::{ Deserialize, Serialize }; 1 + use serde::{Deserialize, Serialize}; 2 2 use serde_json::Value; 3 3 4 4 #[derive(Serialize, Deserialize, Debug, Clone)] 5 - pub struct Node{ 5 + pub struct Node { 6 6 pub id: String, 7 7 pub name: String, 8 - pub outputs: Vec<NodeOutput>, 9 - pub pos: [ f32; 2 ], 8 + pub outputs: Vec<NodeIO>, 9 + pub inputs: Vec<NodeIO>, 10 + pub pos: [f32; 2], 10 11 pub statics: Vec<NodeStatic>, 11 12 12 13 #[serde(rename = "typeId")] 13 - pub type_id: String 14 + pub type_id: String, 14 15 } 15 16 16 17 #[derive(Serialize, Deserialize, Debug, Clone)] 17 - pub struct NodeStatic{ 18 + pub struct NodeStatic { 18 19 pub name: String, 19 20 20 21 #[serde(rename = "type")] 21 22 pub value_type: isize, 22 - pub value: Value 23 + pub value: Value, 23 24 } 24 25 25 26 #[derive(Serialize, Deserialize, Debug, Clone)] 26 - pub struct NodeOutput{ 27 + pub struct NodeIO { 27 28 pub name: String, 28 29 29 30 #[serde(rename = "type")] 30 31 pub value_type: isize, 31 - pub connections: Vec<NodeOutputConnections> 32 + pub connections: Vec<NodeOutputConnections>, 32 33 } 33 34 34 35 #[derive(Serialize, Deserialize, Debug, Clone)] 35 - pub struct NodeOutputConnections{ 36 + pub struct NodeOutputConnections { 36 37 pub name: String, 37 38 pub node: String, 38 39 pub index: isize, 39 40 40 41 #[serde(rename = "type")] 41 - pub value_type: isize 42 - } 42 + pub value_type: isize, 43 + }
+41 -29
src-tauri/src/structs/parameter_types.rs
··· 1 - use anyhow::{ Result, bail }; 1 + use anyhow::{bail, Result}; 2 2 use serde::Serialize; 3 3 4 4 #[derive(Serialize, Clone, Debug, PartialEq)] ··· 13 13 String(String), 14 14 Flow(bool), 15 15 16 - None 16 + None, 17 17 } 18 18 19 - impl ParameterType{ 20 - pub fn as_bool( &self ) -> Result<bool>{ 21 - match self{ 22 - ParameterType::Boolean( val ) => Ok(val.clone()), 23 - ParameterType::Int( val ) => if *val == 0{ Ok(false) } else { Ok(true) }, 24 - _ => bail!("Cannot cast to bool.") 19 + impl ParameterType { 20 + pub fn as_bool(&self) -> Result<bool> { 21 + match self { 22 + ParameterType::Boolean(val) => Ok(val.clone()), 23 + ParameterType::Int(val) => { 24 + if *val == 0 { 25 + Ok(false) 26 + } else { 27 + Ok(true) 28 + } 29 + } 30 + _ => bail!("Cannot cast to bool."), 25 31 } 26 32 } 27 33 28 - pub fn as_int( &self ) -> Result<i32>{ 29 - match self{ 30 - ParameterType::Boolean( val ) => if *val{ Ok(1) } else { Ok(0) }, 31 - ParameterType::Int( val ) => Ok(val.clone()), 32 - ParameterType::Float( val ) => Ok(val.round().clone() as i32), 33 - ParameterType::String( val ) => Ok(val.clone().parse()?), 34 - _ => bail!("Cannot cast to int.") 34 + pub fn as_int(&self) -> Result<i32> { 35 + match self { 36 + ParameterType::Boolean(val) => { 37 + if *val { 38 + Ok(1) 39 + } else { 40 + Ok(0) 41 + } 42 + } 43 + ParameterType::Int(val) => Ok(val.clone()), 44 + ParameterType::Float(val) => Ok(val.round().clone() as i32), 45 + ParameterType::String(val) => Ok(val.clone().parse()?), 46 + _ => bail!("Cannot cast to int."), 35 47 } 36 48 } 37 49 38 - pub fn as_float( &self ) -> Result<f32>{ 39 - match self{ 40 - ParameterType::Int( val ) => Ok(val.clone() as f32), 41 - ParameterType::Float( val ) => Ok(val.clone()), 42 - ParameterType::String( val ) => Ok(val.clone().parse()?), 43 - _ => bail!("Cannot cast to float.") 50 + pub fn as_float(&self) -> Result<f32> { 51 + match self { 52 + ParameterType::Int(val) => Ok(val.clone() as f32), 53 + ParameterType::Float(val) => Ok(val.clone()), 54 + ParameterType::String(val) => Ok(val.clone().parse()?), 55 + _ => bail!("Cannot cast to float."), 44 56 } 45 57 } 46 58 47 - pub fn as_string( &self ) -> Result<String>{ 48 - match self{ 49 - ParameterType::Boolean( val ) => Ok(val.clone().to_string()), 50 - ParameterType::Int( val ) => Ok(val.clone().to_string()), 51 - ParameterType::Float( val ) => Ok(val.clone().to_string()), 52 - ParameterType::String( val ) => Ok(val.clone()), 53 - _ => bail!("Cannot cast to string.") 59 + pub fn as_string(&self) -> Result<String> { 60 + match self { 61 + ParameterType::Boolean(val) => Ok(val.clone().to_string()), 62 + ParameterType::Int(val) => Ok(val.clone().to_string()), 63 + ParameterType::Float(val) => Ok(val.clone().to_string()), 64 + ParameterType::String(val) => Ok(val.clone()), 65 + _ => bail!("Cannot cast to string."), 54 66 } 55 67 } 56 - } 68 + }
+26 -10
src-tauri/src/utils/config.rs
··· 1 - use std::{ collections::HashMap, fs::File, io::{ Read, Write }, path::PathBuf, sync::Mutex }; 1 + use std::{ 2 + collections::HashMap, 3 + fs::File, 4 + io::{Read, Write}, 5 + path::PathBuf, 6 + sync::{Mutex, MutexGuard}, 7 + }; 2 8 3 - use flate2::{ read::GzDecoder, write::GzEncoder, Compression }; 4 - use serde::{ Deserialize, Serialize }; 9 + use chrono::Utc; 10 + use flate2::{read::GzDecoder, write::GzEncoder, Compression}; 11 + use serde::{Deserialize, Serialize}; 5 12 6 13 use crate::structs::nodes::Node; 7 14 8 15 #[derive(Clone, Serialize, Deserialize, Debug)] 9 - pub struct ConfigValues{ 16 + pub struct ConfigValues { 10 17 #[serde(default)] 11 - pub loaded_tabs: HashMap<String, ( Vec<Node>, String, Option<String> )>, 18 + pub loaded_tabs: HashMap<String, (Vec<Node>, String, Option<String>, bool)>, 12 19 13 20 #[serde(default)] 14 - pub hide_editor_on_start: bool 21 + pub hide_editor_on_start: bool, 15 22 } 16 23 17 24 pub struct Config { ··· 33 40 34 41 let json: ConfigValues = serde_json::from_str(&json_string).unwrap(); 35 42 36 - dbg!(&json); 37 - 38 43 Config { 39 44 store: Mutex::new(json), 40 45 path: path, ··· 42 47 } 43 48 44 49 pub fn save(&self) { 45 - let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap(); 46 - dbg!(&dat); 50 + let mut dat = self.store.lock().unwrap(); 51 + 52 + let dat = serde_json::to_string(&*dat).unwrap(); 53 + 54 + let file = File::create(&self.path).unwrap(); 55 + let mut encoder = GzEncoder::new(file, Compression::default()); 56 + 57 + encoder.write_all(dat.as_bytes()).unwrap(); 58 + encoder.finish().unwrap(); 59 + } 60 + 61 + pub fn save_prelocked(&self, mut dat: MutexGuard<'_, ConfigValues>) { 62 + let dat = serde_json::to_string(&*dat).unwrap(); 47 63 48 64 let file = File::create(&self.path).unwrap(); 49 65 let mut encoder = GzEncoder::new(file, Compression::default());
+2 -1
src-tauri/src/utils/mod.rs
··· 1 1 pub mod config; 2 - pub mod setup_traymenu; 2 + pub mod setup_traymenu; 3 + pub mod vrchat_builtin_parameters;
+13 -15
src-tauri/src/utils/setup_traymenu.rs
··· 1 - use tauri::{ AppHandle, Emitter, Manager, menu::{ MenuBuilder, MenuItemBuilder }, tray::TrayIconBuilder }; 1 + use tauri::{ 2 + menu::{MenuBuilder, MenuItemBuilder}, 3 + tray::TrayIconBuilder, 4 + AppHandle, Emitter, Manager, 5 + }; 2 6 3 - pub fn setup_traymenu( handle: &AppHandle ) { 7 + pub fn setup_traymenu(handle: &AppHandle) { 4 8 // Setup the tray icon and menu buttons 5 9 let quit = MenuItemBuilder::new("Quit") 6 10 .id("quit") 7 11 .build(handle) 8 12 .unwrap(); 9 13 10 - let hide = MenuItemBuilder::new("Hide / Show Editor") 11 - .id("hide") 14 + let show = MenuItemBuilder::new("Show Editor") 15 + .id("show") 12 16 .build(handle) 13 17 .unwrap(); 14 18 15 19 let tray_menu = MenuBuilder::new(handle) 16 - .items(&[&quit, &hide]) 20 + .items(&[&quit, &show]) 17 21 .build() 18 22 .unwrap(); 19 23 ··· 26 30 "quit" => { 27 31 app.emit("prompt_to_close", ()).unwrap(); 28 32 } 29 - "hide" => { 33 + "show" => { 30 34 let window = app.get_webview_window("main").unwrap(); 31 35 32 - if window.is_visible().unwrap() { 33 - window.hide().unwrap(); 34 - 35 - window.emit("hide-window", ()).unwrap(); 36 - } else { 37 - window.show().unwrap(); 38 - window.set_focus().unwrap(); 36 + window.show().unwrap(); 37 + window.set_focus().unwrap(); 39 38 40 - window.emit("show-window", ()).unwrap(); 41 - } 39 + window.emit("show-window", ()).unwrap(); 42 40 } 43 41 _ => {} 44 42 })
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
··· 1 + use crate::{osc::OSCMessage, structs::parameter_types::ParameterType}; 2 + 3 + pub fn get_read_addresses() -> Vec<OSCMessage>{ 4 + vec![ 5 + // Avatar Parameters 6 + OSCMessage { 7 + address: "/avatar/change".into(), 8 + values: vec![ ParameterType::String("".into()) ] 9 + }, 10 + 11 + OSCMessage { 12 + address: "/avatar/parameters/VRCEmote".into(), 13 + values: vec![ ParameterType::Int(0) ] 14 + }, 15 + OSCMessage { 16 + address: "/avatar/parameters/VRCFaceBlendV".into(), 17 + values: vec![ ParameterType::Float(0.0) ] 18 + }, 19 + OSCMessage { 20 + address: "/avatar/parameters/VRCFaceBlendH".into(), 21 + values: vec![ ParameterType::Float(0.0) ] 22 + }, 23 + OSCMessage { 24 + address: "/avatar/parameters/PreviewMode".into(), 25 + values: vec![ ParameterType::Int(0) ] 26 + }, 27 + OSCMessage { 28 + address: "/avatar/parameters/IsAnimatorEnabled".into(), 29 + values: vec![ ParameterType::Boolean(true) ] 30 + }, 31 + 32 + OSCMessage { 33 + address: "/avatar/parameters/GestureRightWeight".into(), 34 + values: vec![ ParameterType::Float(0.0) ] 35 + }, 36 + OSCMessage { 37 + address: "/avatar/parameters/GestureLeftWeight".into(), 38 + values: vec![ ParameterType::Float(0.0) ] 39 + }, 40 + OSCMessage { 41 + address: "/avatar/parameters/GestureRight".into(), 42 + values: vec![ ParameterType::Int(0) ] 43 + }, 44 + OSCMessage { 45 + address: "/avatar/parameters/GestureLeft".into(), 46 + values: vec![ ParameterType::Int(0) ] 47 + }, 48 + 49 + OSCMessage { 50 + address: "/avatar/parameters/ScaleModified".into(), 51 + values: vec![ ParameterType::Boolean(true) ] 52 + }, 53 + OSCMessage { 54 + address: "/avatar/parameters/ScaleFactor".into(), 55 + values: vec![ ParameterType::Float(1.0) ] 56 + }, 57 + OSCMessage { 58 + address: "/avatar/parameters/ScaleFactorInverse".into(), 59 + values: vec![ ParameterType::Float(1.0) ] 60 + }, 61 + OSCMessage { 62 + address: "/avatar/parameters/EyeHeightAsPercent".into(), 63 + values: vec![ ParameterType::Float(1.0) ] 64 + }, 65 + 66 + OSCMessage { 67 + address: "/avatar/parameters/Viseme".into(), 68 + values: vec![ ParameterType::Int(0) ] 69 + }, 70 + OSCMessage { 71 + address: "/avatar/parameters/Voice".into(), 72 + values: vec![ ParameterType::Float(0.0) ] 73 + }, 74 + OSCMessage { 75 + address: "/avatar/parameters/Earmuffs".into(), 76 + values: vec![ ParameterType::Boolean(true) ] 77 + }, 78 + OSCMessage { 79 + address: "/avatar/parameters/MuteSelf".into(), 80 + values: vec![ ParameterType::Boolean(true) ] 81 + }, 82 + 83 + OSCMessage { 84 + address: "/avatar/parameters/AFK".into(), 85 + values: vec![ ParameterType::Boolean(true) ] 86 + }, 87 + OSCMessage { 88 + address: "/avatar/parameters/InStation".into(), 89 + values: vec![ ParameterType::Boolean(true) ] 90 + }, 91 + OSCMessage { 92 + address: "/avatar/parameters/Seated".into(), 93 + values: vec![ ParameterType::Boolean(true) ] 94 + }, 95 + OSCMessage { 96 + address: "/avatar/parameters/VRMode".into(), 97 + values: vec![ ParameterType::Int(0) ] 98 + }, 99 + OSCMessage { 100 + address: "/avatar/parameters/TrackingType".into(), 101 + values: vec![ ParameterType::Int(0) ] 102 + }, 103 + 104 + OSCMessage { 105 + address: "/avatar/parameters/Grounded".into(), 106 + values: vec![ ParameterType::Boolean(true) ] 107 + }, 108 + OSCMessage { 109 + address: "/avatar/parameters/Upright".into(), 110 + values: vec![ ParameterType::Float(1.0) ] 111 + }, 112 + OSCMessage { 113 + address: "/avatar/parameters/AngularY".into(), 114 + values: vec![ ParameterType::Float(1.0) ] 115 + }, 116 + OSCMessage { 117 + address: "/avatar/parameters/VelocityX".into(), 118 + values: vec![ ParameterType::Float(1.0) ] 119 + }, 120 + OSCMessage { 121 + address: "/avatar/parameters/VelocityY".into(), 122 + values: vec![ ParameterType::Float(1.0) ] 123 + }, 124 + OSCMessage { 125 + address: "/avatar/parameters/VelocityZ".into(), 126 + values: vec![ ParameterType::Float(1.0) ] 127 + }, 128 + OSCMessage { 129 + address: "/avatar/parameters/VelocityMagnitude".into(), 130 + values: vec![ ParameterType::Float(1.0) ] 131 + }, 132 + 133 + // User Camera 134 + OSCMessage { 135 + address: "/usercamera/Mode".into(), 136 + values: vec![ ParameterType::Int(0) ] 137 + }, 138 + OSCMessage { 139 + address: "/usercamera/Pose".into(), 140 + values: vec![ 141 + ParameterType::Float(0.0), 142 + ParameterType::Float(0.0), 143 + ParameterType::Float(0.0), 144 + 145 + ParameterType::Float(0.0), 146 + ParameterType::Float(0.0), 147 + ParameterType::Float(0.0) 148 + ] 149 + }, 150 + 151 + OSCMessage { 152 + address: "/usercamera/ShowUIInCamera".into(), 153 + values: vec![ ParameterType::Boolean(true) ] 154 + }, 155 + OSCMessage { 156 + address: "/usercamera/LocalPlayer".into(), 157 + values: vec![ ParameterType::Boolean(true) ] 158 + }, 159 + OSCMessage { 160 + address: "/usercamera/RemotePlayer".into(), 161 + values: vec![ ParameterType::Boolean(true) ] 162 + }, 163 + OSCMessage { 164 + address: "/usercamera/Environment".into(), 165 + values: vec![ ParameterType::Boolean(true) ] 166 + }, 167 + OSCMessage { 168 + address: "/usercamera/GreenScreen".into(), 169 + values: vec![ ParameterType::Boolean(true) ] 170 + }, 171 + OSCMessage { 172 + address: "/usercamera/Lock".into(), 173 + values: vec![ ParameterType::Boolean(true) ] 174 + }, 175 + OSCMessage { 176 + address: "/usercamera/SmoothMovement".into(), 177 + values: vec![ ParameterType::Boolean(true) ] 178 + }, 179 + OSCMessage { 180 + address: "/usercamera/LookAtMe".into(), 181 + values: vec![ ParameterType::Boolean(true) ] 182 + }, 183 + OSCMessage { 184 + address: "/usercamera/AutoLevelRoll".into(), 185 + values: vec![ ParameterType::Boolean(true) ] 186 + }, 187 + OSCMessage { 188 + address: "/usercamera/AutoLevelPitch".into(), 189 + values: vec![ ParameterType::Boolean(true) ] 190 + }, 191 + OSCMessage { 192 + address: "/usercamera/Flying".into(), 193 + values: vec![ ParameterType::Boolean(true) ] 194 + }, 195 + OSCMessage { 196 + address: "/usercamera/TriggerTakesPhotos".into(), 197 + values: vec![ ParameterType::Boolean(true) ] 198 + }, 199 + OSCMessage { 200 + address: "/usercamera/DollyPathsStayVisible".into(), 201 + values: vec![ ParameterType::Boolean(true) ] 202 + }, 203 + OSCMessage { 204 + address: "/usercamera/AudioFromCamera".into(), 205 + values: vec![ ParameterType::Boolean(true) ] 206 + }, 207 + OSCMessage { 208 + address: "/usercamera/ShowFocus".into(), 209 + values: vec![ ParameterType::Boolean(true) ] 210 + }, 211 + OSCMessage { 212 + address: "/usercamera/Streaming".into(), 213 + values: vec![ ParameterType::Boolean(true) ] 214 + }, 215 + OSCMessage { 216 + address: "/usercamera/RollWhileFlying".into(), 217 + values: vec![ ParameterType::Boolean(true) ] 218 + }, 219 + OSCMessage { 220 + address: "/usercamera/OrientationIsLandscape".into(), 221 + values: vec![ ParameterType::Boolean(true) ] 222 + }, 223 + 224 + OSCMessage { 225 + address: "/usercamera/Zoom".into(), 226 + values: vec![ ParameterType::Float(0.0) ] 227 + }, 228 + OSCMessage { 229 + address: "/usercamera/Exposure".into(), 230 + values: vec![ ParameterType::Float(0.0) ] 231 + }, 232 + OSCMessage { 233 + address: "/usercamera/Aperture".into(), 234 + values: vec![ ParameterType::Float(0.0) ] 235 + }, 236 + OSCMessage { 237 + address: "/usercamera/Hue".into(), 238 + values: vec![ ParameterType::Float(0.0) ] 239 + }, 240 + OSCMessage { 241 + address: "/usercamera/Saturation".into(), 242 + values: vec![ ParameterType::Float(0.0) ] 243 + }, 244 + OSCMessage { 245 + address: "/usercamera/Lightness".into(), 246 + values: vec![ ParameterType::Float(0.0) ] 247 + }, 248 + OSCMessage { 249 + address: "/usercamera/LookAtMeXOffset".into(), 250 + values: vec![ ParameterType::Float(0.0) ] 251 + }, 252 + OSCMessage { 253 + address: "/usercamera/LookAtMeYOffset".into(), 254 + values: vec![ ParameterType::Float(0.0) ] 255 + }, 256 + OSCMessage { 257 + address: "/usercamera/FlySpeed".into(), 258 + values: vec![ ParameterType::Float(0.0) ] 259 + }, 260 + OSCMessage { 261 + address: "/usercamera/TurnSpeed".into(), 262 + values: vec![ ParameterType::Float(0.0) ] 263 + }, 264 + OSCMessage { 265 + address: "/usercamera/SmoothStrength".into(), 266 + values: vec![ ParameterType::Float(0.0) ] 267 + }, 268 + OSCMessage { 269 + address: "/usercamera/PhotoRate".into(), 270 + values: vec![ ParameterType::Float(0.0) ] 271 + }, 272 + OSCMessage { 273 + address: "/usercamera/Duration".into(), 274 + values: vec![ ParameterType::Float(0.0) ] 275 + }, 276 + 277 + // Trackers 278 + OSCMessage { 279 + address: "/tracking/vrsystem/head/pose".into(), 280 + values: vec![ 281 + ParameterType::Float(0.0), 282 + ParameterType::Float(0.0), 283 + ParameterType::Float(0.0), 284 + 285 + ParameterType::Float(0.0), 286 + ParameterType::Float(0.0), 287 + ParameterType::Float(0.0), 288 + ] 289 + }, 290 + OSCMessage { 291 + address: "/tracking/vrsystem/rightwrist/pose".into(), 292 + values: vec![ 293 + ParameterType::Float(0.0), 294 + ParameterType::Float(0.0), 295 + ParameterType::Float(0.0), 296 + 297 + ParameterType::Float(0.0), 298 + ParameterType::Float(0.0), 299 + ParameterType::Float(0.0), 300 + ] 301 + }, 302 + OSCMessage { 303 + address: "/tracking/vrsystem/leftwrist/pose".into(), 304 + values: vec![ 305 + ParameterType::Float(0.0), 306 + ParameterType::Float(0.0), 307 + ParameterType::Float(0.0), 308 + 309 + ParameterType::Float(0.0), 310 + ParameterType::Float(0.0), 311 + ParameterType::Float(0.0), 312 + ] 313 + }, 314 + ] 315 + }
+1 -1
vite.config.ts
··· 6 6 7 7 // https://vitejs.dev/config/ 8 8 export default defineConfig(async () => ({ 9 - plugins: [solid()], 9 + plugins: [ solid() ], 10 10 11 11 // Vite options tailored for Tauri development and only applied in `tauri dev` or `tauri build` 12 12 //