html-overlay-node 0.1.10 → 0.1.11
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 +9 -9
- package/dist/html-overlay-node.es.js +351 -78
- 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/dist/img/favicon.svg +1 -0
- package/index.css +45 -0
- package/package.json +1 -1
- package/readme.md +143 -134
- package/src/core/Graph.js +13 -8
- package/src/core/Runner.js +359 -201
- package/src/index.js +12 -26
- package/src/interact/Controller.js +25 -3
- package/src/nodes/math.js +2 -0
- package/src/nodes/util.js +241 -176
- package/src/nodes/value.js +3 -2
- package/src/render/CanvasRenderer.js +884 -784
- package/src/render/HtmlOverlay.js +64 -2
- package/src/ui/HelpOverlay.js +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#1f1f1f"><path d="M600-160v-80H440v-200h-80v80H80v-240h280v80h80v-200h160v-80h280v240H600v-80h-80v320h80v-80h280v240H600Zm80-80h120v-80H680v80ZM160-440h120v-80H160v80Zm520-200h120v-80H680v80Zm0 400v-80 80ZM280-440v-80 80Zm400-200v-80 80Z"/></svg>
|
package/index.css
CHANGED
|
@@ -175,6 +175,51 @@ body {
|
|
|
175
175
|
background: rgba(85, 104, 208, 0.06);
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
+
/* ── Mode Toggle ────────────────────────────────────────────── */
|
|
179
|
+
.mode-toggle {
|
|
180
|
+
display: flex;
|
|
181
|
+
background: rgba(0, 0, 0, 0.2);
|
|
182
|
+
padding: 2px;
|
|
183
|
+
border-radius: var(--radius-sm);
|
|
184
|
+
gap: 2px;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
.mode-toggle button {
|
|
188
|
+
padding: 3px 12px !important;
|
|
189
|
+
font-size: 10px !important;
|
|
190
|
+
text-transform: uppercase;
|
|
191
|
+
letter-spacing: 0.5px;
|
|
192
|
+
border: 1px solid transparent !important;
|
|
193
|
+
opacity: 0.4;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
.mode-toggle button:hover {
|
|
197
|
+
opacity: 1;
|
|
198
|
+
background: rgba(255, 255, 255, 0.05) !important;
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
.mode-toggle button.active {
|
|
202
|
+
opacity: 1;
|
|
203
|
+
font-weight: 700;
|
|
204
|
+
box-shadow: 0 0 10px rgba(0, 0, 0, 0.3);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
/* Run Mode Active State (Emerald) */
|
|
208
|
+
.mode-toggle button[data-mode="run"].active {
|
|
209
|
+
color: #10b981 !important;
|
|
210
|
+
background: rgba(16, 185, 129, 0.1) !important;
|
|
211
|
+
border-color: rgba(16, 185, 129, 0.3) !important;
|
|
212
|
+
box-shadow: 0 0 12px rgba(16, 185, 129, 0.15);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/* Step Mode Active State (Violet) */
|
|
216
|
+
.mode-toggle button[data-mode="step"].active {
|
|
217
|
+
color: #a78bfa !important;
|
|
218
|
+
background: rgba(139, 92, 246, 0.1) !important;
|
|
219
|
+
border-color: rgba(139, 92, 246, 0.3) !important;
|
|
220
|
+
box-shadow: 0 0 12px rgba(139, 92, 246, 0.15);
|
|
221
|
+
}
|
|
222
|
+
|
|
178
223
|
/* ── Help Overlay ─────────────────────────────────────────────── */
|
|
179
224
|
#helpOverlay {
|
|
180
225
|
position: absolute;
|
package/package.json
CHANGED
package/readme.md
CHANGED
|
@@ -1,134 +1,143 @@
|
|
|
1
|
-
# HTML-overlay-Node
|
|
2
|
-
|
|
3
|
-
[](https://www.npmjs.com/package/html-overlay-node)
|
|
4
|
-
[](https://opensource.org/licenses/MIT)
|
|
5
|
-
|
|
6
|
-
**HTML-overlay-Node**는 Canvas의
|
|
7
|
-
|
|
8
|
-
---
|
|
9
|
-
|
|
10
|
-
##
|
|
11
|
-
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
import
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
});
|
|
34
|
-
|
|
35
|
-
const { graph, registry, start } = editor;
|
|
36
|
-
|
|
37
|
-
// 노드 등록
|
|
38
|
-
registry.register("math/Add", {
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
```
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
---
|
|
129
|
-
|
|
130
|
-
##
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
1
|
+
# HTML-overlay-Node
|
|
2
|
+
|
|
3
|
+
[](https://www.npmjs.com/package/html-overlay-node)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
|
|
6
|
+
**HTML-overlay-Node**는 Canvas의 고성능 렌더링과 HTML의 유연한 UI 인터페이스를 결합한 전문가용 노드 에디터 엔진입니다. 단순한 시각화를 넘어, 복잡한 로직 설계와 실시간 실행 환경에 최적화된 날카롭고 세련된 엔지니어링 경험을 제공합니다.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## ✨ 핵심 기능 (Key Features)
|
|
11
|
+
|
|
12
|
+
- **하이브리드 렌더링**: 노드 본체와 복잡한 위젯은 HTML로, 연결선(Edge)과 고주파 애니메이션은 Canvas로 처리하여 성능과 커스터마이징의 균형을 잡았습니다.
|
|
13
|
+
- **실시간 실행 흐름 시각화**: 노드 실행 상태와 데이터 흐름을 'Marching Ants' 테두리 및 흐르는 점 애니메이션으로 가시화하여 로직의 흐름을 직관적으로 파악할 수 있습니다.
|
|
14
|
+
- **전문가급 디자인 시스템**: 2px의 정밀한 라운딩, 깊이감 있는 다크 테마, 카테고리별 컬러 코딩으로 대규모 그래프에서도 높은 가독성을 유지합니다.
|
|
15
|
+
- **강력한 듀얼 포트 시스템**: 실행 제어(Execution Flow)와 데이터 전용(Data Flow) 포트를 분리 지원하여 함수형/이벤트 기반 로직 설계를 완벽히 지원합니다.
|
|
16
|
+
- **생산성 도구**: Undo/Redo, 그룹화(Group), 자동 정렬(Aligning), 그리드 스냅, 다중 선택 등 실제 작업 효율을 극대화하는 파워 유저 기능을 내장하고 있습니다.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 🚀 빠른 시작 (Quick Start)
|
|
21
|
+
|
|
22
|
+
Vite나 Webpack 환경에서 다음과 같이 손쉽게 에디터를 구축할 수 있습니다.
|
|
23
|
+
|
|
24
|
+
```javascript
|
|
25
|
+
import { createGraphEditor } from "html-overlay-node";
|
|
26
|
+
import "html-overlay-node/index.css";
|
|
27
|
+
|
|
28
|
+
const editor = createGraphEditor("#editor-container", {
|
|
29
|
+
theme: {
|
|
30
|
+
accent: "#6366f1", // 주요 강조 색상 (Indigo)
|
|
31
|
+
flowSpeed: 150 // 애니메이션 흐름 속도 (px/sec)
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
const { graph, registry, start } = editor;
|
|
36
|
+
|
|
37
|
+
// 노드 등록 및 시작
|
|
38
|
+
registry.register("math/Add", { ... });
|
|
39
|
+
start();
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 🧩 플러그인 시스템 (Plugins & Node Registration)
|
|
45
|
+
|
|
46
|
+
`registry`를 통해 새로운 기능을 동적으로 확장할 수 있습니다.
|
|
47
|
+
|
|
48
|
+
```javascript
|
|
49
|
+
registry.register("math/Multiply", {
|
|
50
|
+
title: "Multiply",
|
|
51
|
+
color: "#f43f5e", // 카테고리 컬러
|
|
52
|
+
inputs: [
|
|
53
|
+
{ name: "a", portType: "data" },
|
|
54
|
+
{ name: "b", portType: "data" }
|
|
55
|
+
],
|
|
56
|
+
outputs: [{ name: "result", portType: "data" }],
|
|
57
|
+
// 실행 로직 정의
|
|
58
|
+
onExecute(node, { getInput, setOutput }) {
|
|
59
|
+
const a = getInput("a") ?? 1;
|
|
60
|
+
const b = getInput("b") ?? 1;
|
|
61
|
+
setOutput("result", a * b);
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
---
|
|
67
|
+
|
|
68
|
+
## 🖼️ UI & 위젯 (HTML Overlays & Widgets)
|
|
69
|
+
|
|
70
|
+
HTML의 강점을 살려 노드 내부에 복잡한 UI를 직접 구현할 수 있습니다. `renderHtml` 함수를 통해 DOM 요소를 직접 제어하거나 내장 위젯을 사용할 수 있습니다.
|
|
71
|
+
|
|
72
|
+
```javascript
|
|
73
|
+
registry.register("ui/Slider", {
|
|
74
|
+
html: true, // HTML 오버레이 활성화
|
|
75
|
+
renderHtml(node, container) {
|
|
76
|
+
container.innerHTML = `<input type="range" class="hon-slider" />`;
|
|
77
|
+
const input = container.querySelector("input");
|
|
78
|
+
input.oninput = (e) => {
|
|
79
|
+
node.state.value = e.target.value;
|
|
80
|
+
// 상태 변경 시 그래프 갱신 알림
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
});
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## 🎨 CSS 디자인 토큰 (Design Tokens)
|
|
89
|
+
|
|
90
|
+
`index.css`에 정의된 CSS 변수를 덮어쓰는 것만으로도 전체 에디터의 룩앤필을 브랜드에 맞게 조정할 수 있습니다.
|
|
91
|
+
|
|
92
|
+
| 변수명 | 설명 | 기본값 |
|
|
93
|
+
| :--- | :--- | :--- |
|
|
94
|
+
| `--hon-bg` | 캔버스 배경색 | `#0d0d0f` |
|
|
95
|
+
| `--hon-node-bg` | 노드 내부 배경색 | `#16161a` |
|
|
96
|
+
| `--hon-node-border` | 노드 테두리 색상 | `rgba(255,255,255,0.08)` |
|
|
97
|
+
| `--hon-accent` | 강조 포인트 컬러 | `#4f46e5` |
|
|
98
|
+
| `--hon-text` | 기본 텍스트 색상 | `#e2e8f0` |
|
|
99
|
+
| `--hon-grid` | 그리드 점 색상 | `rgba(255,255,255,0.03)` |
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## ⌨️ 생산성 단축키
|
|
104
|
+
|
|
105
|
+
| 기능 | 단축키 |
|
|
106
|
+
| :--- | :--- |
|
|
107
|
+
| **노드 삭제** | `Delete` |
|
|
108
|
+
| **수평/수직 정렬** | `A` / `Shift + A` |
|
|
109
|
+
| **그룹 생성** | `Ctrl + G` |
|
|
110
|
+
| **그리드 스냅 토글**| `G` |
|
|
111
|
+
| **실행 취소/재실행** | `Ctrl + Z` / `Ctrl + Y` |
|
|
112
|
+
| **미니맵 토글** | `M` |
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 💾 데이터 직렬화 (Serialization)
|
|
117
|
+
|
|
118
|
+
그래프의 모든 상태는 JSON 형식으로 저장하고 불러올 수 있습니다.
|
|
119
|
+
|
|
120
|
+
```javascript
|
|
121
|
+
// 현재 캔버스 상태 저장
|
|
122
|
+
const snapshot = graph.toJSON();
|
|
123
|
+
|
|
124
|
+
// 데이터 복원 (트랜지션 애니메이션 포함)
|
|
125
|
+
graph.fromJSON(snapshot);
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## 🛠️ 개발 가이드 (Development)
|
|
131
|
+
|
|
132
|
+
```bash
|
|
133
|
+
npm install # 의존성 설치
|
|
134
|
+
npm run dev # 개발 서버 시작
|
|
135
|
+
npm test # 테스트 실행
|
|
136
|
+
npm run build # 프로덕션 빌드
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
---
|
|
140
|
+
|
|
141
|
+
## 📄 라이선스
|
|
142
|
+
|
|
143
|
+
[MIT](LICENSE) © cheonghakim
|
package/src/core/Graph.js
CHANGED
|
@@ -278,14 +278,19 @@ export class Graph {
|
|
|
278
278
|
const outCount = def.outputs?.length || 0;
|
|
279
279
|
const maxPorts = Math.max(inCount, outCount);
|
|
280
280
|
const headerHeight = 26;
|
|
281
|
-
const
|
|
282
|
-
|
|
283
|
-
|
|
281
|
+
const portSpacing = 20;
|
|
282
|
+
|
|
283
|
+
if (def.html) {
|
|
284
|
+
// For HTML overlay nodes: reserve space for content BELOW all port rows.
|
|
285
|
+
// Port idx N bottom: headerHeight + 8 + N*portSpacing + portSpacing/2 + 6
|
|
286
|
+
// = 50 + N*portSpacing (for N=0: 50, N=1: 70, ...)
|
|
287
|
+
// Add ~50px for HTML content + bottom padding.
|
|
288
|
+
const lastPortBottom = maxPorts > 0 ? 50 + (maxPorts - 1) * portSpacing : 26;
|
|
289
|
+
return Math.max(lastPortBottom + 50, 90);
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const padding = 8;
|
|
284
293
|
let h = headerHeight + padding + (maxPorts * portSpacing) + padding;
|
|
285
|
-
|
|
286
|
-
// Add extra space if it has HTML overlay to prevent overlap with ports
|
|
287
|
-
if (def.html) h += 16;
|
|
288
|
-
|
|
289
|
-
return Math.max(h, 40); // Minimum height 40
|
|
294
|
+
return Math.max(h, 40);
|
|
290
295
|
}
|
|
291
296
|
}
|