html-overlay-node 0.1.9 → 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.
- package/dist/example.json +3 -3
- package/dist/html-overlay-node.es.js +704 -298
- package/dist/html-overlay-node.es.js.map +1 -1
- package/dist/html-overlay-node.umd.js +1 -1
- package/dist/html-overlay-node.umd.js.map +1 -1
- package/index.css +391 -232
- package/package.json +1 -1
- package/readme.md +58 -364
- package/src/core/Graph.js +29 -5
- package/src/core/Runner.js +201 -225
- package/src/index.js +17 -0
- package/src/interact/Controller.js +10 -3
- package/src/nodes/core.js +55 -77
- package/src/nodes/logic.js +51 -48
- package/src/nodes/math.js +21 -8
- package/src/nodes/util.js +176 -134
- package/src/nodes/value.js +86 -102
- package/src/render/CanvasRenderer.js +784 -704
- package/src/render/HtmlOverlay.js +1 -1
- package/src/render/hitTest.js +5 -2
- package/src/ui/HelpOverlay.js +158 -0
- package/src/ui/PropertyPanel.css +58 -27
- package/src/ui/PropertyPanel.js +441 -268
package/src/render/hitTest.js
CHANGED
|
@@ -28,8 +28,11 @@ export function portRect(node, port, idx, dir) {
|
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
// Fixed spacing
|
|
31
|
-
|
|
32
|
-
const
|
|
31
|
+
// Sync with CanvasRenderer (headerH = 26)
|
|
32
|
+
const headerHeight = 26;
|
|
33
|
+
const padding = 8;
|
|
34
|
+
const portSpacing = 20;
|
|
35
|
+
const y = ny + headerHeight + padding + (idx * portSpacing) + portSpacing / 2;
|
|
33
36
|
|
|
34
37
|
// Ports centered on node edges (half inside, half outside)
|
|
35
38
|
const portWidth = 12;
|
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* HelpOverlay - Modular help keyboard shortcuts overlay
|
|
3
|
+
*/
|
|
4
|
+
export class HelpOverlay {
|
|
5
|
+
constructor(container, options = {}) {
|
|
6
|
+
this.container = container;
|
|
7
|
+
this.options = {
|
|
8
|
+
shortcuts: options.shortcuts || this._getDefaultShortcuts(),
|
|
9
|
+
onToggle: options.onToggle || null,
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
this.isVisible = false;
|
|
13
|
+
this.overlay = null;
|
|
14
|
+
this.toggleBtn = null;
|
|
15
|
+
|
|
16
|
+
this._createElements();
|
|
17
|
+
this._bindEvents();
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
_getDefaultShortcuts() {
|
|
21
|
+
return [
|
|
22
|
+
{
|
|
23
|
+
group: "Selection",
|
|
24
|
+
items: [
|
|
25
|
+
{ label: "Select node", key: "Click" },
|
|
26
|
+
{ label: "Multi-select", key: "Shift+Click" },
|
|
27
|
+
{ label: "Box select", key: "Ctrl+Drag" },
|
|
28
|
+
],
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
group: "Edit",
|
|
32
|
+
items: [
|
|
33
|
+
{ label: "Delete", key: "Del" },
|
|
34
|
+
{ label: "Undo", key: "Ctrl+Z" },
|
|
35
|
+
{ label: "Redo", key: "Ctrl+Y" },
|
|
36
|
+
],
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
group: "Group & Align",
|
|
40
|
+
items: [
|
|
41
|
+
{ label: "Create group", key: "Ctrl+G" },
|
|
42
|
+
{ label: "Align horizontal", key: "A" },
|
|
43
|
+
{ label: "Align vertical", key: "Shift+A" },
|
|
44
|
+
],
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
group: "View",
|
|
48
|
+
items: [
|
|
49
|
+
{ label: "Toggle snap", key: "G" },
|
|
50
|
+
{ label: "Pan", key: "Mid+Drag" },
|
|
51
|
+
{ label: "Zoom", key: "Scroll" },
|
|
52
|
+
{ label: "Context menu", key: "RClick" },
|
|
53
|
+
],
|
|
54
|
+
},
|
|
55
|
+
];
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
_createElements() {
|
|
59
|
+
// Create Toggle Button
|
|
60
|
+
this.toggleBtn = document.createElement("div");
|
|
61
|
+
this.toggleBtn.id = "helpToggle";
|
|
62
|
+
this.toggleBtn.title = "Keyboard shortcuts (?)";
|
|
63
|
+
this.toggleBtn.textContent = "?";
|
|
64
|
+
this.container.appendChild(this.toggleBtn);
|
|
65
|
+
|
|
66
|
+
// Create Overlay
|
|
67
|
+
this.overlay = document.createElement("div");
|
|
68
|
+
this.overlay.id = "helpOverlay";
|
|
69
|
+
|
|
70
|
+
const sectionsHtml = this.options.shortcuts
|
|
71
|
+
.map(
|
|
72
|
+
(group) => `
|
|
73
|
+
<h4>${group.group}</h4>
|
|
74
|
+
${group.items
|
|
75
|
+
.map(
|
|
76
|
+
(item) => `
|
|
77
|
+
<div class="shortcut-item">
|
|
78
|
+
<span>${item.label}</span>
|
|
79
|
+
<span class="shortcut-key">${item.key}</span>
|
|
80
|
+
</div>
|
|
81
|
+
`
|
|
82
|
+
)
|
|
83
|
+
.join("")}
|
|
84
|
+
`
|
|
85
|
+
)
|
|
86
|
+
.join("");
|
|
87
|
+
|
|
88
|
+
this.overlay.innerHTML = `
|
|
89
|
+
<h3>
|
|
90
|
+
<span>Keyboard Shortcuts</span>
|
|
91
|
+
<button class="close-btn" id="helpClose" title="Close">×</button>
|
|
92
|
+
</h3>
|
|
93
|
+
${sectionsHtml}
|
|
94
|
+
`;
|
|
95
|
+
|
|
96
|
+
this.container.appendChild(this.overlay);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
_bindEvents() {
|
|
100
|
+
this.toggleBtn.addEventListener("click", () => this.toggle());
|
|
101
|
+
|
|
102
|
+
const closeBtn = this.overlay.querySelector("#helpClose");
|
|
103
|
+
if (closeBtn) {
|
|
104
|
+
closeBtn.addEventListener("click", (e) => {
|
|
105
|
+
e.stopPropagation();
|
|
106
|
+
this.close();
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// Close when clicking outside
|
|
111
|
+
document.addEventListener("mousedown", (e) => {
|
|
112
|
+
if (this.isVisible) {
|
|
113
|
+
if (!this.overlay.contains(e.target) && !this.toggleBtn.contains(e.target)) {
|
|
114
|
+
this.close();
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
// Keyboard shortcuts
|
|
120
|
+
window.addEventListener("keydown", (e) => {
|
|
121
|
+
if (e.key === "?" || (e.shiftKey && e.key === "/")) {
|
|
122
|
+
// Only toggle if not typing in an input
|
|
123
|
+
if (!["INPUT", "TEXTAREA", "SELECT"].includes(document.activeElement.tagName)) {
|
|
124
|
+
e.preventDefault();
|
|
125
|
+
this.toggle();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (e.key === "Escape" && this.isVisible) {
|
|
130
|
+
this.close();
|
|
131
|
+
}
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
toggle() {
|
|
136
|
+
if (this.isVisible) this.close();
|
|
137
|
+
else this.open();
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
open() {
|
|
141
|
+
this.isVisible = true;
|
|
142
|
+
this.overlay.classList.add("visible");
|
|
143
|
+
this.toggleBtn.classList.add("active");
|
|
144
|
+
if (this.options.onToggle) this.options.onToggle(true);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
close() {
|
|
148
|
+
this.isVisible = false;
|
|
149
|
+
this.overlay.classList.remove("visible");
|
|
150
|
+
this.toggleBtn.classList.remove("active");
|
|
151
|
+
if (this.options.onToggle) this.options.onToggle(false);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
destroy() {
|
|
155
|
+
this.toggleBtn?.remove();
|
|
156
|
+
this.overlay?.remove();
|
|
157
|
+
}
|
|
158
|
+
}
|
package/src/ui/PropertyPanel.css
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
background: radial-gradient(circle at top right, #3c3c3c 0, #1f1f1f 55%);
|
|
11
11
|
background-color: #262626;
|
|
12
12
|
box-shadow: -4px 0 24px rgba(0, 0, 0, 0.5);
|
|
13
|
-
border-left: 1px solid
|
|
13
|
+
border-left: 1px solid var(--color-border);
|
|
14
14
|
|
|
15
15
|
opacity: 0;
|
|
16
16
|
transform: translateX(20px);
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
align-items: center;
|
|
38
38
|
justify-content: space-between;
|
|
39
39
|
padding-bottom: 16px;
|
|
40
|
-
border-bottom: 1px solid
|
|
40
|
+
border-bottom: 1px solid var(--color-border);
|
|
41
41
|
margin-bottom: 20px;
|
|
42
42
|
flex-shrink: 0;
|
|
43
43
|
}
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
.title-text {
|
|
52
52
|
font-size: 18px;
|
|
53
53
|
font-weight: 600;
|
|
54
|
-
color:
|
|
54
|
+
color: var(--color-text-main);
|
|
55
55
|
letter-spacing: 0.01em;
|
|
56
56
|
}
|
|
57
57
|
|
|
@@ -71,8 +71,8 @@
|
|
|
71
71
|
}
|
|
72
72
|
|
|
73
73
|
.panel-close:hover {
|
|
74
|
-
background:
|
|
75
|
-
color:
|
|
74
|
+
background: var(--color-bg-surface);
|
|
75
|
+
color: var(--color-text-main);
|
|
76
76
|
transform: translateY(-1px);
|
|
77
77
|
}
|
|
78
78
|
|
|
@@ -114,7 +114,7 @@
|
|
|
114
114
|
.section-title {
|
|
115
115
|
font-size: 13px;
|
|
116
116
|
font-weight: 600;
|
|
117
|
-
color:
|
|
117
|
+
color: var(--color-text-dim);
|
|
118
118
|
margin-bottom: 12px;
|
|
119
119
|
text-transform: uppercase;
|
|
120
120
|
letter-spacing: 0.08em;
|
|
@@ -135,39 +135,70 @@
|
|
|
135
135
|
|
|
136
136
|
.field label {
|
|
137
137
|
font-size: 12px;
|
|
138
|
-
color:
|
|
138
|
+
color: var(--color-text-muted);
|
|
139
139
|
font-weight: 500;
|
|
140
140
|
}
|
|
141
141
|
|
|
142
142
|
.field input {
|
|
143
143
|
width: 100%;
|
|
144
144
|
padding: 8px 10px;
|
|
145
|
-
border-radius:
|
|
146
|
-
border: 1px solid
|
|
147
|
-
background: rgba(0, 0, 0, 0.
|
|
148
|
-
color:
|
|
145
|
+
border-radius: var(--radius-md);
|
|
146
|
+
border: 1px solid var(--color-border);
|
|
147
|
+
background: rgba(0, 0, 0, 0.3);
|
|
148
|
+
color: var(--color-text-main);
|
|
149
149
|
font-size: 13px;
|
|
150
150
|
outline: none;
|
|
151
151
|
transition: all 0.2s ease;
|
|
152
|
-
font-family:
|
|
153
|
-
system-ui,
|
|
154
|
-
-apple-system,
|
|
155
|
-
sans-serif;
|
|
152
|
+
font-family: inherit;
|
|
156
153
|
box-sizing: border-box;
|
|
157
154
|
line-height: 1.4;
|
|
158
155
|
}
|
|
159
156
|
|
|
157
|
+
.field input:hover {
|
|
158
|
+
border-color: var(--color-border-focus);
|
|
159
|
+
background: rgba(0, 0, 0, 0.35);
|
|
160
|
+
}
|
|
161
|
+
|
|
160
162
|
.field input:focus {
|
|
161
|
-
border-color:
|
|
163
|
+
border-color: var(--color-primary);
|
|
162
164
|
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
|
|
163
|
-
background: rgba(0, 0, 0, 0.
|
|
165
|
+
background: rgba(0, 0, 0, 0.45);
|
|
164
166
|
}
|
|
165
167
|
|
|
166
168
|
.field input[readonly] {
|
|
167
|
-
background: rgba(0, 0, 0, 0.
|
|
168
|
-
color: #
|
|
169
|
+
background: rgba(0, 0, 0, 0.15);
|
|
170
|
+
color: #707080;
|
|
171
|
+
border-color: rgba(255, 255, 255, 0.04);
|
|
169
172
|
cursor: not-allowed;
|
|
170
|
-
opacity: 0.
|
|
173
|
+
opacity: 0.8;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
.field select {
|
|
177
|
+
width: 100%;
|
|
178
|
+
padding: 8px 10px;
|
|
179
|
+
border-radius: var(--radius-md);
|
|
180
|
+
border: 1px solid var(--color-border);
|
|
181
|
+
background: rgba(0, 0, 0, 0.3);
|
|
182
|
+
color: var(--color-text-main);
|
|
183
|
+
font-size: 13px;
|
|
184
|
+
outline: none;
|
|
185
|
+
transition: all 0.2s ease;
|
|
186
|
+
font-family: inherit;
|
|
187
|
+
cursor: pointer;
|
|
188
|
+
appearance: none;
|
|
189
|
+
background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%238888a8' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
|
|
190
|
+
background-repeat: no-repeat;
|
|
191
|
+
background-position: right 10px center;
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
.field select:hover {
|
|
195
|
+
border-color: var(--color-border-focus);
|
|
196
|
+
background: rgba(0, 0, 0, 0.35);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
.field select:focus {
|
|
200
|
+
border-color: var(--color-primary);
|
|
201
|
+
box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
|
|
171
202
|
}
|
|
172
203
|
|
|
173
204
|
.field-row {
|
|
@@ -256,22 +287,22 @@
|
|
|
256
287
|
}
|
|
257
288
|
|
|
258
289
|
.btn-primary {
|
|
259
|
-
background:
|
|
260
|
-
color:
|
|
290
|
+
background: var(--color-primary);
|
|
291
|
+
color: var(--color-text-main);
|
|
261
292
|
}
|
|
262
293
|
|
|
263
294
|
.btn-primary:hover {
|
|
264
|
-
background:
|
|
295
|
+
background: var(--color-primary-hover);
|
|
265
296
|
transform: translateY(-1px);
|
|
266
297
|
box-shadow: 0 2px 8px rgba(99, 102, 241, 0.3);
|
|
267
298
|
}
|
|
268
299
|
|
|
269
300
|
.btn-secondary {
|
|
270
|
-
background:
|
|
271
|
-
color:
|
|
301
|
+
background: var(--color-bg-surface);
|
|
302
|
+
color: var(--color-text-dim);
|
|
272
303
|
}
|
|
273
304
|
|
|
274
305
|
.btn-secondary:hover {
|
|
275
|
-
background:
|
|
276
|
-
color:
|
|
306
|
+
background: var(--color-bg-surface);
|
|
307
|
+
color: var(--color-text-main);
|
|
277
308
|
}
|