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.
@@ -0,0 +1,959 @@
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 Advanced Tutorial Runner</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
+ /* Footer */
396
+ footer {
397
+ background-color: #f6f8fa;
398
+ border-top: 1px solid #d0d7de;
399
+ padding: 12px 20px;
400
+ flex-shrink: 0;
401
+ font-size: 12px;
402
+ color: #57606a;
403
+ }
404
+
405
+ /* Message Box */
406
+ .message-box {
407
+ background-color: #eaeef2;
408
+ border: 1px solid #d0d7de;
409
+ border-radius: 6px;
410
+ padding: 12px;
411
+ margin-bottom: 12px;
412
+ font-size: 12px;
413
+ color: #57606a;
414
+ }
415
+
416
+ .message-box.warning {
417
+ border-color: #d29922;
418
+ background-color: rgba(210, 153, 34, 0.1);
419
+ color: #d0883c;
420
+ }
421
+
422
+ /* Responsive */
423
+ @media (max-width: 1200px) {
424
+ .container {
425
+ flex-direction: column;
426
+ }
427
+
428
+ .tutorial-panel {
429
+ width: 100%;
430
+ height: 40%;
431
+ border-right: none;
432
+ border-bottom: 1px solid #d0d7de;
433
+ }
434
+
435
+ .app-panel {
436
+ height: 60%;
437
+ }
438
+ }
439
+ </style>
440
+ </head>
441
+ <body>
442
+ <!-- Header -->
443
+ <header>
444
+ <div class="header-content">
445
+ <div class="header-left">
446
+ <div class="header-title">Advanced Tutorial Runner</div>
447
+ </div>
448
+ <div class="progress-section">
449
+ <div class="progress-bar-container">
450
+ <div class="progress-bar" id="progressBar"></div>
451
+ </div>
452
+ <div class="step-counter" id="stepCounter">Step 0 of 32</div>
453
+ </div>
454
+ <div class="header-controls">
455
+ <button class="btn-primary" id="runAllBtn">▶ Run All</button>
456
+ <button class="btn-danger" id="stopBtn" disabled>⏹ Stop</button>
457
+ </div>
458
+ </div>
459
+ </header>
460
+
461
+ <!-- Main Container -->
462
+ <div class="container">
463
+ <!-- Left Panel - Tutorial Steps -->
464
+ <div class="tutorial-panel">
465
+ <div class="steps-container" id="stepsContainer"></div>
466
+ <footer>
467
+ <div id="footerText">Select a step to begin</div>
468
+ </footer>
469
+ </div>
470
+
471
+ <!-- Right Panel - App Preview -->
472
+ <div class="app-panel">
473
+ <div class="app-header">
474
+ <div class="app-header-left">
475
+ <div class="app-indicator" id="appIndicator"></div>
476
+ <div class="app-url">cyclecad.com/app/</div>
477
+ </div>
478
+ <div class="app-actions">
479
+ <button id="openTabBtn">↗ Open in Tab</button>
480
+ <button id="reloadAppBtn">⟳ Reload</button>
481
+ </div>
482
+ </div>
483
+ <iframe id="app-frame" src="../index.html"></iframe>
484
+ <div class="message-box warning" style="margin: 12px; display: none;" id="crossOriginMsg">
485
+ <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.
486
+ </div>
487
+ </div>
488
+ </div>
489
+
490
+ <script>
491
+ // Advanced Tutorial Steps Data
492
+ const TUTORIAL_STEPS = [
493
+ // Agent API (Steps 1-5)
494
+ {
495
+ number: 1,
496
+ title: "Agent API: Create Cylinder",
497
+ description: "Use the Agent API to programmatically create a cylinder shape",
498
+ detail: "The Agent API provides JSON-RPC style access to all cycleCAD geometry operations. This step demonstrates shape.cylinder with dynamic parameters.",
499
+ command: "window.cycleCAD.execute({ method: 'shape.cylinder', params: { radius: 25, height: 80, centered: true } })",
500
+ type: "api",
501
+ category: "Agent API"
502
+ },
503
+ {
504
+ number: 2,
505
+ title: "Agent API: Add Fillet",
506
+ description: "Apply a fillet operation via Agent API",
507
+ detail: "The feature.fillet command takes a radius parameter and applies edge rounding to the selected geometry.",
508
+ command: "window.cycleCAD.execute({ method: 'feature.fillet', params: { radius: 5, edges: [0, 1, 2] } })",
509
+ type: "api",
510
+ category: "Agent API"
511
+ },
512
+ {
513
+ number: 3,
514
+ title: "Agent API: Boolean Operations",
515
+ description: "Perform a boolean cut operation using the Agent API",
516
+ detail: "Use feature.boolean to combine geometries: union, cut, or intersect. This demonstrates a cut operation.",
517
+ command: "window.cycleCAD.execute({ method: 'feature.boolean', params: { operation: 'cut', target: 0, tool: 1 } })",
518
+ type: "api",
519
+ category: "Agent API"
520
+ },
521
+ {
522
+ number: 4,
523
+ title: "Agent API: Create Assembly",
524
+ description: "Create an assembly component using the Agent API",
525
+ detail: "The assembly.add command adds a named component to the current assembly with position and rotation.",
526
+ command: "window.cycleCAD.execute({ method: 'assembly.add', params: { name: 'Part_1', shapeId: 'shape_0', position: [0, 0, 0] } })",
527
+ type: "api",
528
+ category: "Agent API"
529
+ },
530
+ {
531
+ number: 5,
532
+ title: "Agent API: Query Geometry",
533
+ description: "Query geometry properties via the Agent API",
534
+ detail: "Use render.query to inspect selected geometry: dimensions, volume, surface area, and material properties.",
535
+ command: "window.cycleCAD.execute({ method: 'render.query', params: { shapeId: 'shape_0' } })",
536
+ type: "api",
537
+ category: "Agent API"
538
+ },
539
+
540
+ // MCP Server (Steps 6-8)
541
+ {
542
+ number: 6,
543
+ title: "MCP: Register Tool Schema",
544
+ description: "Set up MCP tool schema for external AI agents",
545
+ detail: "The MCP server exposes cycleCAD as a set of tools that Claude and other AI agents can discover and call.",
546
+ command: "Keyboard shortcut: Ctrl+M to toggle MCP panel",
547
+ key: "Ctrl+M",
548
+ type: "key",
549
+ category: "MCP Server"
550
+ },
551
+ {
552
+ number: 7,
553
+ title: "MCP: Test Claude Integration",
554
+ description: "Test MCP connection with Claude AI assistant",
555
+ detail: "Once MCP is registered, Claude can directly call cycleCAD commands to create and modify designs.",
556
+ command: "Click MCP panel, then 'Test Connection'",
557
+ type: "click",
558
+ category: "MCP Server"
559
+ },
560
+ {
561
+ number: 8,
562
+ title: "MCP: View Tool Definitions",
563
+ description: "Inspect the tool schema available to external agents",
564
+ detail: "View all 55 commands exposed via MCP with parameter schemas and descriptions.",
565
+ command: "In MCP panel, click 'Show Tools'",
566
+ type: "click",
567
+ category: "MCP Server"
568
+ },
569
+
570
+ // REST API (Steps 9-11)
571
+ {
572
+ number: 9,
573
+ title: "REST API: Create Shape via HTTP",
574
+ description: "Call REST API endpoint to create geometry",
575
+ detail: "POST /api/shape/cylinder with JSON body: { radius: 25, height: 80 }",
576
+ command: "curl -X POST http://localhost:8080/api/shape/cylinder -H 'Content-Type: application/json' -d '{\"radius\": 25, \"height\": 80}'",
577
+ type: "api",
578
+ category: "REST API"
579
+ },
580
+ {
581
+ number: 10,
582
+ title: "REST API: Export to GLB",
583
+ description: "Export the model via REST API",
584
+ detail: "GET /api/export/glb returns the current model as a binary GLB file.",
585
+ command: "window.fetch('http://localhost:8080/api/export/glb').then(r => r.blob())",
586
+ type: "api",
587
+ category: "REST API"
588
+ },
589
+ {
590
+ number: 11,
591
+ title: "REST API: WebSocket Real-time Updates",
592
+ description: "Connect to WebSocket for live model updates",
593
+ detail: "WebSocket endpoint /ws sends real-time geometry updates when other clients modify the model.",
594
+ command: "const ws = new WebSocket('ws://localhost:8080/ws'); ws.onmessage = (e) => console.log(e.data)",
595
+ type: "api",
596
+ category: "REST API"
597
+ },
598
+
599
+ // CLI Tool (Steps 12-14)
600
+ {
601
+ number: 12,
602
+ title: "CLI: Launch Interactive REPL",
603
+ description: "Start the cycleCAD CLI tool in interactive mode",
604
+ detail: "The CLI provides a command-line interface for CAD operations with tab completion and colored output.",
605
+ command: "npx cyclecad-cli --mode=repl",
606
+ type: "api",
607
+ category: "CLI Tool"
608
+ },
609
+ {
610
+ number: 13,
611
+ title: "CLI: Batch Scripting",
612
+ description: "Run a batch script file with multiple operations",
613
+ detail: "Create a .cad script file with multiple commands (one per line) and execute it.",
614
+ command: "npx cyclecad-cli --script=model.cad --output=model.glb",
615
+ type: "api",
616
+ category: "CLI Tool"
617
+ },
618
+ {
619
+ number: 14,
620
+ title: "CLI: Export Model",
621
+ description: "Export model to STL or GLB from the command line",
622
+ detail: "The CLI supports batch export of multiple formats with compression options.",
623
+ command: "npx cyclecad-cli --model=design.json --export=stl --output=part.stl",
624
+ type: "api",
625
+ category: "CLI Tool"
626
+ },
627
+
628
+ // Token Engine (Steps 15-17)
629
+ {
630
+ number: 15,
631
+ title: "Token Engine: Check Balance",
632
+ description: "View your current $CYCLE token balance",
633
+ detail: "The token dashboard shows spending, earnings, and usage history with per-operation costs.",
634
+ command: "Keyboard shortcut: Ctrl+Shift+T to open token dashboard",
635
+ key: "Ctrl+Shift+T",
636
+ type: "key",
637
+ category: "Token Engine"
638
+ },
639
+ {
640
+ number: 16,
641
+ title: "Token Engine: Buy Tokens",
642
+ description: "Purchase $CYCLE tokens via Stripe or crypto",
643
+ detail: "Tokens are billed per operation: 10 for shape creation, 5 for fillet/chamfer, 2 for render.",
644
+ command: "In token dashboard, click 'Buy Tokens' button",
645
+ type: "click",
646
+ category: "Token Engine"
647
+ },
648
+ {
649
+ number: 17,
650
+ title: "Token Engine: Earn Tokens",
651
+ description: "Earn tokens by publishing designs to the marketplace",
652
+ detail: "When others purchase or use your designs, you earn 70-90% of the tokens spent.",
653
+ command: "In marketplace panel, publish a model and share the link",
654
+ type: "click",
655
+ category: "Token Engine"
656
+ },
657
+
658
+ // Marketplace (Steps 18-20)
659
+ {
660
+ number: 18,
661
+ title: "Marketplace: Publish Design",
662
+ description: "Upload your model to the marketplace",
663
+ detail: "Set pricing tier, license, and preview image. Models can be FREE, PAID (€5-€500), or SUBSCRIPTION.",
664
+ command: "Keyboard shortcut: Ctrl+P to open marketplace panel",
665
+ key: "Ctrl+P",
666
+ type: "key",
667
+ category: "Marketplace"
668
+ },
669
+ {
670
+ number: 19,
671
+ title: "Marketplace: Search Models",
672
+ description: "Browse and purchase models from the community",
673
+ detail: "Full-text search across 1000+ designs with filtering by category, price, and rating.",
674
+ command: "Click marketplace 'Search' tab and filter by 'Mechanical'",
675
+ type: "click",
676
+ category: "Marketplace"
677
+ },
678
+ {
679
+ number: 20,
680
+ title: "Marketplace: View Creator Stats",
681
+ description: "Track sales and earnings from published designs",
682
+ detail: "See detailed analytics on downloads, ratings, revenue, and token earnings per model.",
683
+ command: "In marketplace, click 'My Designs' tab",
684
+ type: "click",
685
+ category: "Marketplace"
686
+ },
687
+
688
+ // Docker Deployment (Steps 21-23)
689
+ {
690
+ number: 21,
691
+ title: "Docker: Build Images",
692
+ description: "Build Docker images for cycleCAD services",
693
+ detail: "The Dockerfile sets up nginx for ExplodeView, Node.js for REST API, and Python for STEP converter.",
694
+ command: "docker-compose build",
695
+ type: "api",
696
+ category: "Docker"
697
+ },
698
+ {
699
+ number: 22,
700
+ title: "Docker: Start Services",
701
+ description: "Launch all three services with docker-compose",
702
+ detail: "Starts ExplodeView (:8080), REST API (:8787), and STEP converter (:9000) simultaneously.",
703
+ command: "docker-compose up -d",
704
+ type: "api",
705
+ category: "Docker"
706
+ },
707
+ {
708
+ number: 23,
709
+ title: "Docker: Scale MCP Servers",
710
+ description: "Run multiple MCP server instances for load balancing",
711
+ detail: "Docker allows horizontal scaling of the MCP server for multi-agent workloads.",
712
+ command: "docker-compose up -d --scale mcp-server=3",
713
+ type: "api",
714
+ category: "Docker"
715
+ },
716
+
717
+ // Multi-Agent Workflows (Steps 24-26)
718
+ {
719
+ number: 24,
720
+ title: "Multi-Agent: Design Review",
721
+ description: "Run parallel AI agents for design feedback",
722
+ detail: "Launch multiple Claude instances with different prompts to validate geometry, manufacturability, and cost.",
723
+ command: "window.cycleCAD.execute({ method: 'validate.designReview', params: { agents: ['claude', 'groq', 'local'] } })",
724
+ type: "api",
725
+ category: "Multi-Agent"
726
+ },
727
+ {
728
+ number: 25,
729
+ title: "Multi-Agent: Collaborative Editing",
730
+ description: "Enable real-time multi-agent geometry editing",
731
+ detail: "Agents coordinate via CRDT (Conflict-free Replicated Data Type) for conflict-free concurrent edits.",
732
+ command: "Keyboard shortcut: Ctrl+Shift+A to enable agent collab mode",
733
+ key: "Ctrl+Shift+A",
734
+ type: "key",
735
+ category: "Multi-Agent"
736
+ },
737
+ {
738
+ number: 26,
739
+ title: "Multi-Agent: Export Comparison",
740
+ description: "Export designs generated by different agent strategies",
741
+ detail: "Compare optimized designs (cost, weight, strength) side-by-side in the viewer.",
742
+ command: "In multi-agent panel, click 'Export All Variants'",
743
+ type: "click",
744
+ category: "Multi-Agent"
745
+ },
746
+
747
+ // Manufacturing Pipeline (Steps 27-29)
748
+ {
749
+ number: 27,
750
+ title: "Connected Fabs: Submit for Fabrication",
751
+ description: "Send model to connected manufacturing network",
752
+ detail: "The fab network handles 3D printing, CNC, sheet metal, and laser cutting across 500+ partner shops.",
753
+ command: "Keyboard shortcut: Ctrl+F to open fab network panel",
754
+ key: "Ctrl+F",
755
+ type: "key",
756
+ category: "Manufacturing"
757
+ },
758
+ {
759
+ number: 28,
760
+ title: "Connected Fabs: Get Quotes",
761
+ description: "Request and compare manufacturing quotes",
762
+ detail: "Fab network auto-estimates cost, lead time, and material availability across different processes.",
763
+ command: "In fab panel, select process (3D print/CNC/LaserCut) and click 'Get Quotes'",
764
+ type: "click",
765
+ category: "Manufacturing"
766
+ },
767
+ {
768
+ number: 29,
769
+ title: "Connected Fabs: Track Order",
770
+ description: "Monitor fabrication progress in real-time",
771
+ detail: "Live GPS tracking, status updates, and estimated delivery for each manufacturing order.",
772
+ command: "In fab panel, click 'Active Orders' to view tracking",
773
+ type: "click",
774
+ category: "Manufacturing"
775
+ },
776
+
777
+ // Advanced Features (Steps 30-32)
778
+ {
779
+ number: 30,
780
+ title: "Plugins: Create Custom Feature",
781
+ description: "Develop a custom feature plugin using JavaScript",
782
+ detail: "Plugins extend cycleCAD with domain-specific operations (gears, springs, threads, fasteners).",
783
+ command: "See /plugins/example-plugin.js for template",
784
+ type: "api",
785
+ category: "Advanced"
786
+ },
787
+ {
788
+ number: 31,
789
+ title: "Plugins: Register in Marketplace",
790
+ description: "Publish your plugin to the cycleCAD plugin marketplace",
791
+ detail: "Monetize plugins: charge per use, monthly subscription, or percentage of model sales.",
792
+ command: "In marketplace, switch to 'Plugins' tab and click 'Publish'",
793
+ type: "click",
794
+ category: "Advanced"
795
+ },
796
+ {
797
+ number: 32,
798
+ title: "Performance Tuning",
799
+ description: "Profile and optimize agent API performance",
800
+ detail: "Use Chrome DevTools to analyze render times, API call latency, and memory usage for large assemblies.",
801
+ command: "Press F12, then Ctrl+Shift+P, type 'performance' and record",
802
+ key: "F12",
803
+ type: "key",
804
+ category: "Advanced"
805
+ }
806
+ ];
807
+
808
+ let currentStepIndex = 0;
809
+ let completedSteps = new Set();
810
+ let isRunningAll = false;
811
+
812
+ function init() {
813
+ renderSteps();
814
+ setupEventListeners();
815
+ updateProgress();
816
+ }
817
+
818
+ function renderSteps() {
819
+ const container = document.getElementById('stepsContainer');
820
+ container.innerHTML = '';
821
+
822
+ TUTORIAL_STEPS.forEach((step, index) => {
823
+ const card = document.createElement('div');
824
+ card.className = 'step-card';
825
+ card.id = `step-${index}`;
826
+
827
+ const isCompleted = completedSteps.has(index);
828
+ if (isCompleted) {
829
+ card.classList.add('completed');
830
+ }
831
+
832
+ card.innerHTML = `
833
+ <div class="step-header">
834
+ <div class="step-badge">${step.number}</div>
835
+ <div class="step-title">${step.title}</div>
836
+ <div class="step-status">${step.category}</div>
837
+ </div>
838
+ <div class="step-description">${step.description}</div>
839
+ <div class="step-details" id="details-${index}">
840
+ <div>${step.detail}</div>
841
+ ${step.command ? `<div class="step-code">Cmd: "${step.command}"</div>` : ''}
842
+ ${step.key ? `<div class="step-code">Key: ${step.key}</div>` : ''}
843
+ </div>
844
+ <div class="step-actions">
845
+ <button class="step-run-btn" onclick="runStep(${index})">▶ Run</button>
846
+ <button class="step-details-toggle" onclick="toggleDetails(${index})">Details</button>
847
+ </div>
848
+ `;
849
+
850
+ card.addEventListener('click', () => {
851
+ document.querySelectorAll('.step-card').forEach(c => c.classList.remove('active'));
852
+ card.classList.add('active');
853
+ currentStepIndex = index;
854
+ updateFooter();
855
+ });
856
+
857
+ container.appendChild(card);
858
+ });
859
+ }
860
+
861
+ function toggleDetails(index) {
862
+ const details = document.getElementById(`details-${index}`);
863
+ details.classList.toggle('expanded');
864
+ }
865
+
866
+ async function runStep(index) {
867
+ const step = TUTORIAL_STEPS[index];
868
+ const card = document.getElementById(`step-${index}`);
869
+ const runBtn = card.querySelector('.step-run-btn');
870
+
871
+ runBtn.disabled = true;
872
+ runBtn.textContent = '⟳ Running...';
873
+
874
+ try {
875
+ const iframe = document.getElementById('app-frame');
876
+
877
+ if (step.type === 'api' && window.cycleCAD) {
878
+ // Try to execute via Agent API
879
+ eval(step.command);
880
+ } else if (step.type === 'key') {
881
+ // Simulate keyboard shortcut hint
882
+ console.log(`Press: ${step.key}`);
883
+ } else if (step.type === 'click') {
884
+ // Log the action to perform manually
885
+ console.log(`Click: ${step.detail}`);
886
+ }
887
+
888
+ // Mark as complete
889
+ completedSteps.add(index);
890
+ card.classList.add('completed');
891
+ updateProgress();
892
+
893
+ await new Promise(resolve => setTimeout(resolve, 500));
894
+ } catch (error) {
895
+ console.error(`Step ${step.number} error:`, error);
896
+ }
897
+
898
+ runBtn.disabled = false;
899
+ runBtn.textContent = '▶ Run';
900
+ }
901
+
902
+ async function runAll() {
903
+ const runAllBtn = document.getElementById('runAllBtn');
904
+ const stopBtn = document.getElementById('stopBtn');
905
+
906
+ runAllBtn.disabled = true;
907
+ stopBtn.disabled = false;
908
+ isRunningAll = true;
909
+
910
+ for (let i = 0; i < TUTORIAL_STEPS.length && isRunningAll; i++) {
911
+ currentStepIndex = i;
912
+ document.querySelectorAll('.step-card').forEach(c => c.classList.remove('active'));
913
+ document.getElementById(`step-${i}`).classList.add('active');
914
+ await runStep(i);
915
+ await new Promise(resolve => setTimeout(resolve, 800));
916
+ }
917
+
918
+ runAllBtn.disabled = false;
919
+ stopBtn.disabled = true;
920
+ isRunningAll = false;
921
+ updateProgress();
922
+ }
923
+
924
+ function stopRunAll() {
925
+ isRunningAll = false;
926
+ document.getElementById('runAllBtn').disabled = false;
927
+ document.getElementById('stopBtn').disabled = true;
928
+ }
929
+
930
+ function updateProgress() {
931
+ const total = TUTORIAL_STEPS.length;
932
+ const completed = completedSteps.size;
933
+ const percent = (completed / total) * 100;
934
+
935
+ document.getElementById('progressBar').style.width = percent + '%';
936
+ document.getElementById('stepCounter').textContent = `Step ${completed} of ${total}`;
937
+ }
938
+
939
+ function updateFooter() {
940
+ const step = TUTORIAL_STEPS[currentStepIndex];
941
+ document.getElementById('footerText').textContent = `Step ${step.number}: ${step.title}`;
942
+ }
943
+
944
+ function setupEventListeners() {
945
+ document.getElementById('runAllBtn').addEventListener('click', runAll);
946
+ document.getElementById('stopBtn').addEventListener('click', stopRunAll);
947
+ document.getElementById('reloadAppBtn').addEventListener('click', () => {
948
+ document.getElementById('app-frame').src = '../index.html';
949
+ });
950
+ document.getElementById('openTabBtn').addEventListener('click', () => {
951
+ window.open('../index.html', '_blank');
952
+ });
953
+ }
954
+
955
+ // Initialize on load
956
+ window.addEventListener('DOMContentLoaded', init);
957
+ </script>
958
+ </body>
959
+ </html>