cyclecad 0.9.8 → 1.0.2

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,1306 @@
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>Intermediate Tutorial Runner - cycleCAD</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">Intermediate Tutorial Runner</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 0</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
+ // Intermediate Tutorial Steps Data
656
+ const TUTORIAL_STEPS = [
657
+ // Section 1: Advanced Sketches
658
+ {
659
+ number: 1,
660
+ title: "Apply Coincident Constraint",
661
+ description: "Fully constrain a sketch by making two points coincident",
662
+ detail: "Constraints define relationships between geometry. Coincident makes points overlap. Select 2 points and apply constraint.",
663
+ command: "sketch; line 0,0 to 100,0; apply coincident",
664
+ type: "chat",
665
+ category: "Advanced Sketches",
666
+ shortcut: "C"
667
+ },
668
+ {
669
+ number: 2,
670
+ title: "Add Horizontal Constraint",
671
+ description: "Make a line perfectly horizontal",
672
+ detail: "Use the horizontal constraint to align geometry to the X-axis. Select a line and apply constraint.",
673
+ command: "sketch; select line; apply horizontal",
674
+ type: "keyboard",
675
+ key: "H",
676
+ category: "Advanced Sketches"
677
+ },
678
+ {
679
+ number: 3,
680
+ title: "Add Distance Dimension",
681
+ description: "Dimension a distance between two points",
682
+ detail: "Apply a distance constraint to define exact spacing. Set value to 50mm.",
683
+ command: "sketch; apply distance 50",
684
+ type: "chat",
685
+ category: "Advanced Sketches"
686
+ },
687
+ {
688
+ number: 4,
689
+ title: "Add Angle Dimension",
690
+ description: "Dimension an angle between two lines",
691
+ detail: "Apply an angle constraint. Set value to 45 degrees.",
692
+ command: "sketch; apply angle 45",
693
+ type: "chat",
694
+ category: "Advanced Sketches"
695
+ },
696
+ {
697
+ number: 5,
698
+ title: "Apply Equal Constraint",
699
+ description: "Make two line segments equal length",
700
+ detail: "Equal constraint ensures two geometry elements are the same size. Select 2 lines.",
701
+ command: "sketch; select 2 lines; apply equal",
702
+ type: "keyboard",
703
+ key: "E",
704
+ category: "Advanced Sketches"
705
+ },
706
+
707
+ // Section 2: Parametric Operations
708
+ {
709
+ number: 6,
710
+ title: "Create Fillet",
711
+ description: "Fillet an edge with 5mm radius",
712
+ detail: "Use the fillet tool to round edges. Select edge and set radius to 5mm.",
713
+ command: "fillet; radius 5",
714
+ type: "chat",
715
+ category: "Parametric Operations"
716
+ },
717
+ {
718
+ number: 7,
719
+ title: "Create Chamfer",
720
+ description: "Chamfer an edge with 2mm distance",
721
+ detail: "Chamfer creates a beveled edge. Select edge and set distance to 2mm.",
722
+ command: "chamfer; distance 2",
723
+ type: "chat",
724
+ category: "Parametric Operations"
725
+ },
726
+ {
727
+ number: 8,
728
+ title: "Boolean Union",
729
+ description: "Combine two solids into one",
730
+ detail: "Union merges two bodies into a single solid. Select two parts.",
731
+ command: "boolean; operation union",
732
+ type: "keyboard",
733
+ key: "U",
734
+ category: "Parametric Operations"
735
+ },
736
+ {
737
+ number: 9,
738
+ title: "Boolean Cut",
739
+ description: "Subtract one solid from another",
740
+ detail: "Cut removes material. Select target body then select tool body.",
741
+ command: "boolean; operation cut",
742
+ type: "keyboard",
743
+ key: "Ctrl+K",
744
+ category: "Parametric Operations"
745
+ },
746
+ {
747
+ number: 10,
748
+ title: "Create Shell",
749
+ description: "Create a hollow part with uniform wall thickness",
750
+ detail: "Shell removes faces and creates walls. Remove bottom face, set thickness 2mm.",
751
+ command: "shell; faces [bottom]; thickness 2",
752
+ type: "chat",
753
+ category: "Parametric Operations"
754
+ },
755
+ {
756
+ number: 11,
757
+ title: "Rectangular Pattern",
758
+ description: "Array features in X and Y directions",
759
+ detail: "Create a 3x4 grid pattern. Set spacing 20mm, count 3 and 4.",
760
+ command: "pattern; type rectangular; rows 3; cols 4; spacing 20",
761
+ type: "chat",
762
+ category: "Parametric Operations"
763
+ },
764
+ {
765
+ number: 12,
766
+ title: "Circular Pattern",
767
+ description: "Array features around a center point",
768
+ detail: "Create radial pattern. Set count 8 around 360 degrees.",
769
+ command: "pattern; type circular; count 8; angle 360",
770
+ type: "chat",
771
+ category: "Parametric Operations"
772
+ },
773
+
774
+ // Section 3: Advanced Operations
775
+ {
776
+ number: 13,
777
+ title: "Sweep Along Path",
778
+ description: "Sweep a profile along a curve",
779
+ detail: "Sweep extrudes a 2D profile along a 3D path. Create a pipe by sweeping a circle.",
780
+ command: "sweep; profile circle; path curve; twist none",
781
+ type: "chat",
782
+ category: "Advanced Operations"
783
+ },
784
+ {
785
+ number: 14,
786
+ title: "Loft Between Profiles",
787
+ description: "Blend between two different cross-sections",
788
+ detail: "Loft creates smooth transitions. Create 2 sketches at different heights and loft.",
789
+ command: "loft; profile1 circle; profile2 square; smooth true",
790
+ type: "chat",
791
+ category: "Advanced Operations"
792
+ },
793
+ {
794
+ number: 15,
795
+ title: "Sheet Metal Bend",
796
+ description: "Bend sheet metal with 90° angle",
797
+ detail: "Create a bend tab with k-factor for material allowance. Set k=0.5.",
798
+ command: "bend; angle 90; radius 2; k-factor 0.5",
799
+ type: "chat",
800
+ category: "Advanced Operations"
801
+ },
802
+ {
803
+ number: 16,
804
+ title: "Unfold Sheet Metal",
805
+ description: "Generate flat pattern from folded sheet",
806
+ detail: "Unfold creates a 2D flat pattern for manufacturing. Auto-calculate bend allowance.",
807
+ command: "unfold; method k-factor; calculate true",
808
+ type: "chat",
809
+ category: "Advanced Operations"
810
+ },
811
+ {
812
+ number: 17,
813
+ title: "Generate Spring",
814
+ description: "Create a helical spring geometry",
815
+ detail: "Spring creates coil geometry. Set pitch 10mm, coils 10, radius 15mm.",
816
+ command: "spring; pitch 10; coils 10; radius 15",
817
+ type: "chat",
818
+ category: "Advanced Operations"
819
+ },
820
+ {
821
+ number: 18,
822
+ title: "Generate Thread",
823
+ description: "Create a screw thread",
824
+ detail: "Thread generates helical cutting geometry. M10 metric, pitch 1.5mm.",
825
+ command: "thread; type M10; pitch 1.5; handedness right",
826
+ type: "chat",
827
+ category: "Advanced Operations"
828
+ },
829
+
830
+ // Section 4: Assembly
831
+ {
832
+ number: 19,
833
+ title: "Switch to Assembly Mode",
834
+ description: "Enter the assembly workspace",
835
+ detail: "Assembly mode combines multiple parts. Click Assembly tab in toolbar.",
836
+ command: "view assembly",
837
+ type: "keyboard",
838
+ key: "A",
839
+ category: "Assembly"
840
+ },
841
+ {
842
+ number: 20,
843
+ title: "Insert Component",
844
+ description: "Add a component to the assembly",
845
+ detail: "Insert from file or create new. Use Insert Component button.",
846
+ command: "assembly; insert component",
847
+ type: "chat",
848
+ category: "Assembly"
849
+ },
850
+ {
851
+ number: 21,
852
+ title: "Apply Coincident Mate",
853
+ description: "Align faces or planes together",
854
+ detail: "Mate constraint makes elements align. Select 2 faces to coincide.",
855
+ command: "mate; type coincident",
856
+ type: "keyboard",
857
+ key: "M",
858
+ category: "Assembly"
859
+ },
860
+ {
861
+ number: 22,
862
+ title: "Apply Distance Mate",
863
+ description: "Set fixed distance between components",
864
+ detail: "Distance mate creates spacing. Select 2 faces, set distance 10mm.",
865
+ command: "mate; type distance; value 10",
866
+ type: "chat",
867
+ category: "Assembly"
868
+ },
869
+ {
870
+ number: 23,
871
+ title: "Create Exploded View",
872
+ description: "Separate components for visualization",
873
+ detail: "Explode moves parts apart. Drag component in 3D view while in explode mode.",
874
+ command: "assembly; explode",
875
+ type: "keyboard",
876
+ key: "Ctrl+E",
877
+ category: "Assembly"
878
+ },
879
+
880
+ // Section 5: Text-to-CAD
881
+ {
882
+ number: 24,
883
+ title: "Activate Text-to-CAD",
884
+ description: "Enable natural language input",
885
+ detail: "Text-to-CAD converts voice/text commands. Click AI Chat tab or press Ctrl+Shift+T.",
886
+ command: "mode text-to-cad",
887
+ type: "keyboard",
888
+ key: "Ctrl+Shift+T",
889
+ category: "Text-to-CAD"
890
+ },
891
+ {
892
+ number: 25,
893
+ title: "Create Shape with Voice",
894
+ description: "Build geometry by speaking",
895
+ detail: "Say 'create cylinder 50mm radius 80mm tall'. Mic button activates speech recognition.",
896
+ command: "create cylinder 50mm radius 80mm tall",
897
+ type: "chat",
898
+ category: "Text-to-CAD"
899
+ },
900
+ {
901
+ number: 26,
902
+ title: "Add Feature from Text",
903
+ description: "Apply operation using natural language",
904
+ detail: "Type 'fillet all edges with 5mm radius'. System auto-detects edges.",
905
+ command: "fillet all edges with 5mm radius",
906
+ type: "chat",
907
+ category: "Text-to-CAD"
908
+ },
909
+ {
910
+ number: 27,
911
+ title: "Generate from Design Brief",
912
+ description: "Create design from specifications",
913
+ detail: "Input: 'rectangular box 100x50x30mm with corner fillets 3mm'. Full design generated.",
914
+ command: "create from: rectangular box 100x50x30mm with corner fillets 3mm",
915
+ type: "chat",
916
+ category: "Text-to-CAD"
917
+ },
918
+
919
+ // Section 6: CAM Pipeline
920
+ {
921
+ number: 28,
922
+ title: "Switch to CAM Workspace",
923
+ description: "Enter CAM design environment",
924
+ detail: "CAM workspace shows toolpath and machining operations. Click CAM tab.",
925
+ command: "view cam",
926
+ type: "keyboard",
927
+ key: "Ctrl+M",
928
+ category: "CAM Pipeline"
929
+ },
930
+ {
931
+ number: 29,
932
+ title: "Create Facing Operation",
933
+ description: "Generate a surface finish pass",
934
+ detail: "Facing cuts the top surface. Set tool diameter 10mm, spindle 1000 RPM, feed 100mm/min.",
935
+ command: "cam; operation facing; tool 10mm; rpm 1000; feed 100",
936
+ type: "chat",
937
+ category: "CAM Pipeline"
938
+ },
939
+ {
940
+ number: 30,
941
+ title: "Create Pocket Operation",
942
+ description: "Machine a rectangular pocket",
943
+ detail: "Pocket removes material from cavity. Select face, set depth 20mm.",
944
+ command: "cam; operation pocket; depth 20",
945
+ type: "chat",
946
+ category: "CAM Pipeline"
947
+ },
948
+ {
949
+ number: 31,
950
+ title: "Create Drill Operation",
951
+ description: "Generate drilling toolpath",
952
+ detail: "Drill creates holes. Tool 5mm, speed 500 RPM, peck depth 10mm.",
953
+ command: "cam; operation drill; tool 5; rpm 500; peck 10",
954
+ type: "chat",
955
+ category: "CAM Pipeline"
956
+ },
957
+ {
958
+ number: 32,
959
+ title: "Generate Adaptive Toolpath",
960
+ description: "AI-optimized cutting strategy",
961
+ detail: "Adaptive maintains constant load. Sets optimal speeds and feeds automatically.",
962
+ command: "cam; operation adaptive; material aluminum",
963
+ type: "chat",
964
+ category: "CAM Pipeline"
965
+ },
966
+ {
967
+ number: 33,
968
+ title: "Export G-Code",
969
+ description: "Generate machine-ready G-code",
970
+ detail: "Export generates CNC program. Select format (Fanuc, Haas, Mazak).",
971
+ command: "cam; export gcode; format fanuc",
972
+ type: "chat",
973
+ category: "CAM Pipeline"
974
+ },
975
+ {
976
+ number: 34,
977
+ title: "Simulate Toolpath",
978
+ description: "Visualize cutting simulation",
979
+ detail: "Simulation shows tool movement and material removal. Play animation.",
980
+ command: "cam; simulate; play",
981
+ type: "keyboard",
982
+ key: "Ctrl+Shift+S",
983
+ category: "CAM Pipeline"
984
+ },
985
+
986
+ // Section 7: DFM Analysis
987
+ {
988
+ number: 35,
989
+ title: "Activate DFM Analysis",
990
+ description: "Check design for manufacturability",
991
+ detail: "DFM shows issues and improvements. Click DFM tab in right panel.",
992
+ command: "analyze dfm",
993
+ type: "keyboard",
994
+ key: "Ctrl+D",
995
+ category: "DFM Analysis"
996
+ },
997
+ {
998
+ number: 36,
999
+ title: "Check Wall Thickness",
1000
+ description: "Verify minimum wall thickness",
1001
+ detail: "DFM highlights thin walls (<1.5mm). Suggests thickness 2-3mm.",
1002
+ command: "analyze; check wall-thickness; minimum 2",
1003
+ type: "chat",
1004
+ category: "DFM Analysis"
1005
+ },
1006
+ {
1007
+ number: 37,
1008
+ title: "Detect Draft Issues",
1009
+ description: "Find walls without draft angle",
1010
+ detail: "Draft 2-3° aids mold release. DFM highlights non-drafted faces.",
1011
+ command: "analyze; check draft; min-angle 2",
1012
+ type: "chat",
1013
+ category: "DFM Analysis"
1014
+ },
1015
+ {
1016
+ number: 38,
1017
+ title: "Check Undercuts",
1018
+ description: "Identify undercut features",
1019
+ detail: "Undercuts complicate molding. DFM flags and suggests core inserts.",
1020
+ command: "analyze; check undercuts",
1021
+ type: "chat",
1022
+ category: "DFM Analysis"
1023
+ },
1024
+ {
1025
+ number: 39,
1026
+ title: "Estimate Machining Time",
1027
+ description: "Calculate CNC machine time",
1028
+ detail: "DFM estimates time and cost for aluminum cutting. Material selection varies result.",
1029
+ command: "analyze; estimate time; material aluminum",
1030
+ type: "chat",
1031
+ category: "DFM Analysis"
1032
+ },
1033
+ {
1034
+ number: 40,
1035
+ title: "Get Design Recommendations",
1036
+ description: "Receive AI-powered DFM suggestions",
1037
+ detail: "AI chatbot suggests geometry changes for better manufacturability.",
1038
+ command: "chat; ask 'how can I improve manufacturability?'",
1039
+ type: "chat",
1040
+ category: "DFM Analysis"
1041
+ },
1042
+
1043
+ // Section 8: Completion
1044
+ {
1045
+ number: 41,
1046
+ title: "Export to STEP Format",
1047
+ description: "Save design as STEP file",
1048
+ detail: "STEP is the industry standard. Preserves all geometry and metadata.",
1049
+ command: "export step",
1050
+ type: "keyboard",
1051
+ key: "Ctrl+E",
1052
+ category: "Export"
1053
+ },
1054
+ {
1055
+ number: 42,
1056
+ title: "Import STEP Assembly",
1057
+ description: "Load a STEP file into cycleCAD",
1058
+ detail: "Import loads geometry and assembly structure. File → Import → Select STEP.",
1059
+ command: "import step",
1060
+ type: "keyboard",
1061
+ key: "Ctrl+I",
1062
+ category: "Import"
1063
+ },
1064
+ {
1065
+ number: 43,
1066
+ title: "Save Project",
1067
+ description: "Save all work to project file",
1068
+ detail: "Project saves CAD data, CAM toolpaths, and metadata. Ctrl+S.",
1069
+ command: "save project",
1070
+ type: "keyboard",
1071
+ key: "Ctrl+S",
1072
+ category: "Project"
1073
+ },
1074
+ {
1075
+ number: 44,
1076
+ title: "Share Design",
1077
+ description: "Generate a shareable link",
1078
+ detail: "Create a public link for collaboration. Others can view and comment.",
1079
+ command: "share; access view",
1080
+ type: "chat",
1081
+ category: "Collaboration"
1082
+ },
1083
+ {
1084
+ number: 45,
1085
+ title: "View in ExplodeView",
1086
+ description: "Open design in 3D viewer",
1087
+ detail: "ExplodeView is the interactive 3D viewer. Shows assembly breakdown.",
1088
+ command: "view explode",
1089
+ type: "keyboard",
1090
+ key: "V",
1091
+ category: "Viewer"
1092
+ }
1093
+ ];
1094
+
1095
+ let currentStepIndex = 0;
1096
+ let isRunning = false;
1097
+
1098
+ // Initialize steps on page load
1099
+ document.addEventListener('DOMContentLoaded', () => {
1100
+ renderSteps();
1101
+ updateProgress();
1102
+ setupEventListeners();
1103
+ loadKeyboardShortcuts();
1104
+ });
1105
+
1106
+ function renderSteps() {
1107
+ const container = document.getElementById('stepsContainer');
1108
+ container.innerHTML = '';
1109
+
1110
+ TUTORIAL_STEPS.forEach((step, index) => {
1111
+ const card = document.createElement('div');
1112
+ card.className = 'step-card';
1113
+ card.id = `step-${index}`;
1114
+
1115
+ card.innerHTML = `
1116
+ <div class="step-header">
1117
+ <div class="step-badge">${step.number}</div>
1118
+ <div class="step-title">${step.title}</div>
1119
+ <div class="step-status">${step.category}</div>
1120
+ </div>
1121
+ <div class="step-description">${step.description}</div>
1122
+ <div class="step-details" id="details-${index}">
1123
+ ${step.detail}
1124
+ ${step.command ? `<div class="step-code">Command: "${step.command}"</div>` : ''}
1125
+ ${step.key ? `<div class="step-code">Key: ${step.key}</div>` : ''}
1126
+ </div>
1127
+ <div class="step-actions">
1128
+ <button class="step-run-btn" onclick="runStep(${index})">▶ Run</button>
1129
+ <button class="step-details-toggle" onclick="toggleDetails(${index})">Details</button>
1130
+ </div>
1131
+ `;
1132
+
1133
+ card.addEventListener('click', (e) => {
1134
+ if (!e.target.closest('button')) {
1135
+ currentStepIndex = index;
1136
+ updateActiveStep();
1137
+ }
1138
+ });
1139
+
1140
+ container.appendChild(card);
1141
+ });
1142
+
1143
+ document.getElementById('stepCounter').textContent = `Step 0 of ${TUTORIAL_STEPS.length}`;
1144
+ }
1145
+
1146
+ function runStep(index) {
1147
+ const step = TUTORIAL_STEPS[index];
1148
+ currentStepIndex = index;
1149
+ updateActiveStep();
1150
+
1151
+ // Execute based on step type
1152
+ if (step.type === 'chat' && step.command) {
1153
+ executeChatCommand(step.command);
1154
+ } else if (step.type === 'keyboard' && step.key) {
1155
+ executeKeyboardShortcut(step.key);
1156
+ }
1157
+
1158
+ markStepComplete(index);
1159
+ }
1160
+
1161
+ function executeChatCommand(command) {
1162
+ const frame = document.getElementById('app-frame');
1163
+ if (frame && frame.contentWindow) {
1164
+ try {
1165
+ frame.contentWindow.postMessage({
1166
+ type: 'chat-command',
1167
+ command: command
1168
+ }, '*');
1169
+ } catch (e) {
1170
+ console.log('Cross-origin execution - manual input required');
1171
+ }
1172
+ }
1173
+ }
1174
+
1175
+ function executeKeyboardShortcut(key) {
1176
+ const frame = document.getElementById('app-frame');
1177
+ if (frame && frame.contentWindow) {
1178
+ try {
1179
+ frame.contentWindow.postMessage({
1180
+ type: 'keyboard-shortcut',
1181
+ key: key
1182
+ }, '*');
1183
+ } catch (e) {
1184
+ console.log('Cross-origin shortcut - manual input required');
1185
+ }
1186
+ }
1187
+ }
1188
+
1189
+ function toggleDetails(index) {
1190
+ const details = document.getElementById(`details-${index}`);
1191
+ details.classList.toggle('expanded');
1192
+ }
1193
+
1194
+ function updateActiveStep() {
1195
+ document.querySelectorAll('.step-card.active').forEach(card => {
1196
+ card.classList.remove('active');
1197
+ const btn = card.querySelector('.step-run-btn');
1198
+ if (btn) btn.disabled = false;
1199
+ });
1200
+
1201
+ const activeCard = document.getElementById(`step-${currentStepIndex}`);
1202
+ if (activeCard) {
1203
+ activeCard.classList.add('active');
1204
+ activeCard.scrollIntoView({ behavior: 'smooth', block: 'center' });
1205
+ document.getElementById('footerText').textContent = `Step ${currentStepIndex + 1}: ${TUTORIAL_STEPS[currentStepIndex].title}`;
1206
+ }
1207
+
1208
+ updateProgress();
1209
+ }
1210
+
1211
+ function markStepComplete(index) {
1212
+ const card = document.getElementById(`step-${index}`);
1213
+ if (card && !card.classList.contains('completed')) {
1214
+ card.classList.add('completed');
1215
+ updateProgress();
1216
+ }
1217
+ }
1218
+
1219
+ function updateProgress() {
1220
+ const completedCount = document.querySelectorAll('.step-card.completed').length;
1221
+ const totalSteps = TUTORIAL_STEPS.length;
1222
+ const percentage = (completedCount / totalSteps) * 100;
1223
+
1224
+ document.getElementById('progressBar').style.width = percentage + '%';
1225
+ document.getElementById('stepCounter').textContent = `Step ${currentStepIndex + 1} of ${totalSteps}`;
1226
+ }
1227
+
1228
+ function setupEventListeners() {
1229
+ document.getElementById('runAllBtn').addEventListener('click', runAllSteps);
1230
+ document.getElementById('stopBtn').addEventListener('click', stopRunning);
1231
+ document.getElementById('openTabBtn').addEventListener('click', () => {
1232
+ window.open('https://cyclecad.com/app/', '_blank');
1233
+ });
1234
+ document.getElementById('reloadAppBtn').addEventListener('click', () => {
1235
+ document.getElementById('app-frame').src = 'https://cyclecad.com/app/';
1236
+ });
1237
+
1238
+ document.getElementById('shortcutsToggle').addEventListener('click', () => {
1239
+ document.getElementById('shortcutsPanel').classList.toggle('visible');
1240
+ });
1241
+
1242
+ document.getElementById('shortcutsClose').addEventListener('click', () => {
1243
+ document.getElementById('shortcutsPanel').classList.remove('visible');
1244
+ });
1245
+ }
1246
+
1247
+ function loadKeyboardShortcuts() {
1248
+ const shortcutsContent = document.getElementById('shortcutsContent');
1249
+ const shortcuts = [
1250
+ { key: 'C', desc: 'Coincident Constraint' },
1251
+ { key: 'H', desc: 'Horizontal Constraint' },
1252
+ { key: 'E', desc: 'Equal Constraint' },
1253
+ { key: 'U', desc: 'Boolean Union' },
1254
+ { key: 'Ctrl+K', desc: 'Boolean Cut' },
1255
+ { key: 'A', desc: 'Assembly Mode' },
1256
+ { key: 'M', desc: 'Apply Mate' },
1257
+ { key: 'Ctrl+E', desc: 'Explode View' },
1258
+ { key: 'Ctrl+Shift+T', desc: 'Text-to-CAD' },
1259
+ { key: 'Ctrl+M', desc: 'CAM Workspace' },
1260
+ { key: 'Ctrl+Shift+S', desc: 'Simulate Toolpath' },
1261
+ { key: 'Ctrl+D', desc: 'DFM Analysis' },
1262
+ { key: 'Ctrl+E', desc: 'Export' },
1263
+ { key: 'Ctrl+I', desc: 'Import' },
1264
+ { key: 'Ctrl+S', desc: 'Save Project' },
1265
+ { key: 'V', desc: 'View (ExplodeView)' }
1266
+ ];
1267
+
1268
+ shortcutsContent.innerHTML = shortcuts.map(sc => `
1269
+ <div class="shortcut-item">
1270
+ <span class="shortcut-desc">${sc.desc}</span>
1271
+ <span class="shortcut-key">${sc.key}</span>
1272
+ </div>
1273
+ `).join('');
1274
+ }
1275
+
1276
+ async function runAllSteps() {
1277
+ isRunning = true;
1278
+ document.getElementById('runAllBtn').disabled = true;
1279
+ document.getElementById('stopBtn').disabled = false;
1280
+
1281
+ for (let i = 0; i < TUTORIAL_STEPS.length && isRunning; i++) {
1282
+ currentStepIndex = i;
1283
+ updateActiveStep();
1284
+
1285
+ const step = TUTORIAL_STEPS[i];
1286
+ if (step.type === 'chat' && step.command) {
1287
+ executeChatCommand(step.command);
1288
+ } else if (step.type === 'keyboard' && step.key) {
1289
+ executeKeyboardShortcut(step.key);
1290
+ }
1291
+
1292
+ markStepComplete(i);
1293
+ await new Promise(resolve => setTimeout(resolve, 800));
1294
+ }
1295
+
1296
+ stopRunning();
1297
+ }
1298
+
1299
+ function stopRunning() {
1300
+ isRunning = false;
1301
+ document.getElementById('runAllBtn').disabled = false;
1302
+ document.getElementById('stopBtn').disabled = true;
1303
+ }
1304
+ </script>
1305
+ </body>
1306
+ </html>