cyclecad 0.9.8 → 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.
@@ -0,0 +1,1108 @@
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>Assembly + ExplodeView 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
+ :root {
12
+ --bg-primary: #1e1e1e;
13
+ --bg-secondary: #2d2d2d;
14
+ --bg-tertiary: #3a3a3a;
15
+ --accent: #58a6ff;
16
+ --success: #3fb950;
17
+ --warning: #d29922;
18
+ --danger: #f85149;
19
+ --border: #404040;
20
+ --text-primary: #e6edf3;
21
+ --text-secondary: #8b949e;
22
+ --transition: 0.2s ease;
23
+ }
24
+
25
+ * {
26
+ margin: 0;
27
+ padding: 0;
28
+ box-sizing: border-box;
29
+ }
30
+
31
+ body {
32
+ font-family: 'Inter', sans-serif;
33
+ background-color: var(--bg-primary);
34
+ color: var(--text-primary);
35
+ line-height: 1.6;
36
+ overflow-x: hidden;
37
+ }
38
+
39
+ /* Navbar */
40
+ .navbar {
41
+ position: sticky;
42
+ top: 0;
43
+ z-index: 1000;
44
+ background: linear-gradient(180deg, var(--bg-secondary) 0%, rgba(45, 45, 45, 0.9) 100%);
45
+ border-bottom: 1px solid var(--border);
46
+ padding: 16px 24px;
47
+ display: flex;
48
+ justify-content: space-between;
49
+ align-items: center;
50
+ backdrop-filter: blur(8px);
51
+ }
52
+
53
+ .navbar-left {
54
+ display: flex;
55
+ align-items: center;
56
+ gap: 16px;
57
+ }
58
+
59
+ .navbar-back {
60
+ color: var(--accent);
61
+ text-decoration: none;
62
+ font-weight: 500;
63
+ font-size: 14px;
64
+ padding: 8px 12px;
65
+ border-radius: 6px;
66
+ transition: background-color var(--transition);
67
+ cursor: pointer;
68
+ }
69
+
70
+ .navbar-back:hover {
71
+ background-color: var(--bg-tertiary);
72
+ }
73
+
74
+ .navbar-title {
75
+ font-size: 18px;
76
+ font-weight: 600;
77
+ }
78
+
79
+ .progress-container {
80
+ flex: 1;
81
+ margin: 0 32px;
82
+ max-width: 300px;
83
+ }
84
+
85
+ .progress-bar {
86
+ width: 100%;
87
+ height: 6px;
88
+ background: var(--bg-tertiary);
89
+ border-radius: 3px;
90
+ overflow: hidden;
91
+ margin-bottom: 4px;
92
+ }
93
+
94
+ .progress-fill {
95
+ height: 100%;
96
+ background: linear-gradient(90deg, var(--accent), var(--success));
97
+ transition: width 0.3s ease;
98
+ width: 0%;
99
+ }
100
+
101
+ .progress-text {
102
+ font-size: 12px;
103
+ color: var(--text-secondary);
104
+ text-align: center;
105
+ }
106
+
107
+ .navbar-right {
108
+ display: flex;
109
+ gap: 12px;
110
+ }
111
+
112
+ .btn-icon {
113
+ width: 36px;
114
+ height: 36px;
115
+ border-radius: 6px;
116
+ border: 1px solid var(--border);
117
+ background: var(--bg-tertiary);
118
+ color: var(--text-primary);
119
+ cursor: pointer;
120
+ font-size: 16px;
121
+ transition: all var(--transition);
122
+ display: flex;
123
+ align-items: center;
124
+ justify-content: center;
125
+ }
126
+
127
+ .btn-icon:hover {
128
+ background: var(--bg-secondary);
129
+ border-color: var(--accent);
130
+ color: var(--accent);
131
+ }
132
+
133
+ /* Main Container */
134
+ .container {
135
+ max-width: 900px;
136
+ margin: 40px auto;
137
+ padding: 0 24px;
138
+ }
139
+
140
+ /* Tab Navigation */
141
+ .tab-nav {
142
+ display: flex;
143
+ gap: 12px;
144
+ margin-bottom: 32px;
145
+ border-bottom: 1px solid var(--border);
146
+ padding-bottom: 16px;
147
+ flex-wrap: wrap;
148
+ }
149
+
150
+ .tab-btn {
151
+ padding: 8px 16px;
152
+ border: none;
153
+ background: none;
154
+ color: var(--text-secondary);
155
+ font-size: 14px;
156
+ font-weight: 500;
157
+ cursor: pointer;
158
+ transition: all var(--transition);
159
+ border-bottom: 2px solid transparent;
160
+ position: relative;
161
+ bottom: -16px;
162
+ }
163
+
164
+ .tab-btn:hover {
165
+ color: var(--text-primary);
166
+ }
167
+
168
+ .tab-btn.active {
169
+ color: var(--accent);
170
+ border-bottom-color: var(--accent);
171
+ }
172
+
173
+ .tab-content {
174
+ display: none;
175
+ }
176
+
177
+ .tab-content.active {
178
+ display: block;
179
+ }
180
+
181
+ /* Step Card */
182
+ .step-card {
183
+ background: var(--bg-secondary);
184
+ border: 1px solid var(--border);
185
+ border-radius: 8px;
186
+ margin-bottom: 24px;
187
+ overflow: hidden;
188
+ transition: all var(--transition);
189
+ }
190
+
191
+ .step-card:hover {
192
+ border-color: var(--accent);
193
+ box-shadow: 0 8px 16px rgba(88, 166, 255, 0.1);
194
+ }
195
+
196
+ .step-header {
197
+ padding: 20px;
198
+ display: flex;
199
+ align-items: center;
200
+ justify-content: space-between;
201
+ cursor: pointer;
202
+ background: linear-gradient(90deg, var(--bg-tertiary) 0%, var(--bg-secondary) 100%);
203
+ transition: background-color var(--transition);
204
+ }
205
+
206
+ .step-header:hover {
207
+ background: linear-gradient(90deg, var(--border) 0%, var(--bg-tertiary) 100%);
208
+ }
209
+
210
+ .step-header-left {
211
+ display: flex;
212
+ align-items: center;
213
+ gap: 16px;
214
+ flex: 1;
215
+ }
216
+
217
+ .step-badge {
218
+ width: 48px;
219
+ height: 48px;
220
+ border-radius: 50%;
221
+ background: linear-gradient(135deg, var(--accent), #0d47a1);
222
+ display: flex;
223
+ align-items: center;
224
+ justify-content: center;
225
+ font-weight: 700;
226
+ font-size: 18px;
227
+ flex-shrink: 0;
228
+ box-shadow: 0 4px 12px rgba(88, 166, 255, 0.2);
229
+ }
230
+
231
+ .step-badge.done {
232
+ background: linear-gradient(135deg, var(--success), #1b5e20);
233
+ }
234
+
235
+ .step-title {
236
+ font-weight: 600;
237
+ font-size: 16px;
238
+ color: var(--text-primary);
239
+ }
240
+
241
+ .step-description {
242
+ font-size: 13px;
243
+ color: var(--text-secondary);
244
+ margin-top: 2px;
245
+ }
246
+
247
+ .step-checkbox {
248
+ width: 24px;
249
+ height: 24px;
250
+ cursor: pointer;
251
+ accent-color: var(--success);
252
+ }
253
+
254
+ .step-body {
255
+ display: none;
256
+ padding: 20px;
257
+ border-top: 1px solid var(--border);
258
+ background: var(--bg-primary);
259
+ }
260
+
261
+ .step-body.expanded {
262
+ display: block;
263
+ }
264
+
265
+ .step-body-grid {
266
+ display: grid;
267
+ grid-template-columns: 1fr 1fr;
268
+ gap: 24px;
269
+ margin-bottom: 24px;
270
+ }
271
+
272
+ @media (max-width: 1024px) {
273
+ .step-body-grid {
274
+ grid-template-columns: 1fr;
275
+ }
276
+ }
277
+
278
+ /* Card Styles */
279
+ .card-section {
280
+ background: var(--bg-tertiary);
281
+ border: 1px solid var(--border);
282
+ border-radius: 6px;
283
+ padding: 16px;
284
+ margin-bottom: 16px;
285
+ }
286
+
287
+ .card-title {
288
+ font-weight: 600;
289
+ font-size: 13px;
290
+ color: var(--accent);
291
+ text-transform: uppercase;
292
+ letter-spacing: 0.5px;
293
+ margin-bottom: 12px;
294
+ }
295
+
296
+ .card-content {
297
+ font-size: 13px;
298
+ line-height: 1.7;
299
+ color: var(--text-primary);
300
+ }
301
+
302
+ .method-item {
303
+ display: flex;
304
+ align-items: flex-start;
305
+ gap: 12px;
306
+ margin-bottom: 10px;
307
+ padding: 10px;
308
+ background: var(--bg-secondary);
309
+ border-radius: 4px;
310
+ border-left: 2px solid var(--accent);
311
+ }
312
+
313
+ .method-label {
314
+ font-weight: 600;
315
+ color: var(--accent);
316
+ min-width: 80px;
317
+ font-size: 12px;
318
+ }
319
+
320
+ .method-text {
321
+ font-size: 13px;
322
+ color: var(--text-secondary);
323
+ flex: 1;
324
+ font-family: 'Courier New', monospace;
325
+ }
326
+
327
+ /* SVG Diagram */
328
+ .diagram-container {
329
+ width: 100%;
330
+ max-width: 300px;
331
+ margin: 0 auto;
332
+ text-align: center;
333
+ }
334
+
335
+ .diagram-bg {
336
+ background: var(--bg-secondary);
337
+ border: 1px solid var(--border);
338
+ border-radius: 4px;
339
+ padding: 16px;
340
+ }
341
+
342
+ svg {
343
+ width: 100%;
344
+ height: auto;
345
+ }
346
+
347
+ /* Pro Tip */
348
+ .pro-tip {
349
+ background: linear-gradient(90deg, rgba(63, 185, 80, 0.1), rgba(88, 166, 255, 0.05));
350
+ border-left: 3px solid var(--success);
351
+ padding: 12px 16px;
352
+ border-radius: 4px;
353
+ margin-top: 16px;
354
+ }
355
+
356
+ .pro-tip-title {
357
+ font-weight: 600;
358
+ color: var(--success);
359
+ font-size: 12px;
360
+ text-transform: uppercase;
361
+ margin-bottom: 4px;
362
+ }
363
+
364
+ .pro-tip-text {
365
+ font-size: 13px;
366
+ color: var(--text-secondary);
367
+ }
368
+
369
+ /* Modal */
370
+ .modal {
371
+ display: none;
372
+ position: fixed;
373
+ top: 0;
374
+ left: 0;
375
+ right: 0;
376
+ bottom: 0;
377
+ background: rgba(0, 0, 0, 0.7);
378
+ z-index: 2000;
379
+ align-items: center;
380
+ justify-content: center;
381
+ backdrop-filter: blur(4px);
382
+ }
383
+
384
+ .modal.active {
385
+ display: flex;
386
+ }
387
+
388
+ .modal-content {
389
+ background: var(--bg-secondary);
390
+ border: 1px solid var(--border);
391
+ border-radius: 8px;
392
+ max-width: 500px;
393
+ max-height: 80vh;
394
+ overflow-y: auto;
395
+ padding: 24px;
396
+ box-shadow: 0 20px 60px rgba(0, 0, 0, 0.3);
397
+ }
398
+
399
+ .modal-header {
400
+ display: flex;
401
+ justify-content: space-between;
402
+ align-items: center;
403
+ margin-bottom: 20px;
404
+ }
405
+
406
+ .modal-title {
407
+ font-size: 18px;
408
+ font-weight: 700;
409
+ }
410
+
411
+ .modal-close {
412
+ background: none;
413
+ border: none;
414
+ color: var(--text-secondary);
415
+ font-size: 24px;
416
+ cursor: pointer;
417
+ transition: color var(--transition);
418
+ }
419
+
420
+ .modal-close:hover {
421
+ color: var(--text-primary);
422
+ }
423
+
424
+ .shortcut-list {
425
+ display: grid;
426
+ gap: 12px;
427
+ }
428
+
429
+ .shortcut-item {
430
+ display: flex;
431
+ justify-content: space-between;
432
+ align-items: center;
433
+ padding: 10px;
434
+ background: var(--bg-tertiary);
435
+ border-radius: 4px;
436
+ }
437
+
438
+ .shortcut-key {
439
+ background: var(--bg-secondary);
440
+ padding: 4px 8px;
441
+ border-radius: 3px;
442
+ font-family: 'Courier New', monospace;
443
+ font-size: 12px;
444
+ font-weight: 600;
445
+ color: var(--accent);
446
+ border: 1px solid var(--border);
447
+ }
448
+
449
+ /* Footer */
450
+ .tutorial-footer {
451
+ margin-top: 60px;
452
+ padding-top: 40px;
453
+ border-top: 1px solid var(--border);
454
+ }
455
+
456
+ .footer-section {
457
+ margin-bottom: 40px;
458
+ }
459
+
460
+ .footer-title {
461
+ font-size: 16px;
462
+ font-weight: 700;
463
+ margin-bottom: 12px;
464
+ color: var(--accent);
465
+ }
466
+
467
+ .footer-content {
468
+ display: flex;
469
+ flex-wrap: wrap;
470
+ gap: 16px;
471
+ }
472
+
473
+ .footer-link {
474
+ display: inline-flex;
475
+ align-items: center;
476
+ gap: 8px;
477
+ padding: 10px 16px;
478
+ background: var(--bg-tertiary);
479
+ border: 1px solid var(--border);
480
+ border-radius: 6px;
481
+ color: var(--accent);
482
+ text-decoration: none;
483
+ font-size: 13px;
484
+ font-weight: 500;
485
+ transition: all var(--transition);
486
+ }
487
+
488
+ .footer-link:hover {
489
+ background: var(--bg-secondary);
490
+ border-color: var(--accent);
491
+ }
492
+
493
+ /* Floating Quick Reference */
494
+ .floating-btn {
495
+ position: fixed;
496
+ bottom: 24px;
497
+ right: 24px;
498
+ width: 56px;
499
+ height: 56px;
500
+ border-radius: 50%;
501
+ background: linear-gradient(135deg, var(--accent), #0d47a1);
502
+ border: none;
503
+ color: white;
504
+ font-size: 24px;
505
+ cursor: pointer;
506
+ box-shadow: 0 4px 12px rgba(88, 166, 255, 0.4);
507
+ transition: all var(--transition);
508
+ z-index: 500;
509
+ display: flex;
510
+ align-items: center;
511
+ justify-content: center;
512
+ }
513
+
514
+ .floating-btn:hover {
515
+ transform: scale(1.1);
516
+ box-shadow: 0 8px 20px rgba(88, 166, 255, 0.6);
517
+ }
518
+
519
+ /* Step Counter */
520
+ .step-counter {
521
+ position: fixed;
522
+ bottom: 24px;
523
+ left: 24px;
524
+ background: var(--bg-secondary);
525
+ border: 1px solid var(--border);
526
+ border-radius: 6px;
527
+ padding: 12px 16px;
528
+ font-size: 12px;
529
+ color: var(--text-secondary);
530
+ font-weight: 500;
531
+ }
532
+
533
+ /* Responsive */
534
+ @media (max-width: 768px) {
535
+ .navbar {
536
+ flex-direction: column;
537
+ gap: 12px;
538
+ padding: 12px 16px;
539
+ }
540
+
541
+ .navbar-left {
542
+ width: 100%;
543
+ justify-content: flex-start;
544
+ }
545
+
546
+ .progress-container {
547
+ width: 100%;
548
+ margin: 0;
549
+ max-width: none;
550
+ }
551
+
552
+ .navbar-title {
553
+ font-size: 16px;
554
+ }
555
+
556
+ .container {
557
+ padding: 0 16px;
558
+ }
559
+
560
+ .step-body-grid {
561
+ gap: 16px;
562
+ }
563
+
564
+ .floating-btn {
565
+ width: 48px;
566
+ height: 48px;
567
+ font-size: 20px;
568
+ bottom: 16px;
569
+ right: 16px;
570
+ }
571
+
572
+ .step-counter {
573
+ bottom: 16px;
574
+ left: 16px;
575
+ font-size: 11px;
576
+ padding: 8px 12px;
577
+ }
578
+ }
579
+
580
+ .feature-grid {
581
+ display: grid;
582
+ grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
583
+ gap: 16px;
584
+ }
585
+
586
+ .feature-card {
587
+ background: var(--bg-tertiary);
588
+ border: 1px solid var(--border);
589
+ border-radius: 6px;
590
+ padding: 16px;
591
+ transition: all var(--transition);
592
+ }
593
+
594
+ .feature-card:hover {
595
+ border-color: var(--accent);
596
+ background: var(--bg-secondary);
597
+ }
598
+
599
+ .feature-icon {
600
+ font-size: 24px;
601
+ margin-bottom: 8px;
602
+ }
603
+
604
+ .feature-name {
605
+ font-weight: 600;
606
+ font-size: 13px;
607
+ color: var(--text-primary);
608
+ margin-bottom: 8px;
609
+ }
610
+
611
+ .feature-desc {
612
+ font-size: 12px;
613
+ color: var(--text-secondary);
614
+ line-height: 1.5;
615
+ }
616
+ </style>
617
+ </head>
618
+ <body>
619
+ <!-- Navbar -->
620
+ <nav class="navbar">
621
+ <div class="navbar-left">
622
+ <a href="../" class="navbar-back">← Back</a>
623
+ <h1 class="navbar-title">Assembly + ExplodeView Tutorial</h1>
624
+ </div>
625
+ <div class="progress-container">
626
+ <div class="progress-bar">
627
+ <div class="progress-fill" id="progressFill"></div>
628
+ </div>
629
+ <div class="progress-text"><span id="progressText">0%</span> Complete</div>
630
+ </div>
631
+ <div class="navbar-right">
632
+ <button class="btn-icon" id="quickRefBtn" title="Keyboard Shortcuts">⌨️</button>
633
+ <a href="../" class="btn-icon" title="Home">🏠</a>
634
+ </div>
635
+ </nav>
636
+
637
+ <!-- Main Container -->
638
+ <div class="container">
639
+ <!-- Tab Navigation -->
640
+ <div class="tab-nav">
641
+ <button class="tab-btn active" data-tab="part1">Part 1: Build Assembly</button>
642
+ <button class="tab-btn" data-tab="part2">Part 2: ExplodeView</button>
643
+ </div>
644
+
645
+ <!-- PART 1: Building Assembly -->
646
+ <div id="part1" class="tab-content active">
647
+ <div id="part1Steps"></div>
648
+ </div>
649
+
650
+ <!-- PART 2: ExplodeView Features -->
651
+ <div id="part2" class="tab-content">
652
+ <div class="tab-nav" style="margin-bottom: 24px;">
653
+ <button class="tab-btn active" data-subtab="view">View (9-16)</button>
654
+ <button class="tab-btn" data-subtab="analyze">Analyze (17-25)</button>
655
+ <button class="tab-btn" data-subtab="create">Create (26-30)</button>
656
+ <button class="tab-btn" data-subtab="export">Export (31-36)</button>
657
+ <button class="tab-btn" data-subtab="ai">AI Tools (37-42)</button>
658
+ <button class="tab-btn" data-subtab="settings">Settings (43-47)</button>
659
+ </div>
660
+ <div id="part2Steps"></div>
661
+ </div>
662
+
663
+ <!-- Footer -->
664
+ <div class="tutorial-footer">
665
+ <div class="footer-section">
666
+ <div class="footer-title">📚 What's Next?</div>
667
+ <div class="footer-content">
668
+ <a href="./" class="footer-link">← Other Tutorials</a>
669
+ <a href="../" class="footer-link">Home</a>
670
+ </div>
671
+ </div>
672
+ <div class="footer-section">
673
+ <div class="footer-title">❓ Need Help?</div>
674
+ <div class="footer-content">
675
+ <a href="../#chat" class="footer-link">💬 Chat</a>
676
+ <a href="../#help" class="footer-link">ℹ️ Help Panel</a>
677
+ <a href="https://github.com/vvlars-cmd/cyclecad/issues" class="footer-link" target="_blank">🐛 Report Issue</a>
678
+ </div>
679
+ </div>
680
+ </div>
681
+ </div>
682
+
683
+ <!-- Keyboard Shortcuts Modal -->
684
+ <div class="modal" id="quickRefModal">
685
+ <div class="modal-content">
686
+ <div class="modal-header">
687
+ <h2 class="modal-title">⌨️ Keyboard Shortcuts</h2>
688
+ <button class="modal-close" id="modalClose">&times;</button>
689
+ </div>
690
+ <div class="shortcut-list" id="shortcutList"></div>
691
+ </div>
692
+ </div>
693
+
694
+ <!-- Step Counter -->
695
+ <div class="step-counter">
696
+ <span id="stepCounter">Step 1 of 47</span>
697
+ </div>
698
+
699
+ <!-- Floating Quick Reference -->
700
+ <button class="floating-btn" id="floatingRef" title="Quick Reference">⌨️</button>
701
+
702
+ <script>
703
+ // Data for Part 1: Assembly Creation
704
+ const part1Steps = [
705
+ {
706
+ num: 1,
707
+ title: 'Create the Caliper Body',
708
+ desc: 'Draw and fillet the main brake caliper housing',
709
+ methods: [
710
+ { type: 'Chat', text: '"Create a box 80mm wide, 40mm tall, 25mm deep, then fillet all edges by 3mm"' },
711
+ { type: 'Toolbar', text: 'Create → Box, set dims 80×40×25, select all edges, Fillet → 3mm' }
712
+ ],
713
+ shouldSee: 'A rounded rectangular box representing the brake caliper body',
714
+ proTip: 'Use Ctrl+A to select all edges after creating the box for faster filleting',
715
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
716
+ <rect x="60" y="40" width="180" height="120" rx="8" fill="none" stroke="#58a6ff" stroke-width="2"/>
717
+ <text x="150" y="105" text-anchor="middle" fill="#8b949e" font-size="12">80×40×25 Caliper Body</text>
718
+ <g opacity="0.5">
719
+ <circle cx="68" cy="48" r="3" fill="#3fb950"/>
720
+ <circle cx="232" cy="48" r="3" fill="#3fb950"/>
721
+ <circle cx="68" cy="152" r="3" fill="#3fb950"/>
722
+ <circle cx="232" cy="152" r="3" fill="#3fb950"/>
723
+ </g>
724
+ </svg>`
725
+ },
726
+ {
727
+ num: 2,
728
+ title: 'Add Mounting Holes',
729
+ desc: 'Create two holes for bracket mounting, 60mm apart',
730
+ methods: [
731
+ { type: 'Chat', text: '"Add two holes, 8mm diameter, centered 60mm apart"' },
732
+ { type: 'Toolbar', text: 'Sketch → Circle (8mm radius 4mm), place 2, Extrude → Hole' }
733
+ ],
734
+ shouldSee: 'Two circular holes drilled through the caliper body',
735
+ proTip: 'Use Ctrl+C to center circles on the sketch before extruding as holes',
736
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
737
+ <rect x="60" y="40" width="180" height="120" rx="8" fill="none" stroke="#58a6ff" stroke-width="2"/>
738
+ <circle cx="105" cy="100" r="12" fill="none" stroke="#f85149" stroke-width="2"/>
739
+ <circle cx="195" cy="100" r="12" fill="none" stroke="#f85149" stroke-width="2"/>
740
+ <line x1="105" y1="115" x2="195" y2="115" stroke="#d29922" stroke-width="1" stroke-dasharray="2,2"/>
741
+ <text x="150" y="135" text-anchor="middle" fill="#8b949e" font-size="11">60mm spacing</text>
742
+ </svg>`
743
+ },
744
+ {
745
+ num: 3,
746
+ title: 'Create the Brake Pad',
747
+ desc: 'Build a rectangular pad that fits inside the caliper',
748
+ methods: [
749
+ { type: 'Chat', text: '"Create a box 60mm wide, 30mm tall, 5mm thick for the brake pad"' },
750
+ { type: 'Toolbar', text: 'Create → Box, set dims 60×30×5, color red' }
751
+ ],
752
+ shouldSee: 'A thin red rectangular pad positioned above the caliper body',
753
+ proTip: 'Set different colors for each component to help distinguish them in 3D view',
754
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
755
+ <rect x="60" y="40" width="180" height="120" rx="8" fill="none" stroke="#58a6ff" stroke-width="2" opacity="0.3"/>
756
+ <rect x="75" y="20" width="150" height="15" rx="2" fill="none" stroke="#f85149" stroke-width="2"/>
757
+ <text x="150" y="50" text-anchor="middle" fill="#8b949e" font-size="11">Brake Pad 60×30×5</text>
758
+ </svg>`
759
+ },
760
+ {
761
+ num: 4,
762
+ title: 'Create the Pivot Pin',
763
+ desc: 'Build the cylinder that acts as the assembly pivot point',
764
+ methods: [
765
+ { type: 'Chat', text: '"Create a cylinder with 4mm radius and 40mm height"' },
766
+ { type: 'Toolbar', text: 'Create → Cylinder, radius 4mm, height 40mm, position center' }
767
+ ],
768
+ shouldSee: 'A vertical cylinder passing through the caliper mounting holes',
769
+ proTip: 'Press G to show the grid for precise positioning of the pivot pin',
770
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
771
+ <ellipse cx="150" cy="40" rx="8" ry="5" fill="none" stroke="#3fb950" stroke-width="2"/>
772
+ <rect x="142" y="40" width="16" height="120" fill="none" stroke="#3fb950" stroke-width="2"/>
773
+ <ellipse cx="150" cy="160" rx="8" ry="5" fill="none" stroke="#3fb950" stroke-width="2"/>
774
+ <text x="150" y="105" text-anchor="middle" fill="#8b949e" font-size="11" dx="20">Pivot Pin</text>
775
+ </svg>`
776
+ },
777
+ {
778
+ num: 5,
779
+ title: 'Create the Spring',
780
+ desc: 'Generate a helical spring for brake pad return force',
781
+ methods: [
782
+ { type: 'Chat', text: '"Generate a spring with 6 coils, 3mm radius, around the pivot pin"' },
783
+ { type: 'Toolbar', text: 'Create → Spring Generator, coils 6, radius 3mm, pitch 5mm' }
784
+ ],
785
+ shouldSee: 'A helical spring wound around the pivot pin',
786
+ proTip: 'Adjust pitch value to control coil spacing; lower pitch = tighter coils',
787
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
788
+ <path d="M 150 30 Q 160 35 160 45 Q 160 55 150 60 Q 140 65 140 75 Q 140 85 150 90" fill="none" stroke="#58a6ff" stroke-width="2"/>
789
+ <path d="M 150 90 Q 160 95 160 105 Q 160 115 150 120" fill="none" stroke="#58a6ff" stroke-width="2"/>
790
+ <text x="180" y="75" fill="#8b949e" font-size="11">6 coils</text>
791
+ </svg>`
792
+ },
793
+ {
794
+ num: 6,
795
+ title: 'Create the Adjustment Bolt',
796
+ desc: 'Add a hex-head bolt for fine brake adjustment',
797
+ methods: [
798
+ { type: 'Chat', text: '"Create a cylinder 3mm radius, 20mm tall, add a hex head"' },
799
+ { type: 'Toolbar', text: 'Create → Cylinder 3mm, height 20mm; Create → Hex Head, add to top' }
800
+ ],
801
+ shouldSee: 'A bolt with cylindrical shaft and hexagonal head on top',
802
+ proTip: 'Use the AI Part Identifier to match bolt specs to McMaster-Carr standards',
803
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
804
+ <polygon points="150,30 165,38 165,54 150,62 135,54 135,38" fill="none" stroke="#d29922" stroke-width="2"/>
805
+ <rect x="142" y="62" width="16" height="80" fill="none" stroke="#d29922" stroke-width="2"/>
806
+ <text x="180" y="105" fill="#8b949e" font-size="11">Adjustment Bolt</text>
807
+ </svg>`
808
+ },
809
+ {
810
+ num: 7,
811
+ title: 'Assemble Components',
812
+ desc: 'Position all parts into the final brake caliper assembly',
813
+ methods: [
814
+ { type: 'Chat', text: '"Move the brake pad inside the caliper, align pivot pin through holes"' },
815
+ { type: 'Toolbar', text: 'Assembly → Move, position pad, pin, spring, bolt; set constraints' }
816
+ ],
817
+ shouldSee: 'All 6 components arranged as a complete brake caliper assembly',
818
+ proTip: 'Use assembly constraints (mate, align) to lock parts in place relative to each other',
819
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
820
+ <rect x="60" y="70" width="180" height="60" rx="4" fill="none" stroke="#58a6ff" stroke-width="2"/>
821
+ <rect x="75" y="45" width="150" height="20" fill="none" stroke="#f85149" stroke-width="2"/>
822
+ <rect x="142" y="20" width="16" height="130" fill="none" stroke="#3fb950" stroke-width="2"/>
823
+ <polygon points="150,15 160,20 160,28 150,32 140,28 140,20" fill="none" stroke="#d29922" stroke-width="1.5"/>
824
+ <text x="150" y="150" text-anchor="middle" fill="#8b949e" font-size="12">Complete Assembly</text>
825
+ </svg>`
826
+ },
827
+ {
828
+ num: 8,
829
+ title: 'Save & Export',
830
+ desc: 'Save your assembly and export it as STL for 3D printing or analysis',
831
+ methods: [
832
+ { type: 'Chat', text: '"Save this assembly as brake-caliper, then export STL"' },
833
+ { type: 'Toolbar', text: 'File → Save (Ctrl+S), Export → STL, choose resolution' }
834
+ ],
835
+ shouldSee: 'File saved in cycleCAD JSON format and STL export dialog opens',
836
+ proTip: 'Use Shift+S for quick screenshot; export high-res STL for 3D printing',
837
+ diagram: `<svg viewBox="0 0 300 200" xmlns="http://www.w3.org/2000/svg">
838
+ <g opacity="0.3">
839
+ <rect x="60" y="70" width="180" height="60" rx="4" fill="none" stroke="#58a6ff" stroke-width="2"/>
840
+ </g>
841
+ <rect x="80" y="100" width="140" height="50" rx="4" fill="none" stroke="#3fb950" stroke-width="2"/>
842
+ <text x="150" y="115" text-anchor="middle" fill="#8b949e" font-size="11">brake-caliper.json</text>
843
+ <text x="150" y="135" text-anchor="middle" fill="#8b949e" font-size="11">+ caliper.stl</text>
844
+ </svg>`
845
+ }
846
+ ];
847
+
848
+ // Data for Part 2: ExplodeView Features
849
+ const part2Steps = {
850
+ view: [
851
+ { num: 9, name: 'Toggle Viewer Mode', icon: '👁️', desc: 'Switch to Viewer Mode to explore the assembly', activation: 'Button: View Tab → Viewer, or press V', expected: 'ExplodeView toolbar appears, 3D view shows exploded brake caliper assembly' },
852
+ { num: 10, name: 'Explode/Collapse Assembly', icon: '💥', desc: 'Slide the explosion slider to separate or combine components', activation: 'Slider in View Tab, or mouse wheel with Alt key', expected: 'All 6 parts spread apart with distance proportional to slider position' },
853
+ { num: 11, name: 'Section Cut (X/Y/Z)', icon: '✂️', desc: 'Slice through the assembly to view internal details', activation: 'View Tab → Section Cut, choose axis (X/Y/Z)', expected: 'Cross-section plane visible, interior geometry shown in translucent view' },
854
+ { num: 12, name: 'Wireframe Toggle', icon: '⬚', desc: 'Show edges and vertices without solid surfaces', activation: 'Button: W key or View Tab → Wireframe', expected: 'All parts displayed as wireframe edges and lines' },
855
+ { num: 13, name: 'Grid Floor', icon: '📏', desc: 'Display ground reference grid and shadow plane', activation: 'Button: G key or View Tab → Grid', expected: 'Checkerboard floor grid appears beneath assembly with shadow plane' },
856
+ { num: 14, name: 'Blueprint Theme', icon: '📘', desc: 'Switch to classic technical drawing blue theme', activation: 'View Tab → Blueprint Theme toggle', expected: 'White wireframe on dark blue background, CAD-style appearance' },
857
+ { num: 15, name: 'Dark/Light Theme', icon: '🌓', desc: 'Toggle between dark and light UI themes', activation: 'View Tab → Theme toggle', expected: 'UI switches color scheme; 3D view brightness inverts' },
858
+ { num: 16, name: 'Multi-View Layout', icon: '📐', desc: 'Split viewport into 2 or 4 synchronized views', activation: 'View Tab → Multi-View (2-up or 4-up)', expected: 'Screen splits, each pane shows same assembly from different angle' }
859
+ ],
860
+ analyze: [
861
+ { num: 17, name: 'Part Selection & Info Card', icon: '🔍', desc: 'Click any part to see properties and metadata', activation: 'Click 3D part or select in tree panel', expected: 'Info card appears with part name, material, volume, weight, dimensions' },
862
+ { num: 18, name: 'Measurement Tool', icon: '📐', desc: 'Measure distances and angles between points', activation: 'Button: M key or Analyze Tab → Measurement', expected: 'Click 2 points for distance, 3rd point shows angle; measurements display in UI' },
863
+ { num: 19, name: 'Weight Estimator', icon: '⚖️', desc: 'Calculate total and per-part weight using material density', activation: 'Analyze Tab → Weight Estimator, select material', expected: 'Dashboard shows weight breakdown: steel caliper 2.1kg, spring 45g, pivot pin 15g, etc.' },
864
+ { num: 20, name: 'Part Comparison', icon: '⚔️', desc: 'Compare dimensions, volume, and mass of 2 selected parts', activation: 'Select 2 parts, Analyze Tab → Compare', expected: 'Comparison table shows caliper vs brake pad side-by-side (vol, mass, dims)' },
865
+ { num: 21, name: 'Clearance Checker', icon: '📏', desc: 'Detect collisions and minimum distances between components', activation: 'Analyze Tab → Clearance, set threshold (e.g., 5mm)', expected: 'Color coding: green (safe), yellow (warning), red (collision) on parts' },
866
+ { num: 22, name: 'Assembly Validator', icon: '✅', desc: 'Score assembly quality A-F based on geometry and constraints', activation: 'Analyze Tab → Assembly Validator', expected: 'Report card: A (excellent), shows issues if hidden parts, duplicate geometry, etc.' },
867
+ { num: 23, name: 'DIN/ISO Standards Identifier', icon: '🔧', desc: 'Identify fasteners and match to DIN/ISO standards with McMaster links', activation: 'Select fastener, Analyze Tab → Standards ID', expected: 'Popup shows: Hex Bolt ISO 4762 M4, link to McMaster-Carr listing' },
868
+ { num: 24, name: 'Thermal Heatmap', icon: '🔥', desc: 'Color parts by simulated temperature/stress zones', activation: 'Analyze Tab → Thermal Heatmap', expected: 'Pivot pin shows orange (high stress), brake pad red, springs yellow' },
869
+ { num: 25, name: 'Vibration Analysis', icon: '📳', desc: 'Animate vibration on rotating/reciprocating parts', activation: 'Analyze Tab → Vibration, select part', expected: 'Pivot pin oscillates; spring bounces; caliper body remains stationary' }
870
+ ],
871
+ create: [
872
+ { num: 26, name: 'Annotation Pins', icon: '📍', desc: 'Place labeled callouts and notes on specific parts', activation: 'Button: A key or Create Tab → Annotation Pin, click location', expected: 'Red pin appears, text label editable, persists on model' },
873
+ { num: 27, name: 'QR Code Per Part', icon: '📱', desc: 'Generate QR code for each part linking to details', activation: 'Right-click part → Generate QR Code', expected: 'QR code displays on selected part (pinned to 3D space), scannable in mobile' },
874
+ { num: 28, name: 'Assembly Animation', icon: '▶️', desc: 'Auto-generate and customize step-by-step disassembly sequence', activation: 'Create Tab → Assembly Animation, press Play', expected: 'Parts sequentially move apart with smooth tweening; Play/Prev/Next buttons active' },
875
+ { num: 29, name: 'AI Assembly Instructions', icon: '🤖', desc: 'Auto-generate HTML step-by-step disassembly manual', activation: 'Create Tab → AI Instructions, export HTML', expected: 'Document shows "Step 1: Remove brake pad, Step 2: Unscrew bolt..." with 3D step images' },
876
+ { num: 30, name: 'Cable/Pipe Router', icon: '🔌', desc: 'Draw routing paths for cables, hoses, wires through assembly', activation: 'Create Tab → Router, click points to define path', expected: 'Tube geometry follows clicked points as spline, editable and re-routeable' }
877
+ ],
878
+ export: [
879
+ { num: 31, name: 'STL Export Per Part', icon: '💾', desc: 'Export individual parts as STL for 3D printing', activation: 'Select part, Export Tab → Export STL', expected: 'STL file downloaded: caliper.stl, brake-pad.stl, pivot-pin.stl, etc.' },
880
+ { num: 32, name: 'BOM Export to CSV', icon: '📊', desc: 'Export bill of materials with quantities and specs', activation: 'Export Tab → BOM CSV, auto-includes fastener specs', expected: 'CSV file downloaded: part name, qty, material, weight, cost estimate' },
881
+ { num: 33, name: 'Screenshot (High-Res)', icon: '📸', desc: 'Capture 2x or 4x retina resolution screenshots', activation: 'Button: S key or Export Tab → Screenshot, select resolution', expected: '1280×720 or higher PNG file downloaded with all UI elements' },
882
+ { num: 34, name: 'Hero Shots', icon: '🎞️', desc: 'Generate polished marketing-quality renders', activation: 'Export Tab → Hero Shots, select angle + lighting preset', expected: '4 angles (top, iso, front, detail) rendered at 4K with professional lighting' },
883
+ { num: 35, name: 'Technical Report', icon: '📋', desc: 'Create comprehensive HTML report with BOM, 3D images, specs', activation: 'Export Tab → Technical Report', expected: 'HTML file with cover, assembly drawings, BOM table, dimensions, maintenance schedule' },
884
+ { num: 36, name: 'Share & Embed', icon: '🔗', desc: 'Generate shareable links and embed code for websites', activation: 'Export Tab → Share, copy link or HTML embed', expected: 'URL and <iframe> code provided; model viewable by anyone without login' }
885
+ ],
886
+ ai: [
887
+ { num: 37, name: 'AI Part Identifier', icon: '🧠', desc: 'Identify parts using geometry analysis, match McMaster-Carr', activation: 'AI Tools → Identify Parts, uses Gemini Vision API', expected: 'Popup shows: Hex Bolt M4, Spring (6mm OD), Cylinder (Steel), with prices' },
888
+ { num: 38, name: 'AI Vision Scanner', icon: '📷', desc: 'Analyze parts using computer vision, detect materials', activation: 'AI Tools → Vision Scan, select part(s)', expected: 'AI returns: "Appears to be steel fastener, M4 ISO standard, ~$0.40 at McMaster"' },
889
+ { num: 39, name: 'Batch AI Scan', icon: '🔄', desc: 'Auto-scan all 47 parts simultaneously with progress bar', activation: 'AI Tools → Batch Scan All, wait for analysis', expected: 'Progress bar shows 47/47 complete, results CSV with all parts identified and priced' },
890
+ { num: 40, name: 'Smart BOM with Pricing', icon: '💰', desc: 'Generate BOM with AI-identified parts and estimated cost', activation: 'AI Tools → Smart BOM, export CSV or order from McMaster', expected: 'BOM shows caliper $8.50, brake pad $2.20, pivot pin $0.60, spring $0.90, bolt $0.40' },
891
+ { num: 41, name: 'Smart NL Search', icon: '🔎', desc: 'Search parts using natural language: "Find all steel parts under 100g"', activation: 'AI Tools → NL Search, type query', expected: 'Results show spring, pivot pin, bolt matching criteria; highlight in 3D' },
892
+ { num: 42, name: 'AI Chatbot', icon: '💬', desc: 'Ask questions about the model: "Why is the spring diameter 3mm?", get AI responses', activation: 'AI Tools → Chat, type question or speak', expected: 'Bot responds: "Spring diameter sized for 5N return force given coil count and material"' }
893
+ ],
894
+ settings: [
895
+ { num: 43, name: 'Performance Monitor', icon: '📊', desc: 'View FPS, memory usage, part count in real-time', activation: 'Button: Ctrl+Shift+F or Settings → Performance', expected: 'Overlay shows: 60 FPS, 128MB mem, 47 parts, 1.2M triangles' },
896
+ { num: 44, name: 'Keyboard Shortcuts', icon: '⌨️', desc: 'View all available keyboard shortcuts', activation: 'Button: ? or Settings → Help', expected: 'Modal opens listing 30+ shortcuts (W=wireframe, G=grid, M=measure, etc.)' },
897
+ { num: 45, name: 'Language Selector', icon: '🌐', desc: 'Switch UI language: EN/DE/FR/ES/IT/NL', activation: 'Settings → Language dropdown', expected: 'All UI labels update; assembly names translated' },
898
+ { num: 46, name: 'Part Favorites', icon: '⭐', desc: 'Star important parts for quick filtering', activation: 'Right-click part → Add to Favorites, or Settings → Favorites', expected: 'Starred parts highlighted in tree; filter shows only favorites' },
899
+ { num: 47, name: 'Quick Notes', icon: '📝', desc: 'Add color-coded notes to parts: Design notes, Manufacturing notes, etc.', activation: 'Right-click part → Add Note, choose category', expected: 'Note attached to part; visible in info card; exportable as CSV' }
900
+ ]
901
+ };
902
+
903
+ // Render Part 1 Steps
904
+ function renderPart1() {
905
+ const container = document.getElementById('part1Steps');
906
+ container.innerHTML = part1Steps.map((step, idx) => `
907
+ <div class="step-card" data-step="${step.num}">
908
+ <div class="step-header">
909
+ <div class="step-header-left">
910
+ <div class="step-badge" id="badge-${step.num}">${step.num}</div>
911
+ <div>
912
+ <div class="step-title">${step.title}</div>
913
+ <div class="step-description">${step.desc}</div>
914
+ </div>
915
+ </div>
916
+ <input type="checkbox" class="step-checkbox" data-step="${step.num}">
917
+ </div>
918
+ <div class="step-body">
919
+ <div class="step-body-grid">
920
+ <div>
921
+ <div class="card-section">
922
+ <div class="card-title">Method</div>
923
+ <div class="card-content">
924
+ ${step.methods.map(m => `
925
+ <div class="method-item">
926
+ <div class="method-label">${m.type}</div>
927
+ <div class="method-text">${m.text}</div>
928
+ </div>
929
+ `).join('')}
930
+ </div>
931
+ </div>
932
+ <div class="card-section">
933
+ <div class="card-title">What You Should See</div>
934
+ <div class="card-content">${step.shouldSee}</div>
935
+ </div>
936
+ <div class="pro-tip">
937
+ <div class="pro-tip-title">Pro Tip</div>
938
+ <div class="pro-tip-text">${step.proTip}</div>
939
+ </div>
940
+ </div>
941
+ <div>
942
+ <div class="card-section">
943
+ <div class="card-title">Diagram</div>
944
+ <div class="diagram-container">
945
+ <div class="diagram-bg">${step.diagram}</div>
946
+ </div>
947
+ </div>
948
+ </div>
949
+ </div>
950
+ </div>
951
+ </div>
952
+ `).join('');
953
+ }
954
+
955
+ // Render Part 2 Steps
956
+ function renderPart2() {
957
+ const container = document.getElementById('part2Steps');
958
+ let html = '';
959
+
960
+ Object.keys(part2Steps).forEach(tab => {
961
+ html += `<div class="subtab-content" data-subtab="${tab}" style="display: ${tab === 'view' ? 'block' : 'none'};">`;
962
+ part2Steps[tab].forEach(step => {
963
+ html += `
964
+ <div class="step-card" data-step="${step.num}">
965
+ <div class="step-header">
966
+ <div class="step-header-left">
967
+ <div class="step-badge" id="badge-${step.num}">${step.num}</div>
968
+ <div>
969
+ <div class="step-title">${step.icon} ${step.name}</div>
970
+ <div class="step-description">${step.desc}</div>
971
+ </div>
972
+ </div>
973
+ <input type="checkbox" class="step-checkbox" data-step="${step.num}">
974
+ </div>
975
+ <div class="step-body">
976
+ <div class="card-section">
977
+ <div class="card-title">How to Activate</div>
978
+ <div class="card-content">${step.activation}</div>
979
+ </div>
980
+ <div class="card-section">
981
+ <div class="card-title">What You'll See</div>
982
+ <div class="card-content">${step.expected}</div>
983
+ </div>
984
+ <div class="pro-tip">
985
+ <div class="pro-tip-title">💡 Tip</div>
986
+ <div class="pro-tip-text">Combine this feature with others: use section cut + measurement tool to verify internal clearances</div>
987
+ </div>
988
+ </div>
989
+ </div>
990
+ `;
991
+ });
992
+ html += '</div>';
993
+ });
994
+
995
+ container.innerHTML = html;
996
+ attachStepEvents();
997
+ }
998
+
999
+ // Attach event listeners to step cards
1000
+ function attachStepEvents() {
1001
+ // Step header click to expand/collapse
1002
+ document.querySelectorAll('.step-header').forEach(header => {
1003
+ header.addEventListener('click', (e) => {
1004
+ if (e.target.classList.contains('step-checkbox')) return;
1005
+ const card = header.closest('.step-card');
1006
+ const body = card.querySelector('.step-body');
1007
+ body.classList.toggle('expanded');
1008
+ });
1009
+ });
1010
+
1011
+ // Checkbox click to mark as done
1012
+ document.querySelectorAll('.step-checkbox').forEach(checkbox => {
1013
+ checkbox.addEventListener('change', (e) => {
1014
+ const stepNum = e.target.dataset.step;
1015
+ const badge = document.getElementById(`badge-${stepNum}`);
1016
+ if (checkbox.checked) {
1017
+ badge.classList.add('done');
1018
+ } else {
1019
+ badge.classList.remove('done');
1020
+ }
1021
+ updateProgress();
1022
+ });
1023
+ });
1024
+ }
1025
+
1026
+ // Calculate and update progress
1027
+ function updateProgress() {
1028
+ const totalSteps = 47;
1029
+ const checkedSteps = document.querySelectorAll('.step-checkbox:checked').length;
1030
+ const percentage = Math.round((checkedSteps / totalSteps) * 100);
1031
+ document.getElementById('progressFill').style.width = percentage + '%';
1032
+ document.getElementById('progressText').textContent = percentage;
1033
+ document.getElementById('stepCounter').textContent = `Step ${Math.min(checkedSteps + 1, totalSteps)} of ${totalSteps}`;
1034
+ }
1035
+
1036
+ // Tab navigation
1037
+ function setupTabNav() {
1038
+ document.querySelectorAll('.tab-btn[data-tab]').forEach(btn => {
1039
+ btn.addEventListener('click', (e) => {
1040
+ document.querySelectorAll('.tab-btn[data-tab]').forEach(b => b.classList.remove('active'));
1041
+ document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
1042
+ e.target.classList.add('active');
1043
+ document.getElementById(e.target.dataset.tab).classList.add('active');
1044
+ });
1045
+ });
1046
+
1047
+ // Subtab navigation (Part 2)
1048
+ document.querySelectorAll('.tab-btn[data-subtab]').forEach(btn => {
1049
+ btn.addEventListener('click', (e) => {
1050
+ document.querySelectorAll('.tab-btn[data-subtab]').forEach(b => b.classList.remove('active'));
1051
+ document.querySelectorAll('.subtab-content').forEach(c => c.style.display = 'none');
1052
+ e.target.classList.add('active');
1053
+ document.querySelectorAll(`[data-subtab="${e.target.dataset.subtab}"]`).forEach(c => c.style.display = 'block');
1054
+ });
1055
+ });
1056
+ }
1057
+
1058
+ // Keyboard shortcuts modal
1059
+ const shortcuts = [
1060
+ { key: 'V', desc: 'Toggle Viewer Mode' },
1061
+ { key: 'W', desc: 'Wireframe toggle' },
1062
+ { key: 'G', desc: 'Show/hide grid' },
1063
+ { key: 'M', desc: 'Measurement tool' },
1064
+ { key: 'A', desc: 'Annotation pins' },
1065
+ { key: 'S', desc: 'Screenshot' },
1066
+ { key: '?', desc: 'Show keyboard shortcuts' },
1067
+ { key: 'Ctrl+Z', desc: 'Undo' },
1068
+ { key: 'Ctrl+Y', desc: 'Redo' },
1069
+ { key: 'Ctrl+S', desc: 'Save' },
1070
+ { key: 'Shift+S', desc: 'Quick screenshot' },
1071
+ { key: 'Ctrl+Shift+F', desc: 'Performance monitor' }
1072
+ ];
1073
+
1074
+ function setupModal() {
1075
+ const modal = document.getElementById('quickRefModal');
1076
+ const closeBtn = document.getElementById('modalClose');
1077
+ const quickRefBtn = document.getElementById('quickRefBtn');
1078
+ const floatingBtn = document.getElementById('floatingRef');
1079
+
1080
+ const shortcutList = document.getElementById('shortcutList');
1081
+ shortcutList.innerHTML = shortcuts.map(s => `
1082
+ <div class="shortcut-item">
1083
+ <span class="shortcut-key">${s.key}</span>
1084
+ <span>${s.desc}</span>
1085
+ </div>
1086
+ `).join('');
1087
+
1088
+ quickRefBtn.addEventListener('click', () => modal.classList.add('active'));
1089
+ floatingBtn.addEventListener('click', () => modal.classList.add('active'));
1090
+ closeBtn.addEventListener('click', () => modal.classList.remove('active'));
1091
+ modal.addEventListener('click', (e) => {
1092
+ if (e.target === modal) modal.classList.remove('active');
1093
+ });
1094
+ }
1095
+
1096
+ // Initialize
1097
+ function init() {
1098
+ renderPart1();
1099
+ renderPart2();
1100
+ setupTabNav();
1101
+ setupModal();
1102
+ updateProgress();
1103
+ }
1104
+
1105
+ init();
1106
+ </script>
1107
+ </body>
1108
+ </html>