cyclecad 0.2.3 → 0.4.0

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,766 @@
1
+ // marketplace-v2.js — GrabCAD-style marketplace with Instructables-inspired detail pages
2
+ // IIFE pattern, registers as window.cycleCAD.marketplaceV2
3
+
4
+ (function () {
5
+ 'use strict';
6
+
7
+ // Demo models with full Instructables-style data
8
+ const DEMO_MODELS = [
9
+ {
10
+ id: 'model-001',
11
+ title: 'Parametric Bearing Housing',
12
+ author: 'Sarah Chen',
13
+ authorId: 'u-001',
14
+ description: 'Customizable bearing housing for industrial spindle applications. Supports standard ISO bearings.',
15
+ category: 'Mechanical',
16
+ difficulty: 'Intermediate',
17
+ buildTime: '45 min',
18
+ license: '$CYCLE Token Required',
19
+ price: 25,
20
+ rating: 4.8,
21
+ downloads: 342,
22
+ reviews: 3,
23
+ coverImage: 'https://via.placeholder.com/400x300/2c3e50/ecf0f1?text=Bearing+Housing',
24
+ steps: [
25
+ { title: 'Design Base Plate', description: 'Create the foundation plate with mounting holes', tips: 'Ensure bore diameter matches ISO standard (50mm for SKF 6210)', warnings: 'Over-torquing mounting bolts can crack the housing' },
26
+ { title: 'Add Cylindrical Bore', description: 'Extrude the main bearing bore', tips: 'Use internal draft angle of 2° for easier assembly', warnings: 'Bore tolerance: H7 (0 to +0.025mm)' },
27
+ { title: 'Create Seal Grooves', description: 'Machine grooves for bearing seals', tips: 'Follow ISO 11934 standard dimensions', warnings: 'Shallow grooves reduce seal effectiveness' },
28
+ { title: 'Add Oil Lubrication Ports', description: 'Drill and tap ports for oil injection', tips: 'Use 1/8" NPT fittings for standard compatibility', warnings: 'Port placement affects pressure distribution' },
29
+ { title: 'Apply Finish', description: 'Add surface treatment (shot peening or anodizing)', tips: 'Shot peening improves fatigue life by 25%', warnings: 'Clean surface before anodizing to prevent etching' },
30
+ { title: 'Final Assembly Check', description: 'Verify all dimensions and fit', tips: 'Use CMM for high-precision verification', warnings: 'Check concentricity of bore to base (runout <0.02mm)' }
31
+ ],
32
+ bom: [
33
+ { part: 'Base Plate (Ductile Iron)', qty: 1, material: 'DCI 400-15', supplier: 'McMaster-Carr', partNum: '8729K41', estimatedCost: '$18.50' },
34
+ { part: 'Bearing Seal (FKM)', qty: 2, material: 'FKM 75', supplier: 'MISUMI', partNum: 'SEAL-6210-SKF', estimatedCost: '$6.20' },
35
+ { part: 'Oil Port Fitting (Brass)', qty: 4, material: 'C36000', supplier: 'McMaster-Carr', partNum: '7734K25', estimatedCost: '$4.80' },
36
+ { part: 'Mounting Bolts (Grade 8.8)', qty: 8, material: 'Steel', supplier: 'MISUMI', partNum: 'SCBM8-50', estimatedCost: '$2.40' }
37
+ ],
38
+ tools: ['CNC Milling Machine', 'Reamer (H7 Tolerance)', 'Tap Set (1/8" NPT)', 'CMM (optional)']
39
+ },
40
+ {
41
+ id: 'model-002',
42
+ title: 'Adjustable Phone Stand',
43
+ author: 'Mike Rodriguez',
44
+ authorId: 'u-002',
45
+ description: 'Simple 3D-printable phone stand with adjustable viewing angles.',
46
+ category: 'Consumer Products',
47
+ difficulty: 'Beginner',
48
+ buildTime: '2 hours',
49
+ license: 'CC-BY',
50
+ price: 0,
51
+ rating: 4.3,
52
+ downloads: 1205,
53
+ reviews: 8,
54
+ coverImage: 'https://via.placeholder.com/400x300/3498db/ecf0f1?text=Phone+Stand',
55
+ steps: [
56
+ { title: 'Create Base Plate', description: 'Design a 100×80mm base for stability', tips: 'Add anti-slip texture on bottom surface', warnings: 'Make base at least 4mm thick for FDM printing' },
57
+ { title: 'Build Adjustable Arm', description: 'Create a ball-joint mechanism for angle adjustment', tips: 'Use 20mm ball diameter for standard phone grip strength', warnings: 'Ensure arm walls are minimum 2mm for 3D printing' },
58
+ { title: 'Add Phone Cradle', description: 'Design phone holder with rubber pads', tips: 'Cradle width should be 65-75mm for universal fit', warnings: 'Add drainage slot to prevent moisture buildup' },
59
+ { title: 'Assembly and Testing', description: 'Print all parts and test fit', tips: 'Sand ball joint surfaces lightly for smooth rotation', warnings: 'Test with actual phone weight to verify stability' }
60
+ ],
61
+ bom: [
62
+ { part: 'PLA Filament', qty: '60g', material: 'PLA', supplier: 'Amazon', partNum: 'PRUSAMENT-PLA', estimatedCost: '$1.80' },
63
+ { part: 'Rubber Pad Sheet', qty: '1 sheet', material: 'EPDM', supplier: 'McMaster-Carr', partNum: '8594K11', estimatedCost: '$3.25' },
64
+ { part: 'Felt Pads (Self-Adhesive)', qty: '8 pcs', material: 'Wool Felt', supplier: 'McMaster-Carr', partNum: '9638K26', estimatedCost: '$2.15' }
65
+ ],
66
+ tools: ['3D Printer (FDM)', 'Sandpaper (120-400 grit)', 'Utility Knife', 'Ruler']
67
+ },
68
+ {
69
+ id: 'model-003',
70
+ title: 'CNC Mill Vise Jaw Set',
71
+ author: 'James Wu',
72
+ authorId: 'u-003',
73
+ description: 'Drop-in replacement jaws for standard CNC mills. Hardened steel with precision ground surfaces.',
74
+ category: 'Fixtures',
75
+ difficulty: 'Advanced',
76
+ buildTime: '3 hours',
77
+ license: '$CYCLE Token Required',
78
+ price: 50,
79
+ rating: 4.9,
80
+ downloads: 287,
81
+ reviews: 5,
82
+ coverImage: 'https://via.placeholder.com/400x300/e74c3c/ecf0f1?text=Vise+Jaws',
83
+ steps: [
84
+ { title: 'Rough Stock to Size', description: 'Machine raw stock to 150×75×30mm', tips: 'Leave 2mm for final finish pass', warnings: 'Use coolant to prevent thermal distortion' },
85
+ { title: 'Face and Bore', description: 'Create gripping surfaces with precision angles', tips: '5° jaw angle improves clamping grip', warnings: 'Keep bore tolerance within ±0.002"' },
86
+ { title: 'Add Serrated Pattern', description: 'Engrave serrations for non-slip grip', tips: 'Use 0.5mm serration spacing', warnings: 'Serrations reduce clamping area by 15%' },
87
+ { title: 'Heat Treat', description: 'Harden to 58-62 HRC for durability', tips: 'Use oil quench for minimal distortion', warnings: 'Temper after hardening to relieve stress' },
88
+ { title: 'Final Grinding', description: 'Grind jaw faces to flatness and parallelism', tips: 'Target flatness: 0.0005" TIR', warnings: 'Over-grinding reduces jaw width' },
89
+ { title: 'Quality Check', description: 'Inspect all critical dimensions', tips: 'Use precision parallels to check flatness', warnings: 'Any chips on edges reduce grip quality' },
90
+ { title: 'Apply Finish', description: 'Coat with protective oil', tips: 'Use light machine oil for rust prevention', warnings: 'Wipe excess oil to prevent slipping' },
91
+ { title: 'Installation', description: 'Install into vise body with locating pin', tips: 'Ensure jaw is fully seated before clamping', warnings: 'Loose jaws can cause dimensional errors' }
92
+ ],
93
+ bom: [
94
+ { part: 'Hardened Steel Blank (AISI D2)', qty: 2, material: 'AISI D2', supplier: 'McMaster-Carr', partNum: '8984K47', estimatedCost: '$35.60' },
95
+ { part: 'Locating Pin (Hardened Steel)', qty: 2, material: '4140 Steel', supplier: 'MISUMI', partNum: 'DOWEL-8-50', estimatedCost: '$3.80' },
96
+ { part: 'Machine Oil (ISO 46)', qty: '1L', material: 'Mineral Oil', supplier: 'McMaster-Carr', partNum: '2162K12', estimatedCost: '$8.40' }
97
+ ],
98
+ tools: ['CNC Milling Machine', 'Heat Treat Furnace', 'Surface Grinder', 'CMM', 'Precision Parallels']
99
+ },
100
+ {
101
+ id: 'model-004',
102
+ title: 'Herringbone Gear Pair',
103
+ author: 'Dr. Lisa Park',
104
+ authorId: 'u-004',
105
+ description: 'Matched herringbone gear pair with balanced axial loads. Perfect for reducing vibration in high-speed applications.',
106
+ category: 'Gears',
107
+ difficulty: 'Intermediate',
108
+ buildTime: '90 min',
109
+ license: 'CC-BY-SA',
110
+ price: 10,
111
+ rating: 4.6,
112
+ downloads: 564,
113
+ reviews: 6,
114
+ coverImage: 'https://via.placeholder.com/400x300/9b59b6/ecf0f1?text=Herringbone+Gears',
115
+ steps: [
116
+ { title: 'Define Gear Specifications', description: 'Set module, pressure angle, helix angle, and tooth count', tips: 'Module 2.5, PA 20°, helix angle 30° for optimal efficiency', warnings: 'Unmatched helix angles cause axial imbalance' },
117
+ { title: 'Generate Tooth Profile', description: 'Create involute tooth form with proper pressure angle', tips: 'Use cam grinding software for accurate profile', warnings: 'Undercut can occur if addendum is too large' },
118
+ { title: 'Create Blank and Hub', description: 'Design hub and keyway for shaft mounting', tips: 'Hub diameter should be 1.2× bore diameter', warnings: 'Keyway weakens hub; keep width to minimum' },
119
+ { title: 'Apply Finishing', description: 'Add chamfers and surface finish specification', tips: 'Chamfer all sharp edges (0.5×45°)', warnings: 'Finish specification affects noise levels' },
120
+ { title: 'Export and Manufacture', description: 'Generate STEP file for CNC hobbing machine', tips: 'Use hobbing for high-precision gears (AGMA 10)', warnings: 'Shaving after hobbing improves surface finish' }
121
+ ],
122
+ bom: [
123
+ { part: 'Steel Blank (AISI 8620)', qty: 2, material: 'AISI 8620', supplier: 'McMaster-Carr', partNum: '8984K12', estimatedCost: '$28.75' },
124
+ { part: 'Metric Keystock (3×3×30mm)', qty: 2, material: '1018 Steel', supplier: 'MISUMI', partNum: 'KEY-3-30', estimatedCost: '$1.20' }
125
+ ],
126
+ tools: ['CNC Hobbing Machine', 'Shaving Cutter Set', 'Gear Measuring Caliper', 'Cam Software']
127
+ },
128
+ {
129
+ id: 'model-005',
130
+ title: 'Raspberry Pi 4 Enclosure',
131
+ author: 'Alex Thompson',
132
+ authorId: 'u-005',
133
+ description: '3D-printable enclosure for Raspberry Pi 4 with GPIO expansion and cooling fan mount.',
134
+ category: 'Electronics Housing',
135
+ difficulty: 'Beginner',
136
+ buildTime: '3 hours',
137
+ license: 'MIT',
138
+ price: 0,
139
+ rating: 4.4,
140
+ downloads: 2341,
141
+ reviews: 12,
142
+ coverImage: 'https://via.placeholder.com/400x300/1abc9c/ecf0f1?text=Pi+Enclosure',
143
+ steps: [
144
+ { title: 'Measure Raspberry Pi Dimensions', description: 'Create accurate mounting holes and cutouts', tips: 'Leave 2mm clearance around components', warnings: 'GPIO headers block standard enclosures' },
145
+ { title: 'Design Base Plate', description: 'Create mounting points for Pi and fan', tips: 'Use M2.5 threaded inserts for durability', warnings: 'Test fit before final print' },
146
+ { title: 'Add Cooling Fan Mount', description: 'Create mount for 30×30mm cooling fan', tips: 'Fan should blow air over CPU (BCM2711)', warnings: 'Ensure fan is isolated from moving parts' },
147
+ { title: 'Design Lid with Vents', description: 'Create removable lid with airflow vents', tips: 'Add cable management clips inside', warnings: 'Vents must not exceed 3mm to prevent dust' }
148
+ ],
149
+ bom: [
150
+ { part: 'PETG Filament', qty: '120g', material: 'PETG', supplier: 'Amazon', partNum: 'PRUSAMENT-PETG', estimatedCost: '$3.60' },
151
+ { part: 'M2.5 Threaded Inserts', qty: 4, material: 'Brass', supplier: 'McMaster-Carr', partNum: '91732A118', estimatedCost: '$2.80' },
152
+ { part: '30mm Axial Fan (5V)', qty: 1, material: 'Plastic', supplier: 'Amazon', partNum: 'JBL30A051-05', estimatedCost: '$4.25' }
153
+ ],
154
+ tools: ['3D Printer (FDM)', 'Soldering Iron', 'Crimpers', 'M2.5 Tap']
155
+ },
156
+ {
157
+ id: 'model-006',
158
+ title: 'DIN Rail Mount Bracket',
159
+ author: 'Georg Müller',
160
+ authorId: 'u-006',
161
+ description: 'Universal DIN rail mounting bracket for enclosure components. Supports 35mm DIN rail standard.',
162
+ category: 'Brackets',
163
+ difficulty: 'Beginner',
164
+ buildTime: '1 hour',
165
+ license: 'CC-BY',
166
+ price: 0,
167
+ rating: 4.5,
168
+ downloads: 891,
169
+ reviews: 4,
170
+ coverImage: 'https://via.placeholder.com/400x300/f39c12/ecf0f1?text=DIN+Bracket',
171
+ steps: [
172
+ { title: 'Create Hook Profile', description: 'Design hook that clips onto DIN rail', tips: 'Hook depth: 7.5mm per DIN 35', warnings: 'Insufficient hook depth causes slipping' },
173
+ { title: 'Add Component Mounting Face', description: 'Create flat surface for device attachment', tips: 'Use M5 tapped holes for universal compatibility', warnings: 'Face should be perpendicular to hook' },
174
+ { title: 'Design for 3D Printing', description: 'Add support blocks and strengthen thin walls', tips: 'Minimum wall thickness: 2mm', warnings: 'Unsupported overhangs fail in FDM' },
175
+ { title: 'Print and Install', description: 'Print and test fit on DIN rail', tips: 'Sand hook lightly for smooth sliding', warnings: 'Adjust hook tension if bracket moves' }
176
+ ],
177
+ bom: [
178
+ { part: 'PETG Filament', qty: '45g', material: 'PETG', supplier: 'Amazon', partNum: 'PRUSAMENT-PETG', estimatedCost: '$1.35' },
179
+ { part: 'M5 Socket Head Cap Screws', qty: 4, material: 'Stainless Steel', supplier: 'McMaster-Carr', partNum: '91290A316', estimatedCost: '$1.80' }
180
+ ],
181
+ tools: ['3D Printer (FDM)', 'Sandpaper (220 grit)', 'Hex Key (4mm)']
182
+ },
183
+ {
184
+ id: 'model-007',
185
+ title: 'Sheet Metal Electrical Box',
186
+ author: 'Patricia Gonzalez',
187
+ authorId: 'u-007',
188
+ description: 'Welded sheet metal electrical enclosure with ventilation and cable entry provisions.',
189
+ category: 'Sheet Metal',
190
+ difficulty: 'Advanced',
191
+ buildTime: '2.5 hours',
192
+ license: '$CYCLE Token Required',
193
+ price: 35,
194
+ rating: 4.7,
195
+ downloads: 456,
196
+ reviews: 7,
197
+ coverImage: 'https://via.placeholder.com/400x300/c0392b/ecf0f1?text=Electrical+Box',
198
+ steps: [
199
+ { title: 'Unfold Sheet Metal Flat Pattern', description: 'Create flat pattern for 1.2mm cold-rolled steel', tips: 'Use bend allowance 1.8× thickness', warnings: 'Bend radius must be ≥2.4mm to avoid cracking' },
200
+ { title: 'Create Flanges for Assembly', description: 'Design flanges with welding tabs', tips: 'Use 25mm flange width for 1.2mm steel', warnings: 'Mismatched flange heights cause assembly issues' },
201
+ { title: 'Add Cable Entry Holes', description: 'Knockout holes for M20 cable glands', tips: 'Use punching rather than drilling for speed', warnings: 'Punch dies create cleaner edges than holes' },
202
+ { title: 'Design Ventilation Louvers', description: 'Add louvered vents for thermal management', tips: 'Louver angle: 30° for optimal airflow', warnings: 'Louvers reduce structural rigidity by 10%' },
203
+ { title: 'Assembly Sequence', description: 'Plan welding order to minimize distortion', tips: 'Weld from center outward to reduce warping', warnings: 'Over-welding causes excessive heat distortion' },
204
+ { title: 'Surface Treatment', description: 'Apply zinc plating and powder coat finish', tips: 'Use white or gray powder coat for visibility', warnings: 'Bake time affects coating durability' },
205
+ { title: 'Final Assembly', description: 'Install DIN rails, cable glands, and gaskets', tips: 'Use neoprene gaskets for IP65 rating', warnings: 'Incorrect gasket placement leaks water' }
206
+ ],
207
+ bom: [
208
+ { part: 'Cold-Rolled Steel (1.2mm)', qty: '1 sheet', material: 'AISI 1010', supplier: 'McMaster-Carr', partNum: '9014K13', estimatedCost: '$24.50' },
209
+ { part: 'M20 Cable Glands (IP67)', qty: 4, material: 'Nylon', supplier: 'MISUMI', partNum: 'CABLE-GLAND-M20', estimatedCost: '$6.80' },
210
+ { part: 'DIN Rail (1m)', qty: 1, material: 'Steel', supplier: 'McMaster-Carr', partNum: '7168K41', estimatedCost: '$4.20' },
211
+ { part: 'Neoprene Gasket Sheet', qty: '0.5m', material: 'Neoprene', supplier: 'McMaster-Carr', partNum: '8759K21', estimatedCost: '$3.75' }
212
+ ],
213
+ tools: ['Sheet Metal Shear', 'Press Brake', 'Welding Machine', 'Punch Set (M20)', 'Deburring Tool']
214
+ },
215
+ {
216
+ id: 'model-008',
217
+ title: 'Planetary Gear Set',
218
+ author: 'Dr. Rajesh Kumar',
219
+ authorId: 'u-008',
220
+ description: 'Complete planetary gear reducer with 10:1 ratio. Compact design for high-torque applications.',
221
+ category: 'Gears',
222
+ difficulty: 'Expert',
223
+ buildTime: '6 hours',
224
+ license: '$CYCLE Token Required',
225
+ price: 100,
226
+ rating: 5.0,
227
+ downloads: 189,
228
+ reviews: 3,
229
+ coverImage: 'https://via.placeholder.com/400x300/8e44ad/ecf0f1?text=Planetary+Gears',
230
+ steps: [
231
+ { title: 'Calculate Gear Specifications', description: 'Design for 10:1 reduction with balanced loads', tips: 'Module 2.0, 90-tooth sun, 108-tooth ring', warnings: 'Unbalanced tooth counts cause vibration' },
232
+ { title: 'Design Sun Gear', description: 'Create central sun gear with shaft', tips: 'Use hardened steel for durability', warnings: 'Sun gear must support radial and axial loads' },
233
+ { title: 'Design Planet Gears (×3)', description: 'Create identical planet gears for symmetry', tips: '3-planet configuration is optimal', warnings: 'Planet count must divide evenly into sun teeth' },
234
+ { title: 'Design Ring Gear', description: 'Create internal ring gear with fixed mounting', tips: 'Ring must be perfectly centered', warnings: 'Runout tolerance: <0.01mm' },
235
+ { title: 'Design Carrier Assembly', description: 'Create carrier to hold planet pins', tips: 'Use lightweight aluminum for low inertia', warnings: 'Carrier rigidity critical for noise control' },
236
+ { title: 'Generate Manufacturing Drawings', description: 'Create detailed STEP files for CNC hobbing', tips: 'Specify AGMA 11-12 quality class', warnings: 'Quality affects efficiency (95-98%)' },
237
+ { title: 'Manufacture and Assemble', description: 'CNC hob all gears, harden, and shave', tips: 'Assembly sequence: sun → planets → ring', warnings: 'Any assembly error causes binding' },
238
+ { title: 'Testing and Validation', description: 'Test torque capacity and noise', tips: 'Run-in procedure: 10 hours at light load', warnings: 'High noise indicates misalignment' },
239
+ { title: 'Quality Documentation', description: 'Generate capacity charts and thermal analysis', tips: 'Document max RPM and temperature limits', warnings: 'Thermal stability critical for reliability' },
240
+ { title: 'Final Inspection', description: 'Verify all specifications and tolerances', tips: 'Use CMM for final acceptance', warnings: 'Any deviation voids warranty' }
241
+ ],
242
+ bom: [
243
+ { part: 'Hardened Steel for Gears (AISI 8620)', qty: '3 kg', material: 'AISI 8620', supplier: 'McMaster-Carr', partNum: '8984K25', estimatedCost: '$67.80' },
244
+ { part: 'Aluminum Carrier Blank', qty: 1, material: '6061-T6', supplier: 'MISUMI', partNum: 'AL-BLANK-150', estimatedCost: '$15.50' },
245
+ { part: 'Precision Bearings (6005)', qty: 3, material: 'Steel/Ceramic Hybrid', supplier: 'McMaster-Carr', partNum: '5972K32', estimatedCost: '$18.90' },
246
+ { part: 'Synthetic Gear Oil (ISO 220)', qty: '1L', material: 'Synthetic PAO', supplier: 'McMaster-Carr', partNum: '2162K42', estimatedCost: '$12.40' }
247
+ ],
248
+ tools: ['CNC Hobbing Machine', 'Shaving Cutter Set', 'Heat Treat Furnace', 'CMM', 'Thermal Chamber', 'Noise Analyzer']
249
+ },
250
+ {
251
+ id: 'model-009',
252
+ title: 'Drone Motor Mount',
253
+ author: 'Emily Foster',
254
+ authorId: 'u-009',
255
+ description: '3D-printable vibration-damping motor mount for quadcopter frames. Reduces propeller noise by 8dB.',
256
+ category: 'Aerospace',
257
+ difficulty: 'Intermediate',
258
+ buildTime: '2 hours',
259
+ license: 'CC-BY-NC',
260
+ price: 5,
261
+ rating: 4.5,
262
+ downloads: 724,
263
+ reviews: 5,
264
+ coverImage: 'https://via.placeholder.com/400x300/16a085/ecf0f1?text=Motor+Mount',
265
+ steps: [
266
+ { title: 'Design Motor Adapter Ring', description: 'Create mounting ring for standard brushless motor (3S-6S)', tips: 'M3 threads for universal motor compatibility', warnings: 'Adapter must not interfere with prop clearance' },
267
+ { title: 'Add Vibration Isolation Pads', description: 'Design pockets for silicone damping pads', tips: 'Use 3mm shore-A40 silicone pads', warnings: 'Too soft reduces frame rigidity' },
268
+ { title: 'Design Arm Attachment', description: 'Create interface for carbon fiber tube arms', tips: 'Use 8mm or 10mm tube compatibility', warnings: 'Tube clamp must not crush carbon fiber' },
269
+ { title: 'Add Cable Management', description: 'Create strain relief for ESC wires', tips: 'Prevent stress on solder joints', warnings: 'Cable routing affects balance' },
270
+ { title: 'Print and Test Fit', description: 'Print and verify fit with actual motor and frame', tips: 'Test weight: target <12g per mount', warnings: 'Overly rigid design negates damping benefit' }
271
+ ],
272
+ bom: [
273
+ { part: 'Carbon Fiber Filament', qty: '75g', material: 'CF/PETG', supplier: 'Amazon', partNum: 'CARBON-PETG-750g', estimatedCost: '$4.50' },
274
+ { part: 'Silicone Damping Pads (3mm)', qty: 12, material: 'Silicone Shore A40', supplier: 'McMaster-Carr', partNum: '9199K58', estimatedCost: '$2.40' },
275
+ { part: 'M3 Socket Head Cap Screws', qty: 12, material: 'Titanium', supplier: 'MISUMI', partNum: 'SCBM3-12', estimatedCost: '$3.20' }
276
+ ],
277
+ tools: ['3D Printer (FDM)', 'Digital Scale', 'Calipers', 'Hex Key Set']
278
+ },
279
+ {
280
+ id: 'model-010',
281
+ title: 'Surgical Tool Handle',
282
+ author: 'Dr. Michael Chen',
283
+ authorId: 'u-010',
284
+ description: 'Ergonomic surgical instrument handle meeting ISO 9397 medical device standards.',
285
+ category: 'Medical',
286
+ difficulty: 'Expert',
287
+ buildTime: '5 hours',
288
+ license: '$CYCLE Token Required',
289
+ price: 75,
290
+ rating: 4.9,
291
+ downloads: 142,
292
+ reviews: 2,
293
+ coverImage: 'https://via.placeholder.com/400x300/2980b9/ecf0f1?text=Surgical+Handle',
294
+ steps: [
295
+ { title: 'Define Ergonomic Specifications', description: 'Design for 50th percentile hand geometry', tips: 'Grip diameter: 22-28mm per ISO 9397', warnings: 'Incorrect ergonomics causes fatigue' },
296
+ { title: 'Create Handle Blank', description: 'Machine stainless steel blank from bar stock', tips: 'Use 316L stainless for biocompatibility', warnings: 'Avoid nickel-rich alloys due to allergies' },
297
+ { title: 'Design Thumb Rest', description: 'Create anatomic thumb rest to reduce fatigue', tips: 'Thumb rest angle: 15-20° from horizontal', warnings: 'Incorrect angle increases surgical fatigue' },
298
+ { title: 'Add Finger Grooves', description: 'Create textured surface for grip security', tips: 'Groove spacing: 8mm center-to-center', warnings: 'Sharp grooves can cut gloved fingers' },
299
+ { title: 'Create Tool Mounting Interface', description: 'Design bayonet or screw mount for interchangeable tips', tips: 'Bayonet: 3-pin keyed design per standard', warnings: 'Loose coupling compromises safety' },
300
+ { title: 'Apply Medical Surface Finish', description: 'Polish and passivate per ASTM A967', tips: 'Final surface finish Ra 0.4µm or better', warnings: 'Poor passivation reduces corrosion resistance' },
301
+ { title: 'Sterilization Validation', description: 'Test compatibility with autoclave (121°C, 15min)', tips: 'Verify no material degradation', warnings: 'Some polymers cannot withstand autoclave' },
302
+ { title: 'Traceability Labeling', description: 'Add permanent ID marking per ISO 11785', tips: 'Use laser marking to prevent label loss', warnings: 'Manual labeling wears off during use' },
303
+ { title: 'Biocompatibility Testing', description: 'Perform cytotoxicity and sensitization tests', tips: 'Follow ISO 10993-5 methodology', warnings: 'Biocompatibility required for regulatory approval' },
304
+ { title: 'Final QA Documentation', description: 'Generate compliance report and certificates', tips: 'Document all test results for FDA submission', warnings: 'Missing documentation blocks market approval' }
305
+ ],
306
+ bom: [
307
+ { part: 'Stainless Steel 316L Bar Stock', qty: '500g', material: '316L', supplier: 'McMaster-Carr', partNum: '8976K24', estimatedCost: '$32.50' },
308
+ { part: 'Medical-Grade Silicone (FDA compliant)', qty: '100g', material: 'Medical Silicone', supplier: 'MISUMI', partNum: 'SILICONE-MED-100g', estimatedCost: '$14.80' },
309
+ { part: 'Polishing Compound (Fine)', qty: '1 jar', material: 'Diamond Paste 1µm', supplier: 'McMaster-Carr', partNum: '2473K31', estimatedCost: '$8.60' }
310
+ ],
311
+ tools: ['CNC Milling Machine', 'Surface Grinder', 'Polishing Machine', 'Autoclave', 'Laser Marker', 'CMM']
312
+ },
313
+ {
314
+ id: 'model-011',
315
+ title: 'Bicycle Stem Adapter',
316
+ author: 'Lucas Silva',
317
+ authorId: 'u-011',
318
+ description: 'Aluminum stem adapter for converting vintage bicycle to modern 31.8mm bar diameter.',
319
+ category: 'Automotive',
320
+ difficulty: 'Intermediate',
321
+ buildTime: '1.5 hours',
322
+ license: 'CC-BY-SA',
323
+ price: 8,
324
+ rating: 4.4,
325
+ downloads: 615,
326
+ reviews: 4,
327
+ coverImage: 'https://via.placeholder.com/400x300/27ae60/ecf0f1?text=Stem+Adapter',
328
+ steps: [
329
+ { title: 'Create Adapter Sleeve', description: 'Design bushing for 25.4mm → 31.8mm conversion', tips: 'Wall thickness: 3mm for structural integrity', warnings: 'Too thin walls will crush under load' },
330
+ { title: 'Add Clamp Mechanism', description: 'Design two-piece clamp with M6 bolts', tips: 'Clamp surface should be knurled', warnings: 'Smooth clamp surfaces cause slipping' },
331
+ { title: 'Design for Lightweight', description: 'Minimize material while maintaining rigidity', tips: 'Use 6061-T6 aluminum for optimal strength/weight', warnings: 'Avoid aluminum alloys with poor fatigue' },
332
+ { title: 'CAM and Test', description: 'Create CAM program for CNC turning', tips: 'Finish pass at high spindle speed (1000 RPM) for smooth surface', warnings: 'Chatter marks reduce clamp grip' },
333
+ { title: 'Post-Processing', description: 'Anodize to Type II finish for durability', tips: 'Use sulfuric acid anodizing for best hardness', warnings: 'Chromic acid anodizing weaker but thicker' }
334
+ ],
335
+ bom: [
336
+ { part: '6061-T6 Aluminum Rod (1" dia)', qty: '1 piece', material: '6061-T6', supplier: 'McMaster-Carr', partNum: '8974K33', estimatedCost: '$9.50' },
337
+ { part: 'M6 Socket Head Screws (20mm)', qty: 2, material: 'Aluminum', supplier: 'MISUMI', partNum: 'SCBM6-20', estimatedCost: '$2.40' },
338
+ { part: 'Knurling Tool', qty: 1, material: 'Carbide', supplier: 'McMaster-Carr', partNum: '3987K15', estimatedCost: '$18.75' }
339
+ ],
340
+ tools: ['CNC Lathe', 'End Mill', 'Tap (M6)', 'Calipers', 'Micrometer']
341
+ },
342
+ {
343
+ id: 'model-012',
344
+ title: 'Modular Shelving System',
345
+ author: 'Anna Kowalski',
346
+ authorId: 'u-012',
347
+ description: '3D-printable modular shelving with tool-free assembly. Supports up to 5kg per shelf.',
348
+ category: 'Assemblies',
349
+ difficulty: 'Intermediate',
350
+ buildTime: '4 hours',
351
+ license: 'CC-BY',
352
+ price: 0,
353
+ rating: 4.6,
354
+ downloads: 1876,
355
+ reviews: 9,
356
+ coverImage: 'https://via.placeholder.com/400x300/34495e/ecf0f1?text=Modular+Shelving',
357
+ steps: [
358
+ { title: 'Design Vertical Posts', description: 'Create posts with built-in hook slots', tips: 'Slot spacing: 20mm for flexible arrangement', warnings: 'Posts must be perfectly straight for stability' },
359
+ { title: 'Create Shelf Brackets', description: 'Design brackets that slide into post slots', tips: 'Use snap-fit connection for tool-free assembly', warnings: 'Snap features easily break if over-tightened' },
360
+ { title: 'Design Shelf Decks', description: 'Create solid shelves with reinforced ribs', tips: 'Underside ribs improve strength-to-weight ratio', warnings: 'Thin shelves will sag under load' },
361
+ { title: 'Add Anti-Slip Surface', description: 'Apply textured coating or adhesive pads', tips: 'Use rubber pads at shelf corners', warnings: 'Smooth surfaces cause items to slide' },
362
+ { title: 'Assembly Instructions', description: 'Document step-by-step assembly with diagrams', tips: 'Include weight capacity labels on each shelf', warnings: 'Exceeding weight limits causes collapse' },
363
+ { title: 'Print and Build', description: 'Print all components and assemble', tips: 'Test stability before loading with items', warnings: 'Uneven floor causes wobbling' },
364
+ { title: 'Finishing Touches', description: 'Sand any rough edges and apply finish', tips: 'Paint with matte black for professional look', warnings: 'Gloss paint looks cheap on cheap materials' },
365
+ { title: 'Install and Decorate', description: 'Wall-mount posts and arrange shelves', tips: 'Level check ensures even appearance', warnings: 'Unlevel installation looks unintentional' }
366
+ ],
367
+ bom: [
368
+ { part: 'PETG Filament', qty: '500g', material: 'PETG', supplier: 'Amazon', partNum: 'PRUSAMENT-PETG-5kg', estimatedCost: '$15.00' },
369
+ { part: 'Rubber Anti-Slip Pads (30mm)', qty: 8, material: 'EPDM', supplier: 'McMaster-Carr', partNum: '9638K11', estimatedCost: '$3.20' },
370
+ { part: 'Drywall Anchors (M8)', qty: 4, material: 'Plastic', supplier: 'McMaster-Carr', partNum: '98149A200', estimatedCost: '$2.15' },
371
+ { part: 'Matte Black Spray Paint', qty: '1 can', material: 'Acrylic', supplier: 'Amazon', partNum: 'RUST-OLEUM-2X', estimatedCost: '$4.99' }
372
+ ],
373
+ tools: ['3D Printer (FDM)', 'Sandpaper (120-220 grit)', 'Level (laser or spirit)', 'Drill (for anchors)', 'Paintbrush']
374
+ }
375
+ ];
376
+
377
+ // Category definitions
378
+ const CATEGORIES = [
379
+ 'Mechanical', 'Enclosures', 'Brackets', 'Gears', 'Fasteners',
380
+ 'Fixtures', 'Sheet Metal', 'Assemblies', 'Electronics Housing',
381
+ 'Automotive', 'Aerospace', 'Medical', 'Consumer Products'
382
+ ];
383
+
384
+ const DIFFICULTY_COLORS = {
385
+ 'Beginner': '#10b981',
386
+ 'Intermediate': '#f59e0b',
387
+ 'Advanced': '#ef4444',
388
+ 'Expert': '#8b5cf6'
389
+ };
390
+
391
+ // Initialize marketplace state
392
+ const marketplaceState = {
393
+ publishedModels: [],
394
+ purchases: {},
395
+ favorites: {},
396
+ reviews: {}
397
+ };
398
+
399
+ // Load from localStorage
400
+ function loadState() {
401
+ const saved = localStorage.getItem('cycleCAD_marketplace_v2');
402
+ if (saved) {
403
+ Object.assign(marketplaceState, JSON.parse(saved));
404
+ }
405
+ // Initialize demo models if not already published
406
+ if (marketplaceState.publishedModels.length === 0) {
407
+ marketplaceState.publishedModels = DEMO_MODELS.map(m => ({ ...m }));
408
+ }
409
+ }
410
+
411
+ function saveState() {
412
+ localStorage.setItem('cycleCAD_marketplace_v2', JSON.stringify(marketplaceState));
413
+ }
414
+
415
+ // Get all published models
416
+ function getAllModels() {
417
+ return marketplaceState.publishedModels;
418
+ }
419
+
420
+ // Get single model by ID
421
+ function getModelById(modelId) {
422
+ return marketplaceState.publishedModels.find(m => m.id === modelId);
423
+ }
424
+
425
+ // Generate model card HTML
426
+ function getModelCardHTML(model) {
427
+ const categoryColor = {
428
+ 'Mechanical': '#3b82f6',
429
+ 'Enclosures': '#06b6d4',
430
+ 'Brackets': '#8b5cf6',
431
+ 'Gears': '#ec4899',
432
+ 'Fasteners': '#f59e0b',
433
+ 'Fixtures': '#10b981',
434
+ 'Sheet Metal': '#ef4444',
435
+ 'Assemblies': '#6366f1',
436
+ 'Electronics Housing': '#14b8a6',
437
+ 'Automotive': '#f97316',
438
+ 'Aerospace': '#0ea5e9',
439
+ 'Medical': '#d946ef',
440
+ 'Consumer Products': '#84cc16'
441
+ }[model.category] || '#6b7280';
442
+
443
+ const stars = '★'.repeat(Math.floor(model.rating)) + '☆'.repeat(5 - Math.floor(model.rating));
444
+
445
+ return `
446
+ <div class="market-card" data-model-id="${model.id}" style="background:#2a2a2a;border-radius:8px;overflow:hidden;box-shadow:0 4px 6px rgba(0,0,0,0.3);cursor:pointer;transition:all 0.2s">
447
+ <div style="position:relative;width:100%;padding-bottom:66.67%;overflow:hidden;background:#1e1e1e">
448
+ <img src="${model.coverImage}" style="position:absolute;top:0;left:0;width:100%;height:100%;object-fit:cover" alt="${model.title}">
449
+ <div style="position:absolute;top:8px;right:8px;background:${categoryColor};color:#fff;padding:4px 8px;border-radius:4px;font-size:11px;font-weight:bold">${model.category}</div>
450
+ ${model.price > 0 ? `<div style="position:absolute;bottom:8px;right:8px;background:#ff9800;color:#000;padding:4px 8px;border-radius:4px;font-size:12px;font-weight:bold">${model.price} tokens</div>` : ''}
451
+ </div>
452
+ <div style="padding:12px">
453
+ <h3 style="margin:0;font-size:14px;font-weight:600;color:#e0e0e0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap">${model.title}</h3>
454
+ <p style="margin:4px 0 0 0;font-size:12px;color:#999">by ${model.author}</p>
455
+ <div style="margin:8px 0;display:flex;gap:4px;align-items:center">
456
+ <span style="color:#fbbf24;font-size:12px">${stars}</span>
457
+ <span style="color:#666;font-size:11px">${model.rating} (${model.reviews})</span>
458
+ </div>
459
+ <div style="display:flex;gap:4px;flex-wrap:wrap;margin:8px 0">
460
+ <span style="background:${DIFFICULTY_COLORS[model.difficulty]};color:#fff;padding:2px 6px;border-radius:3px;font-size:10px;font-weight:600">${model.difficulty}</span>
461
+ <span style="background:#444;color:#aaa;padding:2px 6px;border-radius:3px;font-size:10px">↓ ${model.downloads}</span>
462
+ </div>
463
+ <button class="market-view-btn" style="width:100%;padding:6px;background:#058dd4;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px;font-weight:600;transition:background 0.2s" data-model-id="${model.id}">View Details</button>
464
+ </div>
465
+ </div>
466
+ `;
467
+ }
468
+
469
+ // Generate model detail page HTML
470
+ function getModelDetailHTML(modelId) {
471
+ const model = getModelById(modelId);
472
+ if (!model) return '<div style="color:#f44747">Model not found</div>';
473
+
474
+ const stars = '★'.repeat(Math.floor(model.rating)) + '☆'.repeat(5 - Math.floor(model.rating));
475
+ const isFavorite = marketplaceState.favorites[modelId] || false;
476
+ const categoryColor = {
477
+ 'Mechanical': '#3b82f6',
478
+ 'Enclosures': '#06b6d4',
479
+ 'Brackets': '#8b5cf6',
480
+ 'Gears': '#ec4899',
481
+ 'Fasteners': '#f59e0b',
482
+ 'Fixtures': '#10b981',
483
+ 'Sheet Metal': '#ef4444',
484
+ 'Assemblies': '#6366f1',
485
+ 'Electronics Housing': '#14b8a6',
486
+ 'Automotive': '#f97316',
487
+ 'Aerospace': '#0ea5e9',
488
+ 'Medical': '#d946ef',
489
+ 'Consumer Products': '#84cc16'
490
+ }[model.category] || '#6b7280';
491
+
492
+ let detailHTML = `
493
+ <div style="max-width:1200px;margin:0 auto;padding:20px;color:#e0e0e0">
494
+ <!-- Hero Section -->
495
+ <div style="display:grid;grid-template-columns:1fr 350px;gap:30px;margin-bottom:30px">
496
+ <div>
497
+ <img src="${model.coverImage}" style="width:100%;border-radius:8px;box-shadow:0 10px 25px rgba(0,0,0,0.4)">
498
+ </div>
499
+ <div>
500
+ <h1 style="margin:0 0 16px 0;font-size:28px;color:#fff">${model.title}</h1>
501
+ <p style="margin:0 0 12px 0;color:#999">by <strong>${model.author}</strong></p>
502
+ <div style="display:flex;align-items:center;gap:12px;margin:16px 0">
503
+ <span style="color:#fbbf24;font-size:16px">${stars}</span>
504
+ <span style="color:#aaa">${model.rating}/5 (${model.reviews} reviews)</span>
505
+ </div>
506
+ <div style="background:#2a2a2a;border-radius:8px;padding:12px;margin:16px 0">
507
+ <div style="display:flex;justify-content:space-between;margin:8px 0">
508
+ <span style="color:#999">Downloads:</span>
509
+ <strong>${model.downloads}</strong>
510
+ </div>
511
+ <div style="display:flex;justify-content:space-between;margin:8px 0">
512
+ <span style="color:#999">Difficulty:</span>
513
+ <span style="background:${DIFFICULTY_COLORS[model.difficulty]};color:#fff;padding:2px 8px;border-radius:3px;font-size:12px">${model.difficulty}</span>
514
+ </div>
515
+ <div style="display:flex;justify-content:space-between;margin:8px 0">
516
+ <span style="color:#999">Build Time:</span>
517
+ <strong>${model.buildTime}</strong>
518
+ </div>
519
+ <div style="display:flex;justify-content:space-between;margin:8px 0">
520
+ <span style="color:#999">License:</span>
521
+ <strong>${model.license}</strong>
522
+ </div>
523
+ </div>
524
+ <div style="display:flex;gap:8px;margin:16px 0">
525
+ <button class="market-download-btn" style="flex:1;padding:10px;background:#058dd4;color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:600;font-size:14px" data-model-id="${modelId}">
526
+ ${model.price > 0 ? `Purchase (${model.price} tokens)` : 'Download Free'}
527
+ </button>
528
+ <button class="market-favorite-btn" style="padding:10px 16px;background:#${isFavorite ? 'ff6b6b' : '444'};color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:600" data-model-id="${modelId}">★</button>
529
+ </div>
530
+ </div>
531
+ </div>
532
+
533
+ <!-- Tab Navigation -->
534
+ <div style="display:flex;gap:0;border-bottom:2px solid #333;margin:30px 0 20px 0">
535
+ <button class="market-tab-btn market-tab-overview" style="padding:12px 20px;border:none;background:none;color:#058dd4;cursor:pointer;border-bottom:2px solid #058dd4;font-weight:600" data-tab="overview">Overview</button>
536
+ <button class="market-tab-btn market-tab-steps" style="padding:12px 20px;border:none;background:none;color:#666;cursor:pointer;border-bottom:2px solid transparent;font-weight:600" data-tab="steps">Steps (${model.steps.length})</button>
537
+ <button class="market-tab-btn market-tab-bom" style="padding:12px 20px;border:none;background:none;color:#666;cursor:pointer;border-bottom:2px solid transparent;font-weight:600" data-tab="bom">BOM (${model.bom.length})</button>
538
+ <button class="market-tab-btn market-tab-files" style="padding:12px 20px;border:none;background:none;color:#666;cursor:pointer;border-bottom:2px solid transparent;font-weight:600" data-tab="files">Files</button>
539
+ <button class="market-tab-btn market-tab-reviews" style="padding:12px 20px;border:none;background:none;color:#666;cursor:pointer;border-bottom:2px solid transparent;font-weight:600" data-tab="reviews">Reviews (${model.reviews})</button>
540
+ </div>
541
+
542
+ <!-- Tab Content -->
543
+ <div class="market-tab-content" style="display:none" data-tab-content="overview">
544
+ <h2 style="margin:0 0 16px 0;color:#fff">Overview</h2>
545
+ <p style="line-height:1.6;color:#bbb;margin:0 0 20px 0">${model.description}</p>
546
+ <h3 style="margin:20px 0 12px 0;color:#fff">Specifications</h3>
547
+ <div style="background:#2a2a2a;border-radius:8px;padding:16px">
548
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
549
+ <div>
550
+ <span style="color:#999">Category:</span> <strong>${model.category}</strong>
551
+ </div>
552
+ <div>
553
+ <span style="color:#999">Difficulty:</span> <strong>${model.difficulty}</strong>
554
+ </div>
555
+ <div>
556
+ <span style="color:#999">Build Time:</span> <strong>${model.buildTime}</strong>
557
+ </div>
558
+ <div>
559
+ <span style="color:#999">License:</span> <strong>${model.license}</strong>
560
+ </div>
561
+ </div>
562
+ </div>
563
+ </div>
564
+
565
+ <div class="market-tab-content" style="display:none" data-tab-content="steps">
566
+ <h2 style="margin:0 0 16px 0;color:#fff">Build Instructions (${model.steps.length} steps)</h2>
567
+ ${model.steps.map((step, idx) => `
568
+ <div style="background:#2a2a2a;border-radius:8px;padding:16px;margin:12px 0">
569
+ <h3 style="margin:0 0 8px 0;color:#058dd4">Step ${idx + 1}: ${step.title}</h3>
570
+ <p style="margin:0 0 12px 0;color:#bbb;line-height:1.5">${step.description}</p>
571
+ ${step.tips ? `<div style="background:#3d3d1f;border-left:3px solid #f59e0b;padding:8px 12px;margin:8px 0;border-radius:4px"><strong style="color:#f59e0b">💡 Tip:</strong> <span style="color:#ccc">${step.tips}</span></div>` : ''}
572
+ ${step.warnings ? `<div style="background:#3d1f1f;border-left:3px solid #ef4444;padding:8px 12px;margin:8px 0;border-radius:4px"><strong style="color:#ef4444">⚠️ Warning:</strong> <span style="color:#ccc">${step.warnings}</span></div>` : ''}
573
+ </div>
574
+ `).join('')}
575
+ </div>
576
+
577
+ <div class="market-tab-content" style="display:none" data-tab-content="bom">
578
+ <h2 style="margin:0 0 16px 0;color:#fff">Bill of Materials (${model.bom.length} items)</h2>
579
+ <div style="overflow-x:auto">
580
+ <table style="width:100%;border-collapse:collapse;color:#e0e0e0">
581
+ <thead>
582
+ <tr style="border-bottom:2px solid #333">
583
+ <th style="text-align:left;padding:8px;color:#999;font-weight:600">Part</th>
584
+ <th style="text-align:center;padding:8px;color:#999;font-weight:600">Qty</th>
585
+ <th style="text-align:left;padding:8px;color:#999;font-weight:600">Material</th>
586
+ <th style="text-align:left;padding:8px;color:#999;font-weight:600">Supplier</th>
587
+ <th style="text-align:left;padding:8px;color:#999;font-weight:600">Est. Cost</th>
588
+ </tr>
589
+ </thead>
590
+ <tbody>
591
+ ${model.bom.map(item => `
592
+ <tr style="border-bottom:1px solid #333">
593
+ <td style="padding:8px"><strong>${item.part}</strong></td>
594
+ <td style="text-align:center;padding:8px">${item.qty}</td>
595
+ <td style="padding:8px">${item.material}</td>
596
+ <td style="padding:8px"><a href="https://${item.supplier === 'McMaster-Carr' ? 'mcmaster' : 'misumi'}.com" style="color:#058dd4;text-decoration:none">${item.supplier}</a></td>
597
+ <td style="padding:8px"><strong>${item.estimatedCost}</strong></td>
598
+ </tr>
599
+ `).join('')}
600
+ </tbody>
601
+ </table>
602
+ </div>
603
+ <h3 style="margin:20px 0 12px 0;color:#fff">Required Tools</h3>
604
+ <ul style="margin:0;padding-left:20px;color:#bbb">
605
+ ${model.tools.map(tool => `<li style="margin:4px 0">${tool}</li>`).join('')}
606
+ </ul>
607
+ </div>
608
+
609
+ <div class="market-tab-content" style="display:none" data-tab-content="files">
610
+ <h2 style="margin:0 0 16px 0;color:#fff">Available Files</h2>
611
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px">
612
+ <div style="background:#2a2a2a;border-radius:8px;padding:12px;border-left:3px solid #3b82f6">
613
+ <div style="font-weight:600;color:#3b82f6;margin-bottom:8px">STEP File</div>
614
+ <p style="margin:0 0 8px 0;font-size:12px;color:#999">parametric_model.step (2.4 MB)</p>
615
+ <button style="padding:6px 12px;background:#3b82f6;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">Download</button>
616
+ </div>
617
+ <div style="background:#2a2a2a;border-radius:8px;padding:12px;border-left:3px solid #06b6d4">
618
+ <div style="font-weight:600;color:#06b6d4;margin-bottom:8px">STL File</div>
619
+ <p style="margin:0 0 8px 0;font-size:12px;color:#999">model_assembly.stl (1.8 MB)</p>
620
+ <button style="padding:6px 12px;background:#06b6d4;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">Download</button>
621
+ </div>
622
+ <div style="background:#2a2a2a;border-radius:8px;padding:12px;border-left:3px solid #8b5cf6">
623
+ <div style="font-weight:600;color:#8b5cf6;margin-bottom:8px">glTF File</div>
624
+ <p style="margin:0 0 8px 0;font-size:12px;color:#999">model_preview.glb (1.1 MB)</p>
625
+ <button style="padding:6px 12px;background:#8b5cf6;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">Download</button>
626
+ </div>
627
+ <div style="background:#2a2a2a;border-radius:8px;padding:12px;border-left:3px solid #ec4899">
628
+ <div style="font-weight:600;color:#ec4899;margin-bottom:8px">cycleCAD JSON</div>
629
+ <p style="margin:0 0 8px 0;font-size:12px;color:#999">project.json (568 KB)</p>
630
+ <button style="padding:6px 12px;background:#ec4899;color:#fff;border:none;border-radius:4px;cursor:pointer;font-size:12px">Download</button>
631
+ </div>
632
+ </div>
633
+ </div>
634
+
635
+ <div class="market-tab-content" style="display:none" data-tab-content="reviews">
636
+ <h2 style="margin:0 0 16px 0;color:#fff">Reviews</h2>
637
+ <p style="color:#999">${model.reviews} review(s)</p>
638
+ </div>
639
+ </div>
640
+ `;
641
+
642
+ return detailHTML;
643
+ }
644
+
645
+ // Get full marketplace UI
646
+ function getUI() {
647
+ const models = getAllModels();
648
+
649
+ return `
650
+ <div style="background:#1e1e1e;color:#e0e0e0;padding:20px;border-radius:8px;max-height:600px;overflow-y:auto">
651
+ <div style="display:flex;justify-content:space-between;align-items:center;margin-bottom:20px">
652
+ <h2 style="margin:0;color:#fff">GrabCAD Marketplace</h2>
653
+ <button class="market-publish-btn" style="padding:8px 16px;background:#10b981;color:#fff;border:none;border-radius:4px;cursor:pointer;font-weight:600">+ Publish Model</button>
654
+ </div>
655
+
656
+ <div style="display:flex;gap:12px;margin-bottom:20px">
657
+ <input type="text" class="market-search" placeholder="Search models..." style="flex:1;padding:8px;background:#2a2a2a;border:1px solid #444;border-radius:4px;color:#e0e0e0">
658
+ <select class="market-filter-category" style="padding:8px;background:#2a2a2a;border:1px solid #444;border-radius:4px;color:#e0e0e0">
659
+ <option value="">All Categories</option>
660
+ ${CATEGORIES.map(cat => `<option value="${cat}">${cat}</option>`).join('')}
661
+ </select>
662
+ <select class="market-filter-difficulty" style="padding:8px;background:#2a2a2a;border:1px solid #444;border-radius:4px;color:#e0e0e0">
663
+ <option value="">All Levels</option>
664
+ <option value="Beginner">Beginner</option>
665
+ <option value="Intermediate">Intermediate</option>
666
+ <option value="Advanced">Advanced</option>
667
+ <option value="Expert">Expert</option>
668
+ </select>
669
+ </div>
670
+
671
+ <div class="market-grid" style="display:grid;grid-template-columns:repeat(3,1fr);gap:16px;margin-bottom:20px">
672
+ ${models.map(model => getModelCardHTML(model)).join('')}
673
+ </div>
674
+
675
+ <div style="text-align:center;color:#666">
676
+ Showing ${models.length} models
677
+ </div>
678
+ </div>
679
+ `;
680
+ }
681
+
682
+ // Purchase model
683
+ function purchaseModel(modelId) {
684
+ const model = getModelById(modelId);
685
+ if (!model || model.price === 0) {
686
+ return { success: false, message: 'Model not purchasable' };
687
+ }
688
+
689
+ const tokenEngine = window.cycleCAD?.tokens;
690
+ if (!tokenEngine) {
691
+ return { success: false, message: 'Token engine not available' };
692
+ }
693
+
694
+ // Deduct tokens from buyer
695
+ if (tokenEngine.spend) {
696
+ tokenEngine.spend(model.price, { reason: `Purchase: ${model.title}`, modelId });
697
+ }
698
+
699
+ // Credit creator
700
+ if (tokenEngine.earn) {
701
+ const royalty = Math.floor(model.price * 0.7); // 70% to creator
702
+ tokenEngine.earn(royalty, { reason: `Royalty: ${model.title}`, modelId });
703
+ }
704
+
705
+ // Record purchase
706
+ marketplaceState.purchases[modelId] = { date: new Date().toISOString(), price: model.price };
707
+ saveState();
708
+
709
+ return { success: true, message: `Purchased ${model.title} for ${model.price} tokens` };
710
+ }
711
+
712
+ // Toggle favorite
713
+ function toggleFavorite(modelId) {
714
+ marketplaceState.favorites[modelId] = !marketplaceState.favorites[modelId];
715
+ saveState();
716
+ return marketplaceState.favorites[modelId];
717
+ }
718
+
719
+ // Get earnings for author
720
+ function getEarnings(authorId) {
721
+ let total = 0;
722
+ marketplaceState.publishedModels.forEach(model => {
723
+ if (model.authorId === authorId) {
724
+ const modelPurchases = Object.entries(marketplaceState.purchases)
725
+ .filter(([id]) => id === model.id)
726
+ .length;
727
+ total += modelPurchases * Math.floor(model.price * 0.7);
728
+ }
729
+ });
730
+ return total;
731
+ }
732
+
733
+ // Publish new model
734
+ function publishModel(modelData) {
735
+ const newModel = {
736
+ id: `model-${Date.now()}`,
737
+ ...modelData,
738
+ rating: 0,
739
+ downloads: 0,
740
+ reviews: 0
741
+ };
742
+ marketplaceState.publishedModels.push(newModel);
743
+ saveState();
744
+ return newModel;
745
+ }
746
+
747
+ // Initialize on load
748
+ loadState();
749
+
750
+ // Public API
751
+ window.cycleCAD = window.cycleCAD || {};
752
+ window.cycleCAD.marketplaceV2 = {
753
+ getUI,
754
+ getModelCardHTML,
755
+ getModelDetailHTML,
756
+ getAllModels,
757
+ getModelById,
758
+ purchaseModel,
759
+ toggleFavorite,
760
+ getEarnings,
761
+ publishModel,
762
+ CATEGORIES,
763
+ DIFFICULTY_COLORS
764
+ };
765
+
766
+ })();