+1
public/assets/icons/circle-info-solid-full.svg
+1
public/assets/icons/circle-info-solid-full.svg
···
1
+
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 640 640"><!--!Font Awesome Free 7.1.0 by @fontawesome - https://fontawesome.com License - https://fontawesome.com/license/free Copyright 2025 Fonticons, Inc.--><path fill="#fff" d="M320 576C461.4 576 576 461.4 576 320C576 178.6 461.4 64 320 64C178.6 64 64 178.6 64 320C64 461.4 178.6 576 320 576zM288 224C288 206.3 302.3 192 320 192C337.7 192 352 206.3 352 224C352 241.7 337.7 256 320 256C302.3 256 288 241.7 288 224zM280 288L328 288C341.3 288 352 298.7 352 312L352 400L360 400C373.3 400 384 410.7 384 424C384 437.3 373.3 448 360 448L280 448C266.7 448 256 437.3 256 424C256 410.7 266.7 400 280 400L304 400L304 336L280 336C266.7 336 256 325.3 256 312C256 298.7 266.7 288 280 288z"/></svg>
+18
-8
src/App.tsx
+18
-8
src/App.tsx
···
2
2
import "./App.css";
3
3
import { renderBackgroundGrid, renderContextMenu, renderNodes, renderNullTab, renderTempDrawing } from "./renderer";
4
4
import { lerp } from "./utils/lerp";
5
-
import { Node, NodeIO, NodeIOResolveAnyTypes } from "./structs/node";
5
+
import { Node, NodeIO, NodeIOCanCast, NodeIOResolveAnyTypes } from "./structs/node";
6
6
import { isPointInRect, isPointInRectApplyOffset, screenToWorldSpace } from "./utils/interections";
7
7
import { ControlBar } from "./components/ControlBar";
8
8
import { CanvasContextMenu } from "./ContextMenu/Canvas";
···
13
13
import { ConfirmationPopup } from "./components/ConfirmationPopup";
14
14
15
15
let App = () => {
16
-
// TODO: Delete selected node when delete key is pressed
17
16
// TODO: Keybind system
17
+
// TODO: Delete selected node when delete key is pressed
18
+
// TODO: Copy / paste
18
19
// TODO: Add undo / redo -ing
20
+
// TODO: Multi-select
19
21
20
22
let [ selectedNode, setSelectedNode ] = createSignal<Node | null>(null);
21
23
···
123
125
return;
124
126
}
125
127
128
+
if(e.shiftKey){
129
+
// TODO: Multi-select
130
+
return;
131
+
}
132
+
126
133
if(contextMenu.visible){
127
134
let submenus: ContextMenu[] = [];
128
135
contextMenu.items.map(x => x.menu ? submenus.push(x.menu): null);
···
161
168
162
169
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
163
170
e.clientX, e.clientY,
164
-
node.x, node.y, node.w, node.h
171
+
node.x - 20, node.y, node.w + 40, node.h
165
172
)){
166
173
node.outputs.map(( output, i ) => {
167
174
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
168
175
e.clientX, e.clientY,
169
-
node.x + (node.w - 30),
176
+
node.x + (node.w - 10),
170
177
node.y + 50 + (30 * i),
171
178
20, 20
172
179
)){
173
180
output.index = i;
174
181
175
-
drawingTo = [ node.x + (node.w - 30), node.y + 50 + (30 * i) ];
182
+
drawingTo = [
183
+
node.x + (node.w - 10),
184
+
node.y + 50 + (30 * i)
185
+
];
176
186
drawingFrom = output;
177
187
178
188
isDrawing = true;
···
183
193
node.inputs.map(( input, i ) => {
184
194
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
185
195
e.clientX, e.clientY,
186
-
node.x + 10,
196
+
node.x - 10,
187
197
node.y + 50 + (30 * i),
188
198
20, 20
189
199
)){
···
285
295
node.inputs.map(( input, i ) => {
286
296
if(isPointInRectApplyOffset(canvas, { x: offset[0], y: offset[1], scale },
287
297
e.clientX, e.clientY,
288
-
node.x + 10,
298
+
node.x - 10,
289
299
node.y + 50 + (30 * i),
290
300
20, 20
291
301
)){
···
297
307
drawingFrom!.connections.indexOf(input) === -1 &&
298
308
(
299
309
toType === null ||
300
-
fromType === toType
310
+
NodeIOCanCast(fromType, toType)
301
311
)
302
312
){
303
313
drawingFrom!.connections.push(input);
+2
-2
src/Nodes/OSCTrigger.tsx
+2
-2
src/Nodes/OSCTrigger.tsx
+3
src/components/TabMenu.css
+3
src/components/TabMenu.css
+4
-1
src/components/TabMenu.tsx
+4
-1
src/components/TabMenu.tsx
···
4
4
import { NodeManager } from '../Mangers/NodeManager';
5
5
import { Tab } from '../structs/Tab';
6
6
import { SettingsMenu } from './SettingsMenu';
7
+
import { openUrl } from '@tauri-apps/plugin-opener';
7
8
8
9
export let TabMenu = () => {
9
10
let [ tabImportOpen, setTabImportOpen ] = createSignal(false);
···
25
26
<Show when={settingsOpen()}>
26
27
<SettingsMenu close={() => setSettingsOpen(false)} />
27
28
</Show>
28
-
29
+
29
30
<div class="tab-menu">
30
31
<div class="tab-container">
31
32
<For each={Object.values(tabs())}>
···
86
87
87
88
<div class="tab-icon-bar">
88
89
<img src="/assets/icons/gear-solid-full.svg" width="25" onClick={() => setSettingsOpen(true)} />
90
+
<div style={{ width: 'calc(100% - 50px)' }}></div>
91
+
<img src="/assets/icons/circle-info-solid-full.svg" width="25" onClick={() => openUrl('https://github.com/phaze-the-dumb/VRCMacros/wiki')} />
89
92
</div>
90
93
</div>
91
94
</>
+58
-35
src/renderer.ts
+58
-35
src/renderer.ts
···
63
63
let nodeX = Math.round(node.x / 10) * 10;
64
64
let nodeY = Math.round(node.y / 10) * 10;
65
65
66
-
ctx.fillStyle = '#1f2129';
67
-
ctx.strokeStyle = node.selected ? '#004696ff' : '#fff5';
66
+
ctx.fillStyle = '#343742ff';
67
+
ctx.strokeStyle = node.selected ? '#004696ff' : '#fff0';
68
68
ctx.lineWidth = 5 * position.scale;
69
69
70
70
// Draw Node Box
···
75
75
node.h * position.scale,
76
76
10 * position.scale);
77
77
78
+
ctx.shadowColor = '#0005';
79
+
ctx.shadowBlur = 10;
80
+
78
81
ctx.stroke();
79
82
ctx.fill();
83
+
84
+
ctx.shadowBlur = 0;
80
85
81
86
// Draw Node Name
82
87
ctx.fillStyle = '#fff';
83
-
ctx.font = (25 * position.scale) + 'px Comic Mono';
88
+
ctx.font = (25 * position.scale) + 'px Rubik';
84
89
ctx.textAlign = 'center';
85
90
86
91
ctx.fillText(node.name,
···
89
94
);
90
95
91
96
// Draw Inputs
92
-
ctx.font = (15 * position.scale) + 'px Comic Mono';
97
+
ctx.font = (15 * position.scale) + 'px Rubik';
93
98
ctx.textAlign = 'left';
94
99
95
100
node.inputs.map(( input, i ) => {
96
101
ctx.fillStyle = NodeIOLinkColours(input);
97
-
ctx.fillRect(
98
-
(nodeX + 10 + startX + position.x) * position.scale,
99
-
(nodeY + 50 + (30 * i) + startY + position.y) * position.scale,
100
-
20 * position.scale, 20 * position.scale
101
-
)
102
+
103
+
ctx.beginPath();
104
+
ctx.arc(
105
+
(nodeX - 10 + startX + 10 + position.x) * position.scale,
106
+
(nodeY + 50 + (30 * i) + startY + 10 + position.y) * position.scale,
107
+
7 * position.scale,
108
+
0,
109
+
Math.PI * 2,
110
+
);
111
+
ctx.fill();
102
112
103
113
ctx.fillText(input.name,
104
-
(nodeX + 35 + startX + position.x) * position.scale,
114
+
(nodeX + 15 + startX + position.x) * position.scale,
105
115
(nodeY + 53 + (30 * i) + startY + position.y) * position.scale,
106
116
)
107
117
})
···
111
121
112
122
node.outputs.map(( output, i ) => {
113
123
ctx.fillStyle = NodeIOLinkColours(output);
114
-
ctx.fillRect(
115
-
(nodeX + (node.w - 30) + startX + position.x) * position.scale,
116
-
(nodeY + 50 + (30 * i) + startY + position.y) * position.scale,
117
-
20 * position.scale, 20 * position.scale
118
-
)
124
+
125
+
ctx.beginPath();
126
+
ctx.arc(
127
+
(nodeX + (node.w - 10) + startX + 10 + position.x) * position.scale,
128
+
(nodeY + 50 + (30 * i) + startY + 10 + position.y) * position.scale,
129
+
7 * position.scale,
130
+
0,
131
+
Math.PI * 2,
132
+
);
133
+
ctx.fill();
119
134
120
135
ctx.fillText(output.name,
121
-
(nodeX + (node.w - 35) + startX + position.x) * position.scale,
136
+
(nodeX + (node.w - 15) + startX + position.x) * position.scale,
122
137
(nodeY + 53 + (30 * i) + startY + position.y) * position.scale,
123
138
)
124
139
})
···
131
146
node.outputs.map(( output, i ) => {
132
147
output.connections.map(partner => {
133
148
ctx.strokeStyle = NodeIOLinkColours(output);
149
+
ctx.lineWidth = 3 * position.scale;
150
+
134
151
drawCurve(ctx,
135
-
(nodeX + (node.w - 30) + 10 + startX + position.x) * position.scale,
152
+
(nodeX + (node.w - 10) + 10 + startX + position.x) * position.scale,
136
153
(nodeY + 50 + (30 * i) + 10 + startY + position.y) * position.scale,
137
-
((Math.round(partner.parent.x / 10) * 10) + 20 + startX + position.x) * position.scale,
154
+
((Math.round(partner.parent.x / 10) * 10) + startX + position.x) * position.scale,
138
155
((Math.round(partner.parent.y / 10) * 10) + 60 + (30 * partner.index) + startY + position.y) * position.scale,
139
156
);
140
157
ctx.stroke();
···
148
165
contextMenu: ContextMenu
149
166
) => {
150
167
if(contextMenu.visible){
151
-
ctx.font = '20px Arial';
168
+
ctx.font = '20px Rubik';
152
169
ctx.textBaseline = 'top';
153
170
ctx.textAlign = 'left';
154
171
···
192
209
let startX = canvas.width / -2;
193
210
let startY = canvas.height / -2;
194
211
195
-
ctx.fillStyle = '#f00';
212
+
// DEBUG STUFF
213
+
// ctx.fillStyle = '#f00';
196
214
197
-
ctx.fillRect(
198
-
(drawingTo[0] + 10 + startX + position.x) * position.scale,
199
-
(drawingTo[1] + 10 + startY + position.y) * position.scale,
200
-
10, 10
201
-
);
215
+
// ctx.fillRect(
216
+
// (drawingTo[0] + 10 + startX + position.x) * position.scale,
217
+
// (drawingTo[1] + 10 + startY + position.y) * position.scale,
218
+
// 10, 10
219
+
// );
202
220
203
-
ctx.fillRect(
204
-
(drawingFrom.parent.x + (drawingFrom.parent.w - 30) + 10 + startX + position.x) * position.scale,
205
-
(drawingFrom.parent.y + 50 + (30 * drawingFrom.index) + 10 + startY + position.y) * position.scale,
206
-
10, 10
207
-
);
221
+
// ctx.fillRect(
222
+
// (drawingFrom.parent.x + (drawingFrom.parent.w - 10) + 10 + startX + position.x) * position.scale,
223
+
// (drawingFrom.parent.y + 50 + (30 * drawingFrom.index) + 10 + startY + position.y) * position.scale,
224
+
// 10, 10
225
+
// );
208
226
209
227
ctx.strokeStyle = NodeIOLinkColours(drawingFrom);
228
+
ctx.lineWidth = 3 * position.scale;
229
+
230
+
let nodeX = Math.round(drawingFrom.parent.x / 10) * 10;
231
+
let nodeY = Math.round(drawingFrom.parent.y / 10) * 10;
232
+
210
233
drawCurve(ctx,
211
-
(drawingFrom.parent.x + (drawingFrom.parent.w - 30) + 10 + startX + position.x) * position.scale,
212
-
(drawingFrom.parent.y + 50 + (30 * drawingFrom.index) + 10 + startY + position.y) * position.scale,
234
+
(nodeX + (drawingFrom.parent.w - 10) + 10 + startX + position.x) * position.scale,
235
+
(nodeY + 50 + (30 * drawingFrom.index) + 10 + startY + position.y) * position.scale,
213
236
(drawingTo[0] + 10 + startX + position.x) * position.scale,
214
237
(drawingTo[1] + 10 + startY + position.y) * position.scale,
215
238
);
···
239
262
) => {
240
263
ctx.fillStyle = '#fff';
241
264
242
-
ctx.font = '20px Arial';
265
+
ctx.font = '20px Rubik';
243
266
ctx.textBaseline = 'middle';
244
267
ctx.textAlign = 'center';
245
268
246
269
let textX = lerp((canvas.width / -2) + 200, canvas.width / 2, 0.5);
247
270
let textY = lerp((canvas.height / -2) + 40, canvas.height / 2, 0.5);
248
271
249
-
ctx.font = '40px Arial';
272
+
ctx.font = '40px Rubik';
250
273
ctx.fillText('Welcome to VRCMacros', textX, textY);
251
274
252
-
ctx.font = '20px Arial';
275
+
ctx.font = '20px Rubik';
253
276
ctx.fillText('Create a new tab to get started!', textX, textY + 40);
254
277
}
255
278
+7
src/structs/node.ts
+7
src/structs/node.ts
···
74
74
ParameterList = 10
75
75
}
76
76
77
+
export let NodeIOCanCast = ( input: NodeType | null, output: NodeType | null ): boolean => {
78
+
if(input === output)return true;
79
+
if(!input || !output)return false;
80
+
81
+
return false;
82
+
}
83
+
77
84
export let NodeIOResolveAnyTypes = ( nodeio: NodeIO ): NodeType | null => {
78
85
if(nodeio.type > 0 && nodeio.type < 6){
79
86
// It's a base type