cyclecad 0.9.7 → 1.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/app/explodeview-test-agent.html +1003 -0
- package/app/index.html +304 -39
- package/app/test-agent-v2.html +2 -2
- package/app/tutorials/advanced-runner.html +959 -0
- package/app/tutorials/assembly-explodeview.html +1108 -0
- package/app/tutorials/basic-runner.html +800 -0
- package/app/tutorials/explodeview-integration.html +1 -1
- package/app/tutorials/explodeview-runner.html +1407 -0
- package/app/tutorials/intermediate-runner.html +1306 -0
- package/app/tutorials/tutorial-runner.html +1421 -0
- package/package.json +1 -1
- package/test-report.html +1 -1
|
@@ -0,0 +1,1421 @@
|
|
|
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>cycleCAD + ExplodeView Interactive Tutorial</title>
|
|
7
|
+
<link rel="preconnect" href="https://fonts.googleapis.com">
|
|
8
|
+
<link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
|
|
9
|
+
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap" rel="stylesheet">
|
|
10
|
+
<style>
|
|
11
|
+
* {
|
|
12
|
+
margin: 0;
|
|
13
|
+
padding: 0;
|
|
14
|
+
box-sizing: border-box;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
body {
|
|
18
|
+
font-family: 'Inter', sans-serif;
|
|
19
|
+
background-color: #f0f2f5;
|
|
20
|
+
color: #1a1a2e;
|
|
21
|
+
height: 100vh;
|
|
22
|
+
display: flex;
|
|
23
|
+
flex-direction: column;
|
|
24
|
+
overflow: hidden;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/* Header */
|
|
28
|
+
header {
|
|
29
|
+
background-color: #ffffff;
|
|
30
|
+
border-bottom: 1px solid #d0d7de;
|
|
31
|
+
padding: 16px 20px;
|
|
32
|
+
flex-shrink: 0;
|
|
33
|
+
position: sticky;
|
|
34
|
+
top: 0;
|
|
35
|
+
z-index: 100;
|
|
36
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.08);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
.header-content {
|
|
40
|
+
display: flex;
|
|
41
|
+
align-items: center;
|
|
42
|
+
justify-content: space-between;
|
|
43
|
+
gap: 20px;
|
|
44
|
+
max-width: 2000px;
|
|
45
|
+
margin: 0 auto;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
.header-left {
|
|
49
|
+
display: flex;
|
|
50
|
+
align-items: center;
|
|
51
|
+
gap: 12px;
|
|
52
|
+
flex: 1;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
.header-title {
|
|
56
|
+
font-size: 18px;
|
|
57
|
+
font-weight: 700;
|
|
58
|
+
color: #0969da;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.progress-section {
|
|
62
|
+
display: flex;
|
|
63
|
+
align-items: center;
|
|
64
|
+
gap: 12px;
|
|
65
|
+
flex: 1;
|
|
66
|
+
min-width: 300px;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
.progress-bar-container {
|
|
70
|
+
flex: 1;
|
|
71
|
+
height: 8px;
|
|
72
|
+
background-color: #d0d7de;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
overflow: hidden;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.progress-bar {
|
|
78
|
+
height: 100%;
|
|
79
|
+
background: linear-gradient(90deg, #0969da, #54aeff);
|
|
80
|
+
width: 0%;
|
|
81
|
+
transition: width 0.3s ease;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
.step-counter {
|
|
85
|
+
font-size: 12px;
|
|
86
|
+
color: #57606a;
|
|
87
|
+
white-space: nowrap;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
.header-controls {
|
|
91
|
+
display: flex;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
button {
|
|
96
|
+
padding: 8px 16px;
|
|
97
|
+
border-radius: 6px;
|
|
98
|
+
border: 1px solid #d0d7de;
|
|
99
|
+
background-color: #ffffff;
|
|
100
|
+
color: #1a1a2e;
|
|
101
|
+
cursor: pointer;
|
|
102
|
+
font-size: 13px;
|
|
103
|
+
font-weight: 500;
|
|
104
|
+
transition: all 0.2s ease;
|
|
105
|
+
font-family: 'Inter', sans-serif;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
button:hover:not(:disabled) {
|
|
109
|
+
background-color: #f0f2f5;
|
|
110
|
+
border-color: #0969da;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
.btn-primary {
|
|
114
|
+
background-color: #0969da;
|
|
115
|
+
border-color: #0969da;
|
|
116
|
+
color: #fff;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
.btn-primary:hover:not(:disabled) {
|
|
120
|
+
background-color: #0860ca;
|
|
121
|
+
border-color: #0860ca;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.btn-danger {
|
|
125
|
+
background-color: #cf222e;
|
|
126
|
+
border-color: #cf222e;
|
|
127
|
+
color: #fff;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
.btn-danger:hover:not(:disabled) {
|
|
131
|
+
background-color: #a40e26;
|
|
132
|
+
border-color: #a40e26;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
button:disabled {
|
|
136
|
+
opacity: 0.5;
|
|
137
|
+
cursor: not-allowed;
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/* Main Container */
|
|
141
|
+
.container {
|
|
142
|
+
display: flex;
|
|
143
|
+
flex: 1;
|
|
144
|
+
overflow: hidden;
|
|
145
|
+
gap: 0;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
/* Left Panel - Tutorial Steps */
|
|
149
|
+
.tutorial-panel {
|
|
150
|
+
width: 35%;
|
|
151
|
+
background-color: #ffffff;
|
|
152
|
+
border-right: 1px solid #d0d7de;
|
|
153
|
+
display: flex;
|
|
154
|
+
flex-direction: column;
|
|
155
|
+
overflow: hidden;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
.steps-container {
|
|
159
|
+
flex: 1;
|
|
160
|
+
overflow-y: auto;
|
|
161
|
+
padding: 16px;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
.steps-container::-webkit-scrollbar {
|
|
165
|
+
width: 8px;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
.steps-container::-webkit-scrollbar-track {
|
|
169
|
+
background: transparent;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
.steps-container::-webkit-scrollbar-thumb {
|
|
173
|
+
background-color: #c1c8cf;
|
|
174
|
+
border-radius: 4px;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
.steps-container::-webkit-scrollbar-thumb:hover {
|
|
178
|
+
background-color: #57606a;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/* Step Card */
|
|
182
|
+
.step-card {
|
|
183
|
+
background-color: #f6f8fa;
|
|
184
|
+
border: 1px solid #d0d7de;
|
|
185
|
+
border-radius: 8px;
|
|
186
|
+
padding: 12px;
|
|
187
|
+
margin-bottom: 12px;
|
|
188
|
+
transition: all 0.2s ease;
|
|
189
|
+
cursor: pointer;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
.step-card:hover {
|
|
193
|
+
border-color: #0969da;
|
|
194
|
+
background-color: #eaf5ff;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
.step-card.active {
|
|
198
|
+
background-color: #ddf4ff;
|
|
199
|
+
border-color: #0969da;
|
|
200
|
+
animation: pulse 1s infinite;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
.step-card.active .step-badge {
|
|
204
|
+
background-color: #0969da;
|
|
205
|
+
color: #fff;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
.step-card.completed {
|
|
209
|
+
border-color: #1a7f37;
|
|
210
|
+
background-color: #dafbe1;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
.step-card.completed .step-badge {
|
|
214
|
+
background-color: #1a7f37;
|
|
215
|
+
color: #fff;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
.step-card.completed::after {
|
|
219
|
+
content: " ✓";
|
|
220
|
+
color: #1a7f37;
|
|
221
|
+
font-weight: 700;
|
|
222
|
+
margin-left: 4px;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
@keyframes pulse {
|
|
226
|
+
0%, 100% {
|
|
227
|
+
box-shadow: 0 0 0 0 rgba(9, 105, 218, 0.4);
|
|
228
|
+
}
|
|
229
|
+
50% {
|
|
230
|
+
box-shadow: 0 0 0 10px rgba(9, 105, 218, 0);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
.step-header {
|
|
235
|
+
display: flex;
|
|
236
|
+
align-items: center;
|
|
237
|
+
gap: 12px;
|
|
238
|
+
margin-bottom: 8px;
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
.step-badge {
|
|
242
|
+
background-color: #0969da;
|
|
243
|
+
color: #ffffff;
|
|
244
|
+
width: 28px;
|
|
245
|
+
height: 28px;
|
|
246
|
+
border-radius: 50%;
|
|
247
|
+
display: flex;
|
|
248
|
+
align-items: center;
|
|
249
|
+
justify-content: center;
|
|
250
|
+
font-size: 12px;
|
|
251
|
+
font-weight: 700;
|
|
252
|
+
flex-shrink: 0;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
.step-title {
|
|
256
|
+
font-size: 14px;
|
|
257
|
+
font-weight: 600;
|
|
258
|
+
color: #1a1a2e;
|
|
259
|
+
flex: 1;
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.step-status {
|
|
263
|
+
font-size: 11px;
|
|
264
|
+
color: #57606a;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
.step-description {
|
|
268
|
+
font-size: 13px;
|
|
269
|
+
color: #57606a;
|
|
270
|
+
margin-bottom: 8px;
|
|
271
|
+
line-height: 1.4;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
.step-details {
|
|
275
|
+
display: none;
|
|
276
|
+
background-color: #eaf5ff;
|
|
277
|
+
border-left: 2px solid #0969da;
|
|
278
|
+
padding: 8px 12px;
|
|
279
|
+
border-radius: 4px;
|
|
280
|
+
margin-bottom: 8px;
|
|
281
|
+
font-size: 12px;
|
|
282
|
+
color: #1a1a2e;
|
|
283
|
+
line-height: 1.5;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
.step-details.expanded {
|
|
287
|
+
display: block;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
.step-code {
|
|
291
|
+
background-color: #f6f8fa;
|
|
292
|
+
border: 1px solid #d0d7de;
|
|
293
|
+
border-radius: 4px;
|
|
294
|
+
padding: 8px;
|
|
295
|
+
margin-top: 4px;
|
|
296
|
+
font-family: 'Monaco', monospace;
|
|
297
|
+
font-size: 11px;
|
|
298
|
+
color: #79c0ff;
|
|
299
|
+
overflow-x: auto;
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
.step-actions {
|
|
303
|
+
display: flex;
|
|
304
|
+
gap: 8px;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
.step-run-btn {
|
|
308
|
+
flex: 1;
|
|
309
|
+
padding: 8px 12px;
|
|
310
|
+
font-size: 12px;
|
|
311
|
+
background-color: #0969da;
|
|
312
|
+
border-color: #0969da;
|
|
313
|
+
color: #ffffff;
|
|
314
|
+
font-weight: 600;
|
|
315
|
+
border-radius: 4px;
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
.step-run-btn:hover:not(:disabled) {
|
|
319
|
+
background-color: #79c0ff;
|
|
320
|
+
border-color: #79c0ff;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
.step-run-btn:disabled {
|
|
324
|
+
background-color: #d0d7de;
|
|
325
|
+
border-color: #d0d7de;
|
|
326
|
+
color: #57606a;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
.step-details-toggle {
|
|
330
|
+
padding: 6px 10px;
|
|
331
|
+
font-size: 11px;
|
|
332
|
+
background-color: transparent;
|
|
333
|
+
border: 1px solid #d0d7de;
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/* Right Panel - App Preview */
|
|
337
|
+
.app-panel {
|
|
338
|
+
flex: 1;
|
|
339
|
+
display: flex;
|
|
340
|
+
flex-direction: column;
|
|
341
|
+
background-color: #ffffff;
|
|
342
|
+
overflow: hidden;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
.app-header {
|
|
346
|
+
background-color: #f6f8fa;
|
|
347
|
+
border-bottom: 1px solid #d0d7de;
|
|
348
|
+
padding: 12px 16px;
|
|
349
|
+
display: flex;
|
|
350
|
+
align-items: center;
|
|
351
|
+
justify-content: space-between;
|
|
352
|
+
flex-shrink: 0;
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
.app-header-left {
|
|
356
|
+
display: flex;
|
|
357
|
+
align-items: center;
|
|
358
|
+
gap: 8px;
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
.app-indicator {
|
|
362
|
+
width: 10px;
|
|
363
|
+
height: 10px;
|
|
364
|
+
border-radius: 50%;
|
|
365
|
+
background-color: #238636;
|
|
366
|
+
animation: blink 1s infinite;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
@keyframes blink {
|
|
370
|
+
0%, 49%, 100% { opacity: 1; }
|
|
371
|
+
50%, 99% { opacity: 0.5; }
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
.app-url {
|
|
375
|
+
font-size: 12px;
|
|
376
|
+
color: #57606a;
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
.app-actions {
|
|
380
|
+
display: flex;
|
|
381
|
+
gap: 8px;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
.app-actions button {
|
|
385
|
+
padding: 6px 12px;
|
|
386
|
+
font-size: 12px;
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
#app-frame {
|
|
390
|
+
flex: 1;
|
|
391
|
+
border: none;
|
|
392
|
+
background-color: #fff;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/* Floating Shortcuts Panel */
|
|
396
|
+
.shortcuts-panel {
|
|
397
|
+
position: fixed;
|
|
398
|
+
bottom: 20px;
|
|
399
|
+
right: 20px;
|
|
400
|
+
width: 320px;
|
|
401
|
+
max-height: 500px;
|
|
402
|
+
background-color: #f6f8fa;
|
|
403
|
+
border: 1px solid #d0d7de;
|
|
404
|
+
border-radius: 8px;
|
|
405
|
+
display: none;
|
|
406
|
+
flex-direction: column;
|
|
407
|
+
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.5);
|
|
408
|
+
z-index: 200;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
.shortcuts-panel.visible {
|
|
412
|
+
display: flex;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
.shortcuts-header {
|
|
416
|
+
padding: 12px 16px;
|
|
417
|
+
border-bottom: 1px solid #d0d7de;
|
|
418
|
+
display: flex;
|
|
419
|
+
justify-content: space-between;
|
|
420
|
+
align-items: center;
|
|
421
|
+
flex-shrink: 0;
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
.shortcuts-header h3 {
|
|
425
|
+
font-size: 14px;
|
|
426
|
+
font-weight: 600;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
.shortcuts-close {
|
|
430
|
+
background: none;
|
|
431
|
+
border: none;
|
|
432
|
+
color: #57606a;
|
|
433
|
+
cursor: pointer;
|
|
434
|
+
padding: 0;
|
|
435
|
+
font-size: 16px;
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
.shortcuts-content {
|
|
439
|
+
flex: 1;
|
|
440
|
+
overflow-y: auto;
|
|
441
|
+
padding: 12px 16px;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
.shortcuts-content::-webkit-scrollbar {
|
|
445
|
+
width: 6px;
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
.shortcuts-content::-webkit-scrollbar-thumb {
|
|
449
|
+
background-color: #d0d7de;
|
|
450
|
+
border-radius: 3px;
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
.shortcut-item {
|
|
454
|
+
display: flex;
|
|
455
|
+
justify-content: space-between;
|
|
456
|
+
gap: 12px;
|
|
457
|
+
margin-bottom: 8px;
|
|
458
|
+
font-size: 12px;
|
|
459
|
+
padding-bottom: 8px;
|
|
460
|
+
border-bottom: 1px solid #d0d7de;
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
.shortcut-item:last-child {
|
|
464
|
+
border-bottom: none;
|
|
465
|
+
margin-bottom: 0;
|
|
466
|
+
padding-bottom: 0;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
.shortcut-key {
|
|
470
|
+
background-color: #ffffff;
|
|
471
|
+
border: 1px solid #d0d7de;
|
|
472
|
+
border-radius: 4px;
|
|
473
|
+
padding: 2px 6px;
|
|
474
|
+
font-family: 'Monaco', monospace;
|
|
475
|
+
font-weight: 600;
|
|
476
|
+
color: #0969da;
|
|
477
|
+
white-space: nowrap;
|
|
478
|
+
flex-shrink: 0;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
.shortcut-desc {
|
|
482
|
+
color: #57606a;
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
.shortcuts-toggle {
|
|
486
|
+
position: fixed;
|
|
487
|
+
bottom: 20px;
|
|
488
|
+
right: 20px;
|
|
489
|
+
width: 48px;
|
|
490
|
+
height: 48px;
|
|
491
|
+
border-radius: 50%;
|
|
492
|
+
background-color: #0969da;
|
|
493
|
+
border: none;
|
|
494
|
+
color: #ffffff;
|
|
495
|
+
font-size: 20px;
|
|
496
|
+
font-weight: 700;
|
|
497
|
+
cursor: pointer;
|
|
498
|
+
display: flex;
|
|
499
|
+
align-items: center;
|
|
500
|
+
justify-content: center;
|
|
501
|
+
transition: all 0.2s ease;
|
|
502
|
+
z-index: 150;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
.shortcuts-toggle:hover {
|
|
506
|
+
background-color: #79c0ff;
|
|
507
|
+
transform: scale(1.1);
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
.shortcuts-toggle.hidden {
|
|
511
|
+
display: none;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/* Footer */
|
|
515
|
+
footer {
|
|
516
|
+
background-color: #f6f8fa;
|
|
517
|
+
border-top: 1px solid #d0d7de;
|
|
518
|
+
padding: 12px 20px;
|
|
519
|
+
flex-shrink: 0;
|
|
520
|
+
font-size: 12px;
|
|
521
|
+
color: #57606a;
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
/* Loading Spinner */
|
|
525
|
+
.spinner {
|
|
526
|
+
display: inline-block;
|
|
527
|
+
width: 14px;
|
|
528
|
+
height: 14px;
|
|
529
|
+
border: 2px solid rgba(88, 166, 255, 0.3);
|
|
530
|
+
border-top-color: #0969da;
|
|
531
|
+
border-radius: 50%;
|
|
532
|
+
animation: spin 0.8s linear infinite;
|
|
533
|
+
}
|
|
534
|
+
|
|
535
|
+
@keyframes spin {
|
|
536
|
+
to { transform: rotate(360deg); }
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
/* Responsive */
|
|
540
|
+
@media (max-width: 1200px) {
|
|
541
|
+
.container {
|
|
542
|
+
flex-direction: column;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
.tutorial-panel {
|
|
546
|
+
width: 100%;
|
|
547
|
+
height: 40%;
|
|
548
|
+
border-right: none;
|
|
549
|
+
border-bottom: 1px solid #d0d7de;
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
.app-panel {
|
|
553
|
+
height: 60%;
|
|
554
|
+
}
|
|
555
|
+
}
|
|
556
|
+
|
|
557
|
+
/* Tooltip */
|
|
558
|
+
.tooltip {
|
|
559
|
+
position: absolute;
|
|
560
|
+
background-color: #eaeef2;
|
|
561
|
+
color: #1a1a2e;
|
|
562
|
+
padding: 8px 12px;
|
|
563
|
+
border-radius: 4px;
|
|
564
|
+
border: 1px solid #d0d7de;
|
|
565
|
+
font-size: 12px;
|
|
566
|
+
white-space: nowrap;
|
|
567
|
+
z-index: 1000;
|
|
568
|
+
pointer-events: none;
|
|
569
|
+
opacity: 0;
|
|
570
|
+
transition: opacity 0.2s ease;
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
.tooltip.visible {
|
|
574
|
+
opacity: 1;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
/* Message Box */
|
|
578
|
+
.message-box {
|
|
579
|
+
background-color: #eaeef2;
|
|
580
|
+
border: 1px solid #d0d7de;
|
|
581
|
+
border-radius: 6px;
|
|
582
|
+
padding: 12px;
|
|
583
|
+
margin-bottom: 12px;
|
|
584
|
+
font-size: 12px;
|
|
585
|
+
color: #57606a;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
.message-box.warning {
|
|
589
|
+
border-color: #d29922;
|
|
590
|
+
background-color: rgba(210, 153, 34, 0.1);
|
|
591
|
+
color: #d0883c;
|
|
592
|
+
}
|
|
593
|
+
</style>
|
|
594
|
+
</head>
|
|
595
|
+
<body>
|
|
596
|
+
<!-- Header -->
|
|
597
|
+
<header>
|
|
598
|
+
<div class="header-content">
|
|
599
|
+
<div class="header-left">
|
|
600
|
+
<div class="header-title">cycleCAD + ExplodeView Tutorial</div>
|
|
601
|
+
</div>
|
|
602
|
+
<div class="progress-section">
|
|
603
|
+
<div class="progress-bar-container">
|
|
604
|
+
<div class="progress-bar" id="progressBar"></div>
|
|
605
|
+
</div>
|
|
606
|
+
<div class="step-counter" id="stepCounter">Step 0 of 47</div>
|
|
607
|
+
</div>
|
|
608
|
+
<div class="header-controls">
|
|
609
|
+
<button class="btn-primary" id="runAllBtn">▶ Run All</button>
|
|
610
|
+
<button class="btn-danger" id="stopBtn" disabled>⏹ Stop</button>
|
|
611
|
+
</div>
|
|
612
|
+
</div>
|
|
613
|
+
</header>
|
|
614
|
+
|
|
615
|
+
<!-- Main Container -->
|
|
616
|
+
<div class="container">
|
|
617
|
+
<!-- Left Panel - Tutorial Steps -->
|
|
618
|
+
<div class="tutorial-panel">
|
|
619
|
+
<div class="steps-container" id="stepsContainer"></div>
|
|
620
|
+
<footer>
|
|
621
|
+
<div id="footerText">Select a step to begin</div>
|
|
622
|
+
</footer>
|
|
623
|
+
</div>
|
|
624
|
+
|
|
625
|
+
<!-- Right Panel - App Preview -->
|
|
626
|
+
<div class="app-panel">
|
|
627
|
+
<div class="app-header">
|
|
628
|
+
<div class="app-header-left">
|
|
629
|
+
<div class="app-indicator" id="appIndicator"></div>
|
|
630
|
+
<div class="app-url">cyclecad.com/app/</div>
|
|
631
|
+
</div>
|
|
632
|
+
<div class="app-actions">
|
|
633
|
+
<button id="openTabBtn">↗ Open in Tab</button>
|
|
634
|
+
<button id="reloadAppBtn">⟳ Reload</button>
|
|
635
|
+
</div>
|
|
636
|
+
</div>
|
|
637
|
+
<iframe id="app-frame" src="https://cyclecad.com/app/"></iframe>
|
|
638
|
+
<div class="message-box warning" style="margin: 12px; display: none;" id="crossOriginMsg">
|
|
639
|
+
<strong>Note:</strong> If steps don't auto-execute, the app may need to be on the same origin. Open cycleCAD in a separate tab and follow along manually.
|
|
640
|
+
</div>
|
|
641
|
+
</div>
|
|
642
|
+
</div>
|
|
643
|
+
|
|
644
|
+
<!-- Floating Shortcuts Panel -->
|
|
645
|
+
<button class="shortcuts-toggle" id="shortcutsToggle" title="Keyboard Shortcuts (?)">?</button>
|
|
646
|
+
<div class="shortcuts-panel" id="shortcutsPanel">
|
|
647
|
+
<div class="shortcuts-header">
|
|
648
|
+
<h3>Keyboard Shortcuts</h3>
|
|
649
|
+
<button class="shortcuts-close" id="shortcutsClose">✕</button>
|
|
650
|
+
</div>
|
|
651
|
+
<div class="shortcuts-content" id="shortcutsContent"></div>
|
|
652
|
+
</div>
|
|
653
|
+
|
|
654
|
+
<script>
|
|
655
|
+
// Tutorial Steps Data
|
|
656
|
+
const TUTORIAL_STEPS = [
|
|
657
|
+
// Part 1: Build Assembly (Steps 1-8)
|
|
658
|
+
{
|
|
659
|
+
number: 1,
|
|
660
|
+
title: "Create Caliper Body",
|
|
661
|
+
description: "Create a rectangular box for the caliper main body",
|
|
662
|
+
detail: "This will be the main structural component. We'll use a box shape with dimensions 80x40x25mm.",
|
|
663
|
+
command: "box 80x40x25",
|
|
664
|
+
type: "chat",
|
|
665
|
+
category: "Build Assembly"
|
|
666
|
+
},
|
|
667
|
+
{
|
|
668
|
+
number: 2,
|
|
669
|
+
title: "Add Mounting Holes",
|
|
670
|
+
description: "Create cylindrical holes for mounting",
|
|
671
|
+
detail: "Add cylindrical holes with 5mm radius and 20mm height for mounting the caliper.",
|
|
672
|
+
command: "cylinder 5mm radius 20mm tall",
|
|
673
|
+
type: "chat",
|
|
674
|
+
category: "Build Assembly"
|
|
675
|
+
},
|
|
676
|
+
{
|
|
677
|
+
number: 3,
|
|
678
|
+
title: "Create Brake Pad",
|
|
679
|
+
description: "Add the brake pad component",
|
|
680
|
+
detail: "Create a flat rectangular brake pad that will contact the rotor.",
|
|
681
|
+
command: "box 60x30x8",
|
|
682
|
+
type: "chat",
|
|
683
|
+
category: "Build Assembly"
|
|
684
|
+
},
|
|
685
|
+
{
|
|
686
|
+
number: 4,
|
|
687
|
+
title: "Create Pivot Pin",
|
|
688
|
+
description: "Add the central pivot pin",
|
|
689
|
+
detail: "This cylindrical pin acts as the pivot point for the caliper arms.",
|
|
690
|
+
command: "cylinder 8mm radius 40mm tall",
|
|
691
|
+
type: "chat",
|
|
692
|
+
category: "Build Assembly"
|
|
693
|
+
},
|
|
694
|
+
{
|
|
695
|
+
number: 5,
|
|
696
|
+
title: "Create Spring",
|
|
697
|
+
description: "Add the return spring mechanism",
|
|
698
|
+
detail: "A helical spring that returns the caliper to its neutral position.",
|
|
699
|
+
command: "spring 10mm radius 30mm tall",
|
|
700
|
+
type: "chat",
|
|
701
|
+
category: "Build Assembly"
|
|
702
|
+
},
|
|
703
|
+
{
|
|
704
|
+
number: 6,
|
|
705
|
+
title: "Create Adjustment Bolt",
|
|
706
|
+
description: "Add the adjustment mechanism",
|
|
707
|
+
detail: "A small bolt for fine-tuning the caliper clearance.",
|
|
708
|
+
command: "cylinder 4mm radius 25mm tall",
|
|
709
|
+
type: "chat",
|
|
710
|
+
category: "Build Assembly"
|
|
711
|
+
},
|
|
712
|
+
{
|
|
713
|
+
number: 7,
|
|
714
|
+
title: "Create Retaining Clip",
|
|
715
|
+
description: "Add the safety retaining clip",
|
|
716
|
+
detail: "A small clip that secures the spring in place.",
|
|
717
|
+
command: "box 20x5x2",
|
|
718
|
+
type: "chat",
|
|
719
|
+
category: "Build Assembly"
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
number: 8,
|
|
723
|
+
title: "Assemble Components",
|
|
724
|
+
description: "Combine all parts into final assembly",
|
|
725
|
+
detail: "Use the assembly workspace to position all components together.",
|
|
726
|
+
command: "gear 30mm 12 teeth",
|
|
727
|
+
type: "chat",
|
|
728
|
+
category: "Build Assembly"
|
|
729
|
+
},
|
|
730
|
+
|
|
731
|
+
// Part 2: ExplodeView Features (Steps 9-47)
|
|
732
|
+
{
|
|
733
|
+
number: 9,
|
|
734
|
+
title: "Toggle Viewer Mode",
|
|
735
|
+
description: "Switch to ExplodeView visualization",
|
|
736
|
+
detail: "Press 'V' to toggle between modeling and viewing modes.",
|
|
737
|
+
key: "v",
|
|
738
|
+
type: "key",
|
|
739
|
+
category: "ExplodeView Features"
|
|
740
|
+
},
|
|
741
|
+
{
|
|
742
|
+
number: 10,
|
|
743
|
+
title: "Explode Assembly",
|
|
744
|
+
description: "Expand all assembly components",
|
|
745
|
+
detail: "Visualize how components fit together by exploding the assembly.",
|
|
746
|
+
key: "e",
|
|
747
|
+
type: "key",
|
|
748
|
+
category: "ExplodeView Features"
|
|
749
|
+
},
|
|
750
|
+
{
|
|
751
|
+
number: 11,
|
|
752
|
+
title: "Section Cut",
|
|
753
|
+
description: "Create a cross-section view",
|
|
754
|
+
detail: "Press 'C' to slice through the model and see internal structure.",
|
|
755
|
+
key: "c",
|
|
756
|
+
type: "key",
|
|
757
|
+
category: "ExplodeView Features"
|
|
758
|
+
},
|
|
759
|
+
{
|
|
760
|
+
number: 12,
|
|
761
|
+
title: "Wireframe Toggle",
|
|
762
|
+
description: "Switch to wireframe rendering",
|
|
763
|
+
detail: "Press 'W' to see the model as wireframe for better understanding of edges.",
|
|
764
|
+
key: "w",
|
|
765
|
+
type: "key",
|
|
766
|
+
category: "ExplodeView Features"
|
|
767
|
+
},
|
|
768
|
+
{
|
|
769
|
+
number: 13,
|
|
770
|
+
title: "Grid Floor",
|
|
771
|
+
description: "Display reference grid",
|
|
772
|
+
detail: "Press 'G' to show/hide the ground plane grid for spatial reference.",
|
|
773
|
+
key: "g",
|
|
774
|
+
type: "key",
|
|
775
|
+
category: "ExplodeView Features"
|
|
776
|
+
},
|
|
777
|
+
{
|
|
778
|
+
number: 14,
|
|
779
|
+
title: "Fit to All",
|
|
780
|
+
description: "Auto-zoom to fit entire assembly",
|
|
781
|
+
detail: "Press 'F' to zoom and pan the view to show all components.",
|
|
782
|
+
key: "f",
|
|
783
|
+
type: "key",
|
|
784
|
+
category: "ExplodeView Features"
|
|
785
|
+
},
|
|
786
|
+
{
|
|
787
|
+
number: 15,
|
|
788
|
+
title: "Screenshot Export",
|
|
789
|
+
description: "Capture the current 3D view",
|
|
790
|
+
detail: "Press 'S' to save a high-resolution screenshot of the model.",
|
|
791
|
+
key: "s",
|
|
792
|
+
type: "key",
|
|
793
|
+
category: "ExplodeView Features"
|
|
794
|
+
},
|
|
795
|
+
{
|
|
796
|
+
number: 16,
|
|
797
|
+
title: "Blueprint Theme",
|
|
798
|
+
description: "Apply blueprint color scheme",
|
|
799
|
+
detail: "Toggle the technical blueprint visual theme with white background and blue edges.",
|
|
800
|
+
selector: ".blueprint-btn",
|
|
801
|
+
type: "click",
|
|
802
|
+
category: "ExplodeView Features"
|
|
803
|
+
},
|
|
804
|
+
{
|
|
805
|
+
number: 17,
|
|
806
|
+
title: "AI Part Identifier",
|
|
807
|
+
description: "Identify parts using AI vision",
|
|
808
|
+
detail: "Automatically classify parts and find equivalents on McMaster-Carr.",
|
|
809
|
+
selector: ".ai-identifier-btn",
|
|
810
|
+
type: "click",
|
|
811
|
+
category: "ExplodeView Features"
|
|
812
|
+
},
|
|
813
|
+
{
|
|
814
|
+
number: 18,
|
|
815
|
+
title: "Maintenance Heatmap",
|
|
816
|
+
description: "Visualize maintenance priorities",
|
|
817
|
+
detail: "Color-code parts by maintenance urgency: red=urgent, yellow=soon, green=stable.",
|
|
818
|
+
selector: ".maintenance-btn",
|
|
819
|
+
type: "click",
|
|
820
|
+
category: "ExplodeView Features"
|
|
821
|
+
},
|
|
822
|
+
{
|
|
823
|
+
number: 19,
|
|
824
|
+
title: "Weight Estimator",
|
|
825
|
+
description: "Calculate component weights",
|
|
826
|
+
detail: "Estimate mass of each part based on material properties and volume.",
|
|
827
|
+
selector: ".weight-btn",
|
|
828
|
+
type: "click",
|
|
829
|
+
category: "ExplodeView Features"
|
|
830
|
+
},
|
|
831
|
+
{
|
|
832
|
+
number: 20,
|
|
833
|
+
title: "Part Comparison",
|
|
834
|
+
description: "Compare two parts side-by-side",
|
|
835
|
+
detail: "Select two parts to compare dimensions, weight, and volume.",
|
|
836
|
+
selector: ".comparison-btn",
|
|
837
|
+
type: "click",
|
|
838
|
+
category: "ExplodeView Features"
|
|
839
|
+
},
|
|
840
|
+
{
|
|
841
|
+
number: 21,
|
|
842
|
+
title: "Clearance Checker",
|
|
843
|
+
description: "Detect interference between parts",
|
|
844
|
+
detail: "Identify parts that may be overlapping or too close together.",
|
|
845
|
+
selector: ".clearance-btn",
|
|
846
|
+
type: "click",
|
|
847
|
+
category: "ExplodeView Features"
|
|
848
|
+
},
|
|
849
|
+
{
|
|
850
|
+
number: 22,
|
|
851
|
+
title: "Assembly Validator",
|
|
852
|
+
description: "Score assembly quality",
|
|
853
|
+
detail: "Automatic validation returns A-F rating based on structure and completeness.",
|
|
854
|
+
selector: ".validator-btn",
|
|
855
|
+
type: "click",
|
|
856
|
+
category: "ExplodeView Features"
|
|
857
|
+
},
|
|
858
|
+
{
|
|
859
|
+
number: 23,
|
|
860
|
+
title: "Fastener Wizard",
|
|
861
|
+
description: "Identify and manage fasteners",
|
|
862
|
+
detail: "Detect bolts, screws, and nuts with torque specifications and supplier links.",
|
|
863
|
+
selector: ".fastener-btn",
|
|
864
|
+
type: "click",
|
|
865
|
+
category: "ExplodeView Features"
|
|
866
|
+
},
|
|
867
|
+
{
|
|
868
|
+
number: 24,
|
|
869
|
+
title: "Vibration Analysis",
|
|
870
|
+
description: "Simulate vibration patterns",
|
|
871
|
+
detail: "Animate vibration on rotating components like motors and pumps.",
|
|
872
|
+
selector: ".vibration-btn",
|
|
873
|
+
type: "click",
|
|
874
|
+
category: "ExplodeView Features"
|
|
875
|
+
},
|
|
876
|
+
{
|
|
877
|
+
number: 25,
|
|
878
|
+
title: "Thermal Heatmap",
|
|
879
|
+
description: "Visualize thermal distribution",
|
|
880
|
+
detail: "Color parts from blue (cold) to red (hot) based on thermal analysis.",
|
|
881
|
+
selector: ".thermal-btn",
|
|
882
|
+
type: "click",
|
|
883
|
+
category: "ExplodeView Features"
|
|
884
|
+
},
|
|
885
|
+
{
|
|
886
|
+
number: 26,
|
|
887
|
+
title: "Annotations",
|
|
888
|
+
description: "Add notes and labels to the model",
|
|
889
|
+
detail: "Press 'A' to place annotation pins with custom text labels.",
|
|
890
|
+
key: "a",
|
|
891
|
+
type: "key",
|
|
892
|
+
category: "ExplodeView Features"
|
|
893
|
+
},
|
|
894
|
+
{
|
|
895
|
+
number: 27,
|
|
896
|
+
title: "Measurement Tool",
|
|
897
|
+
description: "Measure distances and angles",
|
|
898
|
+
detail: "Press 'M' to activate the measurement tool for precise dimensions.",
|
|
899
|
+
key: "m",
|
|
900
|
+
type: "key",
|
|
901
|
+
category: "ExplodeView Features"
|
|
902
|
+
},
|
|
903
|
+
{
|
|
904
|
+
number: 28,
|
|
905
|
+
title: "QR Code",
|
|
906
|
+
description: "Generate QR codes for parts",
|
|
907
|
+
detail: "Create QR codes that deep-link to specific parts for documentation.",
|
|
908
|
+
selector: ".qr-btn",
|
|
909
|
+
type: "click",
|
|
910
|
+
category: "ExplodeView Features"
|
|
911
|
+
},
|
|
912
|
+
{
|
|
913
|
+
number: 29,
|
|
914
|
+
title: "Assembly Instructions",
|
|
915
|
+
description: "Generate step-by-step disassembly manual",
|
|
916
|
+
detail: "Auto-generate illustrated assembly/disassembly instructions from AI data.",
|
|
917
|
+
selector: ".instructions-btn",
|
|
918
|
+
type: "click",
|
|
919
|
+
category: "ExplodeView Features"
|
|
920
|
+
},
|
|
921
|
+
{
|
|
922
|
+
number: 30,
|
|
923
|
+
title: "Animation Sequence",
|
|
924
|
+
description: "Create assembly animation",
|
|
925
|
+
detail: "Record and playback assembly/disassembly animations with export.",
|
|
926
|
+
selector: ".animation-btn",
|
|
927
|
+
type: "click",
|
|
928
|
+
category: "ExplodeView Features"
|
|
929
|
+
},
|
|
930
|
+
{
|
|
931
|
+
number: 31,
|
|
932
|
+
title: "BOM Export",
|
|
933
|
+
description: "Export Bill of Materials",
|
|
934
|
+
detail: "Generate comprehensive CSV with part numbers, quantities, and costs.",
|
|
935
|
+
selector: ".bom-btn",
|
|
936
|
+
type: "click",
|
|
937
|
+
category: "ExplodeView Features"
|
|
938
|
+
},
|
|
939
|
+
{
|
|
940
|
+
number: 32,
|
|
941
|
+
title: "Hero Shots",
|
|
942
|
+
description: "Capture professional renderings",
|
|
943
|
+
detail: "Generate high-quality marketing images with custom lighting and camera angles.",
|
|
944
|
+
selector: ".hero-btn",
|
|
945
|
+
type: "click",
|
|
946
|
+
category: "ExplodeView Features"
|
|
947
|
+
},
|
|
948
|
+
{
|
|
949
|
+
number: 33,
|
|
950
|
+
title: "Share & Embed",
|
|
951
|
+
description: "Share model with others",
|
|
952
|
+
detail: "Generate shareable links and embeddable code for web integration.",
|
|
953
|
+
selector: ".share-btn",
|
|
954
|
+
type: "click",
|
|
955
|
+
category: "ExplodeView Features"
|
|
956
|
+
},
|
|
957
|
+
{
|
|
958
|
+
number: 34,
|
|
959
|
+
title: "STL Export",
|
|
960
|
+
description: "Export part as STL file",
|
|
961
|
+
detail: "Save individual parts or entire assembly as 3D printable STL format.",
|
|
962
|
+
selector: ".export-stl-btn",
|
|
963
|
+
type: "click",
|
|
964
|
+
category: "ExplodeView Features"
|
|
965
|
+
},
|
|
966
|
+
{
|
|
967
|
+
number: 35,
|
|
968
|
+
title: "Technical Report",
|
|
969
|
+
description: "Generate comprehensive analysis report",
|
|
970
|
+
detail: "Export full HTML report with BOM, assembly, maintenance, and timeline.",
|
|
971
|
+
selector: ".report-btn",
|
|
972
|
+
type: "click",
|
|
973
|
+
category: "ExplodeView Features"
|
|
974
|
+
},
|
|
975
|
+
{
|
|
976
|
+
number: 36,
|
|
977
|
+
title: "Part Info Card",
|
|
978
|
+
description: "View detailed part information",
|
|
979
|
+
detail: "Click on any 3D part to open detailed info card with specs and exports.",
|
|
980
|
+
type: "manual",
|
|
981
|
+
category: "ExplodeView Features",
|
|
982
|
+
detail: "Click on any part in the 3D view to open its information card."
|
|
983
|
+
},
|
|
984
|
+
{
|
|
985
|
+
number: 37,
|
|
986
|
+
title: "AI Vision Identifier",
|
|
987
|
+
description: "Use AI vision to classify parts",
|
|
988
|
+
detail: "Render and analyze part geometry using Gemini Vision API for auto-identification.",
|
|
989
|
+
selector: ".ai-vision-btn",
|
|
990
|
+
type: "click",
|
|
991
|
+
category: "ExplodeView Features"
|
|
992
|
+
},
|
|
993
|
+
{
|
|
994
|
+
number: 38,
|
|
995
|
+
title: "Batch AI Scan",
|
|
996
|
+
description: "Scan all parts with AI",
|
|
997
|
+
detail: "Automatically scan entire assembly and generate classified BOM with pricing.",
|
|
998
|
+
selector: ".batch-scan-btn",
|
|
999
|
+
type: "click",
|
|
1000
|
+
category: "ExplodeView Features"
|
|
1001
|
+
},
|
|
1002
|
+
{
|
|
1003
|
+
number: 39,
|
|
1004
|
+
title: "Smart Search",
|
|
1005
|
+
description: "Natural language part search",
|
|
1006
|
+
detail: "Search parts using human-friendly queries with automatic keyword expansion.",
|
|
1007
|
+
selector: ".smart-search-btn",
|
|
1008
|
+
type: "click",
|
|
1009
|
+
category: "ExplodeView Features"
|
|
1010
|
+
},
|
|
1011
|
+
{
|
|
1012
|
+
number: 40,
|
|
1013
|
+
title: "AI Chatbot",
|
|
1014
|
+
description: "Chat with the model AI",
|
|
1015
|
+
detail: "Ask questions about the model, assembly process, or get recommendations.",
|
|
1016
|
+
selector: ".chatbot-btn",
|
|
1017
|
+
type: "click",
|
|
1018
|
+
category: "ExplodeView Features"
|
|
1019
|
+
},
|
|
1020
|
+
{
|
|
1021
|
+
number: 41,
|
|
1022
|
+
title: "Service Mode",
|
|
1023
|
+
description: "Activate field technician workflow",
|
|
1024
|
+
detail: "Specialized interface for service engineers with checklist and reporting.",
|
|
1025
|
+
selector: ".service-btn",
|
|
1026
|
+
type: "click",
|
|
1027
|
+
category: "ExplodeView Features"
|
|
1028
|
+
},
|
|
1029
|
+
{
|
|
1030
|
+
number: 42,
|
|
1031
|
+
title: "Smart BOM",
|
|
1032
|
+
description: "AI-enhanced Bill of Materials",
|
|
1033
|
+
detail: "Generate BOM with AI-identified parts, pricing, and McMaster ordering.",
|
|
1034
|
+
selector: ".smart-bom-btn",
|
|
1035
|
+
type: "click",
|
|
1036
|
+
category: "ExplodeView Features"
|
|
1037
|
+
},
|
|
1038
|
+
{
|
|
1039
|
+
number: 43,
|
|
1040
|
+
title: "Dark/Light Theme",
|
|
1041
|
+
description: "Toggle interface theme",
|
|
1042
|
+
detail: "Switch between dark and light UI themes for your preference.",
|
|
1043
|
+
selector: ".theme-toggle-btn",
|
|
1044
|
+
type: "click",
|
|
1045
|
+
category: "ExplodeView Features"
|
|
1046
|
+
},
|
|
1047
|
+
{
|
|
1048
|
+
number: 44,
|
|
1049
|
+
title: "Multi-Language Support",
|
|
1050
|
+
description: "Change interface language",
|
|
1051
|
+
detail: "Select from EN/DE/FR/ES/IT/NL for internationalization.",
|
|
1052
|
+
selector: ".language-selector",
|
|
1053
|
+
type: "click",
|
|
1054
|
+
category: "ExplodeView Features"
|
|
1055
|
+
},
|
|
1056
|
+
{
|
|
1057
|
+
number: 45,
|
|
1058
|
+
title: "Keyboard Shortcuts Help",
|
|
1059
|
+
description: "View all keyboard shortcuts",
|
|
1060
|
+
detail: "Press '?' to open the comprehensive shortcuts reference panel.",
|
|
1061
|
+
key: "?",
|
|
1062
|
+
type: "key",
|
|
1063
|
+
category: "ExplodeView Features"
|
|
1064
|
+
},
|
|
1065
|
+
{
|
|
1066
|
+
number: 46,
|
|
1067
|
+
title: "Performance Monitor",
|
|
1068
|
+
description: "Monitor FPS and memory usage",
|
|
1069
|
+
detail: "Press Ctrl+Shift+F to display real-time performance metrics overlay.",
|
|
1070
|
+
key: "ctrl+shift+f",
|
|
1071
|
+
type: "key",
|
|
1072
|
+
category: "ExplodeView Features"
|
|
1073
|
+
},
|
|
1074
|
+
{
|
|
1075
|
+
number: 47,
|
|
1076
|
+
title: "Help System",
|
|
1077
|
+
description: "Access comprehensive help",
|
|
1078
|
+
detail: "Click help button to view 63 documented features with search functionality.",
|
|
1079
|
+
selector: ".help-btn",
|
|
1080
|
+
type: "click",
|
|
1081
|
+
category: "ExplodeView Features"
|
|
1082
|
+
}
|
|
1083
|
+
];
|
|
1084
|
+
|
|
1085
|
+
// Keyboard Shortcuts Reference
|
|
1086
|
+
const KEYBOARD_SHORTCUTS = [
|
|
1087
|
+
{ key: "V", desc: "Toggle Viewer Mode" },
|
|
1088
|
+
{ key: "E", desc: "Explode Assembly" },
|
|
1089
|
+
{ key: "C", desc: "Section Cut" },
|
|
1090
|
+
{ key: "W", desc: "Wireframe Toggle" },
|
|
1091
|
+
{ key: "G", desc: "Grid Floor" },
|
|
1092
|
+
{ key: "F", desc: "Fit to All" },
|
|
1093
|
+
{ key: "S", desc: "Screenshot Export" },
|
|
1094
|
+
{ key: "A", desc: "Annotations" },
|
|
1095
|
+
{ key: "M", desc: "Measurement Tool" },
|
|
1096
|
+
{ key: "?", desc: "Keyboard Shortcuts" },
|
|
1097
|
+
{ key: "Ctrl+Shift+F", desc: "Performance Monitor" },
|
|
1098
|
+
{ key: "T", desc: "Part Tree" },
|
|
1099
|
+
{ key: "R", desc: "Reset View" },
|
|
1100
|
+
{ key: "H", desc: "Help System" },
|
|
1101
|
+
{ key: "Del", desc: "Delete Part" },
|
|
1102
|
+
{ key: "Esc", desc: "Cancel Operation" }
|
|
1103
|
+
];
|
|
1104
|
+
|
|
1105
|
+
let currentStepIndex = 0;
|
|
1106
|
+
let isRunning = false;
|
|
1107
|
+
let completedSteps = new Set();
|
|
1108
|
+
|
|
1109
|
+
// Initialize
|
|
1110
|
+
function init() {
|
|
1111
|
+
renderSteps();
|
|
1112
|
+
setupEventListeners();
|
|
1113
|
+
populateShortcuts();
|
|
1114
|
+
updateProgress();
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
// Render step cards
|
|
1118
|
+
function renderSteps() {
|
|
1119
|
+
const container = document.getElementById('stepsContainer');
|
|
1120
|
+
container.innerHTML = '';
|
|
1121
|
+
|
|
1122
|
+
TUTORIAL_STEPS.forEach((step, index) => {
|
|
1123
|
+
const card = document.createElement('div');
|
|
1124
|
+
card.className = 'step-card';
|
|
1125
|
+
card.id = `step-${index}`;
|
|
1126
|
+
|
|
1127
|
+
const isCompleted = completedSteps.has(index);
|
|
1128
|
+
if (isCompleted) card.classList.add('completed');
|
|
1129
|
+
|
|
1130
|
+
card.innerHTML = `
|
|
1131
|
+
<div class="step-header">
|
|
1132
|
+
<div class="step-badge">${step.number}</div>
|
|
1133
|
+
<div class="step-title">${step.title}</div>
|
|
1134
|
+
<div class="step-status">${step.category}</div>
|
|
1135
|
+
</div>
|
|
1136
|
+
<div class="step-description">${step.description}</div>
|
|
1137
|
+
<div class="step-details" id="details-${index}">
|
|
1138
|
+
<div>${step.detail}</div>
|
|
1139
|
+
${step.command ? `<div class="step-code">Command: "${step.command}"</div>` : ''}
|
|
1140
|
+
${step.key ? `<div class="step-code">Key: ${step.key}</div>` : ''}
|
|
1141
|
+
</div>
|
|
1142
|
+
<div class="step-actions">
|
|
1143
|
+
<button class="step-run-btn" onclick="runStep(${index})">▶ Run</button>
|
|
1144
|
+
<button class="step-details-toggle" onclick="toggleDetails(${index})">Details</button>
|
|
1145
|
+
</div>
|
|
1146
|
+
`;
|
|
1147
|
+
|
|
1148
|
+
card.addEventListener('click', (e) => {
|
|
1149
|
+
if (!e.target.closest('button')) {
|
|
1150
|
+
currentStepIndex = index;
|
|
1151
|
+
updateProgress();
|
|
1152
|
+
}
|
|
1153
|
+
});
|
|
1154
|
+
|
|
1155
|
+
container.appendChild(card);
|
|
1156
|
+
});
|
|
1157
|
+
}
|
|
1158
|
+
|
|
1159
|
+
// Toggle step details
|
|
1160
|
+
function toggleDetails(index) {
|
|
1161
|
+
const details = document.getElementById(`details-${index}`);
|
|
1162
|
+
details.classList.toggle('expanded');
|
|
1163
|
+
}
|
|
1164
|
+
|
|
1165
|
+
// Run a single step
|
|
1166
|
+
async function runStep(index) {
|
|
1167
|
+
const step = TUTORIAL_STEPS[index];
|
|
1168
|
+
const card = document.getElementById(`step-${index}`);
|
|
1169
|
+
|
|
1170
|
+
card.classList.add('active');
|
|
1171
|
+
const runBtn = card.querySelector('.step-run-btn');
|
|
1172
|
+
runBtn.disabled = true;
|
|
1173
|
+
runBtn.innerHTML = `<span class="spinner"></span> Running...`;
|
|
1174
|
+
|
|
1175
|
+
try {
|
|
1176
|
+
if (step.type === 'chat') {
|
|
1177
|
+
await runChatCommand(step.command);
|
|
1178
|
+
} else if (step.type === 'key') {
|
|
1179
|
+
await runKeyCommand(step.key);
|
|
1180
|
+
} else if (step.type === 'click') {
|
|
1181
|
+
await runClickCommand(step.selector);
|
|
1182
|
+
} else if (step.type === 'manual') {
|
|
1183
|
+
// Manual steps just mark as done
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
await new Promise(resolve => setTimeout(resolve, 800));
|
|
1187
|
+
card.classList.remove('active');
|
|
1188
|
+
card.classList.add('completed');
|
|
1189
|
+
completedSteps.add(index);
|
|
1190
|
+
|
|
1191
|
+
runBtn.disabled = false;
|
|
1192
|
+
runBtn.innerHTML = '✓ Done';
|
|
1193
|
+
} catch (error) {
|
|
1194
|
+
console.error(`Step ${step.number} error:`, error);
|
|
1195
|
+
card.classList.remove('active');
|
|
1196
|
+
runBtn.disabled = false;
|
|
1197
|
+
runBtn.innerHTML = '✕ Error';
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
updateProgress();
|
|
1201
|
+
}
|
|
1202
|
+
|
|
1203
|
+
// Run chat command
|
|
1204
|
+
async function runChatCommand(command) {
|
|
1205
|
+
try {
|
|
1206
|
+
const iframe = document.getElementById('app-frame');
|
|
1207
|
+
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1208
|
+
|
|
1209
|
+
// Try multiple possible selectors for chat input
|
|
1210
|
+
let input = doc.querySelector('#chat-input') ||
|
|
1211
|
+
doc.querySelector('input[placeholder*="chat"]') ||
|
|
1212
|
+
doc.querySelector('input[placeholder*="Chat"]') ||
|
|
1213
|
+
doc.querySelector('textarea[placeholder*="chat"]') ||
|
|
1214
|
+
doc.querySelector('textarea');
|
|
1215
|
+
|
|
1216
|
+
if (!input) {
|
|
1217
|
+
console.warn('Chat input not found');
|
|
1218
|
+
showCrossOriginWarning();
|
|
1219
|
+
return;
|
|
1220
|
+
}
|
|
1221
|
+
|
|
1222
|
+
input.value = command;
|
|
1223
|
+
input.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1224
|
+
input.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1225
|
+
|
|
1226
|
+
// Find and click send button
|
|
1227
|
+
const sendBtn = doc.querySelector('#chat-send') ||
|
|
1228
|
+
doc.querySelector('button:contains("Send")') ||
|
|
1229
|
+
input.closest('form')?.querySelector('button[type="submit"]') ||
|
|
1230
|
+
input.parentElement?.querySelector('button');
|
|
1231
|
+
|
|
1232
|
+
if (sendBtn) {
|
|
1233
|
+
sendBtn.click();
|
|
1234
|
+
} else {
|
|
1235
|
+
input.dispatchEvent(new KeyboardEvent('keydown', {
|
|
1236
|
+
key: 'Enter',
|
|
1237
|
+
code: 'Enter',
|
|
1238
|
+
keyCode: 13,
|
|
1239
|
+
bubbles: true
|
|
1240
|
+
}));
|
|
1241
|
+
}
|
|
1242
|
+
} catch (error) {
|
|
1243
|
+
console.warn('Chat command error (expected for cross-origin):', error);
|
|
1244
|
+
showCrossOriginWarning();
|
|
1245
|
+
}
|
|
1246
|
+
}
|
|
1247
|
+
|
|
1248
|
+
// Run keyboard command
|
|
1249
|
+
async function runKeyCommand(keyStr) {
|
|
1250
|
+
try {
|
|
1251
|
+
const iframe = document.getElementById('app-frame');
|
|
1252
|
+
const iframeWindow = iframe.contentWindow;
|
|
1253
|
+
|
|
1254
|
+
const key = keyStr.toLowerCase();
|
|
1255
|
+
const isCtrlShift = key.includes('ctrl+shift');
|
|
1256
|
+
const ctrlKey = keyStr.toLowerCase().includes('ctrl');
|
|
1257
|
+
const shiftKey = keyStr.toLowerCase().includes('shift');
|
|
1258
|
+
|
|
1259
|
+
// Extract actual key
|
|
1260
|
+
let actualKey = key.replace('ctrl+', '').replace('shift+', '').trim();
|
|
1261
|
+
|
|
1262
|
+
const event = new KeyboardEvent('keydown', {
|
|
1263
|
+
key: actualKey,
|
|
1264
|
+
code: `Key${actualKey.toUpperCase()}`,
|
|
1265
|
+
keyCode: actualKey.charCodeAt(0),
|
|
1266
|
+
ctrlKey: ctrlKey,
|
|
1267
|
+
shiftKey: shiftKey,
|
|
1268
|
+
bubbles: true,
|
|
1269
|
+
cancelable: true
|
|
1270
|
+
});
|
|
1271
|
+
|
|
1272
|
+
iframeWindow.dispatchEvent(event);
|
|
1273
|
+
} catch (error) {
|
|
1274
|
+
console.warn('Keyboard command error (expected for cross-origin):', error);
|
|
1275
|
+
showCrossOriginWarning();
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// Run click command
|
|
1280
|
+
async function runClickCommand(selector) {
|
|
1281
|
+
try {
|
|
1282
|
+
const iframe = document.getElementById('app-frame');
|
|
1283
|
+
const doc = iframe.contentDocument || iframe.contentWindow.document;
|
|
1284
|
+
const element = doc.querySelector(selector);
|
|
1285
|
+
|
|
1286
|
+
if (element) {
|
|
1287
|
+
element.click();
|
|
1288
|
+
element.focus();
|
|
1289
|
+
} else {
|
|
1290
|
+
console.warn(`Selector not found: ${selector}`);
|
|
1291
|
+
showCrossOriginWarning();
|
|
1292
|
+
}
|
|
1293
|
+
} catch (error) {
|
|
1294
|
+
console.warn('Click command error (expected for cross-origin):', error);
|
|
1295
|
+
showCrossOriginWarning();
|
|
1296
|
+
}
|
|
1297
|
+
}
|
|
1298
|
+
|
|
1299
|
+
// Run all steps sequentially
|
|
1300
|
+
async function runAllSteps() {
|
|
1301
|
+
isRunning = true;
|
|
1302
|
+
document.getElementById('runAllBtn').disabled = true;
|
|
1303
|
+
document.getElementById('stopBtn').disabled = false;
|
|
1304
|
+
|
|
1305
|
+
for (let i = 0; i < TUTORIAL_STEPS.length && isRunning; i++) {
|
|
1306
|
+
currentStepIndex = i;
|
|
1307
|
+
updateProgress();
|
|
1308
|
+
await runStep(i);
|
|
1309
|
+
await new Promise(resolve => setTimeout(resolve, 3000));
|
|
1310
|
+
}
|
|
1311
|
+
|
|
1312
|
+
isRunning = false;
|
|
1313
|
+
document.getElementById('runAllBtn').disabled = false;
|
|
1314
|
+
document.getElementById('stopBtn').disabled = true;
|
|
1315
|
+
}
|
|
1316
|
+
|
|
1317
|
+
// Stop running
|
|
1318
|
+
function stopRunning() {
|
|
1319
|
+
isRunning = false;
|
|
1320
|
+
document.getElementById('runAllBtn').disabled = false;
|
|
1321
|
+
document.getElementById('stopBtn').disabled = true;
|
|
1322
|
+
|
|
1323
|
+
// Remove active state from all cards
|
|
1324
|
+
document.querySelectorAll('.step-card.active').forEach(card => {
|
|
1325
|
+
card.classList.remove('active');
|
|
1326
|
+
const btn = card.querySelector('.step-run-btn');
|
|
1327
|
+
if (btn) {
|
|
1328
|
+
btn.disabled = false;
|
|
1329
|
+
btn.innerHTML = '▶ Run';
|
|
1330
|
+
}
|
|
1331
|
+
});
|
|
1332
|
+
}
|
|
1333
|
+
|
|
1334
|
+
// Update progress
|
|
1335
|
+
function updateProgress() {
|
|
1336
|
+
const total = TUTORIAL_STEPS.length;
|
|
1337
|
+
const completed = completedSteps.size;
|
|
1338
|
+
const percentage = (completed / total) * 100;
|
|
1339
|
+
|
|
1340
|
+
document.getElementById('progressBar').style.width = percentage + '%';
|
|
1341
|
+
document.getElementById('stepCounter').textContent = `Step ${currentStepIndex + 1} of ${total}`;
|
|
1342
|
+
document.getElementById('footerText').textContent =
|
|
1343
|
+
`${completed} / ${total} completed • ${TUTORIAL_STEPS[currentStepIndex]?.title || 'Tutorial'}`;
|
|
1344
|
+
|
|
1345
|
+
// Scroll to current step
|
|
1346
|
+
const card = document.getElementById(`step-${currentStepIndex}`);
|
|
1347
|
+
if (card) {
|
|
1348
|
+
card.scrollIntoView({ behavior: 'smooth', block: 'nearest' });
|
|
1349
|
+
}
|
|
1350
|
+
}
|
|
1351
|
+
|
|
1352
|
+
// Show cross-origin warning
|
|
1353
|
+
let warningShown = false;
|
|
1354
|
+
function showCrossOriginWarning() {
|
|
1355
|
+
if (!warningShown) {
|
|
1356
|
+
const msg = document.getElementById('crossOriginMsg');
|
|
1357
|
+
if (msg) {
|
|
1358
|
+
msg.style.display = 'block';
|
|
1359
|
+
warningShown = true;
|
|
1360
|
+
}
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
|
|
1364
|
+
// Populate shortcuts panel
|
|
1365
|
+
function populateShortcuts() {
|
|
1366
|
+
const content = document.getElementById('shortcutsContent');
|
|
1367
|
+
content.innerHTML = KEYBOARD_SHORTCUTS.map(s => `
|
|
1368
|
+
<div class="shortcut-item">
|
|
1369
|
+
<span class="shortcut-desc">${s.desc}</span>
|
|
1370
|
+
<span class="shortcut-key">${s.key}</span>
|
|
1371
|
+
</div>
|
|
1372
|
+
`).join('');
|
|
1373
|
+
}
|
|
1374
|
+
|
|
1375
|
+
// Setup event listeners
|
|
1376
|
+
function setupEventListeners() {
|
|
1377
|
+
document.getElementById('runAllBtn').addEventListener('click', runAllSteps);
|
|
1378
|
+
document.getElementById('stopBtn').addEventListener('click', stopRunning);
|
|
1379
|
+
document.getElementById('openTabBtn').addEventListener('click', () => {
|
|
1380
|
+
window.open('https://cyclecad.com/app/', '_blank');
|
|
1381
|
+
});
|
|
1382
|
+
document.getElementById('reloadAppBtn').addEventListener('click', () => {
|
|
1383
|
+
document.getElementById('app-frame').src = 'https://cyclecad.com/app/';
|
|
1384
|
+
});
|
|
1385
|
+
|
|
1386
|
+
// Shortcuts panel
|
|
1387
|
+
const shortcutsToggle = document.getElementById('shortcutsToggle');
|
|
1388
|
+
const shortcutsPanel = document.getElementById('shortcutsPanel');
|
|
1389
|
+
const shortcutsClose = document.getElementById('shortcutsClose');
|
|
1390
|
+
|
|
1391
|
+
shortcutsToggle.addEventListener('click', () => {
|
|
1392
|
+
shortcutsPanel.classList.toggle('visible');
|
|
1393
|
+
if (shortcutsPanel.classList.contains('visible')) {
|
|
1394
|
+
shortcutsToggle.classList.add('hidden');
|
|
1395
|
+
}
|
|
1396
|
+
});
|
|
1397
|
+
|
|
1398
|
+
shortcutsClose.addEventListener('click', () => {
|
|
1399
|
+
shortcutsPanel.classList.remove('visible');
|
|
1400
|
+
shortcutsToggle.classList.remove('hidden');
|
|
1401
|
+
});
|
|
1402
|
+
|
|
1403
|
+
// Keyboard shortcuts for tutorial
|
|
1404
|
+
document.addEventListener('keydown', (e) => {
|
|
1405
|
+
if (e.key === '?') {
|
|
1406
|
+
e.preventDefault();
|
|
1407
|
+
shortcutsToggle.click();
|
|
1408
|
+
}
|
|
1409
|
+
});
|
|
1410
|
+
|
|
1411
|
+
// Reload app on iframe error
|
|
1412
|
+
document.getElementById('app-frame').addEventListener('error', () => {
|
|
1413
|
+
console.error('App frame error');
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// Start
|
|
1418
|
+
init();
|
|
1419
|
+
</script>
|
|
1420
|
+
</body>
|
|
1421
|
+
</html>
|