+4
-1
.gitignore
+4
-1
.gitignore
-3
.vscode/extensions.json
-3
.vscode/extensions.json
+2
package.json
+2
package.json
···
13
"license": "MIT",
14
"dependencies": {
15
"@tauri-apps/api": "^2.9.0",
16
+
"@tauri-apps/plugin-clipboard-manager": "~2",
17
"@tauri-apps/plugin-dialog": "~2.4.2",
18
"@tauri-apps/plugin-opener": "^2.5.2",
19
+
"@tauri-apps/plugin-os": "~2",
20
"animejs": "^4.2.2",
21
"solid-js": "^1.9.10"
22
},
+20
pnpm-lock.yaml
+20
pnpm-lock.yaml
···
11
'@tauri-apps/api':
12
specifier: ^2.9.0
13
version: 2.9.0
14
'@tauri-apps/plugin-dialog':
15
specifier: ~2.4.2
16
version: 2.4.2
17
'@tauri-apps/plugin-opener':
18
specifier: ^2.5.2
19
version: 2.5.2
20
animejs:
21
specifier: ^4.2.2
22
version: 4.2.2
···
479
engines: {node: '>= 10'}
480
hasBin: true
481
482
'@tauri-apps/plugin-dialog@2.4.2':
483
resolution: {integrity: sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==}
484
485
'@tauri-apps/plugin-opener@2.5.2':
486
resolution: {integrity: sha512-ei/yRRoCklWHImwpCcDK3VhNXx+QXM9793aQ64YxpqVF0BDuuIlXhZgiAkc15wnPVav+IbkYhmDJIv5R326Mew==}
487
488
'@types/animejs@3.1.13':
489
resolution: {integrity: sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==}
···
1065
'@tauri-apps/cli-win32-ia32-msvc': 2.9.3
1066
'@tauri-apps/cli-win32-x64-msvc': 2.9.3
1067
1068
'@tauri-apps/plugin-dialog@2.4.2':
1069
dependencies:
1070
'@tauri-apps/api': 2.9.0
1071
1072
'@tauri-apps/plugin-opener@2.5.2':
1073
dependencies:
1074
'@tauri-apps/api': 2.9.0
1075
···
11
'@tauri-apps/api':
12
specifier: ^2.9.0
13
version: 2.9.0
14
+
'@tauri-apps/plugin-clipboard-manager':
15
+
specifier: ~2
16
+
version: 2.3.2
17
'@tauri-apps/plugin-dialog':
18
specifier: ~2.4.2
19
version: 2.4.2
20
'@tauri-apps/plugin-opener':
21
specifier: ^2.5.2
22
version: 2.5.2
23
+
'@tauri-apps/plugin-os':
24
+
specifier: ~2
25
+
version: 2.3.2
26
animejs:
27
specifier: ^4.2.2
28
version: 4.2.2
···
485
engines: {node: '>= 10'}
486
hasBin: true
487
488
+
'@tauri-apps/plugin-clipboard-manager@2.3.2':
489
+
resolution: {integrity: sha512-CUlb5Hqi2oZbcZf4VUyUH53XWPPdtpw43EUpCza5HWZJwxEoDowFzNUDt1tRUXA8Uq+XPn17Ysfptip33sG4eQ==}
490
+
491
'@tauri-apps/plugin-dialog@2.4.2':
492
resolution: {integrity: sha512-lNIn5CZuw8WZOn8zHzmFmDSzg5zfohWoa3mdULP0YFh/VogVdMVWZPcWSHlydsiJhRQYaTNSYKN7RmZKE2lCYQ==}
493
494
'@tauri-apps/plugin-opener@2.5.2':
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==}
499
500
'@types/animejs@3.1.13':
501
resolution: {integrity: sha512-yWg9l1z7CAv/TKpty4/vupEh24jDGUZXv4r26StRkpUPQm04ztJaftgpto8vwdFs8SiTq6XfaPKCSI+wjzNMvQ==}
···
1077
'@tauri-apps/cli-win32-ia32-msvc': 2.9.3
1078
'@tauri-apps/cli-win32-x64-msvc': 2.9.3
1079
1080
+
'@tauri-apps/plugin-clipboard-manager@2.3.2':
1081
+
dependencies:
1082
+
'@tauri-apps/api': 2.9.0
1083
+
1084
'@tauri-apps/plugin-dialog@2.4.2':
1085
dependencies:
1086
'@tauri-apps/api': 2.9.0
1087
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':
1093
dependencies:
1094
'@tauri-apps/api': 2.9.0
1095
+147
-53
src/App.tsx
+147
-53
src/App.tsx
···
1
-
import { createSignal, onCleanup, onMount } from "solid-js";
2
import "./App.css";
3
import { renderBackgroundGrid, renderContextMenu, renderNodes, renderNullTab, renderTempDrawing } from "./renderer";
4
import { lerp } from "./utils/lerp";
···
13
import { ConfirmationPopup } from "./components/ConfirmationPopup";
14
15
import * as keybinds from './keybinds';
16
17
let App = () => {
18
-
let [ selectedNode, setSelectedNode ] = createSignal<Node | null>(null);
19
20
let canvas!: HTMLCanvasElement;
21
let ctx: CanvasRenderingContext2D;
···
56
visible: false
57
}
58
59
-
onMount(() => {
60
-
NodeManager.Instance.HookTabChange(() => setSelectedNode(null));
61
62
ctx = canvas.getContext('2d')!;
63
···
100
}
101
102
if(clickedNode){
103
-
contextMenu.items = NodeContextMenu(clickedNode, selectedNode, setSelectedNode);
104
} else{
105
contextMenu.items = CanvasContextMenu;
106
}
···
109
contextMenu.visible = true;
110
}
111
112
canvas.onmousedown = ( e ) => {
113
if(
114
e.clientY < 60 ||
115
e.clientX < 220 ||
···
118
119
if(e.button !== 0){
120
contextMenu.visible = false;
121
-
return;
122
-
}
123
-
124
-
if(e.shiftKey){
125
-
// TODO: Multi-select
126
return;
127
}
128
···
160
161
if(nodes){
162
nodes.map(node => {
163
-
node.selected = false;
164
-
165
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
166
e.clientX, e.clientY,
167
node.x - 20, node.y, node.w + 40, node.h
···
220
221
movingNode = clickedNode;
222
223
-
if(clickedNode){
224
-
clickedNode.selected = true;
225
-
setSelectedNode(clickedNode);
226
-
}
227
-
228
isMouseDown = true;
229
mouseStartPos = [ e.clientX, e.clientY ];
230
}
231
232
canvas.onmousemove = ( e ) => {
233
if(isMouseDown){
234
if(isDrawing){
235
drawingTo = screenToWorldSpace(canvas, { x: offset[0], y: offset[1], scale }, e.clientX - 10 * scale, e.clientY - 10 * scale) as [ number, number ];
236
} else if(movingNode){
237
-
movingNode.x = movingNode.x - (mouseStartPos[0] - e.clientX) / scale;
238
-
movingNode.y = movingNode.y - (mouseStartPos[1] - e.clientY) / scale;
239
240
-
mouseStartPos = [ e.clientX, e.clientY ];
241
NodeManager.Instance.UpdateConfig();
242
} else{
243
-
offsetTarget = [ offsetTarget[0] - (mouseStartPos[0] - e.clientX) / scale, offsetTarget[1] - (mouseStartPos[1] - e.clientY) / scale ];
244
-
mouseStartPos = [ e.clientX, e.clientY ];
245
246
screenMoved = true;
247
}
···
285
286
canvas.onmouseup = ( e ) => {
287
let nodes = NodeManager.Instance.GetNodes();
288
289
if(nodes){
290
nodes.map(node => {
291
-
node.inputs.map(( input, i ) => {
292
-
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
293
-
e.clientX, e.clientY,
294
-
node.x - 10,
295
-
node.y + 50 + (30 * i),
296
-
20, 20
297
-
)){
298
-
if(isDrawing){
299
-
let fromType = NodeIOResolveAnyTypes(drawingFrom!);
300
-
let toType = NodeIOResolveAnyTypes(input);
301
302
-
if(
303
-
drawingFrom!.connections.indexOf(input) === -1 &&
304
-
(
305
-
toType === null ||
306
-
NodeIOCanCast(fromType, toType)
307
-
)
308
-
){
309
-
drawingFrom!.connections.push(input);
310
-
input.connections.push(drawingFrom!);
311
312
-
NodeManager.Instance.UpdateConfig();
313
}
314
}
315
-
}
316
-
})
317
})
318
}
319
320
isDrawing = false;
321
isMouseDown = false;
322
}
323
324
-
keybinds.load(selectedNode, setSelectedNode);
325
requestAnimationFrame(update);
326
});
327
328
-
let update = () => { // TODO: Start/Stop render when app is minimised
329
if(stopRender)return;
330
-
331
scale = lerp(scale, targetScale, 0.25);
332
333
offset[0] = lerp(offset[0], offsetTarget[0], 0.5);
···
352
353
let isMouseDown = false;
354
let mouseStartPos = [ 0, 0 ];
355
356
let interval = setInterval(() => {
357
if(screenMoved){
···
361
}
362
}, 1000);
363
364
-
onCleanup(() => {
365
-
stopRender = true;
366
-
window.clearInterval(interval);
367
-
});
368
-
369
return (
370
<>
371
<ConfirmationPopup />
372
<TabMenu />
373
-
<ControlBar node={selectedNode} lockMovement={( lock ) => lockMovement = lock} />
374
<canvas ref={canvas}/>
375
</>
376
);
···
1
+
import { createEffect, createSignal, onCleanup, onMount } from "solid-js";
2
import "./App.css";
3
import { renderBackgroundGrid, renderContextMenu, renderNodes, renderNullTab, renderTempDrawing } from "./renderer";
4
import { lerp } from "./utils/lerp";
···
13
import { ConfirmationPopup } from "./components/ConfirmationPopup";
14
15
import * as keybinds from './keybinds';
16
+
import { listen } from "@tauri-apps/api/event";
17
+
18
+
// TODO: Only allow one node to input on non-flow inputs
19
20
let App = () => {
21
+
let [ selectedNodes, setSelectedNodes ] = createSignal<Node[]>([], { equals: false });
22
+
let [ mousePos, setMousePos ] = createSignal<[ number, number ]>([ 0, 0 ]);
23
24
let canvas!: HTMLCanvasElement;
25
let ctx: CanvasRenderingContext2D;
···
60
visible: false
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
+
})
72
+
73
+
onMount(async () => {
74
+
NodeManager.Instance.HookTabChange(() => setSelectedNodes([]));
75
76
ctx = canvas.getContext('2d')!;
77
···
114
}
115
116
if(clickedNode){
117
+
contextMenu.items = NodeContextMenu(clickedNode, selectedNodes, setSelectedNodes);
118
} else{
119
contextMenu.items = CanvasContextMenu;
120
}
···
123
contextMenu.visible = true;
124
}
125
126
+
let isShiftClick = false;
127
canvas.onmousedown = ( e ) => {
128
+
isShiftClick = e.shiftKey;
129
+
130
if(
131
e.clientY < 60 ||
132
e.clientX < 220 ||
···
135
136
if(e.button !== 0){
137
contextMenu.visible = false;
138
return;
139
}
140
···
172
173
if(nodes){
174
nodes.map(node => {
175
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
176
e.clientX, e.clientY,
177
node.x - 20, node.y, node.w + 40, node.h
···
230
231
movingNode = clickedNode;
232
233
isMouseDown = true;
234
mouseStartPos = [ e.clientX, e.clientY ];
235
+
mouseMovePos = [ e.clientX, e.clientY ];
236
}
237
238
canvas.onmousemove = ( e ) => {
239
+
setMousePos([ e.clientX, e.clientY ]);
240
+
241
+
if(e.shiftKey && isMouseDown){
242
+
let nodes = NodeManager.Instance.GetNodes();
243
+
let hoveredNode: Node | null = null;
244
+
245
+
if(nodes){
246
+
nodes.map(node => {
247
+
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
248
+
e.clientX, e.clientY,
249
+
node.x - 20, node.y, node.w + 40, node.h
250
+
)){
251
+
hoveredNode = node;
252
+
return;
253
+
}
254
+
})
255
+
}
256
+
257
+
if(hoveredNode !== null){
258
+
let snodes = selectedNodes();
259
+
if(!snodes.find(x => x.id === hoveredNode!.id)){
260
+
snodes.push(hoveredNode);
261
+
262
+
// @ts-ignore
263
+
hoveredNode.x = Math.round(hoveredNode.x / 10) * 10;
264
+
// @ts-ignore
265
+
hoveredNode.y = Math.round(hoveredNode.y / 10) * 10;
266
+
267
+
setSelectedNodes(snodes);
268
+
}
269
+
}
270
+
271
+
return;
272
+
} else if(isShiftClick)return;
273
+
274
if(isMouseDown){
275
if(isDrawing){
276
drawingTo = screenToWorldSpace(canvas, { x: offset[0], y: offset[1], scale }, e.clientX - 10 * scale, e.clientY - 10 * scale) as [ number, number ];
277
} else if(movingNode){
278
+
let nodes = selectedNodes();
279
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 ];
286
NodeManager.Instance.UpdateConfig();
287
} else{
288
+
offsetTarget = [ offsetTarget[0] - (mouseMovePos[0] - e.clientX) / scale, offsetTarget[1] - (mouseMovePos[1] - e.clientY) / scale ];
289
+
mouseMovePos = [ e.clientX, e.clientY ];
290
291
screenMoved = true;
292
}
···
330
331
canvas.onmouseup = ( e ) => {
332
let nodes = NodeManager.Instance.GetNodes();
333
+
let clickedNode;
334
335
if(nodes){
336
nodes.map(node => {
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;
342
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);
353
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
+
}
366
}
367
}
368
+
})
369
+
}
370
})
371
}
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
+
403
isDrawing = false;
404
isMouseDown = false;
405
}
406
407
+
keybinds.load(canvas, mousePos, selectedNodes, setSelectedNodes);
408
requestAnimationFrame(update);
409
+
410
+
let unlisten_0 = await listen('hide-window', () => {
411
+
stopRender = true;
412
+
})
413
+
414
+
let unlisten_1 = await listen('show-window', () => {
415
+
if(stopRender)window.location.reload();
416
+
})
417
+
418
+
onCleanup(() => {
419
+
stopRender = true;
420
+
window.clearInterval(interval);
421
+
422
+
unlisten_0();
423
+
unlisten_1();
424
+
});
425
});
426
427
+
let update = () => {
428
if(stopRender)return;
429
scale = lerp(scale, targetScale, 0.25);
430
431
offset[0] = lerp(offset[0], offsetTarget[0], 0.5);
···
450
451
let isMouseDown = false;
452
let mouseStartPos = [ 0, 0 ];
453
+
let mouseMovePos = [ 0, 0 ];
454
455
let interval = setInterval(() => {
456
if(screenMoved){
···
460
}
461
}, 1000);
462
463
return (
464
<>
465
<ConfirmationPopup />
466
<TabMenu />
467
+
<ControlBar node={selectedNodes} lockMovement={( lock ) => lockMovement = lock} />
468
<canvas ref={canvas}/>
469
</>
470
);
+11
-2
src/ContextMenu/Node.tsx
+11
-2
src/ContextMenu/Node.tsx
···
3
import { PositionInfo } from "../renderer";
4
import { Node } from "../structs/node";
5
6
-
export let NodeContextMenu = ( clickedNode: Node, selectedNode: Accessor<Node | null>, setSelectedNode: Setter<Node | null> ) => [
7
{
8
text: "Delete Node",
9
clicked: ( _e: MouseEvent, _canvas: HTMLCanvasElement, _position: PositionInfo ) => {
···
20
})
21
22
let selected = selectedNode();
23
-
if(selected && clickedNode.id === selected.id)setSelectedNode(null);
24
25
NodeManager.Instance.RemoveNode(clickedNode!)
26
},
···
3
import { PositionInfo } from "../renderer";
4
import { Node } from "../structs/node";
5
6
+
export let NodeContextMenu = ( clickedNode: Node, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => [
7
{
8
text: "Delete Node",
9
clicked: ( _e: MouseEvent, _canvas: HTMLCanvasElement, _position: PositionInfo ) => {
···
20
})
21
22
let selected = selectedNode();
23
+
for (let i = 0; i < selected.length; i++) {
24
+
let node = selected[i];
25
+
26
+
if(node.id === clickedNode.id){
27
+
selected.splice(i, 1);
28
+
setSelectedNode(selected);
29
+
30
+
break;
31
+
}
32
+
}
33
34
NodeManager.Instance.RemoveNode(clickedNode!)
35
},
+35
-2
src/Mangers/NodeManager.tsx
+35
-2
src/Mangers/NodeManager.tsx
···
7
import { NodesByID } from "../Nodes/Nodes";
8
import { save } from "@tauri-apps/plugin-dialog";
9
import { ConfirmationManager } from "./ConfirmationManager";
10
11
export interface TabHashMap {
12
[details: string] : Tab;
···
26
setInterval(() => {
27
let tabs = Object.values(this._tabs).filter(x => x.needSync);
28
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
tab.needSync = false;
31
}
32
}, 1000);
···
39
let version = await getVersion();
40
41
for(let tab of Object.entries<any>(tabs)){
42
-
await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
43
tab_name: tab[1][1],
44
version,
45
graph: tab[1][0]
46
}));
47
};
48
});
49
50
listen('prompt_to_close', async _ => {
···
333
334
tab.needSync = false;
335
if(!id)this.UpdateConfig(false);
336
}
337
338
private _generateTabGraph( tabId: string | null ): [ any, Tab | null ]{
···
348
let node = tab.nodes[i];
349
350
let nodeOutputs = [];
351
352
for (let j = 0; j < node.outputs.length; j++) {
353
let output = node.outputs[j];
···
364
})
365
}
366
367
nodesToSave.push({
368
name: node.name,
369
id: node.id,
370
typeId: node.typeId,
371
pos: [ node.x, node.y ],
372
outputs: nodeOutputs,
373
statics: node.statics
374
})
375
}
···
384
invoke('save_graph', { graph: JSON.stringify({
385
tab_name: tab.name,
386
version: await getVersion(),
387
graph: nodesToSave
388
}), path });
389
}
···
7
import { NodesByID } from "../Nodes/Nodes";
8
import { save } from "@tauri-apps/plugin-dialog";
9
import { ConfirmationManager } from "./ConfirmationManager";
10
+
import { platform } from "@tauri-apps/plugin-os";
11
12
export interface TabHashMap {
13
[details: string] : Tab;
···
27
setInterval(() => {
28
let tabs = Object.values(this._tabs).filter(x => x.needSync);
29
for(let tab of tabs){
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
+
38
tab.needSync = false;
39
}
40
}, 1000);
···
47
let version = await getVersion();
48
49
for(let tab of Object.entries<any>(tabs)){
50
+
let loaded_tab = await this._loadFromConfig(tab[1][2], tab[0], JSON.stringify({
51
tab_name: tab[1][1],
52
version,
53
graph: tab[1][0]
54
}));
55
+
56
+
if(loaded_tab)
57
+
loaded_tab.setNeedsSave(tab[1][3]);
58
};
59
+
60
+
this.UpdateConfig();
61
});
62
63
listen('prompt_to_close', async _ => {
···
346
347
tab.needSync = false;
348
if(!id)this.UpdateConfig(false);
349
+
350
+
return tab;
351
}
352
353
private _generateTabGraph( tabId: string | null ): [ any, Tab | null ]{
···
363
let node = tab.nodes[i];
364
365
let nodeOutputs = [];
366
+
let nodeInputs = [];
367
368
for (let j = 0; j < node.outputs.length; j++) {
369
let output = node.outputs[j];
···
380
})
381
}
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
+
398
nodesToSave.push({
399
name: node.name,
400
id: node.id,
401
typeId: node.typeId,
402
pos: [ node.x, node.y ],
403
outputs: nodeOutputs,
404
+
inputs: nodeInputs,
405
statics: node.statics
406
})
407
}
···
416
invoke('save_graph', { graph: JSON.stringify({
417
tab_name: tab.name,
418
version: await getVersion(),
419
+
platform: platform(),
420
graph: nodesToSave
421
}), path });
422
}
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfEqual.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfFalse.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
-1
src/Nodes/Conditional/IfTrue.tsx
+2
src/Nodes/Conditional.tsx
+2
src/Nodes/Conditional.tsx
+2
-1
src/Nodes/Debug.tsx
+2
-1
src/Nodes/Debug.tsx
+24
-3
src/Nodes/Nodes.tsx
+24
-3
src/Nodes/Nodes.tsx
···
1
import { Node, NodeStatic, NodeType } from "../structs/node";
2
3
import { NodeConditional } from "./Conditional";
4
import { NodeDebug } from "./Debug";
5
import { NodeOSCActions } from "./OSCActions";
6
import { NodeOSCTrigger } from "./OSCTrigger";
7
import { NodeStatics } from "./Statics";
8
9
export interface NodeDefinition{
10
isSingle: boolean,
11
name: string,
12
typeId?: string,
13
onStaticsUpdate?: ( node: Node ) => Promise<void>,
14
// build?: ( pos: [ number, number ], onStaticsUpdate: ( node: Node ) => void ) => Promise<Node>,
15
w?: number,
16
-
h?: number,
17
statics?: NodeStatic[],
18
inputs?: { name: string, type: NodeType }[],
19
outputs?: { name: string, type: NodeType }[],
···
25
[details: string] : NodeDefinition;
26
}
27
28
-
export let Nodes: NodeDefinition[] = [
29
NodeOSCTrigger,
30
NodeConditional,
31
NodeStatics,
32
NodeOSCActions,
33
-
NodeDebug
34
]
35
36
export let NodesByID: NodeDefinitionHashMap = {}
37
···
1
import { Node, NodeStatic, NodeType } from "../structs/node";
2
+
import { platform } from '@tauri-apps/plugin-os';
3
4
import { NodeConditional } from "./Conditional";
5
import { NodeDebug } from "./Debug";
6
import { NodeOSCActions } from "./OSCActions";
7
import { NodeOSCTrigger } from "./OSCTrigger";
8
+
import { NodePressKey } from "./PressKey";
9
import { NodeStatics } from "./Statics";
10
+
import { NodeShellCommand } from "./Shell";
11
12
export interface NodeDefinition{
13
+
os: string,
14
isSingle: boolean,
15
name: string,
16
typeId?: string,
17
onStaticsUpdate?: ( node: Node ) => Promise<void>,
18
// build?: ( pos: [ number, number ], onStaticsUpdate: ( node: Node ) => void ) => Promise<Node>,
19
w?: number,
20
statics?: NodeStatic[],
21
inputs?: { name: string, type: NodeType }[],
22
outputs?: { name: string, type: NodeType }[],
···
28
[details: string] : NodeDefinition;
29
}
30
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 = [
41
NodeOSCTrigger,
42
NodeConditional,
43
NodeStatics,
44
NodeOSCActions,
45
+
NodeDebug,
46
+
NodePressKey,
47
+
NodeShellCommand
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
+
}
56
57
export let NodesByID: NodeDefinitionHashMap = {}
58
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
-1
src/Nodes/OSCActions/Send Chatbox.tsx
+2
src/Nodes/OSCActions.tsx
+2
src/Nodes/OSCActions.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
+27
-10
src/Nodes/OSCTrigger.tsx
···
5
import { NodeDefinition } from "./Nodes";
6
7
export let NodeOSCTrigger: NodeDefinition = {
8
isSingle: true,
9
name: 'OSC Trigger',
10
typeId: 'osctrigger',
11
12
w: 200,
13
-
h: 55,
14
15
statics: [
16
{
···
39
}
40
}
41
42
-
node.outputs.map(output => {
43
-
output.connections.map(partner => {
44
-
partner.connections = partner.connections.filter(x => x != output);
45
-
})
46
-
})
47
-
node.outputs = [];
48
49
-
node.outputs.push({
50
name: 'Flow',
51
type: NodeType.Flow,
52
connections: [],
···
73
}
74
75
if(type){
76
-
node.outputs.push({
77
name: dat.desc === '' ? dat.type : dat.desc,
78
type: type,
79
connections: [],
···
83
}
84
});
85
86
-
node.h = 65 + (parameters.length + 1) * 30;
87
NodeManager.Instance.UpdateConfig();
88
}
89
};
···
5
import { NodeDefinition } from "./Nodes";
6
7
export let NodeOSCTrigger: NodeDefinition = {
8
+
os: 'any',
9
+
10
isSingle: true,
11
name: 'OSC Trigger',
12
typeId: 'osctrigger',
13
14
w: 200,
15
16
statics: [
17
{
···
40
}
41
}
42
43
+
let tempOutputs = [];
44
45
+
tempOutputs.push({
46
name: 'Flow',
47
type: NodeType.Flow,
48
connections: [],
···
69
}
70
71
if(type){
72
+
tempOutputs.push({
73
name: dat.desc === '' ? dat.type : dat.desc,
74
type: type,
75
connections: [],
···
79
}
80
});
81
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
+
104
NodeManager.Instance.UpdateConfig();
105
}
106
};
+29
src/Nodes/PressKey.tsx
+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
+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
-1
src/Nodes/Statics/Float.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/Int.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
-1
src/Nodes/Statics/String.tsx
+2
src/Nodes/Statics.tsx
+2
src/Nodes/Statics.tsx
+1
-1
src/components/ConfirmationPopup.tsx
+1
-1
src/components/ConfirmationPopup.tsx
+8
-12
src/components/ControlBar.tsx
+8
-12
src/components/ControlBar.tsx
···
1
import './ControlBar.css';
2
3
-
import { Accessor, createEffect, createSignal, For, Match, Show, Switch } from 'solid-js';
4
import { Node, NodeType } from '../structs/node';
5
import { TextInput } from './TextInput';
6
import { invoke } from '@tauri-apps/api/core';
···
9
import { NodeManager } from '../Mangers/NodeManager';
10
11
export interface ControlBarProps{
12
-
node: Accessor<Node | null>,
13
lockMovement: ( lock: boolean ) => void
14
}
15
16
export let ControlBar = ( props: ControlBarProps ) => {
17
-
createEffect(() => {
18
-
console.log(props.node());
19
-
})
20
-
21
return (
22
<div class="control-bar">
23
-
<For each={props.node()?.statics}>
24
{ ( item ) => {
25
let [ popupOpen, setPopupOpen ] = createSignal(false);
26
···
36
value={item.value || ''}
37
onChange={( el ) => {
38
let value = el.target.value;
39
-
let node = props.node()!;
40
41
item.value = value;
42
node.onStaticsUpdate(node);
···
54
value={item.value !== undefined ? item.value : ''}
55
onChange={( el ) => {
56
let value = el.target.value;
57
-
let node = props.node()!;
58
59
item.value = parseInt(value);
60
node.onStaticsUpdate(node);
···
72
value={item.value !== undefined ? item.value : ''}
73
onChange={( el ) => {
74
let value = el.target.value;
75
-
let node = props.node()!;
76
77
item.value = parseFloat(value);
78
node.onStaticsUpdate(node);
···
92
return addresses.map(x => x.address).filter(x => x.toLowerCase().includes(text.toLowerCase()));
93
}}
94
change={( text ) => {
95
-
let node = props.node()!;
96
97
item.value = text;
98
node.onStaticsUpdate(node);
···
118
}}
119
value={item.value}
120
changed={( value ) => {
121
-
let node = props.node()!;
122
123
item.value = value;
124
node.onStaticsUpdate(node);
···
1
import './ControlBar.css';
2
3
+
import { Accessor, createSignal, For, Match, Show, Switch } from 'solid-js';
4
import { Node, NodeType } from '../structs/node';
5
import { TextInput } from './TextInput';
6
import { invoke } from '@tauri-apps/api/core';
···
9
import { NodeManager } from '../Mangers/NodeManager';
10
11
export interface ControlBarProps{
12
+
node: Accessor<Node[]>,
13
lockMovement: ( lock: boolean ) => void
14
}
15
16
export let ControlBar = ( props: ControlBarProps ) => {
17
return (
18
<div class="control-bar">
19
+
<For each={props.node()[0]?.statics}>
20
{ ( item ) => {
21
let [ popupOpen, setPopupOpen ] = createSignal(false);
22
···
32
value={item.value || ''}
33
onChange={( el ) => {
34
let value = el.target.value;
35
+
let node = props.node()[0]!;
36
37
item.value = value;
38
node.onStaticsUpdate(node);
···
50
value={item.value !== undefined ? item.value : ''}
51
onChange={( el ) => {
52
let value = el.target.value;
53
+
let node = props.node()[0]!;
54
55
item.value = parseInt(value);
56
node.onStaticsUpdate(node);
···
68
value={item.value !== undefined ? item.value : ''}
69
onChange={( el ) => {
70
let value = el.target.value;
71
+
let node = props.node()[0]!;
72
73
item.value = parseFloat(value);
74
node.onStaticsUpdate(node);
···
88
return addresses.map(x => x.address).filter(x => x.toLowerCase().includes(text.toLowerCase()));
89
}}
90
change={( text ) => {
91
+
let node = props.node()[0]!;
92
93
item.value = text;
94
node.onStaticsUpdate(node);
···
114
}}
115
value={item.value}
116
changed={( value ) => {
117
+
let node = props.node()[0]!;
118
119
item.value = value;
120
node.onStaticsUpdate(node);
+23
src/components/ParameterList.css
+23
src/components/ParameterList.css
···
59
60
.parameter-list-button-dropdown > div:hover{
61
color: #aaa;
62
+
}
63
+
64
+
.parameter-list-parameter{
65
+
display: inline-block;
66
+
padding: 5px 10px;
67
+
margin: 5px 0px 5px 10px;
68
+
background: #445077;
69
+
border-radius: 5px;
70
+
transition: 0.1s;
71
+
cursor: pointer;
72
+
user-select: none;
73
+
-webkit-user-select: none;
74
+
}
75
+
76
+
.parameter-list-parameter:hover{
77
+
background: #363f5e;
78
+
}
79
+
80
+
.parameter-list-parameter-delete{
81
+
padding: 0px 5px;
82
+
display: flex;
83
+
justify-content: center;
84
+
align-items: center;
85
}
+12
-2
src/components/ParameterList.tsx
+12
-2
src/components/ParameterList.tsx
···
7
changed: ( value: { type: string, desc: string }[] ) => void
8
}
9
10
-
// TODO: An actual parameter list
11
export let ParameterList = ( props: ParameterListProps ) => {
12
let [ parameters, setParameters ] = createSignal<{ type: string, desc: string }[]>(props.value, { equals: false });
13
let [ addParametersOpen, setAddParametersOpen ] = createSignal(false);
···
23
</div>
24
<div class="parameter-list-content">
25
<For each={parameters()}>
26
-
{ i => <div>{ JSON.stringify(i) }</div>}
27
</For>
28
<div class="button" onClick={() => { setAddParametersOpen(!addParametersOpen()) }}>Add Parameter + </div>
29
<Show when={addParametersOpen()}>
···
7
changed: ( value: { type: string, desc: string }[] ) => void
8
}
9
10
export let ParameterList = ( props: ParameterListProps ) => {
11
let [ parameters, setParameters ] = createSignal<{ type: string, desc: string }[]>(props.value, { equals: false });
12
let [ addParametersOpen, setAddParametersOpen ] = createSignal(false);
···
22
</div>
23
<div class="parameter-list-content">
24
<For each={parameters()}>
25
+
{ ( i, index ) => <div style={{ display: 'flex' }}>
26
+
<div class="parameter-list-parameter">{ i.desc === "" ? i.type : i.desc + ` ${i.type}` }</div>
27
+
<div class="parameter-list-parameter parameter-list-parameter-delete" onClick={() => {
28
+
let params = parameters();
29
+
params.splice(index(), 1);
30
+
31
+
setParameters(params);
32
+
props.changed(params);
33
+
}}>
34
+
<img src="/assets/icons/trash-can-solid-full.svg" width="20" />
35
+
</div>
36
+
</div>}
37
</For>
38
<div class="button" onClick={() => { setAddParametersOpen(!addParametersOpen()) }}>Add Parameter + </div>
39
<Show when={addParametersOpen()}>
+8
-8
src/components/SettingsMenu.css
+8
-8
src/components/SettingsMenu.css
+3
src/components/SettingsMenu.tsx
+3
src/components/SettingsMenu.tsx
+54
-21
src/keybinds.ts
+54
-21
src/keybinds.ts
···
1
import { Accessor, Setter } from "solid-js";
2
import { NodeManager } from "./Mangers/NodeManager";
3
import { Node } from "./structs/node";
4
5
let isKeyDown: any = {};
6
7
-
export let load = ( selectedNode: Accessor<Node | null>, setSelectedNode: Setter<Node | null> ) => {
8
-
// TODO: Keybind system
9
-
// TODO: Copy / paste
10
// TODO: Add undo / redo -ing
11
12
-
window.onkeydown = ( e ) => {
13
-
isKeyDown[e.key] = true;
14
-
15
-
console.log(e.key);
16
-
17
switch(e.key){
18
case 'Delete':
19
-
let node = selectedNode();
20
-
if(!node)return;
21
-
22
-
node.inputs.map(input => {
23
-
input.connections.map(partner => {
24
-
partner.connections = partner.connections.filter(x => x != input);
25
})
26
-
})
27
28
-
node.outputs.map(output => {
29
-
output.connections.map(partner => {
30
-
partner.connections = partner.connections.filter(x => x != output);
31
})
32
-
})
33
34
-
setSelectedNode(null);
35
-
NodeManager.Instance.RemoveNode(node);
36
break;
37
case 's':
38
if(e.ctrlKey){
39
let currentTab = NodeManager.Instance.CurrentTab();
···
50
51
// Save
52
NodeManager.Instance.SaveTab(currentTab, true);
53
}
54
break;
55
}
···
1
import { Accessor, Setter } from "solid-js";
2
import { NodeManager } from "./Mangers/NodeManager";
3
import { Node } from "./structs/node";
4
+
import { readText, writeText } from "@tauri-apps/plugin-clipboard-manager";
5
+
import { decodeNodeList, encodeNodeList } from "./utils/clipboard";
6
7
let isKeyDown: any = {};
8
9
+
export let load = ( canvas: HTMLCanvasElement, mousePos: Accessor<[ number, number ]>, selectedNode: Accessor<Node[]>, setSelectedNode: Setter<Node[]> ) => {
10
// TODO: Add undo / redo -ing
11
12
+
canvas.onkeydown = async ( e ) => {
13
switch(e.key){
14
case 'Delete':
15
+
let nodes = selectedNode();
16
+
for(let node of nodes){
17
+
node.inputs.map(input => {
18
+
input.connections.map(partner => {
19
+
partner.connections = partner.connections.filter(x => x != input);
20
+
})
21
})
22
23
+
node.outputs.map(output => {
24
+
output.connections.map(partner => {
25
+
partner.connections = partner.connections.filter(x => x != output);
26
+
})
27
})
28
+
29
+
NodeManager.Instance.RemoveNode(node);
30
+
}
31
32
+
setSelectedNode([]);
33
break;
34
+
}
35
+
}
36
+
37
+
window.onkeydown = async ( e ) => {
38
+
isKeyDown[e.key] = true;
39
+
40
+
switch(e.key){
41
case 's':
42
if(e.ctrlKey){
43
let currentTab = NodeManager.Instance.CurrentTab();
···
54
55
// Save
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');
86
}
87
break;
88
}
+7
-2
src/structs/node.ts
+7
-2
src/structs/node.ts
···
20
this.typeId = node.typeId!;
21
this.x = pos[0];
22
this.y = pos[1];
23
-
this.w = node.w!;
24
-
this.h = node.h!;
25
26
this.inputs = node.inputs ? node.inputs.map(( x, indx ) => {
27
return {
···
43
}
44
}) : [];
45
46
this.selected = false;
47
this.statics = node.statics!,
48
this.onStaticsUpdate = node.onStaticsUpdate!;
49
}
50
}
51
···
20
this.typeId = node.typeId!;
21
this.x = pos[0];
22
this.y = pos[1];
23
24
this.inputs = node.inputs ? node.inputs.map(( x, indx ) => {
25
return {
···
41
}
42
}) : [];
43
44
+
this.w = node.w || 200;
45
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
46
+
47
this.selected = false;
48
this.statics = node.statics!,
49
this.onStaticsUpdate = node.onStaticsUpdate!;
50
+
}
51
+
52
+
updateSize(){
53
+
this.h = 50 + Math.max(this.outputs.length, this.inputs.length) * 30;
54
}
55
}
56
+80
src/utils/clipboard.ts
+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
+435
-10
src-tauri/Cargo.lock
···
7
version = "0.1.0"
8
dependencies = [
9
"anyhow",
10
"crossbeam-channel",
11
"dirs",
12
"flate2",
13
"serde",
14
"serde_json",
15
"tauri",
16
"tauri-build",
17
"tauri-plugin-dialog",
18
"tauri-plugin-opener",
19
"tokio",
20
]
21
···
65
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
66
67
[[package]]
68
name = "ashpd"
69
version = "0.11.0"
70
source = "registry+https://github.com/rust-lang/crates.io-index"
···
136
"futures-lite",
137
"parking",
138
"polling",
139
-
"rustix",
140
"slab",
141
"windows-sys 0.61.2",
142
]
···
167
"cfg-if",
168
"event-listener",
169
"futures-lite",
170
-
"rustix",
171
]
172
173
[[package]]
···
193
"cfg-if",
194
"futures-core",
195
"futures-io",
196
-
"rustix",
197
"signal-hook-registry",
198
"slab",
199
"windows-sys 0.61.2",
···
495
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
496
dependencies = [
497
"iana-time-zone",
498
"num-traits",
499
"serde",
500
"windows-link 0.2.1",
501
]
502
503
[[package]]
···
565
]
566
567
[[package]]
568
name = "core-graphics-types"
569
version = "0.2.0"
570
source = "registry+https://github.com/rust-lang/crates.io-index"
···
609
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
610
611
[[package]]
612
name = "crypto-common"
613
version = "0.1.6"
614
source = "registry+https://github.com/rust-lang/crates.io-index"
···
874
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
875
876
[[package]]
877
name = "enumflags2"
878
version = "0.7.12"
879
source = "registry+https://github.com/rust-lang/crates.io-index"
···
922
]
923
924
[[package]]
925
name = "event-listener"
926
version = "5.4.1"
927
source = "registry+https://github.com/rust-lang/crates.io-index"
···
949
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
950
951
[[package]]
952
name = "fdeflate"
953
version = "0.3.7"
954
source = "registry+https://github.com/rust-lang/crates.io-index"
···
974
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
975
976
[[package]]
977
name = "flate2"
978
version = "1.1.5"
979
source = "registry+https://github.com/rust-lang/crates.io-index"
···
988
version = "1.0.7"
989
source = "registry+https://github.com/rust-lang/crates.io-index"
990
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
991
992
[[package]]
993
name = "foreign-types"
···
1239
]
1240
1241
[[package]]
1242
name = "getrandom"
1243
version = "0.1.16"
1244
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1421
]
1422
1423
[[package]]
1424
name = "hashbrown"
1425
version = "0.12.3"
1426
source = "registry+https://github.com/rust-lang/crates.io-index"
1427
checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888"
1428
1429
[[package]]
1430
name = "hashbrown"
···
1706
"moxcms",
1707
"num-traits",
1708
"png 0.18.0",
1709
]
1710
1711
[[package]]
···
1949
1950
[[package]]
1951
name = "linux-raw-sys"
1952
version = "0.11.0"
1953
source = "registry+https://github.com/rust-lang/crates.io-index"
1954
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
···
2018
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
2019
2020
[[package]]
2021
name = "memoffset"
2022
version = "0.9.1"
2023
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2033
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
2034
2035
[[package]]
2036
name = "miniz_oxide"
2037
version = "0.8.9"
2038
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2138
version = "0.1.14"
2139
source = "registry+https://github.com/rust-lang/crates.io-index"
2140
checksum = "72ef4a56884ca558e5ddb05a1d1e7e1bfd9a68d9ed024c21704cc98872dae1bb"
2141
2142
[[package]]
2143
name = "num-conv"
···
2280
]
2281
2282
[[package]]
2283
name = "objc2-core-text"
2284
version = "0.3.2"
2285
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2398
dependencies = [
2399
"bitflags 2.10.0",
2400
"objc2 0.6.3",
2401
"objc2-foundation 0.3.2",
2402
]
2403
···
2419
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
2420
dependencies = [
2421
"bitflags 2.10.0",
2422
"objc2 0.6.3",
2423
"objc2-core-foundation",
2424
"objc2-foundation 0.3.2",
2425
]
2426
···
2475
]
2476
2477
[[package]]
2478
name = "pango"
2479
version = "0.18.3"
2480
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2541
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
2542
2543
[[package]]
2544
name = "phf"
2545
version = "0.8.0"
2546
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2752
"concurrent-queue",
2753
"hermit-abi",
2754
"pin-project-lite",
2755
-
"rustix",
2756
"windows-sys 0.61.2",
2757
]
2758
···
2864
]
2865
2866
[[package]]
2867
name = "quick-xml"
2868
version = "0.37.5"
2869
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3148
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
3149
dependencies = [
3150
"semver",
3151
]
3152
3153
[[package]]
···
3159
"bitflags 2.10.0",
3160
"errno",
3161
"libc",
3162
-
"linux-raw-sys",
3163
"windows-sys 0.61.2",
3164
]
3165
···
3519
dependencies = [
3520
"bytemuck",
3521
"cfg_aliases",
3522
-
"core-graphics",
3523
"foreign-types",
3524
"js-sys",
3525
"log",
···
3656
]
3657
3658
[[package]]
3659
name = "system-deps"
3660
version = "6.2.2"
3661
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3677
"bitflags 2.10.0",
3678
"block2 0.6.2",
3679
"core-foundation",
3680
-
"core-graphics",
3681
"crossbeam-channel",
3682
"dispatch",
3683
"dlopen2",
···
3858
]
3859
3860
[[package]]
3861
name = "tauri-plugin-dialog"
3862
version = "2.4.2"
3863
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3920
]
3921
3922
[[package]]
3923
name = "tauri-runtime"
3924
version = "2.9.1"
3925
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4028
"fastrand",
4029
"getrandom 0.3.4",
4030
"once_cell",
4031
-
"rustix",
4032
"windows-sys 0.61.2",
4033
]
4034
···
4084
]
4085
4086
[[package]]
4087
name = "time"
4088
version = "0.3.44"
4089
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4360
]
4361
4362
[[package]]
4363
name = "try-lock"
4364
version = "0.2.5"
4365
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4640
dependencies = [
4641
"cc",
4642
"downcast-rs",
4643
-
"rustix",
4644
"scoped-tls",
4645
"smallvec",
4646
"wayland-sys",
···
4653
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
4654
dependencies = [
4655
"bitflags 2.10.0",
4656
-
"rustix",
4657
"wayland-backend",
4658
"wayland-scanner",
4659
]
···
4671
]
4672
4673
[[package]]
4674
name = "wayland-scanner"
4675
version = "0.31.7"
4676
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4781
"windows",
4782
"windows-core 0.61.2",
4783
]
4784
4785
[[package]]
4786
name = "winapi"
···
5242
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
5243
5244
[[package]]
5245
name = "writeable"
5246
version = "0.6.2"
5247
source = "registry+https://github.com/rust-lang/crates.io-index"
···
5312
"once_cell",
5313
"pkg-config",
5314
]
5315
5316
[[package]]
5317
name = "yoke"
···
5470
"proc-macro2",
5471
"quote",
5472
"syn 2.0.109",
5473
]
5474
5475
[[package]]
···
7
version = "0.1.0"
8
dependencies = [
9
"anyhow",
10
+
"chrono",
11
"crossbeam-channel",
12
"dirs",
13
+
"enigo",
14
"flate2",
15
"serde",
16
"serde_json",
17
"tauri",
18
"tauri-build",
19
+
"tauri-plugin-clipboard-manager",
20
"tauri-plugin-dialog",
21
"tauri-plugin-opener",
22
+
"tauri-plugin-os",
23
"tokio",
24
]
25
···
69
checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61"
70
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]]
93
name = "ashpd"
94
version = "0.11.0"
95
source = "registry+https://github.com/rust-lang/crates.io-index"
···
161
"futures-lite",
162
"parking",
163
"polling",
164
+
"rustix 1.1.2",
165
"slab",
166
"windows-sys 0.61.2",
167
]
···
192
"cfg-if",
193
"event-listener",
194
"futures-lite",
195
+
"rustix 1.1.2",
196
]
197
198
[[package]]
···
218
"cfg-if",
219
"futures-core",
220
"futures-io",
221
+
"rustix 1.1.2",
222
"signal-hook-registry",
223
"slab",
224
"windows-sys 0.61.2",
···
520
checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2"
521
dependencies = [
522
"iana-time-zone",
523
+
"js-sys",
524
"num-traits",
525
"serde",
526
+
"wasm-bindgen",
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",
537
]
538
539
[[package]]
···
601
]
602
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]]
617
name = "core-graphics-types"
618
version = "0.2.0"
619
source = "registry+https://github.com/rust-lang/crates.io-index"
···
658
checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
659
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]]
667
name = "crypto-common"
668
version = "0.1.6"
669
source = "registry+https://github.com/rust-lang/crates.io-index"
···
929
checksum = "a3d8a32ae18130a3c84dd492d4215c3d913c3b07c6b63c2eb3eb7ff1101ab7bf"
930
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]]
953
name = "enumflags2"
954
version = "0.7.12"
955
source = "registry+https://github.com/rust-lang/crates.io-index"
···
998
]
999
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]]
1007
name = "event-listener"
1008
version = "5.4.1"
1009
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1031
checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be"
1032
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]]
1054
name = "fdeflate"
1055
version = "0.3.7"
1056
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1076
checksum = "52051878f80a721bb68ebfbc930e07b65ba72f2da88968ea5c06fd6ca3d3a127"
1077
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]]
1085
name = "flate2"
1086
version = "1.1.5"
1087
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1096
version = "1.0.7"
1097
source = "registry+https://github.com/rust-lang/crates.io-index"
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"
1105
1106
[[package]]
1107
name = "foreign-types"
···
1353
]
1354
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]]
1366
name = "getrandom"
1367
version = "0.1.16"
1368
source = "registry+https://github.com/rust-lang/crates.io-index"
···
1545
]
1546
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]]
1559
name = "hashbrown"
1560
version = "0.12.3"
1561
source = "registry+https://github.com/rust-lang/crates.io-index"
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
+
]
1572
1573
[[package]]
1574
name = "hashbrown"
···
1850
"moxcms",
1851
"num-traits",
1852
"png 0.18.0",
1853
+
"tiff",
1854
]
1855
1856
[[package]]
···
2094
2095
[[package]]
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"
2103
version = "0.11.0"
2104
source = "registry+https://github.com/rust-lang/crates.io-index"
2105
checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039"
···
2169
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
2170
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]]
2181
name = "memoffset"
2182
version = "0.9.1"
2183
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2193
checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a"
2194
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]]
2202
name = "miniz_oxide"
2203
version = "0.8.9"
2204
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2304
version = "0.1.14"
2305
source = "registry+https://github.com/rust-lang/crates.io-index"
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
+
]
2326
2327
[[package]]
2328
name = "num-conv"
···
2465
]
2466
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]]
2478
name = "objc2-core-text"
2479
version = "0.3.2"
2480
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2593
dependencies = [
2594
"bitflags 2.10.0",
2595
"objc2 0.6.3",
2596
+
"objc2-core-foundation",
2597
"objc2-foundation 0.3.2",
2598
]
2599
···
2615
checksum = "d87d638e33c06f577498cbcc50491496a3ed4246998a7fbba7ccb98b1e7eab22"
2616
dependencies = [
2617
"bitflags 2.10.0",
2618
+
"block2 0.6.2",
2619
"objc2 0.6.3",
2620
+
"objc2-cloud-kit",
2621
+
"objc2-core-data",
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",
2639
"objc2-foundation 0.3.2",
2640
]
2641
···
2690
]
2691
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]]
2719
name = "pango"
2720
version = "0.18.3"
2721
source = "registry+https://github.com/rust-lang/crates.io-index"
···
2782
checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220"
2783
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]]
2796
name = "phf"
2797
version = "0.8.0"
2798
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3004
"concurrent-queue",
3005
"hermit-abi",
3006
"pin-project-lite",
3007
+
"rustix 1.1.2",
3008
"windows-sys 0.61.2",
3009
]
3010
···
3116
]
3117
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]]
3125
name = "quick-xml"
3126
version = "0.37.5"
3127
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3406
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
3407
dependencies = [
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",
3422
]
3423
3424
[[package]]
···
3430
"bitflags 2.10.0",
3431
"errno",
3432
"libc",
3433
+
"linux-raw-sys 0.11.0",
3434
"windows-sys 0.61.2",
3435
]
3436
···
3790
dependencies = [
3791
"bytemuck",
3792
"cfg_aliases",
3793
+
"core-graphics 0.24.0",
3794
"foreign-types",
3795
"js-sys",
3796
"log",
···
3927
]
3928
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]]
3939
name = "system-deps"
3940
version = "6.2.2"
3941
source = "registry+https://github.com/rust-lang/crates.io-index"
···
3957
"bitflags 2.10.0",
3958
"block2 0.6.2",
3959
"core-foundation",
3960
+
"core-graphics 0.24.0",
3961
"crossbeam-channel",
3962
"dispatch",
3963
"dlopen2",
···
4138
]
4139
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]]
4156
name = "tauri-plugin-dialog"
4157
version = "2.4.2"
4158
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4215
]
4216
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]]
4236
name = "tauri-runtime"
4237
version = "2.9.1"
4238
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4341
"fastrand",
4342
"getrandom 0.3.4",
4343
"once_cell",
4344
+
"rustix 1.1.2",
4345
"windows-sys 0.61.2",
4346
]
4347
···
4397
]
4398
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]]
4414
name = "time"
4415
version = "0.3.44"
4416
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4687
]
4688
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]]
4701
name = "try-lock"
4702
version = "0.2.5"
4703
source = "registry+https://github.com/rust-lang/crates.io-index"
···
4978
dependencies = [
4979
"cc",
4980
"downcast-rs",
4981
+
"rustix 1.1.2",
4982
"scoped-tls",
4983
"smallvec",
4984
"wayland-sys",
···
4991
checksum = "c66a47e840dc20793f2264eb4b3e4ecb4b75d91c0dd4af04b456128e0bdd449d"
4992
dependencies = [
4993
"bitflags 2.10.0",
4994
+
"rustix 1.1.2",
4995
"wayland-backend",
4996
"wayland-scanner",
4997
]
···
5009
]
5010
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]]
5025
name = "wayland-scanner"
5026
version = "0.31.7"
5027
source = "registry+https://github.com/rust-lang/crates.io-index"
···
5132
"windows",
5133
"windows-core 0.61.2",
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"
5141
5142
[[package]]
5143
name = "winapi"
···
5599
checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59"
5600
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]]
5621
name = "writeable"
5622
version = "0.6.2"
5623
source = "registry+https://github.com/rust-lang/crates.io-index"
···
5688
"once_cell",
5689
"pkg-config",
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"
5725
5726
[[package]]
5727
name = "yoke"
···
5880
"proc-macro2",
5881
"quote",
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",
5898
]
5899
5900
[[package]]
+6
src-tauri/Cargo.toml
+6
src-tauri/Cargo.toml
+4
-1
src-tauri/capabilities/default.json
+4
-1
src-tauri/capabilities/default.json
+2
-2
src-tauri/src/frontend_calls/close_app.rs
+2
-2
src-tauri/src/frontend_calls/close_app.rs
+9
-4
src-tauri/src/frontend_calls/load_previous_tabs.rs
+9
-4
src-tauri/src/frontend_calls/load_previous_tabs.rs
···
2
3
use tauri::{State, Window};
4
5
-
use crate::{ structs::nodes::Node, utils::config::Config };
6
7
#[tauri::command]
8
-
pub fn load_previous_tabs( window: Window, conf: State<Config> ) -> HashMap<String, ( Vec<Node>, String, Option<String> )> {
9
let config = conf.store.lock().unwrap();
10
11
-
if !config.hide_editor_on_start { window.show().unwrap(); }
12
13
let tabs = config.loaded_tabs.clone();
14
tabs
15
-
}
···
2
3
use tauri::{State, Window};
4
5
+
use crate::{structs::nodes::Node, utils::config::Config};
6
7
#[tauri::command]
8
+
pub fn load_previous_tabs(
9
+
window: Window,
10
+
conf: State<Config>,
11
+
) -> HashMap<String, (Vec<Node>, String, Option<String>, bool)> {
12
let config = conf.store.lock().unwrap();
13
14
+
if !config.hide_editor_on_start {
15
+
window.show().unwrap();
16
+
}
17
18
let tabs = config.loaded_tabs.clone();
19
tabs
20
+
}
+3
-3
src-tauri/src/frontend_calls/mod.rs
+3
-3
src-tauri/src/frontend_calls/mod.rs
+2
-2
src-tauri/src/frontend_calls/save_graph.rs
+2
-2
src-tauri/src/frontend_calls/save_graph.rs
···
1
use std::{fs::File, io::Write, path::PathBuf};
2
3
-
use flate2::{ write::GzEncoder, Compression };
4
use tauri::State;
5
6
use crate::utils::config::Config;
7
8
#[tauri::command]
9
-
pub fn save_graph( graph: String, path: PathBuf, conf: State<Config> ) {
10
let file = File::create(&path).unwrap();
11
let mut encoder = GzEncoder::new(file, Compression::default());
12
···
1
use std::{fs::File, io::Write, path::PathBuf};
2
3
+
use flate2::{write::GzEncoder, Compression};
4
use tauri::State;
5
6
use crate::utils::config::Config;
7
8
#[tauri::command]
9
+
pub fn save_graph(graph: String, path: PathBuf, conf: State<Config>) {
10
let file = File::create(&path).unwrap();
11
let mut encoder = GzEncoder::new(file, Compression::default());
12
+4
-4
src-tauri/src/frontend_calls/settings.rs
+4
-4
src-tauri/src/frontend_calls/settings.rs
···
3
use crate::utils::config::Config;
4
5
#[tauri::command]
6
-
pub fn set_hide_editor_on_app_start( value: bool, conf: State<Config> ){
7
let mut config = conf.store.lock().unwrap();
8
config.hide_editor_on_start = value;
9
}
10
11
#[tauri::command]
12
-
pub fn get_hide_editor_on_app_start( conf: State<Config> ) -> bool {
13
-
let mut config = conf.store.lock().unwrap();
14
config.hide_editor_on_start
15
-
}
···
3
use crate::utils::config::Config;
4
5
#[tauri::command]
6
+
pub fn set_hide_editor_on_app_start(value: bool, conf: State<Config>) {
7
let mut config = conf.store.lock().unwrap();
8
config.hide_editor_on_start = value;
9
}
10
11
#[tauri::command]
12
+
pub fn get_hide_editor_on_app_start(conf: State<Config>) -> bool {
13
+
let config = conf.store.lock().unwrap();
14
config.hide_editor_on_start
15
+
}
+19
-6
src-tauri/src/frontend_calls/sync_tab.rs
+19
-6
src-tauri/src/frontend_calls/sync_tab.rs
···
1
use crossbeam_channel::Sender;
2
3
use tauri::State;
4
5
-
use crate::{ runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config };
6
7
#[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();
10
11
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
13
}
14
15
#[tauri::command]
16
-
pub fn discard_tab( id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config> ){
17
cmd.send(RuntimeCommand::RemoveTab(id.clone())).unwrap();
18
19
let mut config = conf.store.lock().unwrap();
20
config.loaded_tabs.remove(&id);
21
-
}
···
1
+
use chrono::Utc;
2
use crossbeam_channel::Sender;
3
4
use tauri::State;
5
6
+
use crate::{runtime::commands::RuntimeCommand, structs::nodes::Node, utils::config::Config};
7
8
#[tauri::command]
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();
21
22
let mut config = conf.store.lock().unwrap();
23
+
config.loaded_tabs.insert(id, (graph, name, location, save_state));
24
+
25
+
conf.save_prelocked(config);
26
}
27
28
#[tauri::command]
29
+
pub fn discard_tab(id: String, cmd: State<Sender<RuntimeCommand>>, conf: State<Config>) {
30
cmd.send(RuntimeCommand::RemoveTab(id.clone())).unwrap();
31
32
let mut config = conf.store.lock().unwrap();
33
config.loaded_tabs.remove(&id);
34
+
}
+10
-5
src-tauri/src/lib.rs
+10
-5
src-tauri/src/lib.rs
···
1
-
use std::{ fs, sync::Mutex };
2
3
use crossbeam_channel::bounded;
4
use frontend_calls::*;
5
6
-
use crate::{ osc::OSCMessage, setup::setup, utils::config::Config };
7
8
mod frontend_calls;
9
mod osc;
10
mod setup;
11
mod structs;
12
mod utils;
13
-
mod runtime;
14
15
#[cfg_attr(mobile, tauri::mobile_entry_point)]
16
#[tokio::main]
···
33
34
static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new());
35
36
-
let ( runtime_sender, runtime_receiver ) = bounded(1024);
37
38
tauri::Builder::default()
39
.plugin(tauri_plugin_dialog::init())
40
.plugin(tauri_plugin_opener::init())
41
.invoke_handler(tauri::generate_handler![
···
45
sync_tab::discard_tab,
46
load_previous_tabs::load_previous_tabs,
47
close_app::close_app,
48
-
49
settings::set_hide_editor_on_app_start,
50
settings::get_hide_editor_on_app_start,
51
])
···
1
+
use std::{fs, sync::Mutex};
2
3
use crossbeam_channel::bounded;
4
use frontend_calls::*;
5
6
+
use crate::{osc::OSCMessage, setup::setup, utils::{config::Config, vrchat_builtin_parameters}};
7
8
mod frontend_calls;
9
mod osc;
10
+
mod runtime;
11
mod setup;
12
mod structs;
13
mod utils;
14
15
#[cfg_attr(mobile, tauri::mobile_entry_point)]
16
#[tokio::main]
···
33
34
static ADDRESSES: Mutex<Vec<OSCMessage>> = Mutex::new(Vec::new());
35
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);
41
42
tauri::Builder::default()
43
+
.plugin(tauri_plugin_os::init())
44
+
.plugin(tauri_plugin_clipboard_manager::init())
45
.plugin(tauri_plugin_dialog::init())
46
.plugin(tauri_plugin_opener::init())
47
.invoke_handler(tauri::generate_handler![
···
51
sync_tab::discard_tab,
52
load_previous_tabs::load_previous_tabs,
53
close_app::close_app,
54
settings::set_hide_editor_on_app_start,
55
settings::get_hide_editor_on_app_start,
56
])
+3
-1
src-tauri/src/main.rs
+3
-1
src-tauri/src/main.rs
-1
src-tauri/src/osc.rs
-1
src-tauri/src/osc.rs
+4
-4
src-tauri/src/runtime/commands.rs
+4
-4
src-tauri/src/runtime/commands.rs
+37
-36
src-tauri/src/runtime/nodes/conditional/ifequal.rs
+37
-36
src-tauri/src/runtime/nodes/conditional/ifequal.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct ConditionalIfEqual{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value1: ParameterType,
6
-
value2: ParameterType
7
}
8
9
-
impl ConditionalIfEqual{
10
-
pub fn new( node: Node ) -> Box<Self>{
11
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,
18
})
19
}
20
}
21
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![]) }
25
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
-
}
33
}
34
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
-
}
45
46
-
false
47
}
48
49
-
fn is_entrypoint( &self ) -> bool { false }
50
-
}
···
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
6
+
pub struct ConditionalIfEqual {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
}
10
11
+
impl ConditionalIfEqual {
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
})
27
}
28
}
29
30
+
impl RuntimeNode for ConditionalIfEqual {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
34
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
37
}
38
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
let is_equal = args[1] == args[2];
41
42
+
vec![
43
+
ParameterType::Flow(is_equal),
44
+
ParameterType::Flow(!is_equal),
45
+
]
46
}
47
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+38
-26
src-tauri/src/runtime/nodes/conditional/iffalse.rs
+38
-26
src-tauri/src/runtime/nodes/conditional/iffalse.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct ConditionalIfFalse{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
runtime_active: bool
6
}
7
8
-
impl ConditionalIfFalse{
9
-
pub fn new( node: Node ) -> Box<Self>{
10
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
16
})
17
}
18
}
19
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![]) }
23
24
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
25
-
Some(vec![ ParameterType::Flow(!self.runtime_active), ParameterType::Flow(self.runtime_active) ])
26
}
27
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
-
}
36
}
37
38
-
fn is_entrypoint( &self ) -> bool { false }
39
-
}
···
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
6
+
pub struct ConditionalIfFalse {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
}
10
11
+
impl ConditionalIfFalse {
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
})
27
}
28
}
29
30
+
impl RuntimeNode for ConditionalIfFalse {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
34
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
37
}
38
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
+
]
46
}
47
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+38
-26
src-tauri/src/runtime/nodes/conditional/iftrue.rs
+38
-26
src-tauri/src/runtime/nodes/conditional/iftrue.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct ConditionalIfTrue{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
runtime_active: bool
6
}
7
8
-
impl ConditionalIfTrue{
9
-
pub fn new( node: Node ) -> Box<Self>{
10
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
16
})
17
}
18
}
19
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![]) }
23
24
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
25
-
Some(vec![ ParameterType::Flow(self.runtime_active), ParameterType::Flow(!self.runtime_active) ])
26
}
27
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
-
}
36
}
37
38
-
fn is_entrypoint( &self ) -> bool { false }
39
-
}
···
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
6
+
pub struct ConditionalIfTrue {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
}
10
11
+
impl ConditionalIfTrue {
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
})
27
}
28
}
29
30
+
impl RuntimeNode for ConditionalIfTrue {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
34
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
37
}
38
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
+
]
46
}
47
48
+
fn is_entrypoint(&self) -> bool {
49
+
false
50
+
}
51
+
}
+2
-2
src-tauri/src/runtime/nodes/conditional/mod.rs
+2
-2
src-tauri/src/runtime/nodes/conditional/mod.rs
+36
-23
src-tauri/src/runtime/nodes/debug.rs
+36
-23
src-tauri/src/runtime/nodes/debug.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct Debug{
4
-
to_log: Option<ParameterType>
5
}
6
7
-
impl Debug{
8
-
pub fn new( _: Node ) -> Box<Self>{
9
-
Box::new(Self { to_log: None })
10
}
11
}
12
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![]) }
16
17
-
fn execute( &mut self ) -> Option<Vec<ParameterType>> {
18
-
dbg!(&self.to_log);
19
-
self.to_log = None;
20
21
-
None
22
}
23
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
-
}
31
}
32
-
33
-
fn is_entrypoint( &self ) -> bool { false }
34
-
}
···
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
6
+
pub struct Debug {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>
9
}
10
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
+
})
27
}
28
}
29
30
+
impl RuntimeNode for Debug {
31
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
32
+
self.outputs.clone()
33
+
}
34
35
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
36
+
self.inputs.clone()
37
+
}
38
39
+
fn execute(&mut self, args: Vec<ParameterType>) -> Vec<ParameterType> {
40
+
dbg!(&args); // TODO: Debug to actual UI instead of console
41
+
vec![]
42
}
43
44
+
fn is_entrypoint(&self) -> bool {
45
+
false
46
}
47
+
}
+1
src-tauri/src/runtime/nodes/oscactions/mod.rs
+1
src-tauri/src/runtime/nodes/oscactions/mod.rs
···
···
1
+
pub mod sendchatbox;
+61
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
+61
src-tauri/src/runtime/nodes/oscactions/sendchatbox.rs
···
···
1
+
use std::vec;
2
+
3
+
use crate::{
4
+
osc,
5
+
runtime::nodes::RuntimeNode,
6
+
structs::{nodes::Node, parameter_types::ParameterType},
7
+
};
8
+
9
+
pub struct OSCActionsSendChatbox {
10
+
outputs: Vec<Vec<(String, isize, isize)>>,
11
+
inputs: Vec<Option<(String, isize, isize)>>,
12
+
}
13
+
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
+
})
30
+
}
31
+
}
32
+
33
+
impl RuntimeNode for OSCActionsSendChatbox {
34
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
35
+
self.outputs.clone()
36
+
}
37
+
38
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
39
+
self.inputs.clone()
40
+
}
41
+
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
+
);
53
+
}
54
+
55
+
vec![]
56
+
}
57
+
58
+
fn is_entrypoint(&self) -> bool {
59
+
false
60
+
}
61
+
}
+49
-42
src-tauri/src/runtime/nodes/osctrigger.rs
+49
-42
src-tauri/src/runtime/nodes/osctrigger.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct OSCTrigger{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
address: Option<String>,
6
-
runtime_active: bool
7
}
8
9
-
impl OSCTrigger{
10
-
pub fn new( node: Node ) -> Box<Self>{
11
let value = &node.statics[0].value;
12
13
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
20
})
21
}
22
}
23
24
-
impl RuntimeNode for OSCTrigger{
25
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
26
self.outputs.clone()
27
}
28
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
-
}
34
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.
43
} else{
44
-
self.runtime_active = false;
45
-
None
46
}
47
} 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;
56
57
-
Some(vec![ ParameterType::Flow(execute) ])
58
}
59
60
-
fn update_arg( &mut self, _: usize, _: ParameterType ) -> bool { false }
61
-
fn is_entrypoint( &self ) -> bool { true }
62
-
}
···
1
+
use crate::{
2
+
runtime::nodes::RuntimeNode,
3
+
structs::{nodes::Node, parameter_types::ParameterType},
4
+
};
5
6
+
pub struct OSCTrigger {
7
+
outputs: Vec<Vec<(String, isize, isize)>>,
8
+
inputs: Vec<Option<(String, isize, isize)>>,
9
+
10
+
address: Option<String>
11
}
12
13
+
impl OSCTrigger {
14
+
pub fn new(node: Node) -> Box<Self> {
15
let value = &node.statics[0].value;
16
17
Box::new(Self {
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
+
},
36
})
37
}
38
}
39
40
+
impl RuntimeNode for OSCTrigger {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
42
self.outputs.clone()
43
}
44
45
+
fn inputs(&self) -> Vec<Option<(String, isize, isize)>> {
46
+
self.inputs.clone()
47
+
}
48
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
55
} else{
56
+
false
57
}
58
} else{
59
+
false
60
+
};
61
62
+
args[0] = ParameterType::Flow(execute);
63
+
args
64
}
65
66
+
fn is_entrypoint(&self) -> bool {
67
+
true
68
+
}
69
+
}
+73
src-tauri/src/runtime/nodes/press_key.rs
+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
+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
+44
-23
src-tauri/src/runtime/nodes/statics/float.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct StaticFloat{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<f32>
6
}
7
8
-
impl StaticFloat{
9
-
pub fn new( node: Node ) -> Box<Self>{
10
let value = &node.statics[0].value;
11
12
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
})
19
}
20
}
21
22
-
impl RuntimeNode for StaticFloat{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
24
self.outputs.clone()
25
}
26
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
32
}
33
}
34
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
-
}
···
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)>>,
9
10
+
value: Option<f32>,
11
}
12
13
+
impl StaticFloat {
14
+
pub fn new(node: Node) -> Box<Self> {
15
let value = &node.statics[0].value;
16
17
Box::new(Self {
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
+
}
36
})
37
}
38
}
39
40
+
impl RuntimeNode for StaticFloat {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
42
self.outputs.clone()
43
}
44
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)]
54
}
55
}
56
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+44
-23
src-tauri/src/runtime/nodes/statics/int.rs
+44
-23
src-tauri/src/runtime/nodes/statics/int.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct StaticInt{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<i32>
6
}
7
8
-
impl StaticInt{
9
-
pub fn new( node: Node ) -> Box<Self>{
10
let value = &node.statics[0].value;
11
12
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
})
19
}
20
}
21
22
-
impl RuntimeNode for StaticInt{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
24
self.outputs.clone()
25
}
26
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
32
}
33
}
34
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
-
}
···
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)>>,
9
10
+
value: Option<i32>,
11
}
12
13
+
impl StaticInt {
14
+
pub fn new(node: Node) -> Box<Self> {
15
let value = &node.statics[0].value;
16
17
Box::new(Self {
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
+
}
36
})
37
}
38
}
39
40
+
impl RuntimeNode for StaticInt {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
42
self.outputs.clone()
43
}
44
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)]
54
}
55
}
56
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+2
-2
src-tauri/src/runtime/nodes/statics/mod.rs
+2
-2
src-tauri/src/runtime/nodes/statics/mod.rs
+44
-23
src-tauri/src/runtime/nodes/statics/string.rs
+44
-23
src-tauri/src/runtime/nodes/statics/string.rs
···
1
-
use crate::{ runtime::nodes::RuntimeNode, structs::{ nodes::Node, parameter_types::ParameterType } };
2
3
-
pub struct StaticString{
4
-
outputs: Vec<Vec<( String, isize, isize )>>,
5
-
value: Option<String>
6
}
7
8
-
impl StaticString{
9
-
pub fn new( node: Node ) -> Box<Self>{
10
let value = &node.statics[0].value;
11
12
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
})
19
}
20
}
21
22
-
impl RuntimeNode for StaticString{
23
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>> {
24
self.outputs.clone()
25
}
26
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
32
}
33
}
34
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
-
}
···
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)>>,
9
10
+
value: Option<String>,
11
}
12
13
+
impl StaticString {
14
+
pub fn new(node: Node) -> Box<Self> {
15
let value = &node.statics[0].value;
16
17
Box::new(Self {
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
+
}
36
})
37
}
38
}
39
40
+
impl RuntimeNode for StaticString {
41
+
fn outputs(&self) -> Vec<Vec<(String, isize, isize)>> {
42
self.outputs.clone()
43
}
44
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())]
54
}
55
}
56
57
+
fn is_entrypoint(&self) -> bool {
58
+
false
59
+
}
60
+
}
+79
-26
src-tauri/src/runtime/nodes.rs
+79
-26
src-tauri/src/runtime/nodes.rs
···
1
-
use std::collections::HashMap;
2
3
-
use crate::{ runtime::nodes::{ conditional::{ ifequal::ConditionalIfEqual, iffalse::ConditionalIfFalse, iftrue::ConditionalIfTrue }, debug::Debug, osctrigger::OSCTrigger, statics::{ float::StaticFloat, int::StaticInt, string::StaticString } }, structs::{ nodes::Node, parameter_types::ParameterType } };
4
5
mod osctrigger;
6
-
mod debug;
7
mod statics;
8
-
mod conditional;
9
10
-
pub struct RuntimeNodeTree{
11
-
pub nodes: HashMap<String, Box<dyn RuntimeNode>>
12
}
13
14
-
unsafe impl Send for RuntimeNodeTree{}
15
16
-
impl RuntimeNodeTree{
17
-
pub fn from( tree: Vec<Node> ) -> Self{
18
let mut runtime_nodes: HashMap<String, Box<dyn RuntimeNode>> = HashMap::new();
19
-
for node in tree{
20
-
match node.type_id.as_str(){
21
-
"osctrigger" => { runtime_nodes.insert(node.id.clone(), OSCTrigger::new(node)); }
22
23
-
"staticstring" => { runtime_nodes.insert(node.id.clone(), StaticString::new(node)); }
24
-
"staticint" => { runtime_nodes.insert(node.id.clone(), StaticInt::new(node)); }
25
-
"staticfloat" => { runtime_nodes.insert(node.id.clone(), StaticFloat::new(node)); }
26
27
-
"iftrue" => { runtime_nodes.insert(node.id.clone(), ConditionalIfTrue::new(node)); }
28
-
"iffalse" => { runtime_nodes.insert(node.id.clone(), ConditionalIfFalse::new(node)); }
29
-
"ifequal" => { runtime_nodes.insert(node.id.clone(), ConditionalIfEqual::new(node)); }
30
31
-
"debug" => { runtime_nodes.insert(node.id.clone(), Debug::new(node)); }
32
_ => {}
33
}
34
}
35
36
-
Self { nodes: runtime_nodes }
37
}
38
}
39
40
-
pub trait RuntimeNode{
41
-
fn outputs( &self ) -> Vec<Vec<( String, isize, isize )>>; // Node ID, input index, output value type
42
-
fn execute_dry( &mut self, msg: &Vec<ParameterType> ) -> Option<Vec<ParameterType>>; // Only update values on the first loop through
43
-
fn execute( &mut self ) -> Option<Vec<ParameterType>>; // Then call functions on the second loop
44
-
fn update_arg( &mut self, index: usize, value: ParameterType ) -> bool;
45
-
fn is_entrypoint( &self ) -> bool;
46
}
···
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
+
};
17
18
+
// #[cfg(target_os = "windows")]
19
+
use crate::runtime::nodes::press_key::PressKey;
20
21
+
mod conditional;
22
+
mod debug;
23
+
mod oscactions;
24
mod osctrigger;
25
mod statics;
26
+
mod shell;
27
+
28
+
// #[cfg(target_os = "windows")]
29
+
mod press_key;
30
31
+
pub struct RuntimeNodeTree {
32
+
pub nodes: HashMap<String, Box<dyn RuntimeNode>>,
33
}
34
35
+
unsafe impl Send for RuntimeNodeTree {}
36
37
+
impl RuntimeNodeTree {
38
+
pub fn from(tree: Vec<Node>, /*#[cfg(target_os = "windows")]*/ enigo: Arc<Mutex<Enigo>>) -> Self {
39
let mut runtime_nodes: HashMap<String, Box<dyn RuntimeNode>> = HashMap::new();
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
+
}
45
+
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
+
}
55
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
+
}
78
+
79
+
"shellcommand" => {
80
+
runtime_nodes.insert(node.id.clone(), ShellCommand::new(node));
81
+
}
82
+
83
_ => {}
84
}
85
}
86
87
+
Self {
88
+
nodes: runtime_nodes,
89
+
}
90
}
91
}
92
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;
99
}
+65
-40
src-tauri/src/runtime.rs
+65
-40
src-tauri/src/runtime.rs
···
1
-
use anyhow::{ bail, Result };
2
3
-
use crate::{ runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType };
4
5
-
pub mod nodes;
6
pub mod commands;
7
8
-
// This is horrible. I know, I'm sorry.
9
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"); }
13
14
-
let node = node.unwrap();
15
16
-
let output_map = node.outputs();
17
-
let args = node.execute_dry(parameters);
18
19
-
if args.is_some(){
20
-
let args = args.unwrap();
21
22
-
for i in 0..args.len(){
23
-
let arg = &args[i];
24
25
-
for output in &output_map[i]{
26
-
if output.2 == 5{ break; } // Ignore flow outputs
27
28
-
let next_node = tab.nodes.get_mut(&output.0);
29
-
if next_node.is_none(){ bail!("Cannot find node {}", output.0) }
30
31
-
let next_node = next_node.unwrap();
32
-
let can_update = next_node.update_arg(output.1 as usize, arg.clone());
33
34
-
if can_update{
35
-
runtime_dry(output.0.clone(), &vec![], tab)?;
36
}
37
}
38
}
···
41
Ok(())
42
}
43
44
-
pub fn runtime( entry: String, tab: &mut RuntimeNodeTree ) -> Result<()>{
45
let node = tab.nodes.get_mut(&entry);
46
-
if node.is_none(){ bail!("Cannot find node"); }
47
48
let node = node.unwrap();
49
50
-
let next = node.execute();
51
-
if next.is_some(){
52
-
let next = next.unwrap();
53
54
-
let outputs = node.outputs();
55
56
-
for i in 0..next.len(){
57
-
let arg = &next[i];
58
-
if i >= outputs.len() { break; }
59
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
-
}
67
}
68
}
69
}
70
71
-
Ok(())
72
-
}
···
1
+
use std::collections::HashMap;
2
+
3
+
use anyhow::{bail, Result};
4
5
+
use crate::{runtime::nodes::RuntimeNodeTree, structs::parameter_types::ParameterType};
6
7
pub mod commands;
8
+
pub mod nodes;
9
10
+
// TODO: Variables
11
12
+
pub fn recurse_runtime(entry: String, tab: &mut RuntimeNodeTree, args: Vec<ParameterType>) -> Result<()>{
13
+
let ( out_args, output_map ) = runtime(entry, tab, args)?;
14
15
+
let mut next_node_args: HashMap<String, Vec<ParameterType>> = HashMap::new();
16
17
+
for i in 0..out_args.len(){
18
+
if output_map.len() <= i { break; }
19
+
let links = &output_map[i];
20
21
+
for ( id, link_index, _ ) in links{
22
+
let link_index = link_index.clone() as usize;
23
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); }
27
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());
32
33
+
next_node_args.insert(id.clone(), args);
34
+
}
35
+
}
36
+
}
37
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];
42
43
+
for ( id, _, _ ) in links{
44
+
let args = next_node_args.remove(id).unwrap();
45
+
recurse_runtime(id.clone(), tab, args)?;
46
}
47
}
48
}
···
51
Ok(())
52
}
53
54
+
pub fn runtime(entry: String, tab: &mut RuntimeNodeTree, mut args: Vec<ParameterType>) -> Result<(Vec<ParameterType>, Vec<Vec<(String, isize, isize)>>)> {
55
let node = tab.nodes.get_mut(&entry);
56
+
if node.is_none() { bail!("Cannot find node"); }
57
58
let node = node.unwrap();
59
+
let inputs = node.inputs();
60
61
+
let mut needed_input_nodes = HashMap::new();
62
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
+
}
74
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];
80
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();
86
}
87
}
88
}
89
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
+62
-39
src-tauri/src/setup.rs
···
1
-
use std::{ collections::HashMap, fs::File, io::Read, sync::Mutex };
2
-
use crossbeam_channel::{ Receiver, bounded };
3
4
use flate2::read::GzDecoder;
5
-
use serde_json::{ Map, Value };
6
-
use tauri::{ App, Emitter, Listener, Manager, WindowEvent };
7
8
-
use crate::{ osc::{ self, OSCMessage }, runtime::{ commands::RuntimeCommand, nodes::RuntimeNodeTree, runtime, runtime_dry }, structs::parameter_types::ParameterType, utils::setup_traymenu::setup_traymenu };
9
10
pub fn setup(
11
app: &mut App,
12
addresses: &'static Mutex<Vec<OSCMessage>>,
13
-
runtime_command_receiver: Receiver<RuntimeCommand>
14
) {
15
let window = app.get_webview_window("main").unwrap();
16
window.hide().unwrap();
17
18
let win_handle = window.clone();
19
-
window.on_window_event(move | event | {
20
-
match event{
21
-
WindowEvent::CloseRequested { api, .. } => {
22
-
api.prevent_close();
23
24
win_handle.hide().unwrap();
25
win_handle.emit("hide-window", ()).unwrap();
26
}
27
-
_ => {}
28
}
29
});
30
31
setup_traymenu(app.handle());
···
49
handle.emit("load_new_tab", Value::Object(map)).unwrap();
50
});
51
52
-
let ( sender, receiver ) = bounded(1024);
53
54
tokio::spawn(async move {
55
osc::start_server(sender, "127.0.0.1:9001");
56
});
57
58
-
let ( runtime_sender, runtime_receiver ) = bounded(1024);
59
60
let runtime_sender_1 = runtime_sender.clone();
61
tokio::spawn(async move {
···
73
74
let msg = message.clone();
75
let mut addrs = addresses.lock().unwrap();
76
-
if !addrs.contains(&msg) { addrs.push(msg); }
77
78
-
runtime_sender.send(RuntimeCommand::OSCMessage(message)).unwrap();
79
}
80
});
81
82
tokio::spawn(async move {
83
let mut tabs: HashMap<String, RuntimeNodeTree> = HashMap::new();
84
85
loop {
86
let cmd = runtime_receiver.recv().unwrap();
87
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();
92
93
-
for id in keys.clone(){
94
let entry = tab.nodes[&id].is_entrypoint();
95
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();
107
108
-
if entry{
109
-
let _ = runtime(id.clone(), &mut tab);
110
}
111
}
112
}
113
-
},
114
115
-
RuntimeCommand::AddTab( graph, id ) => {
116
-
tabs.insert(id, RuntimeNodeTree::from(graph));
117
-
},
118
-
RuntimeCommand::RemoveTab( id ) => {
119
tabs.remove(&id);
120
}
121
}
122
}
123
});
124
-
}
···
1
+
use crossbeam_channel::{bounded, Receiver};
2
+
use std::{
3
+
collections::HashMap,
4
+
fs::File,
5
+
io::Read,
6
+
sync::{Arc, Mutex},
7
+
};
8
9
use flate2::read::GzDecoder;
10
+
use serde_json::{Map, Value};
11
+
use tauri::{App, Emitter, Listener, Manager, WindowEvent};
12
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
+
};
16
17
pub fn setup(
18
app: &mut App,
19
addresses: &'static Mutex<Vec<OSCMessage>>,
20
+
runtime_command_receiver: Receiver<RuntimeCommand>,
21
) {
22
let window = app.get_webview_window("main").unwrap();
23
window.hide().unwrap();
24
25
let win_handle = window.clone();
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{
35
win_handle.hide().unwrap();
36
win_handle.emit("hide-window", ()).unwrap();
37
+
win_handle.unminimize().unwrap();
38
}
39
}
40
+
_ => {}
41
});
42
43
setup_traymenu(app.handle());
···
61
handle.emit("load_new_tab", Value::Object(map)).unwrap();
62
});
63
64
+
let (sender, receiver) = bounded(1024);
65
66
tokio::spawn(async move {
67
osc::start_server(sender, "127.0.0.1:9001");
68
});
69
70
+
let (runtime_sender, runtime_receiver) = bounded(1024);
71
72
let runtime_sender_1 = runtime_sender.clone();
73
tokio::spawn(async move {
···
85
86
let msg = message.clone();
87
let mut addrs = addresses.lock().unwrap();
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();
94
95
+
// TODO: Read avatar paramaters from file
96
+
}
97
+
98
+
runtime_sender
99
+
.send(RuntimeCommand::OSCMessage(message))
100
+
.unwrap();
101
}
102
});
103
104
+
// TODO: Run tabs in seperate threads (really not looking forward to this... thanks rust)
105
+
106
tokio::spawn(async move {
107
let mut tabs: HashMap<String, RuntimeNodeTree> = HashMap::new();
108
109
+
// #[cfg(target_os = "windows")]
110
+
let enigo = Arc::new(Mutex::new(enigo::Enigo::new(&enigo::Settings::default()).unwrap()));
111
+
112
loop {
113
let cmd = runtime_receiver.recv().unwrap();
114
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();
119
120
+
for id in keys {
121
let entry = tab.nodes[&id].is_entrypoint();
122
123
+
if entry {
124
+
let mut args = vec![ ParameterType::String(msg.address.clone())];
125
+
let mut values = msg.values.clone();
126
127
+
args.append(&mut values);
128
+
let _ = recurse_runtime(id.clone(), &mut tab, args);
129
}
130
}
131
}
132
+
}
133
+
134
+
RuntimeCommand::AddTab(graph, id) => {
135
+
// #[cfg(target_os = "windows")]
136
+
tabs.insert(id, RuntimeNodeTree::from(graph, enigo.clone()));
137
138
+
// #[cfg(target_os = "linux")]
139
+
// tabs.insert(id, RuntimeNodeTree::from(graph));
140
+
}
141
+
RuntimeCommand::RemoveTab(id) => {
142
tabs.remove(&id);
143
}
144
}
145
}
146
});
147
+
}
+1
-1
src-tauri/src/structs/mod.rs
+1
-1
src-tauri/src/structs/mod.rs
+13
-12
src-tauri/src/structs/nodes.rs
+13
-12
src-tauri/src/structs/nodes.rs
···
1
-
use serde::{ Deserialize, Serialize };
2
use serde_json::Value;
3
4
#[derive(Serialize, Deserialize, Debug, Clone)]
5
-
pub struct Node{
6
pub id: String,
7
pub name: String,
8
-
pub outputs: Vec<NodeOutput>,
9
-
pub pos: [ f32; 2 ],
10
pub statics: Vec<NodeStatic>,
11
12
#[serde(rename = "typeId")]
13
-
pub type_id: String
14
}
15
16
#[derive(Serialize, Deserialize, Debug, Clone)]
17
-
pub struct NodeStatic{
18
pub name: String,
19
20
#[serde(rename = "type")]
21
pub value_type: isize,
22
-
pub value: Value
23
}
24
25
#[derive(Serialize, Deserialize, Debug, Clone)]
26
-
pub struct NodeOutput{
27
pub name: String,
28
29
#[serde(rename = "type")]
30
pub value_type: isize,
31
-
pub connections: Vec<NodeOutputConnections>
32
}
33
34
#[derive(Serialize, Deserialize, Debug, Clone)]
35
-
pub struct NodeOutputConnections{
36
pub name: String,
37
pub node: String,
38
pub index: isize,
39
40
#[serde(rename = "type")]
41
-
pub value_type: isize
42
-
}
···
1
+
use serde::{Deserialize, Serialize};
2
use serde_json::Value;
3
4
#[derive(Serialize, Deserialize, Debug, Clone)]
5
+
pub struct Node {
6
pub id: String,
7
pub name: String,
8
+
pub outputs: Vec<NodeIO>,
9
+
pub inputs: Vec<NodeIO>,
10
+
pub pos: [f32; 2],
11
pub statics: Vec<NodeStatic>,
12
13
#[serde(rename = "typeId")]
14
+
pub type_id: String,
15
}
16
17
#[derive(Serialize, Deserialize, Debug, Clone)]
18
+
pub struct NodeStatic {
19
pub name: String,
20
21
#[serde(rename = "type")]
22
pub value_type: isize,
23
+
pub value: Value,
24
}
25
26
#[derive(Serialize, Deserialize, Debug, Clone)]
27
+
pub struct NodeIO {
28
pub name: String,
29
30
#[serde(rename = "type")]
31
pub value_type: isize,
32
+
pub connections: Vec<NodeOutputConnections>,
33
}
34
35
#[derive(Serialize, Deserialize, Debug, Clone)]
36
+
pub struct NodeOutputConnections {
37
pub name: String,
38
pub node: String,
39
pub index: isize,
40
41
#[serde(rename = "type")]
42
+
pub value_type: isize,
43
+
}
+41
-29
src-tauri/src/structs/parameter_types.rs
+41
-29
src-tauri/src/structs/parameter_types.rs
···
1
-
use anyhow::{Result, bail};
2
use serde::Serialize;
3
4
#[derive(Serialize, Clone, Debug, PartialEq)]
···
13
String(String),
14
Flow(bool),
15
16
-
None
17
}
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.")
25
}
26
}
27
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.")
35
}
36
}
37
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.")
44
}
45
}
46
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.")
54
}
55
}
56
-
}
···
1
+
use anyhow::{bail, Result};
2
use serde::Serialize;
3
4
#[derive(Serialize, Clone, Debug, PartialEq)]
···
13
String(String),
14
Flow(bool),
15
16
+
None,
17
}
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) => {
24
+
if *val == 0 {
25
+
Ok(false)
26
+
} else {
27
+
Ok(true)
28
+
}
29
+
}
30
+
_ => bail!("Cannot cast to bool."),
31
}
32
}
33
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."),
47
}
48
}
49
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."),
56
}
57
}
58
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."),
66
}
67
}
68
+
}
+26
-10
src-tauri/src/utils/config.rs
+26
-10
src-tauri/src/utils/config.rs
···
1
-
use std::{ collections::HashMap, fs::File, io::{ Read, Write }, path::PathBuf, sync::Mutex };
2
3
-
use flate2::{ read::GzDecoder, write::GzEncoder, Compression };
4
-
use serde::{ Deserialize, Serialize };
5
6
use crate::structs::nodes::Node;
7
8
#[derive(Clone, Serialize, Deserialize, Debug)]
9
-
pub struct ConfigValues{
10
#[serde(default)]
11
-
pub loaded_tabs: HashMap<String, ( Vec<Node>, String, Option<String> )>,
12
13
#[serde(default)]
14
-
pub hide_editor_on_start: bool
15
}
16
17
pub struct Config {
···
33
34
let json: ConfigValues = serde_json::from_str(&json_string).unwrap();
35
36
-
dbg!(&json);
37
-
38
Config {
39
store: Mutex::new(json),
40
path: path,
···
42
}
43
44
pub fn save(&self) {
45
-
let dat = serde_json::to_string(&self.store.lock().unwrap().clone()).unwrap();
46
-
dbg!(&dat);
47
48
let file = File::create(&self.path).unwrap();
49
let mut encoder = GzEncoder::new(file, Compression::default());
···
1
+
use std::{
2
+
collections::HashMap,
3
+
fs::File,
4
+
io::{Read, Write},
5
+
path::PathBuf,
6
+
sync::{Mutex, MutexGuard},
7
+
};
8
9
+
use chrono::Utc;
10
+
use flate2::{read::GzDecoder, write::GzEncoder, Compression};
11
+
use serde::{Deserialize, Serialize};
12
13
use crate::structs::nodes::Node;
14
15
#[derive(Clone, Serialize, Deserialize, Debug)]
16
+
pub struct ConfigValues {
17
#[serde(default)]
18
+
pub loaded_tabs: HashMap<String, (Vec<Node>, String, Option<String>, bool)>,
19
20
#[serde(default)]
21
+
pub hide_editor_on_start: bool,
22
}
23
24
pub struct Config {
···
40
41
let json: ConfigValues = serde_json::from_str(&json_string).unwrap();
42
43
Config {
44
store: Mutex::new(json),
45
path: path,
···
47
}
48
49
pub fn save(&self) {
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();
63
64
let file = File::create(&self.path).unwrap();
65
let mut encoder = GzEncoder::new(file, Compression::default());
+2
-1
src-tauri/src/utils/mod.rs
+2
-1
src-tauri/src/utils/mod.rs
+315
src-tauri/src/utils/vrchat_builtin_parameters.rs
+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
+1
-1
vite.config.ts