3v324v23 commited on
Commit
0b2295a
·
1 Parent(s): dbc94f3

Added neural network playground code from GitHub

Browse files
Files changed (9) hide show
  1. .github/workflows/pages.yml +23 -0
  2. .gitignore +40 -0
  3. .nojekyll +0 -0
  4. README.md +46 -10
  5. css/styles.css +1225 -0
  6. index.html +170 -19
  7. js/drag-drop.js +661 -0
  8. js/main.js +856 -0
  9. js/neural-network.js +460 -0
.github/workflows/pages.yml ADDED
@@ -0,0 +1,23 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ name: Deploy to GitHub Pages
2
+
3
+ on:
4
+ push:
5
+ branches: ["main"]
6
+ workflow_dispatch:
7
+
8
+ jobs:
9
+ deploy:
10
+ runs-on: ubuntu-latest
11
+ permissions:
12
+ contents: write
13
+ steps:
14
+ - name: Checkout
15
+ uses: actions/checkout@v4
16
+
17
+ - name: Deploy to GitHub Pages
18
+ uses: peaceiris/actions-gh-pages@v3
19
+ with:
20
+ github_token: ${{ secrets.GITHUB_TOKEN }}
21
+ publish_dir: ./
22
+ publish_branch: gh-pages
23
+ force_orphan: true
.gitignore ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # macOS files
2
+ .DS_Store
3
+ .AppleDouble
4
+ .LSOverride
5
+
6
+ # Thumbnails
7
+ ._*
8
+
9
+ # Node modules and dependency directories
10
+ node_modules/
11
+ jspm_packages/
12
+
13
+ # Environment files
14
+ .env
15
+ .env.local
16
+ .env.development.local
17
+ .env.test.local
18
+ .env.production.local
19
+
20
+ # IDE files
21
+ .idea/
22
+ .vscode/
23
+ *.sublime-project
24
+ *.sublime-workspace
25
+
26
+ # Log files
27
+ npm-debug.log*
28
+ yarn-debug.log*
29
+ yarn-error.log*
30
+ logs
31
+ *.log
32
+
33
+ # Distribution directories
34
+ dist/
35
+ build/
36
+
37
+ # Python cache files
38
+ __pycache__/
39
+ *.py[cod]
40
+ *$py.class
.nojekyll ADDED
File without changes
README.md CHANGED
@@ -1,10 +1,46 @@
1
- ---
2
- title: Neural Network Playground
3
- emoji: 🌖
4
- colorFrom: pink
5
- colorTo: blue
6
- sdk: static
7
- pinned: false
8
- ---
9
-
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Neural Network Playground
2
+
3
+ An interactive web-based application for visualizing and experimenting with neural network architectures.
4
+
5
+ ## Features
6
+
7
+ - **Drag-and-Drop Interface**: Easily create neural network architectures by dragging and dropping different layer types
8
+ - **Multiple Layer Types**: Support for Input, Hidden, Output, Convolutional, and Pooling layers
9
+ - **Dynamic Connections**: Create connections between layers to define your network topology
10
+ - **Visual Styling**: Beautiful gradient-based styling for different layer types with animations
11
+ - **Layer Properties**: View and edit detailed properties for each layer
12
+ - **Network Validation**: Automatic validation of network architectures
13
+ - **Training Simulation**: Visual simulation of the training process
14
+ - **Responsive Design**: Works on desktop and mobile devices
15
+
16
+ ## Getting Started
17
+
18
+ 1. Clone this repository
19
+ 2. Open `index.html` in your browser or use a local server:
20
+ ```
21
+ python -m http.server
22
+ ```
23
+ 3. Visit `http://localhost:8000` in your browser
24
+
25
+ ## How to Use
26
+
27
+ 1. Drag layer components from the left panel onto the canvas
28
+ 2. Connect layers by dragging from output ports (right side) to input ports (left side)
29
+ 3. Click on a layer to view its properties
30
+ 4. Edit layer properties by clicking the edit button
31
+ 5. Click "Run Network" to simulate training
32
+
33
+ ## Technologies Used
34
+
35
+ - HTML5
36
+ - CSS3 (with animations and gradients)
37
+ - JavaScript (vanilla)
38
+ - No external libraries required!
39
+
40
+ ## License
41
+
42
+ MIT
43
+
44
+ ## Contributing
45
+
46
+ Contributions, issues, and feature requests are welcome!
css/styles.css ADDED
@@ -0,0 +1,1225 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ * {
2
+ margin: 0;
3
+ padding: 0;
4
+ box-sizing: border-box;
5
+ }
6
+
7
+ :root {
8
+ --primary-color: #3498db;
9
+ --secondary-color: #2ecc71;
10
+ --accent-color: #9b59b6;
11
+ --warning-color: #e74c3c;
12
+ --info-color: #f39c12;
13
+ --background-color: #f8f9fa;
14
+ --card-background: #ffffff;
15
+ --text-color: #2c3e50;
16
+ --border-color: #e0e0e0;
17
+ --shadow-sm: 0 2px 4px rgba(0, 0, 0, 0.05);
18
+ --shadow-md: 0 4px 8px rgba(0, 0, 0, 0.1);
19
+ --shadow-lg: 0 8px 16px rgba(0, 0, 0, 0.15);
20
+ --border-radius: 8px;
21
+ --transition-speed: 0.3s;
22
+
23
+ /* New node color variables */
24
+ --input-node-color-1: #3498db;
25
+ --input-node-color-2: #1abc9c;
26
+ --hidden-node-color-1: #9b59b6;
27
+ --hidden-node-color-2: #8e44ad;
28
+ --output-node-color-1: #2ecc71;
29
+ --output-node-color-2: #27ae60;
30
+ --conv-node-color-1: #f39c12;
31
+ --conv-node-color-2: #e67e22;
32
+ --pool-node-color-1: #e74c3c;
33
+ --pool-node-color-2: #c0392b;
34
+ --node-glow: 0 0 15px rgba(255, 255, 255, 0.8);
35
+ }
36
+
37
+ body {
38
+ font-family: 'Roboto', 'Segoe UI', Arial, sans-serif;
39
+ line-height: 1.6;
40
+ color: var(--text-color);
41
+ background-color: var(--background-color);
42
+ padding-bottom: 2rem;
43
+ }
44
+
45
+ header {
46
+ background: linear-gradient(135deg, #3498db, #9b59b6);
47
+ color: white;
48
+ text-align: center;
49
+ padding: 1.5rem 2rem;
50
+ box-shadow: var(--shadow-md);
51
+ position: relative;
52
+ }
53
+
54
+ header::before {
55
+ content: '';
56
+ position: absolute;
57
+ top: 0;
58
+ left: 0;
59
+ right: 0;
60
+ bottom: 0;
61
+ background: url("data:image/svg+xml,%3Csvg width='60' height='60' viewBox='0 0 60 60' xmlns='http://www.w3.org/2000/svg'%3E%3Cg fill='none' fill-rule='evenodd'%3E%3Cg fill='%23ffffff' fill-opacity='0.1'%3E%3Cpath d='M36 34v-4h-2v4h-4v2h4v4h2v-4h4v-2h-4zm0-30V0h-2v4h-4v2h4v4h2V6h4V4h-4zM6 34v-4H4v4H0v2h4v4h2v-4h4v-2H6zM6 4V0H4v4H0v2h4v4h2V6h4V4H6z'/%3E%3C/g%3E%3C/g%3E%3C/svg%3E");
62
+ opacity: 0.1;
63
+ }
64
+
65
+ header h1 {
66
+ position: relative;
67
+ z-index: 1;
68
+ font-weight: 800;
69
+ letter-spacing: -0.5px;
70
+ text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
71
+ }
72
+
73
+ .container {
74
+ max-width: 1400px;
75
+ margin: 0 auto;
76
+ padding: 0 1rem;
77
+ display: grid;
78
+ grid-template-columns: 1fr 2.5fr 1fr;
79
+ gap: 1.5rem;
80
+ }
81
+
82
+ .tools-panel, .properties-panel {
83
+ background: var(--card-background);
84
+ border-radius: var(--border-radius);
85
+ padding: 1.5rem;
86
+ box-shadow: var(--shadow-sm);
87
+ height: 85vh;
88
+ overflow-y: auto;
89
+ display: flex;
90
+ flex-direction: column;
91
+ }
92
+
93
+ .canvas-container {
94
+ grid-column: 2;
95
+ position: relative;
96
+ overflow: hidden;
97
+ }
98
+
99
+ .network-canvas {
100
+ width: 100%;
101
+ height: 100%;
102
+ position: relative;
103
+ background: var(--background-color);
104
+ border-radius: var(--border-radius);
105
+ border: 1px solid var(--border-color);
106
+ }
107
+
108
+ .node-types {
109
+ margin-top: 1.5rem;
110
+ }
111
+
112
+ .node-item {
113
+ padding: 0.8rem 1rem;
114
+ border-radius: var(--border-radius);
115
+ background: var(--background-color);
116
+ cursor: grab;
117
+ transition: all var(--transition-speed) ease;
118
+ margin-bottom: 0.8rem;
119
+ position: relative;
120
+ display: flex;
121
+ align-items: center;
122
+ border: 1px solid var(--border-color);
123
+ }
124
+
125
+ .node-item:hover {
126
+ transform: translateY(-2px);
127
+ box-shadow: var(--shadow-md);
128
+ }
129
+
130
+ .node-item[data-type="input"] {
131
+ background: linear-gradient(135deg, var(--input-node-color-1), var(--input-node-color-2));
132
+ border-color: var(--input-node-color-1);
133
+ color: white;
134
+ }
135
+
136
+ .node-item[data-type="hidden"] {
137
+ background: linear-gradient(135deg, var(--hidden-node-color-1), var(--hidden-node-color-2));
138
+ border-color: var(--hidden-node-color-1);
139
+ color: white;
140
+ }
141
+
142
+ .node-item[data-type="output"] {
143
+ background: linear-gradient(135deg, var(--output-node-color-1), var(--output-node-color-2));
144
+ border-color: var(--output-node-color-1);
145
+ color: white;
146
+ }
147
+
148
+ .node-item[data-type="conv"] {
149
+ background: linear-gradient(135deg, var(--conv-node-color-1), var(--conv-node-color-2));
150
+ border-color: var(--conv-node-color-1);
151
+ color: white;
152
+ }
153
+
154
+ .node-item[data-type="pool"] {
155
+ background: linear-gradient(135deg, var(--pool-node-color-1), var(--pool-node-color-2));
156
+ border-color: var(--pool-node-color-1);
157
+ color: white;
158
+ }
159
+
160
+ .node-icon {
161
+ width: 24px;
162
+ height: 24px;
163
+ margin-right: 10px;
164
+ display: flex;
165
+ align-items: center;
166
+ justify-content: center;
167
+ border-radius: 50%;
168
+ background: rgba(255, 255, 255, 0.8);
169
+ }
170
+
171
+ .node-icon svg {
172
+ width: 16px;
173
+ height: 16px;
174
+ }
175
+
176
+ .node {
177
+ padding: 1rem;
178
+ border-radius: 8px;
179
+ text-align: center;
180
+ font-size: 0.95rem;
181
+ color: white;
182
+ box-shadow: var(--shadow-md);
183
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
184
+ position: relative;
185
+ overflow: hidden;
186
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.2);
187
+ }
188
+
189
+ .node::after {
190
+ content: '';
191
+ position: absolute;
192
+ top: 0;
193
+ left: 0;
194
+ width: 100%;
195
+ height: 100%;
196
+ background: radial-gradient(circle at top right, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
197
+ z-index: 0;
198
+ }
199
+
200
+ .node::before {
201
+ content: '';
202
+ position: absolute;
203
+ top: -50%;
204
+ left: -50%;
205
+ width: 200%;
206
+ height: 200%;
207
+ background: linear-gradient(45deg, rgba(255, 255, 255, 0) 0%, rgba(255, 255, 255, 0.1) 50%, rgba(255, 255, 255, 0) 100%);
208
+ transform: rotate(45deg);
209
+ animation: shimmer 3s infinite;
210
+ z-index: 1;
211
+ pointer-events: none;
212
+ }
213
+
214
+ @keyframes shimmer {
215
+ 0% { transform: translateX(-100%) rotate(45deg); }
216
+ 100% { transform: translateX(100%) rotate(45deg); }
217
+ }
218
+
219
+ .node:hover {
220
+ transform: translateY(-2px) scale(1.05);
221
+ box-shadow: var(--shadow-lg), 0 0 20px rgba(255, 255, 255, 0.2);
222
+ }
223
+
224
+ .input-node {
225
+ background: linear-gradient(135deg, var(--input-node-color-1), var(--input-node-color-2));
226
+ border: 2px solid var(--input-node-color-1);
227
+ color: white;
228
+ }
229
+
230
+ .hidden-node {
231
+ background: linear-gradient(135deg, var(--hidden-node-color-1), var(--hidden-node-color-2));
232
+ border: 2px solid var(--hidden-node-color-1);
233
+ color: white;
234
+ }
235
+
236
+ .output-node {
237
+ background: linear-gradient(135deg, var(--output-node-color-1), var(--output-node-color-2));
238
+ border: 2px solid var(--output-node-color-1);
239
+ color: white;
240
+ }
241
+
242
+ .conv-node {
243
+ background: linear-gradient(135deg, var(--conv-node-color-1), var(--conv-node-color-2));
244
+ border: 2px solid var(--conv-node-color-1);
245
+ color: white;
246
+ }
247
+
248
+ .pool-node {
249
+ background: linear-gradient(135deg, var(--pool-node-color-1), var(--pool-node-color-2));
250
+ border: 2px solid var(--pool-node-color-1);
251
+ color: white;
252
+ }
253
+
254
+ .controls {
255
+ margin-top: 2rem;
256
+ }
257
+
258
+ button {
259
+ display: block;
260
+ width: 100%;
261
+ padding: 0.9rem;
262
+ margin-bottom: 0.75rem;
263
+ background: var(--primary-color);
264
+ color: white;
265
+ border: none;
266
+ border-radius: 8px;
267
+ cursor: pointer;
268
+ transition: all 0.3s;
269
+ font-weight: 600;
270
+ letter-spacing: 0.02em;
271
+ position: relative;
272
+ overflow: hidden;
273
+ box-shadow: var(--shadow-md);
274
+ }
275
+
276
+ button::after {
277
+ content: '';
278
+ position: absolute;
279
+ top: 0;
280
+ left: 0;
281
+ width: 100%;
282
+ height: 100%;
283
+ background: linear-gradient(rgba(255, 255, 255, 0.2), rgba(255, 255, 255, 0));
284
+ z-index: 0;
285
+ }
286
+
287
+ button:hover {
288
+ background: linear-gradient(135deg, var(--primary-color), #3a5ca9);
289
+ box-shadow: var(--shadow-lg);
290
+ transform: translateY(-2px);
291
+ }
292
+
293
+ button:active {
294
+ transform: translateY(1px);
295
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
296
+ }
297
+
298
+ h2 {
299
+ margin-bottom: 1.25rem;
300
+ color: var(--secondary-color);
301
+ font-size: 1.25rem;
302
+ font-weight: 700;
303
+ position: relative;
304
+ padding-bottom: 0.75rem;
305
+ }
306
+
307
+ h2::after {
308
+ content: '';
309
+ position: absolute;
310
+ left: 0;
311
+ bottom: 0;
312
+ width: 40px;
313
+ height: 3px;
314
+ background: var(--primary-color);
315
+ border-radius: 2px;
316
+ }
317
+
318
+ #node-properties {
319
+ padding: 1rem;
320
+ background-color: #f8f9fa;
321
+ border-radius: 8px;
322
+ min-height: 180px;
323
+ box-shadow: inset 0 2px 4px rgba(0, 0, 0, 0.05);
324
+ line-height: 1.7;
325
+ }
326
+
327
+ #node-properties strong {
328
+ color: var(--primary-color);
329
+ }
330
+
331
+ .tooltip {
332
+ position: absolute;
333
+ background: rgba(44, 62, 80, 0.95);
334
+ color: white;
335
+ padding: 12px 16px;
336
+ border-radius: 8px;
337
+ font-size: 0.9rem;
338
+ pointer-events: none;
339
+ opacity: 0;
340
+ transition: opacity 0.2s, transform 0.2s;
341
+ z-index: 1000;
342
+ max-width: 300px;
343
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
344
+ transform: translateY(10px);
345
+ backdrop-filter: blur(2px);
346
+ }
347
+
348
+ .tooltip.visible {
349
+ opacity: 1;
350
+ transform: translateY(0);
351
+ }
352
+
353
+ .tooltip-content {
354
+ line-height: 1.6;
355
+ }
356
+
357
+ footer {
358
+ text-align: center;
359
+ padding: 1.5rem;
360
+ background: linear-gradient(135deg, var(--secondary-color), #34495e);
361
+ color: white;
362
+ font-size: 0.9rem;
363
+ position: relative;
364
+ overflow: hidden;
365
+ }
366
+
367
+ footer::before {
368
+ content: '';
369
+ position: absolute;
370
+ width: 100%;
371
+ height: 100%;
372
+ top: 0;
373
+ left: 0;
374
+ background: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100" viewBox="0 0 100 100"><rect fill="none" width="100" height="100"/><circle fill="rgba(255,255,255,0.05)" cx="25" cy="25" r="5"/><circle fill="rgba(255,255,255,0.05)" cx="75" cy="25" r="5"/><circle fill="rgba(255,255,255,0.05)" cx="25" cy="75" r="5"/><circle fill="rgba(255,255,255,0.05)" cx="75" cy="75" r="5"/></svg>');
375
+ opacity: 0.4;
376
+ z-index: 0;
377
+ }
378
+
379
+ footer p {
380
+ position: relative;
381
+ z-index: 1;
382
+ }
383
+
384
+ /* Styles for nodes on the canvas */
385
+ .canvas-node {
386
+ position: absolute;
387
+ width: 180px;
388
+ padding: 0.8rem;
389
+ border-radius: var(--border-radius);
390
+ color: white;
391
+ box-shadow: var(--shadow-md);
392
+ z-index: 10;
393
+ transition: all 0.3s ease;
394
+ cursor: move;
395
+ background-size: 300% 300%;
396
+ animation: gradientShift 8s ease infinite;
397
+ }
398
+
399
+ @keyframes gradientShift {
400
+ 0% { background-position: 0% 50%; }
401
+ 50% { background-position: 100% 50%; }
402
+ 100% { background-position: 0% 50%; }
403
+ }
404
+
405
+ .canvas-node.dragging {
406
+ cursor: grabbing;
407
+ box-shadow: var(--shadow-lg), 0 0 25px rgba(255, 255, 255, 0.3);
408
+ transform: scale(1.05);
409
+ z-index: 100;
410
+ }
411
+
412
+ .canvas-node[data-type="input"] {
413
+ background: linear-gradient(135deg, var(--input-node-color-1), var(--input-node-color-2), var(--input-node-color-1));
414
+ border: 2px solid var(--input-node-color-1);
415
+ }
416
+
417
+ .canvas-node[data-type="hidden"] {
418
+ background: linear-gradient(135deg, var(--hidden-node-color-1), var(--hidden-node-color-2), var(--hidden-node-color-1));
419
+ border: 2px solid var(--hidden-node-color-1);
420
+ }
421
+
422
+ .canvas-node[data-type="output"] {
423
+ background: linear-gradient(135deg, var(--output-node-color-1), var(--output-node-color-2), var(--output-node-color-1));
424
+ border: 2px solid var(--output-node-color-1);
425
+ }
426
+
427
+ .canvas-node[data-type="conv"] {
428
+ background: linear-gradient(135deg, var(--conv-node-color-1), var(--conv-node-color-2), var(--conv-node-color-1));
429
+ border: 2px solid var(--conv-node-color-1);
430
+ }
431
+
432
+ .canvas-node[data-type="pool"] {
433
+ background: linear-gradient(135deg, var(--pool-node-color-1), var(--pool-node-color-2), var(--pool-node-color-1));
434
+ border: 2px solid var(--pool-node-color-1);
435
+ }
436
+
437
+ .canvas-node .node-title {
438
+ font-weight: 600;
439
+ font-size: 0.9rem;
440
+ margin-bottom: 0.5rem;
441
+ color: white;
442
+ text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
443
+ position: relative;
444
+ z-index: 2;
445
+ }
446
+
447
+ .canvas-node .node-id {
448
+ position: absolute;
449
+ top: 8px;
450
+ right: 10px;
451
+ font-size: 0.7rem;
452
+ color: white;
453
+ background: rgba(0, 0, 0, 0.2);
454
+ border-radius: 4px;
455
+ padding: 0 5px;
456
+ z-index: 2;
457
+ }
458
+
459
+ .canvas-node .node-dimensions {
460
+ font-size: 0.8rem;
461
+ color: rgba(255, 255, 255, 0.9);
462
+ margin-bottom: 0.5rem;
463
+ position: relative;
464
+ z-index: 2;
465
+ }
466
+
467
+ .canvas-node::after {
468
+ content: '';
469
+ position: absolute;
470
+ top: 0;
471
+ left: 0;
472
+ width: 100%;
473
+ height: 100%;
474
+ background: radial-gradient(circle at top right, rgba(255, 255, 255, 0.3), rgba(255, 255, 255, 0));
475
+ z-index: 1;
476
+ border-radius: var(--border-radius);
477
+ pointer-events: none;
478
+ }
479
+
480
+ .connection {
481
+ position: absolute;
482
+ height: 3px;
483
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
484
+ transform-origin: 0 0;
485
+ pointer-events: none;
486
+ z-index: 5;
487
+ box-shadow: 0 0 8px rgba(52, 152, 219, 0.5);
488
+ }
489
+
490
+ .connection::after {
491
+ content: '';
492
+ position: absolute;
493
+ right: -5px;
494
+ top: -5px;
495
+ width: 10px;
496
+ height: 10px;
497
+ border-radius: 50%;
498
+ background: var(--accent-color);
499
+ box-shadow: 0 0 8px rgba(155, 89, 182, 0.8);
500
+ }
501
+
502
+ .temp-connection {
503
+ background: linear-gradient(90deg, rgba(52, 152, 219, 0.5), rgba(155, 89, 182, 0.5));
504
+ box-shadow: 0 0 12px rgba(52, 152, 219, 0.3);
505
+ transition: none;
506
+ }
507
+
508
+ .temp-connection::after {
509
+ background: rgba(155, 89, 182, 0.7);
510
+ box-shadow: 0 0 12px rgba(155, 89, 182, 0.5);
511
+ }
512
+
513
+ .connected-node {
514
+ box-shadow: 0 0 0 3px rgba(46, 204, 113, 0.2), var(--shadow-sm);
515
+ }
516
+
517
+ .canvas-tooltip {
518
+ position: absolute;
519
+ background: white;
520
+ border-radius: var(--border-radius);
521
+ padding: 1rem;
522
+ box-shadow: var(--shadow-md);
523
+ z-index: 1000;
524
+ max-width: 300px;
525
+ display: none;
526
+ pointer-events: none;
527
+ border: 1px solid var(--border-color);
528
+ }
529
+
530
+ .tooltip-header {
531
+ font-weight: 600;
532
+ margin-bottom: 0.5rem;
533
+ padding-bottom: 0.5rem;
534
+ border-bottom: 1px solid #eee;
535
+ }
536
+
537
+ .tooltip-content {
538
+ font-size: 0.9rem;
539
+ }
540
+
541
+ .tooltip-row {
542
+ display: flex;
543
+ justify-content: space-between;
544
+ margin-bottom: 0.3rem;
545
+ }
546
+
547
+ .tooltip-label {
548
+ color: #666;
549
+ }
550
+
551
+ .tooltip-value {
552
+ font-weight: 500;
553
+ }
554
+
555
+ .control-group {
556
+ margin-bottom: 1.5rem;
557
+ }
558
+
559
+ .sample-data {
560
+ display: flex;
561
+ gap: 0.5rem;
562
+ flex-wrap: wrap;
563
+ margin-bottom: 1rem;
564
+ }
565
+
566
+ .sample-item {
567
+ width: 50px;
568
+ height: 50px;
569
+ border-radius: var(--border-radius);
570
+ border: 1px solid var(--border-color);
571
+ overflow: hidden;
572
+ cursor: pointer;
573
+ transition: all 0.2s ease;
574
+ }
575
+
576
+ .sample-item:hover {
577
+ transform: scale(1.1);
578
+ box-shadow: var(--shadow-sm);
579
+ }
580
+
581
+ .sample-item img {
582
+ width: 100%;
583
+ height: 100%;
584
+ object-fit: cover;
585
+ }
586
+
587
+ .button-group {
588
+ display: flex;
589
+ gap: 0.8rem;
590
+ margin-bottom: 1.5rem;
591
+ }
592
+
593
+ .btn {
594
+ padding: 0.7rem 1.2rem;
595
+ border-radius: var(--border-radius);
596
+ border: none;
597
+ cursor: pointer;
598
+ font-weight: 500;
599
+ transition: all var(--transition-speed) ease;
600
+ display: inline-flex;
601
+ align-items: center;
602
+ justify-content: center;
603
+ }
604
+
605
+ .btn:hover {
606
+ transform: translateY(-2px);
607
+ box-shadow: var(--shadow-sm);
608
+ }
609
+
610
+ .btn:active {
611
+ transform: translateY(0);
612
+ }
613
+
614
+ .btn-primary {
615
+ background: var(--primary-color);
616
+ color: white;
617
+ }
618
+
619
+ .btn-primary:hover {
620
+ background: #2980b9;
621
+ }
622
+
623
+ .btn-secondary {
624
+ background: var(--secondary-color);
625
+ color: white;
626
+ }
627
+
628
+ .btn-secondary:hover {
629
+ background: #27ae60;
630
+ }
631
+
632
+ .btn-danger {
633
+ background: var(--warning-color);
634
+ color: white;
635
+ }
636
+
637
+ .btn-danger:hover {
638
+ background: #c0392b;
639
+ }
640
+
641
+ .btn-sm {
642
+ padding: 0.4rem 0.8rem;
643
+ font-size: 0.9rem;
644
+ }
645
+
646
+ .btn-icon {
647
+ margin-right: 0.5rem;
648
+ }
649
+
650
+ .network-settings {
651
+ margin-bottom: 1.5rem;
652
+ }
653
+
654
+ .setting-group {
655
+ margin-bottom: 1rem;
656
+ }
657
+
658
+ .setting-label {
659
+ font-size: 0.9rem;
660
+ color: #666;
661
+ margin-bottom: 0.3rem;
662
+ display: block;
663
+ }
664
+
665
+ .range-slider {
666
+ width: 100%;
667
+ -webkit-appearance: none;
668
+ height: 6px;
669
+ border-radius: 5px;
670
+ background: #ddd;
671
+ outline: none;
672
+ }
673
+
674
+ .range-slider::-webkit-slider-thumb {
675
+ -webkit-appearance: none;
676
+ appearance: none;
677
+ width: 18px;
678
+ height: 18px;
679
+ border-radius: 50%;
680
+ background: var(--primary-color);
681
+ cursor: pointer;
682
+ transition: all 0.2s ease;
683
+ }
684
+
685
+ .range-slider::-webkit-slider-thumb:hover {
686
+ background: #2980b9;
687
+ box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.3);
688
+ }
689
+
690
+ .range-value {
691
+ font-size: 0.9rem;
692
+ color: var(--primary-color);
693
+ font-weight: 600;
694
+ text-align: right;
695
+ margin-top: 0.3rem;
696
+ }
697
+
698
+ select {
699
+ width: 100%;
700
+ padding: 0.5rem;
701
+ border-radius: var(--border-radius);
702
+ border: 1px solid var(--border-color);
703
+ background: white;
704
+ font-size: 0.9rem;
705
+ color: var(--text-color);
706
+ cursor: pointer;
707
+ }
708
+
709
+ .props-content {
710
+ flex: 1;
711
+ display: flex;
712
+ flex-direction: column;
713
+ }
714
+
715
+ .props-section {
716
+ margin-bottom: 1.5rem;
717
+ }
718
+
719
+ .props-heading {
720
+ font-size: 1rem;
721
+ font-weight: 600;
722
+ margin-bottom: 0.8rem;
723
+ color: var(--primary-color);
724
+ display: flex;
725
+ align-items: center;
726
+ }
727
+
728
+ .props-heading i {
729
+ margin-right: 0.5rem;
730
+ }
731
+
732
+ .props-row {
733
+ display: flex;
734
+ justify-content: space-between;
735
+ align-items: center;
736
+ padding: 0.5rem 0;
737
+ border-bottom: 1px solid #f0f0f0;
738
+ }
739
+
740
+ .props-key {
741
+ color: #666;
742
+ font-size: 0.9rem;
743
+ }
744
+
745
+ .props-value {
746
+ font-weight: 500;
747
+ font-size: 0.9rem;
748
+ }
749
+
750
+ .activation-function {
751
+ background: #f8f9fa;
752
+ border-radius: var(--border-radius);
753
+ padding: 1rem;
754
+ margin-bottom: 1.5rem;
755
+ }
756
+
757
+ .layer-weights {
758
+ display: flex;
759
+ justify-content: center;
760
+ margin-bottom: 1.5rem;
761
+ background: #f8f9fa;
762
+ border-radius: var(--border-radius);
763
+ padding: 1rem;
764
+ }
765
+
766
+ .weights-img {
767
+ max-width: 100%;
768
+ border-radius: var(--border-radius);
769
+ }
770
+
771
+ .training-progress {
772
+ margin-bottom: 1.5rem;
773
+ }
774
+
775
+ .progress-bar-container {
776
+ width: 100%;
777
+ height: 10px;
778
+ background: #f0f0f0;
779
+ border-radius: 5px;
780
+ overflow: hidden;
781
+ margin-bottom: 0.5rem;
782
+ }
783
+
784
+ .progress-bar {
785
+ height: 100%;
786
+ background: linear-gradient(90deg, var(--primary-color), var(--accent-color));
787
+ border-radius: 5px;
788
+ transition: width 0.3s ease;
789
+ width: 0%;
790
+ }
791
+
792
+ .metrics {
793
+ display: flex;
794
+ justify-content: space-between;
795
+ margin-top: 0.5rem;
796
+ }
797
+
798
+ .metric {
799
+ text-align: center;
800
+ flex: 1;
801
+ }
802
+
803
+ .metric-value {
804
+ font-weight: 600;
805
+ font-size: 1.1rem;
806
+ color: var(--primary-color);
807
+ }
808
+
809
+ .metric-label {
810
+ font-size: 0.8rem;
811
+ color: #666;
812
+ }
813
+
814
+ .node-controls {
815
+ display: flex;
816
+ justify-content: flex-end;
817
+ margin-top: 0.5rem;
818
+ }
819
+
820
+ .node-edit-btn, .node-delete-btn {
821
+ background: none;
822
+ border: none;
823
+ cursor: pointer;
824
+ padding: 3px;
825
+ border-radius: 4px;
826
+ transition: all 0.2s ease;
827
+ margin-left: 0.3rem;
828
+ }
829
+
830
+ .node-edit-btn:hover {
831
+ background: #e0e0e0;
832
+ }
833
+
834
+ .node-delete-btn:hover {
835
+ background: #ffcdd2;
836
+ }
837
+
838
+ .icon {
839
+ font-style: normal;
840
+ font-size: 16px;
841
+ }
842
+
843
+ /* Modal styles */
844
+ .modal {
845
+ display: none;
846
+ position: fixed;
847
+ z-index: 1000;
848
+ left: 0;
849
+ top: 0;
850
+ width: 100%;
851
+ height: 100%;
852
+ background-color: rgba(0, 0, 0, 0.5);
853
+ align-items: center;
854
+ justify-content: center;
855
+ }
856
+
857
+ .modal-content {
858
+ background-color: white;
859
+ padding: 2rem;
860
+ border-radius: var(--border-radius);
861
+ width: 90%;
862
+ max-width: 700px;
863
+ max-height: 90vh;
864
+ overflow-y: auto;
865
+ position: relative;
866
+ animation: modalFadeIn 0.3s;
867
+ }
868
+
869
+ .close-modal {
870
+ position: absolute;
871
+ right: 1.5rem;
872
+ top: 1.5rem;
873
+ font-size: 1.5rem;
874
+ color: #aaa;
875
+ cursor: pointer;
876
+ transition: color 0.2s ease;
877
+ }
878
+
879
+ .close-modal:hover {
880
+ color: var(--text-color);
881
+ }
882
+
883
+ .modal-title {
884
+ font-size: 1.5rem;
885
+ margin-bottom: 1.5rem;
886
+ color: var(--primary-color);
887
+ padding-bottom: 1rem;
888
+ border-bottom: 1px solid var(--border-color);
889
+ }
890
+
891
+ .modal-section {
892
+ margin-bottom: 2rem;
893
+ }
894
+
895
+ .modal-section-title {
896
+ font-size: 1.2rem;
897
+ margin-bottom: 1rem;
898
+ color: var(--text-color);
899
+ }
900
+
901
+ .modal-text {
902
+ line-height: 1.7;
903
+ margin-bottom: 1rem;
904
+ }
905
+
906
+ .modal-list {
907
+ list-style: none;
908
+ margin: 1rem 0;
909
+ }
910
+
911
+ .modal-list li {
912
+ padding: 0.5rem 0;
913
+ border-bottom: 1px solid #f0f0f0;
914
+ display: flex;
915
+ align-items: flex-start;
916
+ }
917
+
918
+ .modal-list li::before {
919
+ content: '•';
920
+ color: var(--primary-color);
921
+ font-weight: bold;
922
+ margin-right: 0.5rem;
923
+ }
924
+
925
+ /* Footer styles */
926
+ .footer {
927
+ text-align: center;
928
+ margin-top: 3rem;
929
+ padding: 1.5rem;
930
+ background: white;
931
+ box-shadow: var(--shadow-sm);
932
+ }
933
+
934
+ .footer-links {
935
+ display: flex;
936
+ justify-content: center;
937
+ gap: 1.5rem;
938
+ margin-bottom: 1rem;
939
+ }
940
+
941
+ .footer-link {
942
+ color: var(--primary-color);
943
+ text-decoration: none;
944
+ transition: color 0.2s ease;
945
+ }
946
+
947
+ .footer-link:hover {
948
+ color: #2980b9;
949
+ text-decoration: underline;
950
+ }
951
+
952
+ .footer-text {
953
+ color: #666;
954
+ font-size: 0.9rem;
955
+ }
956
+
957
+ /* Animation keyframes */
958
+ @keyframes pulse {
959
+ 0% {
960
+ box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.5);
961
+ }
962
+ 70% {
963
+ box-shadow: 0 0 0 5px rgba(46, 204, 113, 0);
964
+ }
965
+ 100% {
966
+ box-shadow: 0 0 0 0 rgba(46, 204, 113, 0);
967
+ }
968
+ }
969
+
970
+ @keyframes modalFadeIn {
971
+ from {
972
+ opacity: 0;
973
+ transform: translateY(-30px);
974
+ }
975
+ to {
976
+ opacity: 1;
977
+ transform: translateY(0);
978
+ }
979
+ }
980
+
981
+ /* Layer editor modal styles */
982
+ .layer-editor-modal .modal-content {
983
+ max-width: 550px;
984
+ }
985
+
986
+ .layer-form {
987
+ display: grid;
988
+ grid-template-columns: 1fr 1fr;
989
+ gap: 1rem;
990
+ }
991
+
992
+ .form-group {
993
+ margin-bottom: 1rem;
994
+ }
995
+
996
+ .form-group label {
997
+ display: block;
998
+ font-size: 0.9rem;
999
+ color: #666;
1000
+ margin-bottom: 0.3rem;
1001
+ }
1002
+
1003
+ .form-group input, .form-group select {
1004
+ width: 100%;
1005
+ padding: 0.5rem;
1006
+ border-radius: var(--border-radius);
1007
+ border: 1px solid var(--border-color);
1008
+ }
1009
+
1010
+ .form-group input:focus, .form-group select:focus {
1011
+ outline: none;
1012
+ border-color: var(--primary-color);
1013
+ box-shadow: 0 0 0 2px rgba(52, 152, 219, 0.2);
1014
+ }
1015
+
1016
+ .form-grid-full {
1017
+ grid-column: 1 / -1;
1018
+ }
1019
+
1020
+ .parameters-preview {
1021
+ grid-column: 1 / -1;
1022
+ background: #f8f9fa;
1023
+ border-radius: var(--border-radius);
1024
+ padding: 1rem;
1025
+ margin-top: 1rem;
1026
+ }
1027
+
1028
+ .parameters-title {
1029
+ font-weight: 600;
1030
+ margin-bottom: 0.5rem;
1031
+ color: var(--text-color);
1032
+ font-size: 0.9rem;
1033
+ }
1034
+
1035
+ .parameters-grid {
1036
+ display: grid;
1037
+ grid-template-columns: repeat(auto-fill, minmax(120px, 1fr));
1038
+ gap: 0.5rem;
1039
+ }
1040
+
1041
+ .parameter-item {
1042
+ background: white;
1043
+ border-radius: var(--border-radius);
1044
+ padding: 0.7rem;
1045
+ box-shadow: var(--shadow-sm);
1046
+ text-align: center;
1047
+ }
1048
+
1049
+ .parameter-value {
1050
+ font-weight: 600;
1051
+ color: var(--primary-color);
1052
+ font-size: 1rem;
1053
+ margin-bottom: 0.2rem;
1054
+ }
1055
+
1056
+ .parameter-label {
1057
+ font-size: 0.8rem;
1058
+ color: #666;
1059
+ }
1060
+
1061
+ /* Responsive styles */
1062
+ @media screen and (max-width: 1200px) {
1063
+ .container {
1064
+ grid-template-columns: 1fr;
1065
+ grid-template-rows: auto;
1066
+ }
1067
+
1068
+ .tools-panel, .canvas-container, .props-panel {
1069
+ grid-column: 1;
1070
+ height: auto;
1071
+ }
1072
+
1073
+ .tools-panel {
1074
+ display: grid;
1075
+ grid-template-columns: 1fr 1fr;
1076
+ gap: 1rem;
1077
+ height: auto;
1078
+ }
1079
+
1080
+ .network-canvas {
1081
+ height: 60vh;
1082
+ }
1083
+
1084
+ .canvas-node {
1085
+ width: 160px;
1086
+ }
1087
+ }
1088
+
1089
+ @media screen and (max-width: 768px) {
1090
+ .tools-panel {
1091
+ grid-template-columns: 1fr;
1092
+ }
1093
+
1094
+ .canvas-node {
1095
+ width: 140px;
1096
+ padding: 0.6rem;
1097
+ }
1098
+
1099
+ .header-title {
1100
+ font-size: 2rem;
1101
+ }
1102
+
1103
+ .button-group {
1104
+ flex-wrap: wrap;
1105
+ }
1106
+
1107
+ .btn {
1108
+ flex: 1;
1109
+ min-width: 120px;
1110
+ }
1111
+
1112
+ .props-row {
1113
+ flex-direction: column;
1114
+ align-items: flex-start;
1115
+ }
1116
+
1117
+ .props-value {
1118
+ margin-top: 0.3rem;
1119
+ }
1120
+ }
1121
+
1122
+ /* Highlights and animations */
1123
+ .highlight-pulse {
1124
+ animation: highlightPulse 1.5s ease-in-out;
1125
+ }
1126
+
1127
+ @keyframes highlightPulse {
1128
+ 0% {
1129
+ box-shadow: 0 0 0 0 rgba(52, 152, 219, 0.5);
1130
+ }
1131
+ 50% {
1132
+ box-shadow: 0 0 0 12px rgba(52, 152, 219, 0.3);
1133
+ }
1134
+ 100% {
1135
+ box-shadow: 0 0 0 0 rgba(52, 152, 219, 0);
1136
+ }
1137
+ }
1138
+
1139
+ .node-port {
1140
+ width: 14px;
1141
+ height: 14px;
1142
+ border-radius: 50%;
1143
+ background: white;
1144
+ border: 2px solid #aaa;
1145
+ position: absolute;
1146
+ cursor: pointer;
1147
+ transition: all 0.2s ease;
1148
+ z-index: 20;
1149
+ box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
1150
+ }
1151
+
1152
+ .port-in {
1153
+ top: 50%;
1154
+ left: -7px;
1155
+ transform: translateY(-50%);
1156
+ }
1157
+
1158
+ .port-out {
1159
+ top: 50%;
1160
+ right: -7px;
1161
+ transform: translateY(-50%);
1162
+ }
1163
+
1164
+ .port-in:hover, .port-out:hover, .port-hover {
1165
+ background: rgba(255, 255, 255, 0.9);
1166
+ border-color: white;
1167
+ transform: translateY(-50%) scale(1.3);
1168
+ box-shadow: 0 0 12px rgba(255, 255, 255, 0.8);
1169
+ }
1170
+
1171
+ .active-port {
1172
+ background: rgba(255, 255, 255, 0.9);
1173
+ border-color: white;
1174
+ box-shadow: 0 0 12px rgba(255, 255, 255, 0.8);
1175
+ animation: portPulse 1.5s infinite;
1176
+ }
1177
+
1178
+ @keyframes portPulse {
1179
+ 0% {
1180
+ box-shadow: 0 0 0 0 rgba(255, 255, 255, 0.7);
1181
+ }
1182
+ 70% {
1183
+ box-shadow: 0 0 0 8px rgba(255, 255, 255, 0);
1184
+ }
1185
+ 100% {
1186
+ box-shadow: 0 0 0 0 rgba(255, 255, 255, 0);
1187
+ }
1188
+ }
1189
+
1190
+ .valid-target {
1191
+ background: var(--secondary-color);
1192
+ border-color: white;
1193
+ animation: validPulse 1.5s infinite;
1194
+ }
1195
+
1196
+ @keyframes validPulse {
1197
+ 0% {
1198
+ box-shadow: 0 0 0 0 rgba(46, 204, 113, 0.7);
1199
+ }
1200
+ 70% {
1201
+ box-shadow: 0 0 0 8px rgba(46, 204, 113, 0);
1202
+ }
1203
+ 100% {
1204
+ box-shadow: 0 0 0 0 rgba(46, 204, 113, 0);
1205
+ }
1206
+ }
1207
+
1208
+ .invalid-target {
1209
+ background: var(--warning-color);
1210
+ border-color: white;
1211
+ cursor: not-allowed;
1212
+ animation: invalidPulse 1.5s infinite;
1213
+ }
1214
+
1215
+ @keyframes invalidPulse {
1216
+ 0% {
1217
+ box-shadow: 0 0 0 0 rgba(231, 76, 60, 0.7);
1218
+ }
1219
+ 70% {
1220
+ box-shadow: 0 0 0 8px rgba(231, 76, 60, 0);
1221
+ }
1222
+ 100% {
1223
+ box-shadow: 0 0 0 0 rgba(231, 76, 60, 0);
1224
+ }
1225
+ }
index.html CHANGED
@@ -1,19 +1,170 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Neural Network Playground</title>
7
+ <link rel="stylesheet" href="css/styles.css">
8
+ <link rel="preconnect" href="https://fonts.googleapis.com">
9
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
10
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700;800&display=swap" rel="stylesheet">
11
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/d3/7.8.5/d3.min.js"></script>
12
+ </head>
13
+ <body>
14
+ <header>
15
+ <h1>Neural Network Playground</h1>
16
+ <p class="header-subtitle">Interactive visualization of neural network architectures and concepts</p>
17
+ </header>
18
+
19
+ <main>
20
+ <div class="container">
21
+ <div class="tools-panel">
22
+ <h2>Network Components</h2>
23
+
24
+ <p class="hint-text">Drag components to the canvas to build your neural network</p>
25
+
26
+ <div class="node-types">
27
+ <div class="node-item" draggable="true" data-type="input">
28
+ <div class="node input-node">Input Layer</div>
29
+ </div>
30
+ <div class="node-item" draggable="true" data-type="hidden">
31
+ <div class="node hidden-node">Hidden Layer</div>
32
+ </div>
33
+ <div class="node-item" draggable="true" data-type="output">
34
+ <div class="node output-node">Output Layer</div>
35
+ </div>
36
+ <div class="node-item" draggable="true" data-type="conv">
37
+ <div class="node conv-node">Convolutional</div>
38
+ </div>
39
+ <div class="node-item" draggable="true" data-type="pool">
40
+ <div class="node pool-node">Pooling</div>
41
+ </div>
42
+ </div>
43
+
44
+ <h3 class="section-title">Sample Data</h3>
45
+ <div class="sample-data">
46
+ <div class="sample-item" data-sample="1">5</div>
47
+ <div class="sample-item" data-sample="2">7</div>
48
+ <div class="sample-item" data-sample="3">3</div>
49
+ </div>
50
+
51
+ <div class="controls">
52
+ <button id="run-network">Run Network</button>
53
+ <button id="clear-canvas">Clear Canvas</button>
54
+ </div>
55
+
56
+ <h3 class="section-title">Network Settings</h3>
57
+ <div class="network-settings">
58
+ <div class="setting-group">
59
+ <label for="learning-rate">Learning Rate:</label>
60
+ <input type="range" id="learning-rate" min="0.001" max="1" step="0.001" value="0.1">
61
+ <span class="setting-value" id="learning-rate-value">0.1</span>
62
+ </div>
63
+ <div class="setting-group">
64
+ <label for="activation">Activation:</label>
65
+ <select id="activation">
66
+ <option value="relu">ReLU</option>
67
+ <option value="sigmoid">Sigmoid</option>
68
+ <option value="tanh">Tanh</option>
69
+ </select>
70
+ </div>
71
+ </div>
72
+ </div>
73
+
74
+ <div class="canvas-container">
75
+ <div id="network-canvas" class="network-canvas">
76
+ <div class="canvas-hint">
77
+ <strong>Build Your Neural Network</strong>
78
+ Drag components from the left panel and drop them here.
79
+ <br>Connect them by dragging from output (right) to input (left) ports.
80
+ </div>
81
+ </div>
82
+ </div>
83
+
84
+ <div class="properties-panel">
85
+ <h2>Layer Properties</h2>
86
+ <div id="node-properties">
87
+ <p>Hover over a node to see its properties</p>
88
+ </div>
89
+
90
+ <h3 class="section-title">Activation Function</h3>
91
+ <div class="activation-graph">
92
+ <svg class="activation-curve" viewBox="0 0 100 100" preserveAspectRatio="none">
93
+ <!-- Will be populated by JavaScript -->
94
+ </svg>
95
+ </div>
96
+
97
+ <h3 class="section-title">Layer Weights</h3>
98
+ <div id="weight-visualization"></div>
99
+
100
+ <h3 class="section-title">Training Progress</h3>
101
+ <div class="training-progress">
102
+ <div class="progress-bar-container">
103
+ <div class="progress-bar" style="width: 0%"></div>
104
+ </div>
105
+ <div class="metrics">
106
+ <div class="metric">
107
+ <span class="metric-label">Loss:</span>
108
+ <span class="metric-value" id="loss-value">-</span>
109
+ </div>
110
+ <div class="metric">
111
+ <span class="metric-label">Accuracy:</span>
112
+ <span class="metric-value" id="accuracy-value">-</span>
113
+ </div>
114
+ </div>
115
+ </div>
116
+ </div>
117
+ </div>
118
+ </main>
119
+
120
+ <div class="tooltip" id="node-tooltip">
121
+ <div class="tooltip-content"></div>
122
+ </div>
123
+
124
+ <footer>
125
+ <p>Neural Network Playground - Learn and visualize neural networks interactively</p>
126
+ <div class="footer-links">
127
+ <a href="#" id="about-link">About</a>
128
+ <a href="#" id="guide-link">User Guide</a>
129
+ <a href="https://github.com/yourusername/neural-network-playground" target="_blank">GitHub</a>
130
+ </div>
131
+ </footer>
132
+
133
+ <div class="modal" id="about-modal">
134
+ <div class="modal-content">
135
+ <div class="modal-header">
136
+ <h3>About Neural Network Playground</h3>
137
+ <button class="close-modal">&times;</button>
138
+ </div>
139
+ <div class="modal-body">
140
+ <p>This playground allows you to experiment with neural networks visually. Build networks by dragging and dropping layer components, connecting them, and running simulations.</p>
141
+ <p>Learn about different layer types, activation functions, and see how data flows through the network.</p>
142
+ <h4>Key Concepts:</h4>
143
+ <ul>
144
+ <li><strong>Input Layer:</strong> Receives raw data (like images) and passes it to the network</li>
145
+ <li><strong>Hidden Layers:</strong> Extract and process features from the data</li>
146
+ <li><strong>Output Layer:</strong> Provides the final prediction or classification</li>
147
+ <li><strong>Convolutional Layer:</strong> Specialized for image processing, detects spatial patterns</li>
148
+ <li><strong>Pooling Layer:</strong> Reduces dimensions while preserving important features</li>
149
+ </ul>
150
+ </div>
151
+ </div>
152
+ </div>
153
+
154
+ <!-- Layer Editor Modal -->
155
+ <div id="layer-editor-modal" class="modal layer-editor-modal">
156
+ <div class="modal-content">
157
+ <span class="close-modal">&times;</span>
158
+ <h2 class="modal-title">Edit Layer</h2>
159
+ <form class="layer-form">
160
+ <!-- Form fields will be dynamically generated based on layer type -->
161
+ </form>
162
+ </div>
163
+ </div>
164
+ <!-- End Layer Editor Modal -->
165
+
166
+ <script src="js/main.js"></script>
167
+ <script src="js/neural-network.js"></script>
168
+ <script src="js/drag-drop.js"></script>
169
+ </body>
170
+ </html>
js/drag-drop.js ADDED
@@ -0,0 +1,661 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Initialize drag and drop functionality
2
+ function initializeDragAndDrop() {
3
+ const nodeItems = document.querySelectorAll('.node-item');
4
+ const canvas = document.getElementById('network-canvas');
5
+ let draggedNode = null;
6
+ let offsetX, offsetY;
7
+ let isDragging = false;
8
+ let isConnecting = false;
9
+ let startNode = null;
10
+ let connectionLine = null;
11
+ let nodeCounter = {};
12
+
13
+ // Track layers for proper architecture building
14
+ let networkLayers = {
15
+ layers: [],
16
+ connections: []
17
+ };
18
+
19
+ // Add event listeners to draggable items
20
+ nodeItems.forEach(item => {
21
+ item.addEventListener('dragstart', handleDragStart);
22
+ });
23
+
24
+ // Canvas events for dropping nodes
25
+ canvas.addEventListener('dragover', handleDragOver);
26
+ canvas.addEventListener('drop', handleDrop);
27
+
28
+ // Handle drag start event
29
+ function handleDragStart(e) {
30
+ draggedNode = this;
31
+ e.dataTransfer.setData('text/plain', this.getAttribute('data-type'));
32
+
33
+ // Set a ghost image for drag (optional)
34
+ const ghost = this.cloneNode(true);
35
+ ghost.style.opacity = '0.5';
36
+ document.body.appendChild(ghost);
37
+ e.dataTransfer.setDragImage(ghost, 0, 0);
38
+ setTimeout(() => {
39
+ document.body.removeChild(ghost);
40
+ }, 0);
41
+ }
42
+
43
+ // Handle drag over event
44
+ function handleDragOver(e) {
45
+ e.preventDefault();
46
+ e.dataTransfer.dropEffect = 'copy';
47
+ }
48
+
49
+ // Handle drop event to create new nodes on the canvas
50
+ function handleDrop(e) {
51
+ e.preventDefault();
52
+
53
+ // Hide the canvas hint when nodes are added
54
+ const canvasHint = document.querySelector('.canvas-hint');
55
+ if (canvasHint) {
56
+ canvasHint.style.display = 'none';
57
+ }
58
+
59
+ const nodeType = e.dataTransfer.getData('text/plain');
60
+
61
+ if (nodeType) {
62
+ // Generate unique layer ID
63
+ const layerId = window.neuralNetwork.getNextLayerId(nodeType);
64
+
65
+ // Create a new node on the canvas
66
+ const canvasNode = document.createElement('div');
67
+ canvasNode.className = `canvas-node ${nodeType}-node`;
68
+ canvasNode.setAttribute('data-type', nodeType);
69
+ canvasNode.setAttribute('data-id', layerId);
70
+
71
+ // Set node position
72
+ const rect = canvas.getBoundingClientRect();
73
+ const x = e.clientX - rect.left;
74
+ const y = e.clientY - rect.top;
75
+
76
+ canvasNode.style.left = `${x}px`;
77
+ canvasNode.style.top = `${y}px`;
78
+
79
+ // Set node content based on type
80
+ let nodeName, dimensions, units;
81
+
82
+ switch(nodeType) {
83
+ case 'input':
84
+ nodeName = 'Input Layer';
85
+ dimensions = '1 × 28 × 28';
86
+ break;
87
+ case 'hidden':
88
+ // Customize if it's the first hidden layer
89
+ const hiddenCount = document.querySelectorAll('.canvas-node[data-type="hidden"]').length;
90
+ units = hiddenCount === 0 ? 128 : 64;
91
+ nodeName = `Hidden Layer ${hiddenCount + 1}`;
92
+ dimensions = `${units}`;
93
+ break;
94
+ case 'output':
95
+ nodeName = 'Output Layer';
96
+ dimensions = '10';
97
+ break;
98
+ case 'conv':
99
+ const convCount = document.querySelectorAll('.canvas-node[data-type="conv"]').length;
100
+ const filters = 32 * (convCount + 1);
101
+ nodeName = `Conv2D ${convCount + 1}`;
102
+ dimensions = `${filters} × 26 × 26`;
103
+ break;
104
+ case 'pool':
105
+ const poolCount = document.querySelectorAll('.canvas-node[data-type="pool"]').length;
106
+ nodeName = `MaxPool ${poolCount + 1}`;
107
+ dimensions = '32 × 13 × 13';
108
+ break;
109
+ default:
110
+ nodeName = 'Neural Node';
111
+ dimensions = '64';
112
+ }
113
+
114
+ canvasNode.innerHTML = `
115
+ <div class="node-title">${nodeName}</div>
116
+ <div class="node-id">${layerId}</div>
117
+ <div class="node-dimensions">${dimensions}</div>
118
+ <div class="node-port port-in"></div>
119
+ <div class="node-port port-out"></div>
120
+ <div class="node-controls">
121
+ <button class="node-edit-btn" title="Edit layer parameters"><i class="icon">⚙️</i></button>
122
+ <button class="node-delete-btn" title="Delete layer"><i class="icon">🗑️</i></button>
123
+ </div>
124
+ `;
125
+
126
+ // Store dimensions for hover display
127
+ canvasNode.setAttribute('data-dimensions', dimensions);
128
+ canvasNode.setAttribute('data-name', nodeName);
129
+
130
+ // Add to network layers
131
+ const layerInfo = {
132
+ id: layerId,
133
+ type: nodeType,
134
+ name: nodeName,
135
+ dimensions: dimensions,
136
+ position: { x, y }
137
+ };
138
+
139
+ networkLayers.layers.push(layerInfo);
140
+
141
+ // Add to canvas
142
+ canvas.appendChild(canvasNode);
143
+
144
+ // Add events for moving nodes on the canvas
145
+ canvasNode.addEventListener('mousedown', startDrag);
146
+
147
+ // Connection handling
148
+ const portIn = canvasNode.querySelector('.port-in');
149
+ const portOut = canvasNode.querySelector('.port-out');
150
+
151
+ portOut.addEventListener('mousedown', (e) => {
152
+ e.stopPropagation();
153
+ startConnection(canvasNode, e);
154
+ });
155
+
156
+ portIn.addEventListener('mouseup', (e) => {
157
+ e.stopPropagation();
158
+ endConnection(canvasNode);
159
+ });
160
+
161
+ // Button event listeners
162
+ const editBtn = canvasNode.querySelector('.node-edit-btn');
163
+ if (editBtn) {
164
+ editBtn.addEventListener('click', (e) => {
165
+ e.stopPropagation();
166
+ openLayerEditor(canvasNode);
167
+ });
168
+ }
169
+
170
+ const deleteBtn = canvasNode.querySelector('.node-delete-btn');
171
+ if (deleteBtn) {
172
+ deleteBtn.addEventListener('click', (e) => {
173
+ e.stopPropagation();
174
+ deleteNode(canvasNode);
175
+ });
176
+ }
177
+
178
+ // Update node parameters (for sequential model validation)
179
+ updateLayerConnectivity();
180
+ }
181
+ }
182
+
183
+ // Start dragging an existing node on the canvas
184
+ function startDrag(e) {
185
+ if (isConnecting) return;
186
+
187
+ // Only start drag if not clicking on buttons or ports
188
+ if (e.target.closest('.node-controls') || e.target.closest('.node-port')) {
189
+ return;
190
+ }
191
+
192
+ isDragging = true;
193
+ const target = e.target.closest('.canvas-node');
194
+ const rect = target.getBoundingClientRect();
195
+
196
+ // Calculate offset
197
+ offsetX = e.clientX - rect.left;
198
+ offsetY = e.clientY - rect.top;
199
+
200
+ document.addEventListener('mousemove', dragNode);
201
+ document.addEventListener('mouseup', stopDrag);
202
+
203
+ // Reference to the dragged node
204
+ draggedNode = target;
205
+
206
+ // Make the dragged node appear on top
207
+ draggedNode.style.zIndex = "100";
208
+
209
+ // Add dragging class for visual feedback
210
+ draggedNode.classList.add('dragging');
211
+
212
+ // Prevent default behavior
213
+ e.preventDefault();
214
+ }
215
+
216
+ // Drag node on the canvas
217
+ function dragNode(e) {
218
+ if (!isDragging) return;
219
+
220
+ const canvasRect = canvas.getBoundingClientRect();
221
+ let x = e.clientX - canvasRect.left - offsetX;
222
+ let y = e.clientY - canvasRect.top - offsetY;
223
+
224
+ // Constrain to canvas
225
+ x = Math.max(0, Math.min(canvasRect.width - draggedNode.offsetWidth, x));
226
+ y = Math.max(0, Math.min(canvasRect.height - draggedNode.offsetHeight, y));
227
+
228
+ draggedNode.style.left = `${x}px`;
229
+ draggedNode.style.top = `${y}px`;
230
+
231
+ // Update node position in network layers
232
+ const nodeId = draggedNode.getAttribute('data-id');
233
+ const layerIndex = networkLayers.layers.findIndex(layer => layer.id === nodeId);
234
+ if (layerIndex !== -1) {
235
+ networkLayers.layers[layerIndex].position = { x, y };
236
+ }
237
+
238
+ // Update connected lines if any
239
+ updateConnections();
240
+ }
241
+
242
+ // Stop dragging
243
+ function stopDrag() {
244
+ if (!isDragging) return;
245
+
246
+ isDragging = false;
247
+ document.removeEventListener('mousemove', dragNode);
248
+ document.removeEventListener('mouseup', stopDrag);
249
+
250
+ // Reset z-index and remove dragging class
251
+ if (draggedNode) {
252
+ draggedNode.style.zIndex = "10";
253
+ draggedNode.classList.remove('dragging');
254
+
255
+ // Trigger connections update one more time
256
+ updateConnections();
257
+ }
258
+ }
259
+
260
+ // Start creating a connection between nodes
261
+ function startConnection(node, e) {
262
+ isConnecting = true;
263
+ startNode = node;
264
+
265
+ // Create a temporary line
266
+ connectionLine = document.createElement('div');
267
+ connectionLine.className = 'connection temp-connection';
268
+
269
+ // Get start position (center of the port)
270
+ const portOut = node.querySelector('.port-out');
271
+ const portRect = portOut.getBoundingClientRect();
272
+ const canvasRect = canvas.getBoundingClientRect();
273
+
274
+ const startX = portRect.left + portRect.width / 2 - canvasRect.left;
275
+ const startY = portRect.top + portRect.height / 2 - canvasRect.top;
276
+
277
+ // Position the line
278
+ connectionLine.style.left = `${startX}px`;
279
+ connectionLine.style.top = `${startY}px`;
280
+ connectionLine.style.width = '0px';
281
+ connectionLine.style.transform = 'rotate(0deg)';
282
+
283
+ // Add active class to the starting port
284
+ portOut.classList.add('active-port');
285
+
286
+ // Highlight valid target ports
287
+ highlightValidConnectionTargets(node);
288
+
289
+ canvas.appendChild(connectionLine);
290
+
291
+ // Add event listeners for drawing the line
292
+ document.addEventListener('mousemove', drawConnection);
293
+ document.addEventListener('mouseup', cancelConnection);
294
+
295
+ e.preventDefault();
296
+ }
297
+
298
+ // Highlight valid targets for connection
299
+ function highlightValidConnectionTargets(sourceNode) {
300
+ const sourceType = sourceNode.getAttribute('data-type');
301
+ const sourceId = sourceNode.getAttribute('data-id');
302
+
303
+ document.querySelectorAll('.canvas-node').forEach(node => {
304
+ if (node !== sourceNode) {
305
+ const nodeType = node.getAttribute('data-type');
306
+ const nodeId = node.getAttribute('data-id');
307
+ const isValidTarget = isValidConnection(sourceType, nodeType, sourceId, nodeId);
308
+
309
+ const portIn = node.querySelector('.port-in');
310
+ if (isValidTarget) {
311
+ portIn.classList.add('valid-target');
312
+ } else {
313
+ portIn.classList.add('invalid-target');
314
+ }
315
+ }
316
+ });
317
+ }
318
+
319
+ // Remove highlights from all ports
320
+ function removePortHighlights() {
321
+ document.querySelectorAll('.port-in, .port-out').forEach(port => {
322
+ port.classList.remove('active-port', 'valid-target', 'invalid-target');
323
+ });
324
+ }
325
+
326
+ // Check if a connection between two node types is valid
327
+ function isValidConnection(sourceType, targetType, sourceId, targetId) {
328
+ // Basic hierarchy validation
329
+ if (sourceType === 'output' || targetType === 'input') {
330
+ return false; // Output can't have outgoing connections, Input can't have incoming
331
+ }
332
+
333
+ // Prevent cycles
334
+ const existingConnection = networkLayers.connections.find(
335
+ conn => conn.target === sourceId && conn.source === targetId
336
+ );
337
+ if (existingConnection) {
338
+ return false;
339
+ }
340
+
341
+ // Specific connection rules
342
+ switch(sourceType) {
343
+ case 'input':
344
+ return ['hidden', 'conv'].includes(targetType);
345
+ case 'conv':
346
+ return ['conv', 'pool', 'hidden'].includes(targetType);
347
+ case 'pool':
348
+ return ['conv', 'hidden'].includes(targetType);
349
+ case 'hidden':
350
+ return ['hidden', 'output'].includes(targetType);
351
+ default:
352
+ return false;
353
+ }
354
+ }
355
+
356
+ // Draw the connection line as mouse moves
357
+ function drawConnection(e) {
358
+ if (!isConnecting || !connectionLine) return;
359
+
360
+ const canvasRect = canvas.getBoundingClientRect();
361
+ const portOut = startNode.querySelector('.port-out');
362
+ const portRect = portOut.getBoundingClientRect();
363
+
364
+ // Calculate start and end points
365
+ const startX = portRect.left + portRect.width / 2 - canvasRect.left;
366
+ const startY = portRect.top + portRect.height / 2 - canvasRect.top;
367
+ const endX = e.clientX - canvasRect.left;
368
+ const endY = e.clientY - canvasRect.top;
369
+
370
+ // Calculate length and angle
371
+ const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
372
+ const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI;
373
+
374
+ // Update line
375
+ connectionLine.style.width = `${length}px`;
376
+ connectionLine.style.transform = `rotate(${angle}deg)`;
377
+
378
+ // Highlight the port under cursor
379
+ document.querySelectorAll('.canvas-node').forEach(node => {
380
+ if (node !== startNode) {
381
+ const nodeRect = node.getBoundingClientRect();
382
+ const portIn = node.querySelector('.port-in');
383
+ const portInRect = portIn.getBoundingClientRect();
384
+
385
+ // Check if mouse is over the input port
386
+ if (e.clientX >= portInRect.left && e.clientX <= portInRect.right &&
387
+ e.clientY >= portInRect.top && e.clientY <= portInRect.bottom) {
388
+ portIn.classList.add('port-hover');
389
+ } else {
390
+ portIn.classList.remove('port-hover');
391
+ }
392
+ }
393
+ });
394
+ }
395
+
396
+ // Cancel connection creation
397
+ function cancelConnection(e) {
398
+ if (!isConnecting) return;
399
+
400
+ // Find if we're over a valid input port
401
+ let targetNode = null;
402
+ document.querySelectorAll('.canvas-node').forEach(node => {
403
+ if (node !== startNode) {
404
+ const portIn = node.querySelector('.port-in');
405
+ const portRect = portIn.getBoundingClientRect();
406
+
407
+ if (e.clientX >= portRect.left && e.clientX <= portRect.right &&
408
+ e.clientY >= portRect.top && e.clientY <= portRect.bottom) {
409
+
410
+ // Check if this would be a valid connection
411
+ const sourceType = startNode.getAttribute('data-type');
412
+ const targetType = node.getAttribute('data-type');
413
+ const sourceId = startNode.getAttribute('data-id');
414
+ const targetId = node.getAttribute('data-id');
415
+
416
+ if (isValidConnection(sourceType, targetType, sourceId, targetId)) {
417
+ targetNode = node;
418
+ }
419
+ }
420
+ }
421
+ });
422
+
423
+ // If we found a valid target, create the connection
424
+ if (targetNode) {
425
+ endConnection(targetNode);
426
+ } else {
427
+ // Otherwise, remove the temporary line
428
+ if (connectionLine && connectionLine.parentNode) {
429
+ connectionLine.parentNode.removeChild(connectionLine);
430
+ }
431
+ }
432
+
433
+ // Remove all port highlights
434
+ removePortHighlights();
435
+ document.querySelectorAll('.port-hover').forEach(port => {
436
+ port.classList.remove('port-hover');
437
+ });
438
+
439
+ // Reset variables
440
+ isConnecting = false;
441
+ startNode = null;
442
+ connectionLine = null;
443
+
444
+ // Remove event listeners
445
+ document.removeEventListener('mousemove', drawConnection);
446
+ document.removeEventListener('mouseup', cancelConnection);
447
+ }
448
+
449
+ // End creating a connection
450
+ function endConnection(targetNode) {
451
+ if (!isConnecting) return;
452
+
453
+ // Check if a valid node port was targeted
454
+ if (targetNode && targetNode.classList && targetNode.classList.contains('canvas-node')) {
455
+ // Get node IDs for the connection
456
+ const sourceId = startNode.getAttribute('data-id');
457
+ const targetId = targetNode.getAttribute('data-id');
458
+
459
+ // Check if connection already exists
460
+ const exists = networkLayers.connections.some(conn =>
461
+ conn.source === sourceId && conn.target === targetId
462
+ );
463
+
464
+ if (!exists) {
465
+ // Create permanent connection
466
+ const connection = connectionLine.cloneNode(true);
467
+ connection.classList.remove('temp-connection');
468
+ connection.setAttribute('data-source', sourceId);
469
+ connection.setAttribute('data-target', targetId);
470
+ canvas.appendChild(connection);
471
+
472
+ // Add to connections array
473
+ networkLayers.connections.push({
474
+ source: sourceId,
475
+ target: targetId,
476
+ sourceType: startNode.getAttribute('data-type'),
477
+ targetType: targetNode.getAttribute('data-type')
478
+ });
479
+
480
+ // Update parameters for model consistency
481
+ updateLayerConnectivity();
482
+
483
+ console.log(`Connected ${sourceId} to ${targetId}`);
484
+ }
485
+ }
486
+
487
+ // Remove temporary line
488
+ if (connectionLine && connectionLine.parentNode) {
489
+ connectionLine.parentNode.removeChild(connectionLine);
490
+ }
491
+
492
+ // Remove port highlights
493
+ removePortHighlights();
494
+
495
+ // Reset variables
496
+ isConnecting = false;
497
+ startNode = null;
498
+ connectionLine = null;
499
+
500
+ // Remove event listeners
501
+ document.removeEventListener('mousemove', drawConnection);
502
+ document.removeEventListener('mouseup', cancelConnection);
503
+ }
504
+
505
+ // Update layer connectivity to ensure model consistency
506
+ function updateLayerConnectivity() {
507
+ // This is where we'd propagate input/output shapes between connected layers
508
+ // For now we'll just highlight connected nodes
509
+
510
+ // Reset all nodes
511
+ document.querySelectorAll('.canvas-node').forEach(node => {
512
+ node.classList.remove('connected-node');
513
+ });
514
+
515
+ // Mark all nodes that have connections
516
+ const connectedNodeIds = new Set();
517
+ networkLayers.connections.forEach(conn => {
518
+ connectedNodeIds.add(conn.source);
519
+ connectedNodeIds.add(conn.target);
520
+ });
521
+
522
+ connectedNodeIds.forEach(id => {
523
+ const node = document.querySelector(`.canvas-node[data-id="${id}"]`);
524
+ if (node) {
525
+ node.classList.add('connected-node');
526
+ }
527
+ });
528
+
529
+ // Trigger a custom event that the main script can listen for
530
+ const event = new CustomEvent('networkUpdated', { detail: networkLayers });
531
+ document.dispatchEvent(event);
532
+ }
533
+
534
+ // Delete a node and its connections
535
+ function deleteNode(node) {
536
+ if (!node) return;
537
+
538
+ const nodeId = node.getAttribute('data-id');
539
+
540
+ // Remove all connections to/from this node
541
+ document.querySelectorAll(`.connection[data-source="${nodeId}"], .connection[data-target="${nodeId}"]`).forEach(conn => {
542
+ conn.parentNode.removeChild(conn);
543
+ });
544
+
545
+ // Remove from network layers
546
+ networkLayers.layers = networkLayers.layers.filter(layer => layer.id !== nodeId);
547
+ networkLayers.connections = networkLayers.connections.filter(conn =>
548
+ conn.source !== nodeId && conn.target !== nodeId
549
+ );
550
+
551
+ // Remove the node
552
+ node.parentNode.removeChild(node);
553
+
554
+ // Update layer connectivity
555
+ updateLayerConnectivity();
556
+ }
557
+
558
+ // Open layer editor modal
559
+ function openLayerEditor(node) {
560
+ if (!node) return;
561
+
562
+ const nodeId = node.getAttribute('data-id');
563
+ const nodeType = node.getAttribute('data-type');
564
+ const nodeName = node.getAttribute('data-name');
565
+ const dimensions = node.getAttribute('data-dimensions');
566
+
567
+ // Trigger custom event
568
+ const event = new CustomEvent('openLayerEditor', {
569
+ detail: { id: nodeId, type: nodeType, name: nodeName, dimensions: dimensions }
570
+ });
571
+ document.dispatchEvent(event);
572
+ }
573
+
574
+ // Update connections when nodes are moved
575
+ function updateConnections() {
576
+ const connections = document.querySelectorAll('.connection');
577
+ connections.forEach(connection => {
578
+ const sourceId = connection.getAttribute('data-source');
579
+ const targetId = connection.getAttribute('data-target');
580
+
581
+ const sourceNode = document.querySelector(`.canvas-node[data-id="${sourceId}"]`);
582
+ const targetNode = document.querySelector(`.canvas-node[data-id="${targetId}"]`);
583
+
584
+ if (sourceNode && targetNode) {
585
+ const sourcePort = sourceNode.querySelector('.port-out');
586
+ const targetPort = targetNode.querySelector('.port-in');
587
+
588
+ if (sourcePort && targetPort) {
589
+ const sourceRect = sourcePort.getBoundingClientRect();
590
+ const targetRect = targetPort.getBoundingClientRect();
591
+ const canvasRect = canvas.getBoundingClientRect();
592
+
593
+ const startX = sourceRect.left + sourceRect.width / 2 - canvasRect.left;
594
+ const startY = sourceRect.top + sourceRect.height / 2 - canvasRect.top;
595
+ const endX = targetRect.left + targetRect.width / 2 - canvasRect.left;
596
+ const endY = targetRect.top + targetRect.height / 2 - canvasRect.top;
597
+
598
+ const length = Math.sqrt(Math.pow(endX - startX, 2) + Math.pow(endY - startY, 2));
599
+ const angle = Math.atan2(endY - startY, endX - startX) * 180 / Math.PI;
600
+
601
+ connection.style.left = `${startX}px`;
602
+ connection.style.top = `${startY}px`;
603
+ connection.style.width = `${length}px`;
604
+ connection.style.transform = `rotate(${angle}deg)`;
605
+ }
606
+ } else {
607
+ // If either node is missing, remove the connection
608
+ if (connection.parentNode) {
609
+ connection.parentNode.removeChild(connection);
610
+
611
+ // Remove from the connections array
612
+ const connIndex = networkLayers.connections.findIndex(conn =>
613
+ conn.source === sourceId && conn.target === targetId
614
+ );
615
+ if (connIndex !== -1) {
616
+ networkLayers.connections.splice(connIndex, 1);
617
+ }
618
+ }
619
+ }
620
+ });
621
+ }
622
+
623
+ // Get the current network architecture
624
+ function getNetworkArchitecture() {
625
+ return networkLayers;
626
+ }
627
+
628
+ // Clear all nodes from the canvas
629
+ function clearAllNodes() {
630
+ // Clear all nodes and connections
631
+ document.querySelectorAll('.canvas-node, .connection').forEach(el => {
632
+ el.parentNode.removeChild(el);
633
+ });
634
+
635
+ // Reset network layers
636
+ networkLayers = {
637
+ layers: [],
638
+ connections: []
639
+ };
640
+
641
+ // Reset layer counter
642
+ window.neuralNetwork.resetLayerCounter();
643
+
644
+ // Show the canvas hint
645
+ const canvasHint = document.querySelector('.canvas-hint');
646
+ if (canvasHint) {
647
+ canvasHint.style.display = 'block';
648
+ }
649
+
650
+ // Trigger network updated event
651
+ const event = new CustomEvent('networkUpdated', { detail: networkLayers });
652
+ document.dispatchEvent(event);
653
+ }
654
+
655
+ // Export functions
656
+ window.dragDrop = {
657
+ getNetworkArchitecture,
658
+ clearAllNodes,
659
+ updateConnections
660
+ };
661
+ }
js/main.js ADDED
@@ -0,0 +1,856 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Initialize the application when the DOM is fully loaded
2
+ document.addEventListener('DOMContentLoaded', () => {
3
+ console.log('Neural Network Playground Initialized');
4
+
5
+ // Initialize the canvas and tooltip
6
+ const canvas = document.getElementById('network-canvas');
7
+ const tooltip = document.createElement('div');
8
+ tooltip.className = 'canvas-tooltip';
9
+ tooltip.innerHTML = `
10
+ <div class="tooltip-header"></div>
11
+ <div class="tooltip-content"></div>
12
+ `;
13
+ document.body.appendChild(tooltip);
14
+
15
+ // Initialize drag and drop functionality
16
+ initializeDragAndDrop();
17
+
18
+ // Network configuration (from UI controls)
19
+ let networkConfig = {
20
+ learningRate: 0.01,
21
+ activation: 'relu',
22
+ batchSize: 32,
23
+ epochs: 10
24
+ };
25
+
26
+ // Initialize UI controls
27
+ setupUIControls();
28
+
29
+ // Layer editor modal
30
+ setupLayerEditor();
31
+
32
+ // Listen for network updates
33
+ document.addEventListener('networkUpdated', handleNetworkUpdate);
34
+
35
+ // Listen for layer editor events
36
+ document.addEventListener('openLayerEditor', handleOpenLayerEditor);
37
+
38
+ // Setup UI controls and event listeners
39
+ function setupUIControls() {
40
+ // Learning rate slider
41
+ const learningRateSlider = document.getElementById('learning-rate');
42
+ const learningRateValue = document.getElementById('learning-rate-value');
43
+
44
+ if (learningRateSlider && learningRateValue) {
45
+ learningRateSlider.value = networkConfig.learningRate;
46
+ learningRateValue.textContent = networkConfig.learningRate.toFixed(3);
47
+
48
+ learningRateSlider.addEventListener('input', (e) => {
49
+ networkConfig.learningRate = parseFloat(e.target.value);
50
+ learningRateValue.textContent = networkConfig.learningRate.toFixed(3);
51
+ });
52
+ }
53
+
54
+ // Activation function dropdown
55
+ const activationSelect = document.getElementById('activation');
56
+ if (activationSelect) {
57
+ activationSelect.value = networkConfig.activation;
58
+
59
+ activationSelect.addEventListener('change', (e) => {
60
+ networkConfig.activation = e.target.value;
61
+ updateActivationFunctionGraph(networkConfig.activation);
62
+ });
63
+ }
64
+
65
+ // Initialize activation function graph
66
+ updateActivationFunctionGraph(networkConfig.activation);
67
+
68
+ // Sample data event handlers
69
+ const sampleItems = document.querySelectorAll('.sample-item');
70
+ sampleItems.forEach(item => {
71
+ item.addEventListener('click', () => {
72
+ const sampleId = item.getAttribute('data-sample');
73
+ handleSampleSelection(sampleId);
74
+ });
75
+ });
76
+
77
+ // Button event listeners
78
+ const runButton = document.getElementById('run-network');
79
+ if (runButton) {
80
+ runButton.addEventListener('click', runNetwork);
81
+ }
82
+
83
+ const clearButton = document.getElementById('clear-canvas');
84
+ if (clearButton) {
85
+ clearButton.addEventListener('click', clearCanvas);
86
+ }
87
+
88
+ // Modal handlers
89
+ setupModals();
90
+ }
91
+
92
+ // Setup modal handlers
93
+ function setupModals() {
94
+ const aboutModal = document.getElementById('about-modal');
95
+ const aboutLink = document.getElementById('about-link');
96
+
97
+ if (aboutLink && aboutModal) {
98
+ aboutLink.addEventListener('click', (e) => {
99
+ e.preventDefault();
100
+ openModal(aboutModal);
101
+ });
102
+
103
+ const closeButtons = aboutModal.querySelectorAll('.close-modal');
104
+ closeButtons.forEach(btn => {
105
+ btn.addEventListener('click', () => {
106
+ closeModal(aboutModal);
107
+ });
108
+ });
109
+
110
+ // Close modal when clicking outside
111
+ aboutModal.addEventListener('click', (e) => {
112
+ if (e.target === aboutModal) {
113
+ closeModal(aboutModal);
114
+ }
115
+ });
116
+ }
117
+ }
118
+
119
+ // Setup layer editor modal
120
+ function setupLayerEditor() {
121
+ const layerEditorModal = document.getElementById('layer-editor-modal');
122
+
123
+ if (layerEditorModal) {
124
+ const closeButtons = layerEditorModal.querySelectorAll('.close-modal');
125
+ closeButtons.forEach(btn => {
126
+ btn.addEventListener('click', () => {
127
+ closeModal(layerEditorModal);
128
+ });
129
+ });
130
+
131
+ // Close modal when clicking outside
132
+ layerEditorModal.addEventListener('click', (e) => {
133
+ if (e.target === layerEditorModal) {
134
+ closeModal(layerEditorModal);
135
+ }
136
+ });
137
+
138
+ // Save button
139
+ const saveButton = layerEditorModal.querySelector('.save-layer-btn');
140
+ if (saveButton) {
141
+ saveButton.addEventListener('click', saveLayerConfig);
142
+ }
143
+ }
144
+ }
145
+
146
+ // Open modal
147
+ function openModal(modal) {
148
+ if (modal) {
149
+ modal.style.display = 'flex';
150
+ }
151
+ }
152
+
153
+ // Close modal
154
+ function closeModal(modal) {
155
+ if (modal) {
156
+ modal.style.display = 'none';
157
+ }
158
+ }
159
+
160
+ // Handle network updates
161
+ function handleNetworkUpdate(e) {
162
+ const networkLayers = e.detail;
163
+ console.log('Network updated:', networkLayers);
164
+
165
+ // Update the properties panel
166
+ updatePropertiesPanel(networkLayers);
167
+ }
168
+
169
+ // Update properties panel with network information
170
+ function updatePropertiesPanel(networkLayers) {
171
+ const propertiesPanel = document.querySelector('.props-panel');
172
+ if (!propertiesPanel) return;
173
+
174
+ // Find the properties content section
175
+ const propsContent = propertiesPanel.querySelector('.props-content');
176
+ if (!propsContent) return;
177
+
178
+ // Basic network stats
179
+ const layerCount = networkLayers.layers.length;
180
+ const connectionCount = networkLayers.connections.length;
181
+
182
+ let layerTypeCounts = {};
183
+ networkLayers.layers.forEach(layer => {
184
+ layerTypeCounts[layer.type] = (layerTypeCounts[layer.type] || 0) + 1;
185
+ });
186
+
187
+ // Check network validity
188
+ const validationResult = window.neuralNetwork.validateNetwork(
189
+ networkLayers.layers,
190
+ networkLayers.connections
191
+ );
192
+
193
+ // Update network architecture section
194
+ let networkArchitectureHTML = `
195
+ <div class="props-section">
196
+ <div class="props-heading">
197
+ <i class="icon">🔍</i> Network Architecture
198
+ </div>
199
+ <div class="props-row">
200
+ <div class="props-key">Total Layers</div>
201
+ <div class="props-value">${layerCount}</div>
202
+ </div>
203
+ <div class="props-row">
204
+ <div class="props-key">Connections</div>
205
+ <div class="props-value">${connectionCount}</div>
206
+ </div>
207
+ `;
208
+
209
+ // Add layer type counts
210
+ Object.entries(layerTypeCounts).forEach(([type, count]) => {
211
+ networkArchitectureHTML += `
212
+ <div class="props-row">
213
+ <div class="props-key">${type.charAt(0).toUpperCase() + type.slice(1)} Layers</div>
214
+ <div class="props-value">${count}</div>
215
+ </div>
216
+ `;
217
+ });
218
+
219
+ // Add validation status
220
+ networkArchitectureHTML += `
221
+ <div class="props-row">
222
+ <div class="props-key">Validity</div>
223
+ <div class="props-value" style="color: ${validationResult.valid ? 'var(--secondary-color)' : 'var(--warning-color)'}">
224
+ ${validationResult.valid ? 'Valid' : 'Invalid'}
225
+ </div>
226
+ </div>
227
+ `;
228
+
229
+ // If there are validation errors, show them
230
+ if (!validationResult.valid && validationResult.errors.length > 0) {
231
+ networkArchitectureHTML += `
232
+ <div class="props-row">
233
+ <div class="props-key">Errors</div>
234
+ <div class="props-value" style="color: var(--warning-color)">
235
+ ${validationResult.errors.join('<br>')}
236
+ </div>
237
+ </div>
238
+ `;
239
+ }
240
+
241
+ networkArchitectureHTML += `</div>`;
242
+
243
+ // Calculate total parameters if we have layers
244
+ let totalParameters = 0;
245
+ let totalFlops = 0;
246
+ let totalMemory = 0;
247
+
248
+ if (layerCount > 0) {
249
+ // Calculate model stats
250
+ const modelStatsHTML = `
251
+ <div class="props-section">
252
+ <div class="props-heading">
253
+ <i class="icon">📊</i> Model Statistics
254
+ </div>
255
+ <div class="props-row">
256
+ <div class="props-key">Parameters</div>
257
+ <div class="props-value">${formatNumber(totalParameters)}</div>
258
+ </div>
259
+ <div class="props-row">
260
+ <div class="props-key">FLOPs</div>
261
+ <div class="props-value">${formatNumber(totalFlops)}</div>
262
+ </div>
263
+ <div class="props-row">
264
+ <div class="props-key">Memory</div>
265
+ <div class="props-value">${formatMemorySize(totalMemory)}</div>
266
+ </div>
267
+ </div>
268
+ `;
269
+
270
+ // Update the properties content
271
+ propsContent.innerHTML = networkArchitectureHTML + modelStatsHTML;
272
+ } else {
273
+ // Just show basic architecture info
274
+ propsContent.innerHTML = networkArchitectureHTML;
275
+ }
276
+ }
277
+
278
+ // Format number with K, M, B suffixes
279
+ function formatNumber(num) {
280
+ if (num === 0) return '0';
281
+ if (!num) return 'N/A';
282
+
283
+ if (num >= 1e9) return (num / 1e9).toFixed(2) + 'B';
284
+ if (num >= 1e6) return (num / 1e6).toFixed(2) + 'M';
285
+ if (num >= 1e3) return (num / 1e3).toFixed(2) + 'K';
286
+ return num.toString();
287
+ }
288
+
289
+ // Format memory size in bytes to KB, MB, GB
290
+ function formatMemorySize(bytes) {
291
+ if (bytes === 0) return '0 Bytes';
292
+ if (!bytes) return 'N/A';
293
+
294
+ const k = 1024;
295
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
296
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
297
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
298
+ }
299
+
300
+ // Handle opening the layer editor
301
+ function handleOpenLayerEditor(e) {
302
+ const layerDetails = e.detail;
303
+ console.log('Opening layer editor for:', layerDetails);
304
+
305
+ const layerEditorModal = document.getElementById('layer-editor-modal');
306
+ if (!layerEditorModal) return;
307
+
308
+ // Get the form and populate it
309
+ const layerForm = layerEditorModal.querySelector('.layer-form');
310
+ if (!layerForm) return;
311
+
312
+ // Set the layer ID in a data attribute for retrieval when saving
313
+ layerForm.setAttribute('data-layer-id', layerDetails.id);
314
+ layerForm.setAttribute('data-layer-type', layerDetails.type);
315
+
316
+ // Set modal title
317
+ const modalTitle = layerEditorModal.querySelector('.modal-title');
318
+ if (modalTitle) {
319
+ modalTitle.textContent = `Edit ${layerDetails.name}`;
320
+ }
321
+
322
+ // Get layer config template
323
+ const layerConfig = window.neuralNetwork.nodeConfigTemplates[layerDetails.type];
324
+
325
+ // Generate form fields based on layer type
326
+ layerForm.innerHTML = '';
327
+
328
+ // Add common fields
329
+ layerForm.innerHTML += `
330
+ <div class="form-group">
331
+ <label for="layer-name">Layer Name</label>
332
+ <input type="text" id="layer-name" value="${layerDetails.name}">
333
+ </div>
334
+ `;
335
+
336
+ // Add type-specific fields
337
+ switch (layerDetails.type) {
338
+ case 'input':
339
+ layerForm.innerHTML += `
340
+ <div class="form-group">
341
+ <label for="input-shape">Input Shape</label>
342
+ <input type="text" id="input-shape" value="${layerConfig.shape.join(' × ')}">
343
+ </div>
344
+ <div class="form-group">
345
+ <label for="batch-size">Batch Size</label>
346
+ <input type="number" id="batch-size" value="${layerConfig.batchSize}">
347
+ </div>
348
+ `;
349
+ break;
350
+
351
+ case 'hidden':
352
+ layerForm.innerHTML += `
353
+ <div class="form-group">
354
+ <label for="units">Units</label>
355
+ <input type="number" id="units" value="${layerConfig.units}">
356
+ </div>
357
+ <div class="form-group">
358
+ <label for="activation">Activation</label>
359
+ <select id="activation">
360
+ <option value="relu" ${layerConfig.activation === 'relu' ? 'selected' : ''}>ReLU</option>
361
+ <option value="sigmoid" ${layerConfig.activation === 'sigmoid' ? 'selected' : ''}>Sigmoid</option>
362
+ <option value="tanh" ${layerConfig.activation === 'tanh' ? 'selected' : ''}>Tanh</option>
363
+ <option value="linear" ${layerConfig.activation === 'linear' ? 'selected' : ''}>Linear</option>
364
+ </select>
365
+ </div>
366
+ <div class="form-group">
367
+ <label for="use-bias">Use Bias</label>
368
+ <select id="use-bias">
369
+ <option value="true" ${layerConfig.useBias ? 'selected' : ''}>Yes</option>
370
+ <option value="false" ${!layerConfig.useBias ? 'selected' : ''}>No</option>
371
+ </select>
372
+ </div>
373
+ <div class="form-group">
374
+ <label for="dropout-rate">Dropout Rate</label>
375
+ <input type="number" id="dropout-rate" min="0" max="0.9" step="0.1" value="${layerConfig.dropoutRate}">
376
+ </div>
377
+ `;
378
+ break;
379
+
380
+ case 'output':
381
+ layerForm.innerHTML += `
382
+ <div class="form-group">
383
+ <label for="units">Units</label>
384
+ <input type="number" id="units" value="${layerConfig.units}">
385
+ </div>
386
+ <div class="form-group">
387
+ <label for="activation">Activation</label>
388
+ <select id="activation">
389
+ <option value="softmax" ${layerConfig.activation === 'softmax' ? 'selected' : ''}>Softmax</option>
390
+ <option value="sigmoid" ${layerConfig.activation === 'sigmoid' ? 'selected' : ''}>Sigmoid</option>
391
+ <option value="linear" ${layerConfig.activation === 'linear' ? 'selected' : ''}>Linear</option>
392
+ </select>
393
+ </div>
394
+ `;
395
+ break;
396
+
397
+ case 'conv':
398
+ layerForm.innerHTML += `
399
+ <div class="form-group">
400
+ <label for="filters">Filters</label>
401
+ <input type="number" id="filters" value="${layerConfig.filters}">
402
+ </div>
403
+ <div class="form-group">
404
+ <label for="kernel-size">Kernel Size</label>
405
+ <input type="text" id="kernel-size" value="${layerConfig.kernelSize.join(' × ')}">
406
+ </div>
407
+ <div class="form-group">
408
+ <label for="strides">Strides</label>
409
+ <input type="text" id="strides" value="${layerConfig.strides.join(' × ')}">
410
+ </div>
411
+ <div class="form-group">
412
+ <label for="padding">Padding</label>
413
+ <select id="padding">
414
+ <option value="valid" ${layerConfig.padding === 'valid' ? 'selected' : ''}>Valid</option>
415
+ <option value="same" ${layerConfig.padding === 'same' ? 'selected' : ''}>Same</option>
416
+ </select>
417
+ </div>
418
+ <div class="form-group">
419
+ <label for="activation">Activation</label>
420
+ <select id="activation">
421
+ <option value="relu" ${layerConfig.activation === 'relu' ? 'selected' : ''}>ReLU</option>
422
+ <option value="sigmoid" ${layerConfig.activation === 'sigmoid' ? 'selected' : ''}>Sigmoid</option>
423
+ <option value="tanh" ${layerConfig.activation === 'tanh' ? 'selected' : ''}>Tanh</option>
424
+ <option value="linear" ${layerConfig.activation === 'linear' ? 'selected' : ''}>Linear</option>
425
+ </select>
426
+ </div>
427
+ `;
428
+ break;
429
+
430
+ case 'pool':
431
+ layerForm.innerHTML += `
432
+ <div class="form-group">
433
+ <label for="pool-size">Pool Size</label>
434
+ <input type="text" id="pool-size" value="${layerConfig.poolSize.join(' × ')}">
435
+ </div>
436
+ <div class="form-group">
437
+ <label for="strides">Strides</label>
438
+ <input type="text" id="strides" value="${layerConfig.strides.join(' × ')}">
439
+ </div>
440
+ <div class="form-group">
441
+ <label for="padding">Padding</label>
442
+ <select id="padding">
443
+ <option value="valid" ${layerConfig.padding === 'valid' ? 'selected' : ''}>Valid</option>
444
+ <option value="same" ${layerConfig.padding === 'same' ? 'selected' : ''}>Same</option>
445
+ </select>
446
+ </div>
447
+ `;
448
+ break;
449
+ }
450
+
451
+ // Add save button
452
+ layerForm.innerHTML += `
453
+ <div class="form-group form-grid-full">
454
+ <button type="button" class="btn btn-primary save-layer-btn">Save Changes</button>
455
+ </div>
456
+ `;
457
+
458
+ // Show the modal
459
+ openModal(layerEditorModal);
460
+ }
461
+
462
+ // Save layer configuration
463
+ function saveLayerConfig() {
464
+ const layerEditorModal = document.getElementById('layer-editor-modal');
465
+ if (!layerEditorModal) return;
466
+
467
+ const layerForm = layerEditorModal.querySelector('.layer-form');
468
+ if (!layerForm) return;
469
+
470
+ const layerId = layerForm.getAttribute('data-layer-id');
471
+ const layerType = layerForm.getAttribute('data-layer-type');
472
+
473
+ // Get node on canvas
474
+ const node = document.querySelector(`.canvas-node[data-id="${layerId}"]`);
475
+ if (!node) return;
476
+
477
+ // Get form values
478
+ const name = document.getElementById('layer-name').value;
479
+
480
+ // Update node title
481
+ const nodeTitle = node.querySelector('.node-title');
482
+ if (nodeTitle) {
483
+ nodeTitle.textContent = name;
484
+ }
485
+
486
+ // Update node data attribute
487
+ node.setAttribute('data-name', name);
488
+
489
+ // Update dimensions based on layer type
490
+ let dimensions = '';
491
+ switch (layerType) {
492
+ case 'input':
493
+ const inputShape = document.getElementById('input-shape').value;
494
+ dimensions = inputShape;
495
+ break;
496
+
497
+ case 'hidden':
498
+ case 'output':
499
+ const units = document.getElementById('units').value;
500
+ dimensions = units;
501
+ break;
502
+
503
+ case 'conv':
504
+ const filters = document.getElementById('filters').value;
505
+ dimensions = `${filters} × 26 × 26`; // Simplified
506
+ break;
507
+
508
+ case 'pool':
509
+ dimensions = '32 × 13 × 13'; // Simplified
510
+ break;
511
+ }
512
+
513
+ // Update node dimensions
514
+ const nodeDimensions = node.querySelector('.node-dimensions');
515
+ if (nodeDimensions) {
516
+ nodeDimensions.textContent = dimensions;
517
+ }
518
+
519
+ // Update node data attribute
520
+ node.setAttribute('data-dimensions', dimensions);
521
+
522
+ // Update network layers in drag-drop module
523
+ const networkLayers = window.dragDrop.getNetworkArchitecture();
524
+ const layerIndex = networkLayers.layers.findIndex(layer => layer.id === layerId);
525
+
526
+ if (layerIndex !== -1) {
527
+ networkLayers.layers[layerIndex].name = name;
528
+ networkLayers.layers[layerIndex].dimensions = dimensions;
529
+ }
530
+
531
+ // Trigger network updated event
532
+ const event = new CustomEvent('networkUpdated', { detail: networkLayers });
533
+ document.dispatchEvent(event);
534
+
535
+ // Close the modal
536
+ closeModal(layerEditorModal);
537
+ }
538
+
539
+ // Handle sample selection
540
+ function handleSampleSelection(sampleId) {
541
+ // Set active sample
542
+ document.querySelectorAll('.sample-item').forEach(item => {
543
+ item.classList.remove('active');
544
+ if (item.getAttribute('data-sample') === sampleId) {
545
+ item.classList.add('active');
546
+ }
547
+ });
548
+
549
+ // Get sample data
550
+ const sampleData = window.neuralNetwork.sampleData[sampleId];
551
+ if (!sampleData) return;
552
+
553
+ console.log(`Selected sample: ${sampleData.name}`);
554
+
555
+ // Update properties panel to show sample info
556
+ const propertiesPanel = document.querySelector('.props-panel');
557
+ if (!propertiesPanel) return;
558
+
559
+ const propsContent = propertiesPanel.querySelector('.props-content');
560
+ if (!propsContent) return;
561
+
562
+ propsContent.innerHTML = `
563
+ <div class="props-section">
564
+ <div class="props-heading">
565
+ <i class="icon">📊</i> ${sampleData.name}
566
+ </div>
567
+ <div class="props-row">
568
+ <div class="props-key">Input Shape</div>
569
+ <div class="props-value">${sampleData.inputShape.join(' × ')}</div>
570
+ </div>
571
+ <div class="props-row">
572
+ <div class="props-key">Classes</div>
573
+ <div class="props-value">${sampleData.numClasses}</div>
574
+ </div>
575
+ <div class="props-row">
576
+ <div class="props-key">Training Samples</div>
577
+ <div class="props-value">${sampleData.trainSamples.toLocaleString()}</div>
578
+ </div>
579
+ <div class="props-row">
580
+ <div class="props-key">Test Samples</div>
581
+ <div class="props-value">${sampleData.testSamples.toLocaleString()}</div>
582
+ </div>
583
+ <div class="props-row">
584
+ <div class="props-key">Description</div>
585
+ <div class="props-value">${sampleData.description}</div>
586
+ </div>
587
+ </div>
588
+
589
+ <div class="props-section">
590
+ <p class="hint-text">Click "Run Network" to train on this dataset</p>
591
+ </div>
592
+ `;
593
+ }
594
+
595
+ // Function to run the neural network simulation
596
+ function runNetwork() {
597
+ console.log('Running neural network simulation with config:', networkConfig);
598
+
599
+ // Get the current network architecture
600
+ const networkLayers = window.dragDrop.getNetworkArchitecture();
601
+
602
+ // Check if we have a valid network
603
+ if (networkLayers.layers.length === 0) {
604
+ alert('Please add some nodes to the network first!');
605
+ return;
606
+ }
607
+
608
+ // Validate the network
609
+ const validationResult = window.neuralNetwork.validateNetwork(
610
+ networkLayers.layers,
611
+ networkLayers.connections
612
+ );
613
+
614
+ if (!validationResult.valid) {
615
+ alert('Network is not valid: ' + validationResult.errors.join('\n'));
616
+ return;
617
+ }
618
+
619
+ // Add animation class to all nodes
620
+ document.querySelectorAll('.canvas-node').forEach(node => {
621
+ node.classList.add('highlight-pulse');
622
+ });
623
+
624
+ // Animate connections to show data flow
625
+ document.querySelectorAll('.connection').forEach((connection, index) => {
626
+ setTimeout(() => {
627
+ connection.style.background = 'linear-gradient(90deg, var(--primary-color), var(--accent-color))';
628
+
629
+ // Reset after animation
630
+ setTimeout(() => {
631
+ connection.style.background = '';
632
+ }, 800);
633
+ }, 300 * index);
634
+ });
635
+
636
+ // Simulate training
637
+ simulateTraining();
638
+
639
+ // Reset animations after completion
640
+ setTimeout(() => {
641
+ document.querySelectorAll('.canvas-node').forEach(node => {
642
+ node.classList.remove('highlight-pulse');
643
+ });
644
+ }, 3000);
645
+ }
646
+
647
+ // Simulate training progress
648
+ function simulateTraining() {
649
+ const progressBar = document.querySelector('.progress-bar');
650
+ const lossValue = document.getElementById('loss-value');
651
+ const accuracyValue = document.getElementById('accuracy-value');
652
+
653
+ if (!progressBar || !lossValue || !accuracyValue) return;
654
+
655
+ // Reset progress
656
+ progressBar.style.width = '0%';
657
+ lossValue.textContent = '2.3021';
658
+ accuracyValue.textContent = '0.12';
659
+
660
+ // Simulate progress over time
661
+ let progress = 0;
662
+ let loss = 2.3021;
663
+ let accuracy = 0.12;
664
+
665
+ const interval = setInterval(() => {
666
+ progress += 10;
667
+ loss *= 0.85; // Decrease loss over time
668
+ accuracy = Math.min(0.99, accuracy * 1.2); // Increase accuracy over time
669
+
670
+ progressBar.style.width = `${progress}%`;
671
+ lossValue.textContent = loss.toFixed(4);
672
+ accuracyValue.textContent = accuracy.toFixed(2);
673
+
674
+ if (progress >= 100) {
675
+ clearInterval(interval);
676
+ }
677
+ }, 300);
678
+ }
679
+
680
+ // Function to clear all nodes from the canvas
681
+ function clearCanvas() {
682
+ if (window.dragDrop && typeof window.dragDrop.clearAllNodes === 'function') {
683
+ window.dragDrop.clearAllNodes();
684
+ }
685
+
686
+ // Reset progress indicators
687
+ const progressBar = document.querySelector('.progress-bar');
688
+ const lossValue = document.getElementById('loss-value');
689
+ const accuracyValue = document.getElementById('accuracy-value');
690
+
691
+ if (progressBar) progressBar.style.width = '0%';
692
+ if (lossValue) lossValue.textContent = '-';
693
+ if (accuracyValue) accuracyValue.textContent = '-';
694
+ }
695
+
696
+ // Update activation function graph
697
+ function updateActivationFunctionGraph(activationType) {
698
+ const activationGraph = document.querySelector('.activation-function');
699
+ if (!activationGraph) return;
700
+
701
+ // Clear previous graph
702
+ let canvas = activationGraph.querySelector('canvas');
703
+ if (!canvas) {
704
+ canvas = document.createElement('canvas');
705
+ canvas.width = 200;
706
+ canvas.height = 100;
707
+ activationGraph.appendChild(canvas);
708
+ }
709
+
710
+ const ctx = canvas.getContext('2d');
711
+
712
+ // Clear canvas
713
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
714
+
715
+ // Set background
716
+ ctx.fillStyle = '#f8f9fa';
717
+ ctx.fillRect(0, 0, canvas.width, canvas.height);
718
+
719
+ // Draw axes
720
+ ctx.strokeStyle = '#ccc';
721
+ ctx.lineWidth = 1;
722
+ ctx.beginPath();
723
+ ctx.moveTo(0, canvas.height / 2);
724
+ ctx.lineTo(canvas.width, canvas.height / 2);
725
+ ctx.moveTo(canvas.width / 2, 0);
726
+ ctx.lineTo(canvas.width / 2, canvas.height);
727
+ ctx.stroke();
728
+
729
+ // Draw function
730
+ ctx.strokeStyle = 'var(--primary-color)';
731
+ ctx.lineWidth = 2;
732
+ ctx.beginPath();
733
+
734
+ switch(activationType) {
735
+ case 'relu':
736
+ ctx.moveTo(0, canvas.height / 2);
737
+ ctx.lineTo(canvas.width / 2, canvas.height / 2);
738
+ ctx.lineTo(canvas.width, 0);
739
+ break;
740
+
741
+ case 'sigmoid':
742
+ for (let x = 0; x < canvas.width; x++) {
743
+ const normalizedX = (x / canvas.width - 0.5) * 10;
744
+ const sigmoidY = 1 / (1 + Math.exp(-normalizedX));
745
+ const y = canvas.height - sigmoidY * canvas.height;
746
+ if (x === 0) ctx.moveTo(x, y);
747
+ else ctx.lineTo(x, y);
748
+ }
749
+ break;
750
+
751
+ case 'tanh':
752
+ for (let x = 0; x < canvas.width; x++) {
753
+ const normalizedX = (x / canvas.width - 0.5) * 6;
754
+ const tanhY = Math.tanh(normalizedX);
755
+ const y = canvas.height / 2 - tanhY * canvas.height / 2;
756
+ if (x === 0) ctx.moveTo(x, y);
757
+ else ctx.lineTo(x, y);
758
+ }
759
+ break;
760
+
761
+ case 'softmax':
762
+ // Just a representative curve for softmax
763
+ ctx.moveTo(0, canvas.height * 0.8);
764
+ ctx.bezierCurveTo(
765
+ canvas.width * 0.3, canvas.height * 0.7,
766
+ canvas.width * 0.6, canvas.height * 0.3,
767
+ canvas.width, canvas.height * 0.2
768
+ );
769
+ break;
770
+
771
+ default: // Linear
772
+ ctx.moveTo(0, canvas.height * 0.8);
773
+ ctx.lineTo(canvas.width, canvas.height * 0.2);
774
+ }
775
+
776
+ ctx.stroke();
777
+
778
+ // Add label
779
+ ctx.fillStyle = 'var(--text-color)';
780
+ ctx.font = '12px Arial';
781
+ ctx.textAlign = 'center';
782
+ ctx.fillText(activationType, canvas.width / 2, canvas.height - 10);
783
+ }
784
+
785
+ // Setup node hover effects for tooltips
786
+ canvas.addEventListener('mouseover', (e) => {
787
+ const node = e.target.closest('.canvas-node');
788
+ if (node) {
789
+ const rect = node.getBoundingClientRect();
790
+ const nodeType = node.getAttribute('data-type');
791
+ const nodeName = node.getAttribute('data-name');
792
+ const dimensions = node.getAttribute('data-dimensions');
793
+
794
+ // Show tooltip
795
+ tooltip.style.display = 'block';
796
+ tooltip.style.left = `${rect.right + 10}px`;
797
+ tooltip.style.top = `${rect.top}px`;
798
+
799
+ const tooltipHeader = tooltip.querySelector('.tooltip-header');
800
+ const tooltipContent = tooltip.querySelector('.tooltip-content');
801
+
802
+ if (tooltipHeader && tooltipContent) {
803
+ tooltipHeader.textContent = nodeName;
804
+
805
+ let content = '';
806
+ content += `<div class="tooltip-row">
807
+ <div class="tooltip-label">Type:</div>
808
+ <div class="tooltip-value">${nodeType.charAt(0).toUpperCase() + nodeType.slice(1)}</div>
809
+ </div>`;
810
+
811
+ content += `<div class="tooltip-row">
812
+ <div class="tooltip-label">Dimensions:</div>
813
+ <div class="tooltip-value">${dimensions}</div>
814
+ </div>`;
815
+
816
+ // Get config template
817
+ const configTemplate = window.neuralNetwork.nodeConfigTemplates[nodeType];
818
+
819
+ if (configTemplate) {
820
+ if (configTemplate.activation) {
821
+ content += `<div class="tooltip-row">
822
+ <div class="tooltip-label">Activation:</div>
823
+ <div class="tooltip-value">${configTemplate.activation}</div>
824
+ </div>`;
825
+ }
826
+
827
+ if (configTemplate.description) {
828
+ content += `<div class="tooltip-row">
829
+ <div class="tooltip-label">Description:</div>
830
+ <div class="tooltip-value">${configTemplate.description}</div>
831
+ </div>`;
832
+ }
833
+ }
834
+
835
+ tooltipContent.innerHTML = content;
836
+ }
837
+ }
838
+ });
839
+
840
+ canvas.addEventListener('mouseout', (e) => {
841
+ const node = e.target.closest('.canvas-node');
842
+ if (node) {
843
+ tooltip.style.display = 'none';
844
+ }
845
+ });
846
+
847
+ // Make sure tooltip follows cursor for nodes that are being dragged
848
+ canvas.addEventListener('mousemove', (e) => {
849
+ const node = e.target.closest('.canvas-node');
850
+ if (node && node.classList.contains('dragging')) {
851
+ const rect = node.getBoundingClientRect();
852
+ tooltip.style.left = `${rect.right + 10}px`;
853
+ tooltip.style.top = `${rect.top}px`;
854
+ }
855
+ });
856
+ });
js/neural-network.js ADDED
@@ -0,0 +1,460 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ /**
2
+ * Neural Network Tools and Utilities
3
+ * Provides helper functions for managing neural network layers,
4
+ * calculating parameters, and managing network architecture
5
+ */
6
+
7
+ (function() {
8
+ // Layer counters to track unique IDs for each layer type
9
+ const layerCounters = {
10
+ 'input': 0,
11
+ 'hidden': 0,
12
+ 'output': 0,
13
+ 'conv': 0,
14
+ 'pool': 0
15
+ };
16
+
17
+ // Default configuration templates for different layer types
18
+ const nodeConfigTemplates = {
19
+ 'input': {
20
+ units: 784,
21
+ shape: [28, 28, 1],
22
+ batchSize: 32,
23
+ description: 'Input layer for raw data',
24
+ parameters: 0
25
+ },
26
+ 'hidden': {
27
+ units: 128,
28
+ activation: 'relu',
29
+ useBias: true,
30
+ kernelInitializer: 'glorotUniform',
31
+ biasInitializer: 'zeros',
32
+ dropoutRate: 0.2,
33
+ description: 'Dense hidden layer with ReLU activation'
34
+ },
35
+ 'output': {
36
+ units: 10,
37
+ activation: 'softmax',
38
+ useBias: true,
39
+ kernelInitializer: 'glorotUniform',
40
+ biasInitializer: 'zeros',
41
+ description: 'Output layer with Softmax activation for classification'
42
+ },
43
+ 'conv': {
44
+ filters: 32,
45
+ kernelSize: [3, 3],
46
+ strides: [1, 1],
47
+ padding: 'valid',
48
+ activation: 'relu',
49
+ useBias: true,
50
+ kernelInitializer: 'glorotUniform',
51
+ biasInitializer: 'zeros',
52
+ description: 'Convolutional layer for feature extraction'
53
+ },
54
+ 'pool': {
55
+ poolSize: [2, 2],
56
+ strides: [2, 2],
57
+ padding: 'valid',
58
+ description: 'Max pooling layer for spatial downsampling'
59
+ }
60
+ };
61
+
62
+ // Mock data structure for sample datasets
63
+ const sampleData = {
64
+ 'mnist': {
65
+ name: 'MNIST Handwritten Digits',
66
+ inputShape: [28, 28, 1],
67
+ numClasses: 10,
68
+ trainSamples: 60000,
69
+ testSamples: 10000,
70
+ description: 'Dataset of handwritten digits for classification'
71
+ },
72
+ 'cifar10': {
73
+ name: 'CIFAR-10',
74
+ inputShape: [32, 32, 3],
75
+ numClasses: 10,
76
+ trainSamples: 50000,
77
+ testSamples: 10000,
78
+ description: 'Dataset of common objects like airplanes, cars, birds, etc.'
79
+ },
80
+ 'fashion': {
81
+ name: 'Fashion MNIST',
82
+ inputShape: [28, 28, 1],
83
+ numClasses: 10,
84
+ trainSamples: 60000,
85
+ testSamples: 10000,
86
+ description: 'Dataset of fashion items like shirts, shoes, bags, etc.'
87
+ }
88
+ };
89
+
90
+ /**
91
+ * Get the next unique ID for a specific layer type
92
+ * @param {string} layerType - The type of the layer (input, hidden, output, conv, pool)
93
+ * @returns {string} - A unique ID for the layer
94
+ */
95
+ function getNextLayerId(layerType) {
96
+ layerCounters[layerType]++;
97
+ return `${layerType}-${layerCounters[layerType]}`;
98
+ }
99
+
100
+ /**
101
+ * Reset all layer counters
102
+ * Used when clearing the canvas
103
+ */
104
+ function resetLayerCounter() {
105
+ for (let key in layerCounters) {
106
+ layerCounters[key] = 0;
107
+ }
108
+ }
109
+
110
+ /**
111
+ * Create a configuration object for a layer
112
+ * @param {string} layerType - The type of the layer
113
+ * @param {Object} customConfig - Custom configuration for the layer
114
+ * @returns {Object} - Complete layer configuration
115
+ */
116
+ function createNodeConfig(layerType, customConfig = {}) {
117
+ const baseConfig = { ...nodeConfigTemplates[layerType] };
118
+
119
+ // Merge custom config with base config
120
+ const config = { ...baseConfig, ...customConfig };
121
+
122
+ // Calculate parameters if not provided
123
+ if (config.parameters === undefined) {
124
+ config.parameters = calculateParameters(layerType, config);
125
+ }
126
+
127
+ return config;
128
+ }
129
+
130
+ /**
131
+ * Calculate the number of parameters for a layer
132
+ * @param {string} layerType - The type of the layer
133
+ * @param {Object} config - Layer configuration
134
+ * @param {Object} prevLayerConfig - Previous layer configuration (for connections)
135
+ * @returns {number} - Number of trainable parameters
136
+ */
137
+ function calculateParameters(layerType, config, prevLayerConfig = null) {
138
+ let parameters = 0;
139
+
140
+ switch(layerType) {
141
+ case 'input':
142
+ parameters = 0; // Input layer has no trainable parameters
143
+ break;
144
+
145
+ case 'hidden':
146
+ if (prevLayerConfig) {
147
+ const inputUnits = prevLayerConfig.units ||
148
+ (prevLayerConfig.shape ?
149
+ prevLayerConfig.shape.reduce((a, b) => a * b, 1) :
150
+ 784);
151
+
152
+ // Weight parameters: input_units * output_units
153
+ parameters = inputUnits * config.units;
154
+
155
+ // Add bias parameters if using bias
156
+ if (config.useBias) {
157
+ parameters += config.units;
158
+ }
159
+ }
160
+ break;
161
+
162
+ case 'output':
163
+ if (prevLayerConfig) {
164
+ const inputUnits = prevLayerConfig.units || 128;
165
+
166
+ // Weight parameters: input_units * output_units
167
+ parameters = inputUnits * config.units;
168
+
169
+ // Add bias parameters if using bias
170
+ if (config.useBias) {
171
+ parameters += config.units;
172
+ }
173
+ }
174
+ break;
175
+
176
+ case 'conv':
177
+ if (prevLayerConfig) {
178
+ const inputChannels = prevLayerConfig.shape ?
179
+ prevLayerConfig.shape[2] || 1 :
180
+ (prevLayerConfig.filters || 1);
181
+
182
+ // Weight parameters: kernel_height * kernel_width * input_channels * filters
183
+ const kernelSize = Array.isArray(config.kernelSize) ?
184
+ config.kernelSize[0] * config.kernelSize[1] :
185
+ config.kernelSize * config.kernelSize;
186
+
187
+ parameters = kernelSize * inputChannels * config.filters;
188
+
189
+ // Add bias parameters if using bias
190
+ if (config.useBias) {
191
+ parameters += config.filters;
192
+ }
193
+ }
194
+ break;
195
+
196
+ case 'pool':
197
+ parameters = 0; // Pooling layers have no trainable parameters
198
+ break;
199
+
200
+ default:
201
+ parameters = 0;
202
+ }
203
+
204
+ return parameters;
205
+ }
206
+
207
+ /**
208
+ * Calculate FLOPs (floating point operations) for a layer
209
+ * @param {string} layerType - The type of the layer
210
+ * @param {Object} config - Layer configuration
211
+ * @param {Object} inputDims - Input dimensions
212
+ * @returns {number} - Approximate FLOPs for forward pass
213
+ */
214
+ function calculateFLOPs(layerType, config, inputDims) {
215
+ let flops = 0;
216
+
217
+ switch(layerType) {
218
+ case 'input':
219
+ flops = 0;
220
+ break;
221
+
222
+ case 'hidden':
223
+ // FLOPs = 2 * input_dim * output_dim (multiply-add operations)
224
+ flops = 2 * inputDims.reduce((a, b) => a * b, 1) * config.units;
225
+ break;
226
+
227
+ case 'output':
228
+ // Same as hidden layer
229
+ flops = 2 * inputDims.reduce((a, b) => a * b, 1) * config.units;
230
+ break;
231
+
232
+ case 'conv':
233
+ // Output dimensions after convolution
234
+ const outputHeight = Math.floor((inputDims[0] - config.kernelSize[0] + 2 *
235
+ (config.padding === 'same' ? config.kernelSize[0] / 2 : 0)) /
236
+ config.strides[0] + 1);
237
+
238
+ const outputWidth = Math.floor((inputDims[1] - config.kernelSize[1] + 2 *
239
+ (config.padding === 'same' ? config.kernelSize[1] / 2 : 0)) /
240
+ config.strides[1] + 1);
241
+
242
+ // FLOPs per output point = 2 * kernel_height * kernel_width * input_channels
243
+ const flopsPerPoint = 2 * config.kernelSize[0] * config.kernelSize[1] * inputDims[2];
244
+
245
+ // Total FLOPs = output_points * flops_per_point * output_channels
246
+ flops = outputHeight * outputWidth * flopsPerPoint * config.filters;
247
+ break;
248
+
249
+ case 'pool':
250
+ // Output dimensions after pooling
251
+ const poolOutputHeight = Math.floor((inputDims[0] - config.poolSize[0]) /
252
+ config.strides[0] + 1);
253
+
254
+ const poolOutputWidth = Math.floor((inputDims[1] - config.poolSize[1]) /
255
+ config.strides[1] + 1);
256
+
257
+ // For max pooling, approximately one comparison per element in the pooling window
258
+ flops = poolOutputHeight * poolOutputWidth * inputDims[2] *
259
+ config.poolSize[0] * config.poolSize[1];
260
+ break;
261
+
262
+ default:
263
+ flops = 0;
264
+ }
265
+
266
+ return flops;
267
+ }
268
+
269
+ /**
270
+ * Calculate memory usage for a layer
271
+ * @param {string} layerType - The type of the layer
272
+ * @param {Object} config - Layer configuration
273
+ * @param {Object} batchSize - Batch size for calculation
274
+ * @returns {Object} - Memory usage statistics
275
+ */
276
+ function calculateMemoryUsage(layerType, config, batchSize = 32) {
277
+ // Assume 4 bytes per parameter (float32)
278
+ const bytesPerParam = 4;
279
+ let outputShape = [];
280
+ let parameters = 0;
281
+ let activationMemory = 0;
282
+
283
+ switch(layerType) {
284
+ case 'input':
285
+ outputShape = config.shape || [28, 28, 1];
286
+ parameters = 0;
287
+ break;
288
+
289
+ case 'hidden':
290
+ outputShape = [config.units];
291
+ parameters = config.parameters || 0;
292
+ break;
293
+
294
+ case 'output':
295
+ outputShape = [config.units];
296
+ parameters = config.parameters || 0;
297
+ break;
298
+
299
+ case 'conv':
300
+ // This is a simplified calculation, actual dimensions depend on padding and strides
301
+ const inputShape = config.inputShape || [28, 28, 1];
302
+ const outputHeight = Math.floor((inputShape[0] - config.kernelSize[0] + 2 *
303
+ (config.padding === 'same' ? config.kernelSize[0] / 2 : 0)) /
304
+ config.strides[0] + 1);
305
+
306
+ const outputWidth = Math.floor((inputShape[1] - config.kernelSize[1] + 2 *
307
+ (config.padding === 'same' ? config.kernelSize[1] / 2 : 0)) /
308
+ config.strides[1] + 1);
309
+
310
+ outputShape = [outputHeight, outputWidth, config.filters];
311
+ parameters = config.parameters || 0;
312
+ break;
313
+
314
+ case 'pool':
315
+ const poolInputShape = config.inputShape || [28, 28, 32];
316
+ const poolOutputHeight = Math.floor((poolInputShape[0] - config.poolSize[0]) /
317
+ config.strides[0] + 1);
318
+
319
+ const poolOutputWidth = Math.floor((poolInputShape[1] - config.poolSize[1]) /
320
+ config.strides[1] + 1);
321
+
322
+ outputShape = [poolOutputHeight, poolOutputWidth, poolInputShape[2]];
323
+ parameters = 0;
324
+ break;
325
+
326
+ default:
327
+ outputShape = [0];
328
+ parameters = 0;
329
+ }
330
+
331
+ // Calculate memory for the activations (output of this layer)
332
+ activationMemory = batchSize * outputShape.reduce((a, b) => a * b, 1) * bytesPerParam;
333
+
334
+ // Calculate memory for the parameters
335
+ const paramMemory = parameters * bytesPerParam;
336
+
337
+ return {
338
+ parameters: parameters,
339
+ paramMemory: paramMemory, // in bytes
340
+ activationMemory: activationMemory, // in bytes
341
+ totalMemory: paramMemory + activationMemory, // in bytes
342
+ outputShape: outputShape
343
+ };
344
+ }
345
+
346
+ /**
347
+ * Generate a human-readable description of a layer
348
+ * @param {string} layerType - The type of the layer
349
+ * @param {Object} config - Layer configuration
350
+ * @returns {string} - Description of the layer
351
+ */
352
+ function generateLayerDescription(layerType, config) {
353
+ let description = '';
354
+
355
+ switch(layerType) {
356
+ case 'input':
357
+ description = `Input Layer: Shape=${config.shape.join('×')}`;
358
+ break;
359
+
360
+ case 'hidden':
361
+ description = `Dense Layer: ${config.units} units, ${config.activation} activation`;
362
+ if (config.dropoutRate > 0) {
363
+ description += `, dropout ${config.dropoutRate}`;
364
+ }
365
+ break;
366
+
367
+ case 'output':
368
+ description = `Output Layer: ${config.units} units, ${config.activation} activation`;
369
+ break;
370
+
371
+ case 'conv':
372
+ description = `Conv2D: ${config.filters} filters, ${config.kernelSize.join('×')} kernel, ${config.activation} activation`;
373
+ break;
374
+
375
+ case 'pool':
376
+ description = `MaxPooling2D: ${config.poolSize.join('×')} pool size`;
377
+ break;
378
+
379
+ default:
380
+ description = 'Unknown layer type';
381
+ }
382
+
383
+ return description;
384
+ }
385
+
386
+ /**
387
+ * Validate a network architecture
388
+ * @param {Object} layers - Array of layer configurations
389
+ * @param {Object} connections - Array of connections between layers
390
+ * @returns {Object} - Validation result with errors if any
391
+ */
392
+ function validateNetwork(layers, connections) {
393
+ const errors = [];
394
+
395
+ // Check if there's exactly one input layer
396
+ const inputLayers = layers.filter(layer => layer.type === 'input');
397
+ if (inputLayers.length === 0) {
398
+ errors.push('Network must have at least one input layer');
399
+ } else if (inputLayers.length > 1) {
400
+ errors.push('Network can have only one input layer');
401
+ }
402
+
403
+ // Check if there's at least one output layer
404
+ const outputLayers = layers.filter(layer => layer.type === 'output');
405
+ if (outputLayers.length === 0) {
406
+ errors.push('Network must have at least one output layer');
407
+ }
408
+
409
+ // Check for isolated nodes (nodes with no connections)
410
+ const connectedNodes = new Set();
411
+ connections.forEach(conn => {
412
+ connectedNodes.add(conn.source);
413
+ connectedNodes.add(conn.target);
414
+ });
415
+
416
+ const isolatedNodes = layers.filter(layer => !connectedNodes.has(layer.id));
417
+ if (isolatedNodes.length > 0) {
418
+ isolatedNodes.forEach(node => {
419
+ if (node.type !== 'input' && node.type !== 'output') {
420
+ errors.push(`Layer "${node.name}" (${node.id}) is isolated`);
421
+ }
422
+ });
423
+ }
424
+
425
+ // Check if input layer has incoming connections
426
+ inputLayers.forEach(layer => {
427
+ const incomingConnections = connections.filter(conn => conn.target === layer.id);
428
+ if (incomingConnections.length > 0) {
429
+ errors.push(`Input layer "${layer.name}" cannot have incoming connections`);
430
+ }
431
+ });
432
+
433
+ // Check if output layer has outgoing connections
434
+ outputLayers.forEach(layer => {
435
+ const outgoingConnections = connections.filter(conn => conn.source === layer.id);
436
+ if (outgoingConnections.length > 0) {
437
+ errors.push(`Output layer "${layer.name}" cannot have outgoing connections`);
438
+ }
439
+ });
440
+
441
+ return {
442
+ valid: errors.length === 0,
443
+ errors: errors
444
+ };
445
+ }
446
+
447
+ // Expose functions to the global scope
448
+ window.neuralNetwork = {
449
+ getNextLayerId,
450
+ resetLayerCounter,
451
+ createNodeConfig,
452
+ calculateParameters,
453
+ calculateFLOPs,
454
+ calculateMemoryUsage,
455
+ generateLayerDescription,
456
+ validateNetwork,
457
+ nodeConfigTemplates,
458
+ sampleData
459
+ };
460
+ })();