html-overlay-node 0.1.6 → 0.1.10

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,99 @@
1
+ /**
2
+ * Math Nodes Package
3
+ * Provides basic mathematical operation nodes
4
+ */
5
+
6
+ export function registerMathNodes(registry) {
7
+ // Add Node
8
+ registry.register("math/Add", {
9
+ title: "Add",
10
+ color: "#f43f5e", // math (rose)
11
+ size: { w: 140 },
12
+ inputs: [
13
+ { name: "exec", portType: "exec" },
14
+ { name: "a", portType: "data", datatype: "number" },
15
+ { name: "b", portType: "data", datatype: "number" },
16
+ ],
17
+ outputs: [
18
+ { name: "exec", portType: "exec" },
19
+ { name: "result", portType: "data", datatype: "number" },
20
+ ],
21
+ onCreate(node) {
22
+ node.state.a = 0;
23
+ node.state.b = 0;
24
+ },
25
+ onExecute(node, { getInput, setOutput }) {
26
+ const a = getInput("a") ?? node.state.a ?? 0;
27
+ const b = getInput("b") ?? node.state.b ?? 0;
28
+ const result = a + b;
29
+ // Sync state so PropertyPanel shows actual values
30
+ node.state.a = a;
31
+ node.state.b = b;
32
+ node.state.result = result;
33
+ console.log("[Add] a:", a, "b:", b, "result:", result);
34
+ setOutput("result", result);
35
+ },
36
+ });
37
+
38
+ // Subtract Node
39
+ registry.register("math/Subtract", {
40
+ title: "Subtract",
41
+ color: "#f43f5e", // math (rose)
42
+ size: { w: 140 },
43
+ inputs: [
44
+ { name: "a", datatype: "number" },
45
+ { name: "b", datatype: "number" },
46
+ ],
47
+ outputs: [{ name: "result", datatype: "number" }],
48
+ onExecute(node, { getInput, setOutput }) {
49
+ const a = getInput("a") ?? 0;
50
+ const b = getInput("b") ?? 0;
51
+ setOutput("result", a - b);
52
+ },
53
+ });
54
+
55
+ // Multiply Node
56
+ registry.register("math/Multiply", {
57
+ title: "Multiply",
58
+ color: "#f43f5e", // math (rose)
59
+ size: { w: 140 },
60
+ inputs: [
61
+ { name: "exec", portType: "exec" },
62
+ { name: "a", portType: "data", datatype: "number" },
63
+ { name: "b", portType: "data", datatype: "number" },
64
+ ],
65
+ outputs: [
66
+ { name: "exec", portType: "exec" },
67
+ { name: "result", portType: "data", datatype: "number" },
68
+ ],
69
+ onExecute(node, { getInput, setOutput }) {
70
+ const a = getInput("a") ?? node.state?.a ?? 0;
71
+ const b = getInput("b") ?? node.state?.b ?? 0;
72
+ const result = a * b;
73
+ if (node.state) {
74
+ node.state.a = a;
75
+ node.state.b = b;
76
+ node.state.result = result;
77
+ }
78
+ console.log("[Multiply] a:", a, "b:", b, "result:", result);
79
+ setOutput("result", result);
80
+ },
81
+ });
82
+
83
+ // Divide Node
84
+ registry.register("math/Divide", {
85
+ title: "Divide",
86
+ color: "#f43f5e", // math (rose)
87
+ size: { w: 140 },
88
+ inputs: [
89
+ { name: "a", datatype: "number" },
90
+ { name: "b", datatype: "number" },
91
+ ],
92
+ outputs: [{ name: "result", datatype: "number" }],
93
+ onExecute(node, { getInput, setOutput }) {
94
+ const a = getInput("a") ?? 0;
95
+ const b = getInput("b") ?? 1;
96
+ setOutput("result", b !== 0 ? a / b : 0);
97
+ },
98
+ });
99
+ }
@@ -0,0 +1,176 @@
1
+ // Duration each exec edge stays active during sequential animation (ms)
2
+ const STEP_DURATION = 620;
3
+
4
+ export function registerUtilNodes(registry) {
5
+ registry.register("util/Trigger", {
6
+ title: "Trigger",
7
+ color: "#f59e0b", // event (amber)
8
+ size: { w: 140 },
9
+ outputs: [{ name: "triggered", portType: "exec" }],
10
+ html: {
11
+ init(node, el, { body }) {
12
+ el.classList.add("node-overlay");
13
+
14
+ body.style.display = "flex";
15
+ body.style.alignItems = "center";
16
+ body.style.justifyContent = "center";
17
+
18
+ const button = document.createElement("button");
19
+ button.className = "premium-button";
20
+ button.textContent = "Execute";
21
+ button.style.width = "100%";
22
+ button.style.textTransform = "uppercase";
23
+ button.style.letterSpacing = "1px";
24
+
25
+ button.addEventListener("click", (e) => {
26
+ e.stopPropagation();
27
+ e.preventDefault();
28
+
29
+ if (node.state._firing) return;
30
+
31
+ const editor = window.editor;
32
+ if (!editor?.controller || !editor?.runner) return;
33
+
34
+ const controller = editor.controller;
35
+ const runner = editor.runner;
36
+
37
+ node.state.triggered = true;
38
+ node.state._firing = true;
39
+
40
+ // Active state styling
41
+ button.style.borderColor = "#4f62c0";
42
+ button.style.color = "#7080d8";
43
+ button.style.background = "rgba(79,98,192,0.12)";
44
+
45
+ const { connectedEdges, execEdgeOrder } = runner.runOnce(node.id, 0);
46
+
47
+ controller.activeEdgeTimes = new Map();
48
+
49
+ if (execEdgeOrder.length > 0) {
50
+ // Sequential: animate one exec edge at a time
51
+ const startTime = performance.now();
52
+ const totalDuration = execEdgeOrder.length * STEP_DURATION + 80;
53
+
54
+ const animate = () => {
55
+ const now = performance.now();
56
+ const elapsed = now - startTime;
57
+ const step = Math.floor(elapsed / STEP_DURATION);
58
+
59
+ const activeNow = new Set();
60
+ const activeNodeNow = new Set();
61
+ if (step < execEdgeOrder.length) {
62
+ const edgeId = execEdgeOrder[step];
63
+ activeNow.add(edgeId);
64
+ if (!controller.activeEdgeTimes.has(edgeId)) {
65
+ controller.activeEdgeTimes.set(edgeId, startTime + step * STEP_DURATION);
66
+ }
67
+ // Highlight the destination node of this exec edge
68
+ const edge = runner.graph.edges.get(edgeId);
69
+ if (edge?.toNode) activeNodeNow.add(edge.toNode);
70
+ }
71
+
72
+ controller.activeEdges = activeNow;
73
+ controller.activeNodes = activeNodeNow;
74
+ controller.render();
75
+
76
+ if (elapsed < totalDuration) {
77
+ requestAnimationFrame(animate);
78
+ } else {
79
+ _resetTrigger(controller, node, button);
80
+ }
81
+ };
82
+ requestAnimationFrame(animate);
83
+ } else if (connectedEdges.size > 0) {
84
+ // Fallback: all data edges at once
85
+ const startTime = performance.now();
86
+ const totalDuration = STEP_DURATION;
87
+ const now = performance.now();
88
+ for (const id of connectedEdges) {
89
+ controller.activeEdgeTimes.set(id, now);
90
+ }
91
+
92
+ const animate = () => {
93
+ controller.activeEdges = connectedEdges;
94
+ controller.render();
95
+ if (performance.now() - startTime < totalDuration) {
96
+ requestAnimationFrame(animate);
97
+ } else {
98
+ _resetTrigger(controller, node, button);
99
+ }
100
+ };
101
+ requestAnimationFrame(animate);
102
+ } else {
103
+ _resetTrigger(controller, node, button);
104
+ }
105
+ });
106
+
107
+ body.appendChild(button);
108
+ el._btn = button;
109
+ },
110
+ },
111
+ onExecute(node, { setOutput }) {
112
+ if (node.state.triggered) {
113
+ setOutput("triggered", true);
114
+ }
115
+ },
116
+ });
117
+
118
+ registry.register("util/Watch", {
119
+ title: "Watch",
120
+ color: "#10b981", // info (emerald)
121
+ size: { w: 180 },
122
+ inputs: [{ name: "value", portType: "data", datatype: "any" }],
123
+ onExecute(node, { getInput }) {
124
+ const value = getInput("value");
125
+ console.log("[Watch] onExecute called, value:", value);
126
+ },
127
+ });
128
+
129
+ registry.register("util/Print", {
130
+ title: "Print",
131
+ color: "#10b981", // info (emerald)
132
+ size: { w: 140 },
133
+ inputs: [
134
+ { name: "", portType: "exec" },
135
+ { name: "value", portType: "data", datatype: "any" },
136
+ ],
137
+ outputs: [{ name: "", portType: "exec" }],
138
+ onExecute(node, { getInput, setOutput }) {
139
+ const value = getInput("value");
140
+ console.log("[Print]", value);
141
+ setOutput("", true);
142
+ },
143
+ });
144
+
145
+ registry.register("util/Timer", {
146
+ title: "Timer",
147
+ color: "#f59e0b", // event (amber)
148
+ size: { w: 140 },
149
+ inputs: [
150
+ { name: "", portType: "exec" },
151
+ { name: "delay (ms)", portType: "data", datatype: "number" },
152
+ ],
153
+ outputs: [{ name: "", portType: "exec" }],
154
+ async onExecute(node, { getInput, setOutput }) {
155
+ const delay = getInput("delay (ms)") || 0;
156
+ await new Promise((resolve) => {
157
+ setTimeout(() => {
158
+ resolve();
159
+ }, delay);
160
+ });
161
+ setOutput("", true);
162
+ },
163
+ });
164
+ }
165
+
166
+ function _resetTrigger(controller, node, button) {
167
+ controller.activeEdges = new Set();
168
+ controller.activeEdgeTimes = new Map();
169
+ controller.activeNodes = new Set();
170
+ controller.render();
171
+ node.state.triggered = false;
172
+ node.state._firing = false;
173
+ button.style.borderColor = "#383858";
174
+ button.style.color = "#8888aa";
175
+ button.style.background = "transparent";
176
+ }
@@ -0,0 +1,100 @@
1
+ /**
2
+ * Value Nodes Package
3
+ * Provides constant value nodes (Number, String, Boolean)
4
+ */
5
+
6
+ export function registerValueNodes(registry) {
7
+ // Number Node
8
+ registry.register("value/Number", {
9
+ title: "Number",
10
+ color: "#3b82f6", // data (blue)
11
+ size: { w: 140 },
12
+ outputs: [{ name: "value", portType: "data", datatype: "number" }],
13
+ onCreate(node) {
14
+ node.state.value = 0;
15
+ },
16
+ onExecute(node, { setOutput }) {
17
+ console.log("[Number] Outputting value:", node.state.value ?? 0);
18
+ setOutput("value", node.state.value ?? 0);
19
+ },
20
+ html: {
21
+ init(node, el, { body }) {
22
+ el.classList.add("node-overlay");
23
+
24
+ body.style.display = "flex";
25
+ body.style.alignItems = "center";
26
+ body.style.justifyContent = "center";
27
+
28
+ const input = document.createElement("input");
29
+ input.className = "premium-input";
30
+ input.type = "number";
31
+ input.style.textAlign = "center";
32
+ input.value = node.state.value ?? 0;
33
+
34
+ input.addEventListener("change", (e) => {
35
+ node.state.value = parseFloat(e.target.value) || 0;
36
+ });
37
+
38
+ input.addEventListener("mousedown", (e) => e.stopPropagation());
39
+ input.addEventListener("keydown", (e) => e.stopPropagation());
40
+
41
+ body.appendChild(input);
42
+ },
43
+ update(_node, _el, _opts) {
44
+ // Selection is handled by the canvas renderer
45
+ },
46
+ },
47
+ onDraw(node, { ctx }) {
48
+ // const { x, y } = node.computed;
49
+ // ctx.fillStyle = "#8f8";
50
+ // ctx.font = "14px sans-serif";
51
+ // ctx.textAlign = "center";
52
+ // ctx.fillText(String(node.state.value ?? 0), x + 70, y + 42);
53
+ },
54
+ });
55
+
56
+ // String Node
57
+ registry.register("value/String", {
58
+ title: "String",
59
+ color: "#3b82f6", // data (blue)
60
+ size: { w: 160 },
61
+ outputs: [{ name: "value", datatype: "string" }],
62
+ onCreate(node) {
63
+ node.state.value = "Hello";
64
+ },
65
+ onExecute(node, { setOutput }) {
66
+ setOutput("value", node.state.value ?? "");
67
+ },
68
+ onDraw(node, { ctx }) {
69
+ const { x, y } = node.computed;
70
+ ctx.fillStyle = "#8f8";
71
+ ctx.font = "12px sans-serif";
72
+ ctx.textAlign = "center";
73
+ const text = String(node.state.value ?? "");
74
+ const displayText = text.length > 15 ? text.substring(0, 15) + "..." : text;
75
+ ctx.fillText(displayText, x + 80, y + 42);
76
+ },
77
+ });
78
+
79
+ // Boolean Node
80
+ registry.register("value/Boolean", {
81
+ title: "Boolean",
82
+ color: "#3b82f6", // data (blue)
83
+ size: { w: 140 },
84
+ outputs: [{ name: "value", portType: "data", datatype: "boolean" }],
85
+ onCreate(node) {
86
+ node.state.value = true;
87
+ },
88
+ onExecute(node, { setOutput }) {
89
+ console.log("[Boolean] Outputting value:", node.state.value ?? false);
90
+ setOutput("value", node.state.value ?? false);
91
+ },
92
+ onDraw(node, { ctx }) {
93
+ const { x, y } = node.computed;
94
+ ctx.fillStyle = node.state.value ? "#8f8" : "#f88";
95
+ ctx.font = "600 14px var(--font-main)";
96
+ ctx.textAlign = "center";
97
+ ctx.fillText(String(node.state.value), x + 70, y + 42);
98
+ },
99
+ });
100
+ }