tukangkustom commited on
Commit
ca06a8e
·
verified ·
1 Parent(s): a0101bc

Add 3 files

Browse files
Files changed (3) hide show
  1. README.md +7 -5
  2. index.html +1719 -19
  3. prompts.txt +0 -0
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Bomindflow
3
- emoji: 🏃
4
- colorFrom: gray
5
- colorTo: gray
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: bomindflow
3
+ emoji: 🐳
4
+ colorFrom: pink
5
+ colorTo: purple
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,1719 @@
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>MindMapper Pro</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ @keyframes fadeIn {
11
+ from { opacity: 0; transform: translateY(10px); }
12
+ to { opacity: 1; transform: translateY(0); }
13
+ }
14
+
15
+ @keyframes pulse {
16
+ 0%, 100% { transform: scale(1); }
17
+ 50% { transform: scale(1.05); }
18
+ }
19
+
20
+ @keyframes buttonClick {
21
+ 0% { transform: scale(1); }
22
+ 50% { transform: scale(0.95); }
23
+ 100% { transform: scale(1); }
24
+ }
25
+
26
+ @keyframes buttonHover {
27
+ from { transform: translateY(0); }
28
+ to { transform: translateY(-2px); }
29
+ }
30
+
31
+ @keyframes buttonActive {
32
+ 0% { transform: scale(1); }
33
+ 50% { transform: scale(0.98); }
34
+ 100% { transform: scale(1); }
35
+ }
36
+
37
+ @keyframes ripple {
38
+ 0% { transform: scale(0); opacity: 1; }
39
+ 100% { transform: scale(4); opacity: 0; }
40
+ }
41
+
42
+ .node-animation {
43
+ animation: fadeIn 0.3s ease-out forwards;
44
+ }
45
+
46
+ .pulse-animation {
47
+ animation: pulse 1.5s infinite;
48
+ }
49
+
50
+ .button-click {
51
+ animation: buttonClick 0.3s ease;
52
+ }
53
+
54
+ .wireframe {
55
+ position: fixed;
56
+ top: 0;
57
+ left: 0;
58
+ width: 100%;
59
+ height: 100%;
60
+ background-color: rgba(255, 255, 255, 0.9);
61
+ z-index: 1000;
62
+ display: none;
63
+ overflow-y: auto;
64
+ padding: 20px;
65
+ }
66
+
67
+ .connector {
68
+ position: absolute;
69
+ background-color: #94a3b8;
70
+ z-index: -1;
71
+ }
72
+
73
+ .mindmap-container {
74
+ transform-origin: center center;
75
+ transition: transform 0.3s ease;
76
+ }
77
+
78
+ .node-content {
79
+ min-width: 120px;
80
+ min-height: 40px;
81
+ }
82
+
83
+ .color-palette {
84
+ display: none;
85
+ position: absolute;
86
+ z-index: 10;
87
+ background: white;
88
+ border-radius: 8px;
89
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
90
+ padding: 8px;
91
+ }
92
+
93
+ .color-option {
94
+ width: 24px;
95
+ height: 24px;
96
+ border-radius: 50%;
97
+ margin: 4px;
98
+ cursor: pointer;
99
+ border: 2px solid transparent;
100
+ transition: transform 0.2s;
101
+ }
102
+
103
+ .color-option:hover {
104
+ transform: scale(1.1);
105
+ }
106
+
107
+ .color-option.selected {
108
+ border-color: #000;
109
+ }
110
+
111
+ .tooltip {
112
+ position: absolute;
113
+ background-color: #333;
114
+ color: white;
115
+ padding: 4px 8px;
116
+ border-radius: 4px;
117
+ font-size: 12px;
118
+ white-space: nowrap;
119
+ z-index: 100;
120
+ pointer-events: none;
121
+ opacity: 0;
122
+ transition: opacity 0.2s;
123
+ }
124
+
125
+ .button-container:hover .tooltip {
126
+ opacity: 1;
127
+ }
128
+
129
+ .tutorial-highlight {
130
+ animation: pulse 2s infinite;
131
+ box-shadow: 0 0 0 4px rgba(59, 130, 246, 0.5);
132
+ border-radius: 8px;
133
+ }
134
+
135
+ .node-text {
136
+ cursor: text;
137
+ }
138
+
139
+ .node-text:focus {
140
+ outline: none;
141
+ background-color: rgba(255, 255, 255, 0.2);
142
+ border-radius: 4px;
143
+ padding: 2px 4px;
144
+ }
145
+
146
+ /* Enhanced button styles */
147
+ .btn {
148
+ position: relative;
149
+ overflow: hidden;
150
+ transition: all 0.3s cubic-bezier(0.25, 0.8, 0.25, 1);
151
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
152
+ }
153
+
154
+ .btn:hover {
155
+ transform: translateY(-2px);
156
+ box-shadow: 0 4px 8px rgba(0, 0, 0, 0.15);
157
+ animation: buttonHover 0.3s ease-out forwards;
158
+ }
159
+
160
+ .btn:active {
161
+ transform: scale(0.98);
162
+ animation: buttonActive 0.2s ease-out forwards;
163
+ }
164
+
165
+ .btn-primary {
166
+ background-color: #4f46e5;
167
+ color: white;
168
+ }
169
+
170
+ .btn-primary:hover {
171
+ background-color: #4338ca;
172
+ }
173
+
174
+ .btn-secondary {
175
+ background-color: white;
176
+ color: #4f46e5;
177
+ border: 1px solid #e5e7eb;
178
+ }
179
+
180
+ .btn-secondary:hover {
181
+ background-color: #f9fafb;
182
+ }
183
+
184
+ .btn-danger {
185
+ background-color: #ef4444;
186
+ color: white;
187
+ }
188
+
189
+ .btn-danger:hover {
190
+ background-color: #dc2626;
191
+ }
192
+
193
+ .btn-warning {
194
+ background-color: #f59e0b;
195
+ color: white;
196
+ }
197
+
198
+ .btn-warning:hover {
199
+ background-color: #d97706;
200
+ }
201
+
202
+ .btn-success {
203
+ background-color: #10b981;
204
+ color: white;
205
+ }
206
+
207
+ .btn-success:hover {
208
+ background-color: #059669;
209
+ }
210
+
211
+ .btn-info {
212
+ background-color: #3b82f6;
213
+ color: white;
214
+ }
215
+
216
+ .btn-info:hover {
217
+ background-color: #2563eb;
218
+ }
219
+
220
+ .ripple {
221
+ position: absolute;
222
+ border-radius: 50%;
223
+ background-color: rgba(255, 255, 255, 0.4);
224
+ transform: scale(0);
225
+ animation: ripple 0.6s linear;
226
+ pointer-events: none;
227
+ }
228
+
229
+ /* Link creation mode */
230
+ .link-mode {
231
+ background-color: rgba(79, 70, 229, 0.2);
232
+ border: 2px dashed #4f46e5;
233
+ }
234
+
235
+ .node-linkable {
236
+ cursor: crosshair;
237
+ box-shadow: 0 0 0 2px rgba(79, 70, 229, 0.5);
238
+ }
239
+
240
+ .custom-link {
241
+ stroke: #4f46e5;
242
+ stroke-width: 2;
243
+ stroke-dasharray: 5, 5;
244
+ }
245
+ </style>
246
+ </head>
247
+ <body class="bg-gray-50 font-sans">
248
+ <!-- Wireframe Overlay -->
249
+ <div id="wireframe" class="wireframe">
250
+ <div class="container mx-auto">
251
+ <div class="flex justify-between items-center mb-6">
252
+ <h2 class="text-2xl font-bold text-gray-800">MindMapper Wireframe</h2>
253
+ <button id="close-wireframe" class="btn btn-danger px-4 py-2 rounded-lg transition flex items-center">
254
+ <i class="fas fa-times mr-2"></i> Close Wireframe
255
+ </button>
256
+ </div>
257
+ <div class="grid grid-cols-1 md:grid-cols-2 gap-8">
258
+ <div class="bg-white p-6 rounded-lg shadow-md">
259
+ <h3 class="text-xl font-semibold mb-4 text-gray-700">Main Interface</h3>
260
+ <div class="space-y-4">
261
+ <div class="h-8 bg-blue-100 rounded"></div>
262
+ <div class="h-8 bg-blue-100 rounded w-3/4"></div>
263
+ <div class="flex space-x-4">
264
+ <div class="h-8 bg-blue-200 rounded w-1/4"></div>
265
+ <div class="h-8 bg-blue-200 rounded w-1/4"></div>
266
+ <div class="h-8 bg-blue-200 rounded w-1/4"></div>
267
+ </div>
268
+ <div class="h-64 bg-gray-100 rounded-lg flex items-center justify-center">
269
+ <div class="text-center">
270
+ <div class="h-6 bg-blue-300 rounded-full w-6 mx-auto mb-2"></div>
271
+ <div class="h-4 bg-blue-300 rounded w-24 mx-auto"></div>
272
+ </div>
273
+ </div>
274
+ </div>
275
+ </div>
276
+ <div class="bg-white p-6 rounded-lg shadow-md">
277
+ <h3 class="text-xl font-semibold mb-4 text-gray-700">Node Structure</h3>
278
+ <div class="flex justify-center">
279
+ <div class="relative">
280
+ <div class="h-8 bg-green-300 rounded-full w-8 mx-auto mb-2"></div>
281
+ <div class="h-4 bg-green-300 rounded w-20 mx-auto mb-6"></div>
282
+ <div class="flex justify-center space-x-8">
283
+ <div class="text-center">
284
+ <div class="h-6 bg-green-200 rounded-full w-6 mx-auto mb-2"></div>
285
+ <div class="h-3 bg-green-200 rounded w-16 mx-auto"></div>
286
+ </div>
287
+ <div class="text-center">
288
+ <div class="h-6 bg-green-200 rounded-full w-6 mx-auto mb-2"></div>
289
+ <div class="h-3 bg-green-200 rounded w-16 mx-auto"></div>
290
+ </div>
291
+ </div>
292
+ </div>
293
+ </div>
294
+ </div>
295
+ <div class="bg-white p-6 rounded-lg shadow-md">
296
+ <h3 class="text-xl font-semibold mb-4 text-gray-700">Toolbar</h3>
297
+ <div class="flex flex-wrap gap-2">
298
+ <div class="h-8 w-8 bg-purple-200 rounded"></div>
299
+ <div class="h-8 w-8 bg-purple-200 rounded"></div>
300
+ <div class="h-8 w-8 bg-purple-200 rounded"></div>
301
+ <div class="h-8 w-8 bg-purple-200 rounded"></div>
302
+ <div class="h-8 w-8 bg-purple-200 rounded"></div>
303
+ <div class="h-8 w-16 bg-purple-300 rounded"></div>
304
+ </div>
305
+ </div>
306
+ <div class="bg-white p-6 rounded-lg shadow-md">
307
+ <h3 class="text-xl font-semibold mb-4 text-gray-700">Color Palette</h3>
308
+ <div class="flex flex-wrap gap-2">
309
+ <div class="h-8 w-8 bg-red-400 rounded-full"></div>
310
+ <div class="h-8 w-8 bg-blue-400 rounded-full"></div>
311
+ <div class="h-8 w-8 bg-green-400 rounded-full"></div>
312
+ <div class="h-8 w-8 bg-yellow-400 rounded-full"></div>
313
+ <div class="h-8 w-8 bg-purple-400 rounded-full"></div>
314
+ <div class="h-8 w-8 bg-pink-400 rounded-full"></div>
315
+ </div>
316
+ </div>
317
+ </div>
318
+ </div>
319
+ </div>
320
+
321
+ <!-- Main App -->
322
+ <div class="min-h-screen flex flex-col">
323
+ <!-- Header -->
324
+ <header class="bg-indigo-600 text-white shadow-lg">
325
+ <div class="container mx-auto px-4 py-3 flex justify-between items-center">
326
+ <div class="flex items-center space-x-2">
327
+ <i class="fas fa-brain text-2xl"></i>
328
+ <h1 class="text-2xl font-bold">MindMapper Pro</h1>
329
+ </div>
330
+ <div class="flex items-center space-x-4">
331
+ <button id="tutorial-btn" class="btn btn-warning px-4 py-2 rounded-lg transition flex items-center">
332
+ <i class="fas fa-question-circle mr-2"></i> Tutorial
333
+ </button>
334
+ <button id="show-wireframe" class="btn btn-secondary px-4 py-2 rounded-lg transition flex items-center">
335
+ <i class="fas fa-project-diagram mr-2"></i> Wireframe
336
+ </button>
337
+ <button id="export-btn" class="btn btn-primary px-4 py-2 rounded-lg transition flex items-center">
338
+ <i class="fas fa-file-export mr-2"></i> Export
339
+ </button>
340
+ </div>
341
+ </div>
342
+ </header>
343
+
344
+ <!-- Toolbar -->
345
+ <div class="bg-gray-100 border-b border-gray-200 py-2 px-4">
346
+ <div class="container mx-auto flex flex-wrap items-center gap-3">
347
+ <div class="button-container relative">
348
+ <button id="add-child-btn" class="btn btn-success px-3 py-1 rounded-lg transition flex items-center">
349
+ <i class="fas fa-plus-circle mr-2"></i> Add Child
350
+ </button>
351
+ <div class="tooltip">Add a child node to selected node (Enter)</div>
352
+ </div>
353
+
354
+ <div class="button-container relative">
355
+ <button id="add-sibling-btn" class="btn btn-info px-3 py-1 rounded-lg transition flex items-center">
356
+ <i class="fas fa-plus-square mr-2"></i> Add Sibling
357
+ </button>
358
+ <div class="tooltip">Add a sibling node to selected node (Shift+Enter)</div>
359
+ </div>
360
+
361
+ <div class="button-container relative">
362
+ <button id="delete-node-btn" class="btn btn-danger px-3 py-1 rounded-lg transition flex items-center">
363
+ <i class="fas fa-trash-alt mr-2"></i> Delete
364
+ </button>
365
+ <div class="tooltip">Delete selected node (Delete)</div>
366
+ </div>
367
+
368
+ <div class="button-container relative">
369
+ <button id="edit-text-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
370
+ <i class="fas fa-edit mr-2"></i> Edit Text
371
+ </button>
372
+ <div class="tooltip">Edit node text (Double Click)</div>
373
+ </div>
374
+
375
+ <div class="button-container relative">
376
+ <div class="relative">
377
+ <button id="color-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
378
+ <i class="fas fa-palette mr-2"></i> Color
379
+ </button>
380
+ <div class="tooltip">Change node color</div>
381
+ <div id="color-palette" class="color-palette grid grid-cols-5 gap-1">
382
+ <div class="color-option bg-red-400" data-color="bg-red-400"></div>
383
+ <div class="color-option bg-blue-400" data-color="bg-blue-400"></div>
384
+ <div class="color-option bg-green-400" data-color="bg-green-400"></div>
385
+ <div class="color-option bg-yellow-400" data-color="bg-yellow-400"></div>
386
+ <div class="color-option bg-purple-400" data-color="bg-purple-400"></div>
387
+ <div class="color-option bg-pink-400" data-color="bg-pink-400"></div>
388
+ <div class="color-option bg-indigo-400" data-color="bg-indigo-400"></div>
389
+ <div class="color-option bg-teal-400" data-color="bg-teal-400"></div>
390
+ <div class="color-option bg-orange-400" data-color="bg-orange-400"></div>
391
+ <div class="color-option bg-gray-400" data-color="bg-gray-400"></div>
392
+ </div>
393
+ </div>
394
+ </div>
395
+
396
+ <div class="button-container relative">
397
+ <button id="zoom-in-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
398
+ <i class="fas fa-search-plus mr-2"></i> Zoom In
399
+ </button>
400
+ <div class="tooltip">Zoom in (Ctrl + +)</div>
401
+ </div>
402
+
403
+ <div class="button-container relative">
404
+ <button id="zoom-out-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
405
+ <i class="fas fa-search-minus mr-2"></i> Zoom Out
406
+ </button>
407
+ <div class="tooltip">Zoom out (Ctrl + -)</div>
408
+ </div>
409
+
410
+ <div class="button-container relative">
411
+ <button id="reset-zoom-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
412
+ <i class="fas fa-expand mr-2"></i> Reset Zoom
413
+ </button>
414
+ <div class="tooltip">Reset zoom (Ctrl + 0)</div>
415
+ </div>
416
+
417
+ <div class="button-container relative">
418
+ <button id="toggle-layout-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
419
+ <i class="fas fa-sitemap mr-2"></i> Toggle Layout
420
+ </button>
421
+ <div class="tooltip">Switch between radial and horizontal layout (L)</div>
422
+ </div>
423
+
424
+ <div class="button-container relative">
425
+ <button id="animate-btn" class="btn btn-primary px-3 py-1 rounded-lg transition flex items-center">
426
+ <i class="fas fa-play-circle mr-2"></i> Animate
427
+ </button>
428
+ <div class="tooltip">Play visualization animation (A)</div>
429
+ </div>
430
+
431
+ <div class="button-container relative">
432
+ <button id="create-link-btn" class="btn btn-success px-3 py-1 rounded-lg transition flex items-center">
433
+ <i class="fas fa-link mr-2"></i> Create Link
434
+ </button>
435
+ <div class="tooltip">Create a custom link between nodes</div>
436
+ </div>
437
+
438
+ <div class="button-container relative">
439
+ <button id="undo-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
440
+ <i class="fas fa-undo mr-2"></i> Undo
441
+ </button>
442
+ <div class="tooltip">Undo last action (Ctrl+Z)</div>
443
+ </div>
444
+
445
+ <div class="button-container relative">
446
+ <button id="redo-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
447
+ <i class="fas fa-redo mr-2"></i> Redo
448
+ </button>
449
+ <div class="tooltip">Redo last action (Ctrl+Y)</div>
450
+ </div>
451
+
452
+ <div class="button-container relative">
453
+ <button id="center-view-btn" class="btn btn-secondary px-3 py-1 rounded-lg transition flex items-center">
454
+ <i class="fas fa-crosshairs mr-2"></i> Center View
455
+ </button>
456
+ <div class="tooltip">Center view on selected node (C)</div>
457
+ </div>
458
+ </div>
459
+ </div>
460
+
461
+ <!-- Main Content -->
462
+ <main class="flex-grow overflow-hidden relative">
463
+ <div id="mindmap-container" class="mindmap-container w-full h-full absolute">
464
+ <!-- Mind map will be rendered here -->
465
+ </div>
466
+
467
+ <!-- Temporary link line during creation -->
468
+ <svg id="temp-link" class="absolute top-0 left-0 w-full h-full pointer-events-none" style="z-index: 10; display: none;">
469
+ <line id="temp-line" class="custom-link" />
470
+ </svg>
471
+
472
+ <!-- Tutorial Highlights -->
473
+ <div id="tutorial-highlights" class="hidden">
474
+ <div id="tutorial-add-child" class="absolute tutorial-highlight"></div>
475
+ <div id="tutorial-add-sibling" class="absolute tutorial-highlight"></div>
476
+ <div id="tutorial-edit-text" class="absolute tutorial-highlight"></div>
477
+ <div id="tutorial-zoom" class="absolute tutorial-highlight"></div>
478
+ </div>
479
+ </main>
480
+
481
+ <!-- Status Bar -->
482
+ <footer class="bg-gray-800 text-gray-300 text-sm py-1 px-4">
483
+ <div class="container mx-auto flex justify-between items-center">
484
+ <div>
485
+ <span id="node-count">Nodes: 1</span>
486
+ <span class="mx-2">|</span>
487
+ <span id="zoom-level">Zoom: 100%</span>
488
+ <span class="mx-2">|</span>
489
+ <span id="layout-type">Layout: Radial</span>
490
+ <span class="mx-2">|</span>
491
+ <span id="link-mode-status" class="hidden text-indigo-300">Link Mode: Select source node</span>
492
+ </div>
493
+ <div>
494
+ <span id="status-message">Ready</span>
495
+ <span class="mx-2">|</span>
496
+ <span id="shortcut-hint">Tip: Press H for help</span>
497
+ </div>
498
+ </div>
499
+ </footer>
500
+ </div>
501
+
502
+ <!-- Export Modal -->
503
+ <div id="export-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
504
+ <div class="bg-white rounded-lg p-6 w-full max-w-md">
505
+ <div class="flex justify-between items-center mb-4">
506
+ <h3 class="text-xl font-bold">Export Mind Map</h3>
507
+ <button id="close-export-modal" class="text-gray-500 hover:text-gray-700">
508
+ <i class="fas fa-times"></i>
509
+ </button>
510
+ </div>
511
+ <div class="space-y-4">
512
+ <div>
513
+ <label class="block text-sm font-medium text-gray-700 mb-1">Format</label>
514
+ <select id="export-format" class="w-full border border-gray-300 rounded-lg px-3 py-2">
515
+ <option value="png">PNG Image</option>
516
+ <option value="json">JSON Data</option>
517
+ <option value="svg">SVG Vector</option>
518
+ <option value="pdf">PDF Document</option>
519
+ </select>
520
+ </div>
521
+ <div id="export-png-options">
522
+ <label class="block text-sm font-medium text-gray-700 mb-1">Quality</label>
523
+ <input type="range" id="export-quality" min="1" max="10" value="8" class="w-full">
524
+ <div class="flex justify-between text-xs text-gray-500">
525
+ <span>Low</span>
526
+ <span>High</span>
527
+ </div>
528
+ </div>
529
+ <div id="export-pdf-options" class="hidden">
530
+ <label class="block text-sm font-medium text-gray-700 mb-1">Page Size</label>
531
+ <select id="pdf-page-size" class="w-full border border-gray-300 rounded-lg px-3 py-2">
532
+ <option value="A4">A4</option>
533
+ <option value="Letter">Letter</option>
534
+ <option value="A3">A3</option>
535
+ </select>
536
+ </div>
537
+ <div class="flex justify-end space-x-3 pt-4">
538
+ <button id="cancel-export" class="btn btn-secondary px-4 py-2 rounded-lg">
539
+ Cancel
540
+ </button>
541
+ <button id="confirm-export" class="btn btn-primary px-4 py-2 rounded-lg">
542
+ Export
543
+ </button>
544
+ </div>
545
+ </div>
546
+ </div>
547
+ </div>
548
+
549
+ <!-- Help Modal -->
550
+ <div id="help-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden">
551
+ <div class="bg-white rounded-lg p-6 w-full max-w-2xl max-h-[80vh] overflow-y-auto">
552
+ <div class="flex justify-between items-center mb-4">
553
+ <h3 class="text-xl font-bold">MindMapper Help & Shortcuts</h3>
554
+ <button id="close-help-modal" class="text-gray-500 hover:text-gray-700">
555
+ <i class="fas fa-times"></i>
556
+ </button>
557
+ </div>
558
+ <div class="space-y-4">
559
+ <div>
560
+ <h4 class="font-semibold text-lg mb-2">Basic Controls</h4>
561
+ <ul class="space-y-2">
562
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Click</span> - Select a node</li>
563
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Double Click</span> - Edit node text</li>
564
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Drag</span> - Move the canvas</li>
565
+ </ul>
566
+ </div>
567
+
568
+ <div>
569
+ <h4 class="font-semibold text-lg mb-2">Node Operations</h4>
570
+ <ul class="space-y-2">
571
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Enter</span> - Add child node</li>
572
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Shift+Enter</span> - Add sibling node</li>
573
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Delete</span> - Delete selected node</li>
574
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Tab</span> - Focus on first child</li>
575
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Shift+Tab</span> - Focus on parent</li>
576
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">L</span> - Create custom link between nodes</li>
577
+ </ul>
578
+ </div>
579
+
580
+ <div>
581
+ <h4 class="font-semibold text-lg mb-2">View Controls</h4>
582
+ <ul class="space-y-2">
583
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Ctrl + +</span> - Zoom in</li>
584
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Ctrl + -</span> - Zoom out</li>
585
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Ctrl + 0</span> - Reset zoom</li>
586
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">L</span> - Toggle layout</li>
587
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">C</span> - Center view</li>
588
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">A</span> - Animate</li>
589
+ </ul>
590
+ </div>
591
+
592
+ <div>
593
+ <h4 class="font-semibold text-lg mb-2">Other Shortcuts</h4>
594
+ <ul class="space-y-2">
595
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Ctrl+Z</span> - Undo</li>
596
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">Ctrl+Y</span> - Redo</li>
597
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">H</span> - Show help</li>
598
+ <li><span class="font-mono bg-gray-100 px-2 py-1 rounded">E</span> - Export</li>
599
+ </ul>
600
+ </div>
601
+
602
+ <div class="pt-4 border-t">
603
+ <button id="start-tutorial" class="btn btn-warning px-4 py-2 rounded-lg">
604
+ <i class="fas fa-play mr-2"></i> Start Interactive Tutorial
605
+ </button>
606
+ </div>
607
+ </div>
608
+ </div>
609
+ </div>
610
+
611
+ <script>
612
+ document.addEventListener('DOMContentLoaded', function() {
613
+ // App state
614
+ const state = {
615
+ nodes: [],
616
+ selectedNode: null,
617
+ nextId: 1,
618
+ zoomLevel: 1,
619
+ layout: 'radial', // 'radial' or 'horizontal'
620
+ connectors: [],
621
+ customLinks: [],
622
+ history: [],
623
+ historyIndex: -1,
624
+ isDragging: false,
625
+ dragStartX: 0,
626
+ dragStartY: 0,
627
+ translateX: 0,
628
+ translateY: 0,
629
+ tutorialStep: 0,
630
+ tutorialActive: false,
631
+ linkMode: false,
632
+ linkSourceNode: null
633
+ };
634
+
635
+ // DOM elements
636
+ const mindmapContainer = document.getElementById('mindmap-container');
637
+ const wireframe = document.getElementById('wireframe');
638
+ const showWireframeBtn = document.getElementById('show-wireframe');
639
+ const closeWireframeBtn = document.getElementById('close-wireframe');
640
+ const addChildBtn = document.getElementById('add-child-btn');
641
+ const addSiblingBtn = document.getElementById('add-sibling-btn');
642
+ const deleteNodeBtn = document.getElementById('delete-node-btn');
643
+ const editTextBtn = document.getElementById('edit-text-btn');
644
+ const colorBtn = document.getElementById('color-btn');
645
+ const colorPalette = document.getElementById('color-palette');
646
+ const zoomInBtn = document.getElementById('zoom-in-btn');
647
+ const zoomOutBtn = document.getElementById('zoom-out-btn');
648
+ const resetZoomBtn = document.getElementById('reset-zoom-btn');
649
+ const toggleLayoutBtn = document.getElementById('toggle-layout-btn');
650
+ const animateBtn = document.getElementById('animate-btn');
651
+ const createLinkBtn = document.getElementById('create-link-btn');
652
+ const exportBtn = document.getElementById('export-btn');
653
+ const exportModal = document.getElementById('export-modal');
654
+ const closeExportModal = document.getElementById('close-export-modal');
655
+ const cancelExport = document.getElementById('cancel-export');
656
+ const confirmExport = document.getElementById('confirm-export');
657
+ const nodeCountSpan = document.getElementById('node-count');
658
+ const zoomLevelSpan = document.getElementById('zoom-level');
659
+ const statusMessageSpan = document.getElementById('status-message');
660
+ const layoutTypeSpan = document.getElementById('layout-type');
661
+ const linkModeStatus = document.getElementById('link-mode-status');
662
+ const shortcutHintSpan = document.getElementById('shortcut-hint');
663
+ const tutorialBtn = document.getElementById('tutorial-btn');
664
+ const helpModal = document.getElementById('help-modal');
665
+ const closeHelpModal = document.getElementById('close-help-modal');
666
+ const startTutorialBtn = document.getElementById('start-tutorial');
667
+ const tutorialHighlights = document.getElementById('tutorial-highlights');
668
+ const exportFormatSelect = document.getElementById('export-format');
669
+ const undoBtn = document.getElementById('undo-btn');
670
+ const redoBtn = document.getElementById('redo-btn');
671
+ const centerViewBtn = document.getElementById('center-view-btn');
672
+ const tempLink = document.getElementById('temp-link');
673
+ const tempLine = document.getElementById('temp-line');
674
+
675
+ // Initialize the mind map with a root node
676
+ createNode('Main Idea', null, true);
677
+ saveState();
678
+
679
+ // Add ripple effect to buttons
680
+ function createRipple(event) {
681
+ const button = event.currentTarget;
682
+ const circle = document.createElement("span");
683
+ const rect = button.getBoundingClientRect();
684
+ const diameter = Math.max(rect.width, rect.height);
685
+ const radius = diameter / 2;
686
+
687
+ circle.style.width = circle.style.height = `${diameter}px`;
688
+ circle.style.left = `${event.clientX - rect.left - radius}px`;
689
+ circle.style.top = `${event.clientY - rect.top - radius}px`;
690
+ circle.classList.add("ripple");
691
+
692
+ const ripple = button.getElementsByClassName("ripple")[0];
693
+ if (ripple) {
694
+ ripple.remove();
695
+ }
696
+
697
+ button.appendChild(circle);
698
+ }
699
+
700
+ const buttons = document.querySelectorAll('.btn');
701
+ buttons.forEach(button => {
702
+ button.addEventListener('click', createRipple);
703
+ });
704
+
705
+ // Node creation function
706
+ function createNode(text, parentId, isRoot = false) {
707
+ const nodeId = state.nextId++;
708
+ const node = {
709
+ id: nodeId,
710
+ text: text,
711
+ parentId: parentId,
712
+ children: [],
713
+ color: isRoot ? 'bg-indigo-400' : 'bg-gray-200',
714
+ x: 0,
715
+ y: 0,
716
+ collapsed: false
717
+ };
718
+
719
+ state.nodes.push(node);
720
+
721
+ // If this isn't the root node, add it to its parent's children
722
+ if (parentId !== null) {
723
+ const parent = state.nodes.find(n => n.id === parentId);
724
+ if (parent) {
725
+ parent.children.push(nodeId);
726
+ }
727
+ }
728
+
729
+ // If this is the root node, select it
730
+ if (isRoot) {
731
+ selectNode(node);
732
+ }
733
+
734
+ renderMindMap();
735
+ updateNodeCount();
736
+ return node;
737
+ }
738
+
739
+ // Render the entire mind map
740
+ function renderMindMap() {
741
+ // Clear the container
742
+ mindmapContainer.innerHTML = '';
743
+ state.connectors = [];
744
+
745
+ // Create SVG for custom links
746
+ const svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
747
+ svg.setAttribute('class', 'absolute top-0 left-0 w-full h-full pointer-events-none');
748
+ svg.setAttribute('style', 'z-index: 0;');
749
+ mindmapContainer.appendChild(svg);
750
+
751
+ // Find root node(s)
752
+ const rootNodes = state.nodes.filter(node => node.parentId === null);
753
+
754
+ // Position and render nodes
755
+ rootNodes.forEach(rootNode => {
756
+ if (state.layout === 'radial') {
757
+ positionNodesRadially(rootNode, mindmapContainer.clientWidth / 2 + state.translateX, mindmapContainer.clientHeight / 2 + state.translateY, 0);
758
+ } else {
759
+ positionNodesHorizontally(rootNode, mindmapContainer.clientWidth / 2 + state.translateX, 100 + state.translateY, 200);
760
+ }
761
+ });
762
+
763
+ // Render custom links
764
+ state.customLinks.forEach(link => {
765
+ const fromNode = state.nodes.find(n => n.id === link.from);
766
+ const toNode = state.nodes.find(n => n.id === link.to);
767
+
768
+ if (fromNode && toNode) {
769
+ const line = document.createElementNS("http://www.w3.org/2000/svg", "line");
770
+ line.setAttribute('x1', fromNode.x);
771
+ line.setAttribute('y1', fromNode.y);
772
+ line.setAttribute('x2', toNode.x);
773
+ line.setAttribute('y2', toNode.y);
774
+ line.setAttribute('class', 'custom-link');
775
+ line.setAttribute('stroke', '#4f46e5');
776
+ line.setAttribute('stroke-width', '2');
777
+ line.setAttribute('stroke-dasharray', '5,5');
778
+ svg.appendChild(line);
779
+ }
780
+ });
781
+
782
+ // Render connectors first (so they appear behind nodes)
783
+ renderConnectors();
784
+
785
+ // Then render nodes
786
+ state.nodes.forEach(node => {
787
+ if (!node.collapsed || node === state.selectedNode) {
788
+ renderNode(node);
789
+ }
790
+ });
791
+
792
+ // Update layout type display
793
+ layoutTypeSpan.textContent = `Layout: ${state.layout.charAt(0).toUpperCase() + state.layout.slice(1)}`;
794
+
795
+ // Center view if tutorial is active
796
+ if (state.tutorialActive) {
797
+ centerView();
798
+ }
799
+ }
800
+
801
+ // Position nodes in a radial layout
802
+ function positionNodesRadially(node, centerX, centerY, level, angle = 0, angleRange = Math.PI * 2) {
803
+ const radius = 150 + (level * 120);
804
+ const childCount = node.children.length;
805
+
806
+ if (level === 0) {
807
+ // Root node position
808
+ node.x = centerX;
809
+ node.y = centerY;
810
+ } else {
811
+ // Position child nodes in an arc
812
+ const angleStep = childCount > 1 ? angleRange / (childCount - 1) : 0;
813
+ const startAngle = angle - (angleRange / 2);
814
+
815
+ node.children.forEach((childId, index) => {
816
+ const childNode = state.nodes.find(n => n.id === childId);
817
+ if (childNode) {
818
+ const childAngle = startAngle + (index * angleStep);
819
+ childNode.x = centerX + Math.cos(childAngle) * radius;
820
+ childNode.y = centerY + Math.sin(childAngle) * radius;
821
+
822
+ // Store connector information
823
+ state.connectors.push({
824
+ fromX: node.x,
825
+ fromY: node.y,
826
+ toX: childNode.x,
827
+ toY: childNode.y,
828
+ color: node.color
829
+ });
830
+
831
+ // Position grandchildren recursively
832
+ if (!childNode.collapsed) {
833
+ positionNodesRadially(childNode, childNode.x, childNode.y, level + 1, childAngle, angleRange / 2);
834
+ }
835
+ }
836
+ });
837
+ }
838
+ }
839
+
840
+ // Position nodes in a horizontal layout
841
+ function positionNodesHorizontally(node, x, y, levelWidth) {
842
+ node.x = x;
843
+ node.y = y;
844
+
845
+ if (node.children.length > 0 && !node.collapsed) {
846
+ const childCount = node.children.length;
847
+ const totalWidth = (childCount - 1) * levelWidth;
848
+ const startX = x - totalWidth / 2;
849
+
850
+ node.children.forEach((childId, index) => {
851
+ const childNode = state.nodes.find(n => n.id === childId);
852
+ if (childNode) {
853
+ const childX = startX + (index * levelWidth);
854
+ const childY = y + 100;
855
+
856
+ childNode.x = childX;
857
+ childNode.y = childY;
858
+
859
+ // Store connector information
860
+ state.connectors.push({
861
+ fromX: node.x,
862
+ fromY: node.y,
863
+ toX: childNode.x,
864
+ toY: childNode.y,
865
+ color: node.color
866
+ });
867
+
868
+ // Position grandchildren recursively
869
+ positionNodesHorizontally(childNode, childX, childY, levelWidth * 0.7);
870
+ }
871
+ });
872
+ }
873
+ }
874
+
875
+ // Render a single node
876
+ function renderNode(node) {
877
+ const nodeElement = document.createElement('div');
878
+ nodeElement.className = `absolute node ${node.color} text-white rounded-lg shadow-md transition-all duration-200 hover:shadow-lg ${node === state.selectedNode ? 'ring-2 ring-yellow-400' : ''} ${state.linkMode ? 'node-linkable' : ''}`;
879
+ nodeElement.style.left = `${node.x}px`;
880
+ nodeElement.style.top = `${node.y}px`;
881
+ nodeElement.style.transform = `translate(-50%, -50%)`;
882
+ nodeElement.dataset.nodeId = node.id;
883
+
884
+ // Add animation class if this is a new node
885
+ if (node.id === state.nextId - 1) {
886
+ nodeElement.classList.add('node-animation');
887
+ }
888
+
889
+ // Node content
890
+ nodeElement.innerHTML = `
891
+ <div class="node-content p-3 flex flex-col items-center">
892
+ <div class="font-semibold text-center mb-1 node-text" contenteditable="false">${node.text}</div>
893
+ ${node.children.length > 0 ? `
894
+ <button class="toggle-children text-xs bg-black bg-opacity-20 px-2 py-1 rounded-full mt-1" data-node-id="${node.id}">
895
+ ${node.collapsed ? '<i class="fas fa-plus mr-1"></i> Expand' : '<i class="fas fa-minus mr-1"></i> Collapse'}
896
+ </button>
897
+ ` : ''}
898
+ </div>
899
+ `;
900
+
901
+ // Add event listeners
902
+ nodeElement.addEventListener('click', (e) => {
903
+ if (!e.target.classList.contains('toggle-children') && !e.target.classList.contains('node-text')) {
904
+ if (state.linkMode) {
905
+ handleLinkModeClick(node);
906
+ } else {
907
+ selectNode(node);
908
+ }
909
+ }
910
+ });
911
+
912
+ // Double click to edit text
913
+ const textElement = nodeElement.querySelector('.node-text');
914
+ textElement.addEventListener('dblclick', () => {
915
+ editNodeText(textElement, node);
916
+ });
917
+
918
+ const toggleBtn = nodeElement.querySelector('.toggle-children');
919
+ if (toggleBtn) {
920
+ toggleBtn.addEventListener('click', (e) => {
921
+ e.stopPropagation();
922
+ toggleChildren(node);
923
+ });
924
+ }
925
+
926
+ mindmapContainer.appendChild(nodeElement);
927
+ }
928
+
929
+ // Handle node selection in link mode
930
+ function handleLinkModeClick(node) {
931
+ if (!state.linkSourceNode) {
932
+ // First node selected (source)
933
+ state.linkSourceNode = node;
934
+ linkModeStatus.textContent = `Link Mode: Select target node (ESC to cancel)`;
935
+
936
+ // Show temporary line
937
+ tempLink.style.display = 'block';
938
+ tempLine.setAttribute('x1', node.x);
939
+ tempLine.setAttribute('y1', node.y);
940
+ tempLine.setAttribute('x2', node.x);
941
+ tempLine.setAttribute('y2', node.y);
942
+
943
+ // Update line on mouse move
944
+ const updateTempLine = (e) => {
945
+ const rect = mindmapContainer.getBoundingClientRect();
946
+ const x = e.clientX - rect.left;
947
+ const y = e.clientY - rect.top;
948
+ tempLine.setAttribute('x2', x);
949
+ tempLine.setAttribute('y2', y);
950
+ };
951
+
952
+ document.addEventListener('mousemove', updateTempLine);
953
+
954
+ // Cancel link mode on ESC
955
+ const cancelLinkMode = (e) => {
956
+ if (e.key === 'Escape') {
957
+ state.linkMode = false;
958
+ state.linkSourceNode = null;
959
+ linkModeStatus.classList.add('hidden');
960
+ tempLink.style.display = 'none';
961
+ document.removeEventListener('mousemove', updateTempLine);
962
+ document.removeEventListener('keydown', cancelLinkMode);
963
+ renderMindMap();
964
+ updateStatus('Link creation canceled');
965
+ }
966
+ };
967
+
968
+ document.addEventListener('keydown', cancelLinkMode);
969
+ } else {
970
+ // Second node selected (target)
971
+ if (node.id === state.linkSourceNode.id) {
972
+ updateStatus('Cannot link a node to itself', 'error');
973
+ return;
974
+ }
975
+
976
+ // Check if link already exists
977
+ const linkExists = state.customLinks.some(link =>
978
+ (link.from === state.linkSourceNode.id && link.to === node.id) ||
979
+ (link.from === node.id && link.to === state.linkSourceNode.id)
980
+ );
981
+
982
+ if (linkExists) {
983
+ updateStatus('Link already exists between these nodes', 'error');
984
+ return;
985
+ }
986
+
987
+ // Create the link
988
+ state.customLinks.push({
989
+ from: state.linkSourceNode.id,
990
+ to: node.id
991
+ });
992
+
993
+ // Exit link mode
994
+ state.linkMode = false;
995
+ state.linkSourceNode = null;
996
+ linkModeStatus.classList.add('hidden');
997
+ tempLink.style.display = 'none';
998
+
999
+ saveState();
1000
+ renderMindMap();
1001
+ updateStatus(`Created link between nodes`);
1002
+ }
1003
+ }
1004
+
1005
+ // Edit node text
1006
+ function editNodeText(element, node) {
1007
+ element.setAttribute('contenteditable', 'true');
1008
+ element.focus();
1009
+
1010
+ const range = document.createRange();
1011
+ range.selectNodeContents(element);
1012
+ const selection = window.getSelection();
1013
+ selection.removeAllRanges();
1014
+ selection.addRange(range);
1015
+
1016
+ function saveText() {
1017
+ element.setAttribute('contenteditable', 'false');
1018
+ const newText = element.textContent.trim();
1019
+ if (newText !== node.text) {
1020
+ node.text = newText;
1021
+ saveState();
1022
+ updateStatus(`Updated node text to "${node.text}"`);
1023
+ }
1024
+ element.removeEventListener('blur', saveText);
1025
+ element.removeEventListener('keydown', handleTextEditKeydown);
1026
+ }
1027
+
1028
+ function handleTextEditKeydown(e) {
1029
+ if (e.key === 'Enter') {
1030
+ e.preventDefault();
1031
+ saveText();
1032
+ } else if (e.key === 'Escape') {
1033
+ element.textContent = node.text;
1034
+ saveText();
1035
+ }
1036
+ }
1037
+
1038
+ element.addEventListener('blur', saveText);
1039
+ element.addEventListener('keydown', handleTextEditKeydown);
1040
+ }
1041
+
1042
+ // Render connectors between nodes
1043
+ function renderConnectors() {
1044
+ state.connectors.forEach(connector => {
1045
+ const line = document.createElement('div');
1046
+ line.className = 'connector';
1047
+
1048
+ // Calculate line position and dimensions
1049
+ const x1 = connector.fromX;
1050
+ const y1 = connector.fromY;
1051
+ const x2 = connector.toX;
1052
+ const y2 = connector.toY;
1053
+
1054
+ const length = Math.sqrt(Math.pow(x2 - x1, 2) + Math.pow(y2 - y1, 2));
1055
+ const angle = Math.atan2(y2 - y1, x2 - x1) * 180 / Math.PI;
1056
+
1057
+ line.style.width = `${length}px`;
1058
+ line.style.height = '2px';
1059
+ line.style.left = `${x1}px`;
1060
+ line.style.top = `${y1}px`;
1061
+ line.style.transform = `translate(-50%, -50%) rotate(${angle}deg)`;
1062
+ line.style.transformOrigin = '0 0';
1063
+ line.style.backgroundColor = connector.color.replace('bg-', '').replace('-400', '');
1064
+
1065
+ mindmapContainer.appendChild(line);
1066
+ });
1067
+ }
1068
+
1069
+ // Select a node
1070
+ function selectNode(node) {
1071
+ state.selectedNode = node;
1072
+ renderMindMap();
1073
+ updateStatus(`Selected: ${node.text}`);
1074
+
1075
+ // If tutorial is active, advance to next step
1076
+ if (state.tutorialActive) {
1077
+ advanceTutorial();
1078
+ }
1079
+ }
1080
+
1081
+ // Add a child node to the selected node
1082
+ function addChildNode() {
1083
+ if (state.selectedNode) {
1084
+ const newNode = createNode('New Child', state.selectedNode.id);
1085
+ newNode.color = state.selectedNode.color;
1086
+ saveState();
1087
+ renderMindMap();
1088
+ updateStatus(`Added child node to "${state.selectedNode.text}"`);
1089
+
1090
+ // If tutorial is active, advance to next step
1091
+ if (state.tutorialActive) {
1092
+ advanceTutorial();
1093
+ }
1094
+ } else {
1095
+ updateStatus('Please select a node first', 'error');
1096
+ }
1097
+ }
1098
+
1099
+ // Add a sibling node to the selected node
1100
+ function addSiblingNode() {
1101
+ if (state.selectedNode && state.selectedNode.parentId !== null) {
1102
+ const newNode = createNode('New Sibling', state.selectedNode.parentId);
1103
+ newNode.color = state.selectedNode.color;
1104
+ saveState();
1105
+ renderMindMap();
1106
+ updateStatus(`Added sibling node to "${state.selectedNode.text}"`);
1107
+
1108
+ // If tutorial is active, advance to next step
1109
+ if (state.tutorialActive) {
1110
+ advanceTutorial();
1111
+ }
1112
+ } else if (state.selectedNode) {
1113
+ updateStatus('Cannot add sibling to root node', 'error');
1114
+ } else {
1115
+ updateStatus('Please select a node first', 'error');
1116
+ }
1117
+ }
1118
+
1119
+ // Edit selected node text
1120
+ function editSelectedNodeText() {
1121
+ if (state.selectedNode) {
1122
+ const nodeElement = document.querySelector(`.node[data-node-id="${state.selectedNode.id}"]`);
1123
+ if (nodeElement) {
1124
+ const textElement = nodeElement.querySelector('.node-text');
1125
+ editNodeText(textElement, state.selectedNode);
1126
+ }
1127
+ } else {
1128
+ updateStatus('Please select a node first', 'error');
1129
+ }
1130
+ }
1131
+
1132
+ // Delete the selected node
1133
+ function deleteSelectedNode() {
1134
+ if (state.selectedNode) {
1135
+ if (state.selectedNode.parentId === null && state.nodes.length > 1) {
1136
+ updateStatus('Cannot delete the only root node', 'error');
1137
+ return;
1138
+ }
1139
+
1140
+ const parent = state.nodes.find(n => n.id === state.selectedNode.parentId);
1141
+ if (parent) {
1142
+ parent.children = parent.children.filter(id => id !== state.selectedNode.id);
1143
+ }
1144
+
1145
+ // Recursively delete children
1146
+ const deleteChildren = (nodeId) => {
1147
+ const node = state.nodes.find(n => n.id === nodeId);
1148
+ if (node) {
1149
+ node.children.forEach(deleteChildren);
1150
+ state.nodes = state.nodes.filter(n => n.id !== nodeId);
1151
+ }
1152
+ };
1153
+
1154
+ deleteChildren(state.selectedNode.id);
1155
+
1156
+ // Also remove any custom links involving this node
1157
+ state.customLinks = state.customLinks.filter(link =>
1158
+ link.from !== state.selectedNode.id && link.to !== state.selectedNode.id
1159
+ );
1160
+
1161
+ // Select parent node if available, otherwise select first root node
1162
+ if (parent) {
1163
+ selectNode(parent);
1164
+ } else {
1165
+ const rootNodes = state.nodes.filter(n => n.parentId === null);
1166
+ if (rootNodes.length > 0) {
1167
+ selectNode(rootNodes[0]);
1168
+ }
1169
+ }
1170
+
1171
+ saveState();
1172
+ renderMindMap();
1173
+ updateNodeCount();
1174
+ updateStatus(`Deleted node "${state.selectedNode.text}"`);
1175
+
1176
+ // If tutorial is active, advance to next step
1177
+ if (state.tutorialActive) {
1178
+ advanceTutorial();
1179
+ }
1180
+ } else {
1181
+ updateStatus('Please select a node first', 'error');
1182
+ }
1183
+ }
1184
+
1185
+ // Toggle children visibility
1186
+ function toggleChildren(node) {
1187
+ node.collapsed = !node.collapsed;
1188
+ saveState();
1189
+ renderMindMap();
1190
+ updateStatus(`${node.collapsed ? 'Collapsed' : 'Expanded'} children of "${node.text}"`);
1191
+ }
1192
+
1193
+ // Toggle link creation mode
1194
+ function toggleLinkMode() {
1195
+ state.linkMode = !state.linkMode;
1196
+ state.linkSourceNode = null;
1197
+
1198
+ if (state.linkMode) {
1199
+ linkModeStatus.classList.remove('hidden');
1200
+ linkModeStatus.textContent = 'Link Mode: Select source node (ESC to cancel)';
1201
+ updateStatus('Link mode: Select first node to connect');
1202
+ } else {
1203
+ linkModeStatus.classList.add('hidden');
1204
+ tempLink.style.display = 'none';
1205
+ updateStatus('Link mode canceled');
1206
+ }
1207
+
1208
+ renderMindMap();
1209
+ }
1210
+
1211
+ // Zoom functions
1212
+ function zoomIn() {
1213
+ state.zoomLevel = Math.min(state.zoomLevel + 0.1, 2);
1214
+ applyZoom();
1215
+ }
1216
+
1217
+ function zoomOut() {
1218
+ state.zoomLevel = Math.max(state.zoomLevel - 0.1, 0.5);
1219
+ applyZoom();
1220
+ }
1221
+
1222
+ function resetZoom() {
1223
+ state.zoomLevel = 1;
1224
+ applyZoom();
1225
+ }
1226
+
1227
+ function applyZoom() {
1228
+ mindmapContainer.style.transform = `scale(${state.zoomLevel}) translate(${state.translateX}px, ${state.translateY}px)`;
1229
+ zoomLevelSpan.textContent = `Zoom: ${Math.round(state.zoomLevel * 100)}%`;
1230
+ }
1231
+
1232
+ // Toggle layout between radial and horizontal
1233
+ function toggleLayout() {
1234
+ state.layout = state.layout === 'radial' ? 'horizontal' : 'radial';
1235
+ saveState();
1236
+ renderMindMap();
1237
+ updateStatus(`Switched to ${state.layout} layout`);
1238
+ }
1239
+
1240
+ // Animate the mind map
1241
+ function animateMindMap() {
1242
+ const nodes = mindmapContainer.querySelectorAll('.node');
1243
+ nodes.forEach(node => {
1244
+ node.classList.add('pulse-animation');
1245
+ });
1246
+
1247
+ setTimeout(() => {
1248
+ nodes.forEach(node => {
1249
+ node.classList.remove('pulse-animation');
1250
+ });
1251
+ }, 3000);
1252
+
1253
+ updateStatus('Playing animation');
1254
+ }
1255
+
1256
+ // Color palette functions
1257
+ function toggleColorPalette(e) {
1258
+ e.stopPropagation();
1259
+ if (state.selectedNode) {
1260
+ const rect = colorBtn.getBoundingClientRect();
1261
+ colorPalette.style.display = 'grid';
1262
+ colorPalette.style.top = `${rect.bottom + 5}px`;
1263
+ colorPalette.style.left = `${rect.left}px`;
1264
+
1265
+ // Mark current color as selected
1266
+ const colorOptions = colorPalette.querySelectorAll('.color-option');
1267
+ colorOptions.forEach(option => {
1268
+ option.classList.toggle('selected', option.dataset.color === state.selectedNode.color);
1269
+ });
1270
+ } else {
1271
+ updateStatus('Please select a node first', 'error');
1272
+ }
1273
+ }
1274
+
1275
+ function closeColorPalette(e) {
1276
+ if (!colorPalette.contains(e.target) && e.target !== colorBtn) {
1277
+ colorPalette.style.display = 'none';
1278
+ } else if (e.target.classList.contains('color-option')) {
1279
+ if (state.selectedNode) {
1280
+ state.selectedNode.color = e.target.dataset.color;
1281
+ saveState();
1282
+ renderMindMap();
1283
+ updateStatus(`Changed node color`);
1284
+ }
1285
+ colorPalette.style.display = 'none';
1286
+ }
1287
+ }
1288
+
1289
+ // Export functions
1290
+ function showExportModal() {
1291
+ exportModal.classList.remove('hidden');
1292
+ }
1293
+
1294
+ function hideExportModal() {
1295
+ exportModal.classList.add('hidden');
1296
+ }
1297
+
1298
+ function exportMindMap() {
1299
+ const format = document.getElementById('export-format').value;
1300
+
1301
+ if (format === 'png') {
1302
+ // In a real app, you would use html2canvas or similar library
1303
+ updateStatus('Exporting as PNG... (simulated)');
1304
+ setTimeout(() => {
1305
+ updateStatus('Exported as PNG');
1306
+ hideExportModal();
1307
+ }, 1500);
1308
+ } else if (format === 'json') {
1309
+ const data = {
1310
+ nodes: state.nodes,
1311
+ layout: state.layout,
1312
+ customLinks: state.customLinks
1313
+ };
1314
+
1315
+ const blob = new Blob([JSON.stringify(data, null, 2)], { type: 'application/json' });
1316
+ const url = URL.createObjectURL(blob);
1317
+
1318
+ const a = document.createElement('a');
1319
+ a.href = url;
1320
+ a.download = 'mindmap.json';
1321
+ a.click();
1322
+
1323
+ URL.revokeObjectURL(url);
1324
+ updateStatus('Exported as JSON');
1325
+ hideExportModal();
1326
+ } else if (format === 'svg') {
1327
+ updateStatus('Exporting as SVG... (simulated)');
1328
+ setTimeout(() => {
1329
+ updateStatus('Exported as SVG');
1330
+ hideExportModal();
1331
+ }, 1500);
1332
+ } else if (format === 'pdf') {
1333
+ updateStatus('Exporting as PDF... (simulated)');
1334
+ setTimeout(() => {
1335
+ updateStatus('Exported as PDF');
1336
+ hideExportModal();
1337
+ }, 1500);
1338
+ }
1339
+ }
1340
+
1341
+ // Help modal functions
1342
+ function showHelpModal() {
1343
+ helpModal.classList.remove('hidden');
1344
+ }
1345
+
1346
+ function hideHelpModal() {
1347
+ helpModal.classList.add('hidden');
1348
+ }
1349
+
1350
+ // Tutorial functions
1351
+ function startTutorial() {
1352
+ state.tutorialActive = true;
1353
+ state.tutorialStep = 0;
1354
+ tutorialHighlights.classList.remove('hidden');
1355
+ hideHelpModal();
1356
+ advanceTutorial();
1357
+ }
1358
+
1359
+ function advanceTutorial() {
1360
+ const steps = [
1361
+ {
1362
+ message: "Welcome to MindMapper! Let's start by selecting the root node.",
1363
+ highlight: null,
1364
+ action: () => {
1365
+ const rootNode = state.nodes.find(n => n.parentId === null);
1366
+ if (rootNode) selectNode(rootNode);
1367
+ }
1368
+ },
1369
+ {
1370
+ message: "Great! Now try adding a child node by clicking the 'Add Child' button or pressing Enter.",
1371
+ highlight: 'tutorial-add-child',
1372
+ action: () => {
1373
+ const highlight = document.getElementById('tutorial-add-child');
1374
+ const btnRect = addChildBtn.getBoundingClientRect();
1375
+ highlight.style.width = `${btnRect.width}px`;
1376
+ highlight.style.height = `${btnRect.height}px`;
1377
+ highlight.style.top = `${btnRect.top}px`;
1378
+ highlight.style.left = `${btnRect.left}px`;
1379
+ }
1380
+ },
1381
+ {
1382
+ message: "Excellent! Now select the new node and try adding a sibling with the 'Add Sibling' button or Shift+Enter.",
1383
+ highlight: 'tutorial-add-sibling',
1384
+ action: () => {
1385
+ const highlight = document.getElementById('tutorial-add-sibling');
1386
+ const btnRect = addSiblingBtn.getBoundingClientRect();
1387
+ highlight.style.width = `${btnRect.width}px`;
1388
+ highlight.style.height = `${btnRect.height}px`;
1389
+ highlight.style.top = `${btnRect.top}px`;
1390
+ highlight.style.left = `${btnRect.left}px`;
1391
+ }
1392
+ },
1393
+ {
1394
+ message: "Well done! Now double-click a node or use the 'Edit Text' button to change its text.",
1395
+ highlight: 'tutorial-edit-text',
1396
+ action: () => {
1397
+ const highlight = document.getElementById('tutorial-edit-text');
1398
+ const btnRect = editTextBtn.getBoundingClientRect();
1399
+ highlight.style.width = `${btnRect.width}px`;
1400
+ highlight.style.height = `${btnRect.height}px`;
1401
+ highlight.style.top = `${btnRect.top}px`;
1402
+ highlight.style.left = `${btnRect.left}px`;
1403
+ }
1404
+ },
1405
+ {
1406
+ message: "Almost done! Try zooming in/out with the buttons or Ctrl + +/-.",
1407
+ highlight: 'tutorial-zoom',
1408
+ action: () => {
1409
+ const highlight = document.getElementById('tutorial-zoom');
1410
+ const btnRect = zoomInBtn.getBoundingClientRect();
1411
+ highlight.style.width = `${btnRect.width * 3 + 24}px`; // Cover all zoom buttons
1412
+ highlight.style.height = `${btnRect.height}px`;
1413
+ highlight.style.top = `${btnRect.top}px`;
1414
+ highlight.style.left = `${btnRect.left}px`;
1415
+ }
1416
+ },
1417
+ {
1418
+ message: "Congratulations! You've completed the tutorial. Explore other features on your own!",
1419
+ highlight: null,
1420
+ action: () => {
1421
+ state.tutorialActive = false;
1422
+ tutorialHighlights.classList.add('hidden');
1423
+ }
1424
+ }
1425
+ ];
1426
+
1427
+ if (state.tutorialStep < steps.length) {
1428
+ const step = steps[state.tutorialStep];
1429
+ updateStatus(step.message);
1430
+ if (step.highlight) {
1431
+ document.querySelectorAll('[id^="tutorial-"]').forEach(el => {
1432
+ el.style.display = 'none';
1433
+ });
1434
+ document.getElementById(step.highlight).style.display = 'block';
1435
+ step.action();
1436
+ } else {
1437
+ document.querySelectorAll('[id^="tutorial-"]').forEach(el => {
1438
+ el.style.display = 'none';
1439
+ });
1440
+ step.action();
1441
+ }
1442
+ state.tutorialStep++;
1443
+ }
1444
+ }
1445
+
1446
+ // Undo/redo functionality
1447
+ function saveState() {
1448
+ // Remove any states after current index (if we're not at the end)
1449
+ state.history = state.history.slice(0, state.historyIndex + 1);
1450
+
1451
+ // Save current state
1452
+ const stateCopy = {
1453
+ nodes: JSON.parse(JSON.stringify(state.nodes)),
1454
+ selectedNodeId: state.selectedNode ? state.selectedNode.id : null,
1455
+ customLinks: JSON.parse(JSON.stringify(state.customLinks)),
1456
+ layout: state.layout,
1457
+ zoomLevel: state.zoomLevel,
1458
+ translateX: state.translateX,
1459
+ translateY: state.translateY
1460
+ };
1461
+
1462
+ state.history.push(stateCopy);
1463
+ state.historyIndex = state.history.length - 1;
1464
+
1465
+ // Update undo/redo button states
1466
+ updateUndoRedoButtons();
1467
+ }
1468
+
1469
+ function undo() {
1470
+ if (state.historyIndex > 0) {
1471
+ state.historyIndex--;
1472
+ restoreState();
1473
+ }
1474
+ }
1475
+
1476
+ function redo() {
1477
+ if (state.historyIndex < state.history.length - 1) {
1478
+ state.historyIndex++;
1479
+ restoreState();
1480
+ }
1481
+ }
1482
+
1483
+ function restoreState() {
1484
+ const savedState = state.history[state.historyIndex];
1485
+
1486
+ state.nodes = JSON.parse(JSON.stringify(savedState.nodes));
1487
+ state.customLinks = JSON.parse(JSON.stringify(savedState.customLinks));
1488
+ state.layout = savedState.layout;
1489
+ state.zoomLevel = savedState.zoomLevel;
1490
+ state.translateX = savedState.translateX;
1491
+ state.translateY = savedState.translateY;
1492
+
1493
+ if (savedState.selectedNodeId) {
1494
+ const node = state.nodes.find(n => n.id === savedState.selectedNodeId);
1495
+ if (node) {
1496
+ state.selectedNode = node;
1497
+ }
1498
+ }
1499
+
1500
+ // Exit any special modes
1501
+ state.linkMode = false;
1502
+ state.linkSourceNode = null;
1503
+ linkModeStatus.classList.add('hidden');
1504
+ tempLink.style.display = 'none';
1505
+
1506
+ renderMindMap();
1507
+ updateNodeCount();
1508
+ updateStatus('State restored');
1509
+ updateUndoRedoButtons();
1510
+ }
1511
+
1512
+ function updateUndoRedoButtons() {
1513
+ undoBtn.disabled = state.historyIndex <= 0;
1514
+ redoBtn.disabled = state.historyIndex >= state.history.length - 1;
1515
+ }
1516
+
1517
+ // Panning functionality
1518
+ function startDrag(e) {
1519
+ if (e.target === mindmapContainer) {
1520
+ state.isDragging = true;
1521
+ state.dragStartX = e.clientX - state.translateX;
1522
+ state.dragStartY = e.clientY - state.translateY;
1523
+ mindmapContainer.style.cursor = 'grabbing';
1524
+ }
1525
+ }
1526
+
1527
+ function drag(e) {
1528
+ if (state.isDragging) {
1529
+ state.translateX = e.clientX - state.dragStartX;
1530
+ state.translateY = e.clientY - state.dragStartY;
1531
+ applyZoom();
1532
+ }
1533
+ }
1534
+
1535
+ function endDrag() {
1536
+ state.isDragging = false;
1537
+ mindmapContainer.style.cursor = '';
1538
+ }
1539
+
1540
+ // Center view on selected node
1541
+ function centerView() {
1542
+ if (state.selectedNode) {
1543
+ const containerWidth = mindmapContainer.clientWidth;
1544
+ const containerHeight = mindmapContainer.clientHeight;
1545
+
1546
+ state.translateX = containerWidth / 2 - state.selectedNode.x;
1547
+ state.translateY = containerHeight / 2 - state.selectedNode.y;
1548
+
1549
+ applyZoom();
1550
+ updateStatus(`Centered view on "${state.selectedNode.text}"`);
1551
+ }
1552
+ }
1553
+
1554
+ // Keyboard shortcuts
1555
+ function handleKeyboardShortcuts(e) {
1556
+ // Don't handle shortcuts when editing text
1557
+ if (document.activeElement.hasAttribute('contenteditable')) {
1558
+ return;
1559
+ }
1560
+
1561
+ // Check for Ctrl key combinations
1562
+ if (e.ctrlKey) {
1563
+ switch (e.key.toLowerCase()) {
1564
+ case 'z':
1565
+ e.preventDefault();
1566
+ undo();
1567
+ break;
1568
+ case 'y':
1569
+ e.preventDefault();
1570
+ redo();
1571
+ break;
1572
+ case '+':
1573
+ case '=':
1574
+ e.preventDefault();
1575
+ zoomIn();
1576
+ break;
1577
+ case '-':
1578
+ e.preventDefault();
1579
+ zoomOut();
1580
+ break;
1581
+ case '0':
1582
+ e.preventDefault();
1583
+ resetZoom();
1584
+ break;
1585
+ }
1586
+ return;
1587
+ }
1588
+
1589
+ // Single key shortcuts
1590
+ switch (e.key) {
1591
+ case 'Enter':
1592
+ e.preventDefault();
1593
+ if (e.shiftKey) {
1594
+ addSiblingNode();
1595
+ } else {
1596
+ addChildNode();
1597
+ }
1598
+ break;
1599
+ case 'Delete':
1600
+ e.preventDefault();
1601
+ deleteSelectedNode();
1602
+ break;
1603
+ case 'l':
1604
+ e.preventDefault();
1605
+ if (state.linkMode) {
1606
+ toggleLinkMode();
1607
+ } else {
1608
+ toggleLayout();
1609
+ }
1610
+ break;
1611
+ case 'a':
1612
+ e.preventDefault();
1613
+ animateMindMap();
1614
+ break;
1615
+ case 'h':
1616
+ e.preventDefault();
1617
+ showHelpModal();
1618
+ break;
1619
+ case 'e':
1620
+ e.preventDefault();
1621
+ showExportModal();
1622
+ break;
1623
+ case 'c':
1624
+ e.preventDefault();
1625
+ centerView();
1626
+ break;
1627
+ case 'Tab':
1628
+ e.preventDefault();
1629
+ if (state.selectedNode) {
1630
+ if (e.shiftKey) {
1631
+ // Focus on parent
1632
+ if (state.selectedNode.parentId) {
1633
+ const parent = state.nodes.find(n => n.id === state.selectedNode.parentId);
1634
+ if (parent) selectNode(parent);
1635
+ }
1636
+ } else {
1637
+ // Focus on first child
1638
+ if (state.selectedNode.children.length > 0) {
1639
+ const firstChild = state.nodes.find(n => n.id === state.selectedNode.children[0]);
1640
+ if (firstChild) selectNode(firstChild);
1641
+ }
1642
+ }
1643
+ }
1644
+ break;
1645
+ case 'Escape':
1646
+ if (state.linkMode) {
1647
+ e.preventDefault();
1648
+ toggleLinkMode();
1649
+ }
1650
+ break;
1651
+ }
1652
+ }
1653
+
1654
+ // UI update functions
1655
+ function updateNodeCount() {
1656
+ nodeCountSpan.textContent = `Nodes: ${state.nodes.length}`;
1657
+ }
1658
+
1659
+ function updateStatus(message, type = 'info') {
1660
+ statusMessageSpan.textContent = message;
1661
+ statusMessageSpan.className = '';
1662
+ statusMessageSpan.classList.add(type === 'error' ? 'text-red-500' : 'text-gray-600');
1663
+ }
1664
+
1665
+ // Wireframe functions
1666
+ function showWireframe() {
1667
+ wireframe.style.display = 'block';
1668
+ updateStatus('Showing wireframe');
1669
+ }
1670
+
1671
+ function hideWireframe() {
1672
+ wireframe.style.display = 'none';
1673
+ updateStatus('Ready');
1674
+ }
1675
+
1676
+ // Event listeners
1677
+ showWireframeBtn.addEventListener('click', showWireframe);
1678
+ closeWireframeBtn.addEventListener('click', hideWireframe);
1679
+ addChildBtn.addEventListener('click', addChildNode);
1680
+ addSiblingBtn.addEventListener('click', addSiblingNode);
1681
+ deleteNodeBtn.addEventListener('click', deleteSelectedNode);
1682
+ editTextBtn.addEventListener('click', editSelectedNodeText);
1683
+ colorBtn.addEventListener('click', toggleColorPalette);
1684
+ zoomInBtn.addEventListener('click', zoomIn);
1685
+ zoomOutBtn.addEventListener('click', zoomOut);
1686
+ resetZoomBtn.addEventListener('click', resetZoom);
1687
+ toggleLayoutBtn.addEventListener('click', toggleLayout);
1688
+ createLinkBtn.addEventListener('click', toggleLinkMode);
1689
+ animateBtn.addEventListener('click', animateMindMap);
1690
+ exportBtn.addEventListener('click', showExportModal);
1691
+ closeExportModal.addEventListener('click', hideExportModal);
1692
+ cancelExport.addEventListener('click', hideExportModal);
1693
+ confirmExport.addEventListener('click', exportMindMap);
1694
+ document.addEventListener('click', closeColorPalette);
1695
+ tutorialBtn.addEventListener('click', showHelpModal);
1696
+ helpModal.addEventListener('click', (e) => e.stopPropagation());
1697
+ closeHelpModal.addEventListener('click', hideHelpModal);
1698
+ startTutorialBtn.addEventListener('click', startTutorial);
1699
+ undoBtn.addEventListener('click', undo);
1700
+ redoBtn.addEventListener('click', redo);
1701
+ centerViewBtn.addEventListener('click', centerView);
1702
+
1703
+ // Export format change
1704
+ exportFormatSelect.addEventListener('change', function() {
1705
+ document.getElementById('export-png-options').classList.toggle('hidden', this.value !== 'png');
1706
+ document.getElementById('export-pdf-options').classList.toggle('hidden', this.value !== 'pdf');
1707
+ });
1708
+
1709
+ // Panning functionality
1710
+ mindmapContainer.addEventListener('mousedown', startDrag);
1711
+ document.addEventListener('mousemove', drag);
1712
+ document.addEventListener('mouseup', endDrag);
1713
+
1714
+ // Keyboard shortcuts
1715
+ document.addEventListener('keydown', handleKeyboardShortcuts);
1716
+ });
1717
+ </script>
1718
+ <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=tukangkustom/bomindflow" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
1719
+ </html>
prompts.txt ADDED
File without changes