cyclecad 0.8.6 → 0.9.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CLAUDE.md +146 -0
- package/app/duo-manifest.json +7375 -0
- package/app/index.html +596 -46
- package/app/js/ai-chat.js +1295 -711
- package/app/js/operations.js +99 -1
- package/app/js/shortcuts.js +1 -1
- package/app/js/token-dashboard.js +1 -1
- package/app/js/tree.js +14 -1
- package/app/js/viewport.js +8 -0
- package/linkedin-post.md +20 -15
- package/package.json +1 -1
package/app/js/operations.js
CHANGED
|
@@ -397,8 +397,106 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
397
397
|
);
|
|
398
398
|
break;
|
|
399
399
|
|
|
400
|
+
case 'bracket': {
|
|
401
|
+
// L-shaped bracket from width, height, thickness
|
|
402
|
+
const bw = params.width || 80;
|
|
403
|
+
const bh = params.height || 40;
|
|
404
|
+
const bt = params.thickness || 5;
|
|
405
|
+
const shape = new THREE.Shape();
|
|
406
|
+
shape.moveTo(0, 0);
|
|
407
|
+
shape.lineTo(bw, 0);
|
|
408
|
+
shape.lineTo(bw, bt);
|
|
409
|
+
shape.lineTo(bt, bt);
|
|
410
|
+
shape.lineTo(bt, bh);
|
|
411
|
+
shape.lineTo(0, bh);
|
|
412
|
+
shape.lineTo(0, 0);
|
|
413
|
+
geometry = new THREE.ExtrudeGeometry(shape, { depth: bt, bevelEnabled: false });
|
|
414
|
+
geometry.center();
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
case 'flange': {
|
|
419
|
+
// Annular flange: outer ring with center hole
|
|
420
|
+
const fo = (params.outerDiameter || params.outerRadius * 2 || 60) / 2;
|
|
421
|
+
const fi = (params.innerDiameter || params.innerRadius * 2 || 20) / 2;
|
|
422
|
+
const fh = params.height || params.thickness || 10;
|
|
423
|
+
const outerShape = new THREE.Shape();
|
|
424
|
+
outerShape.absarc(0, 0, fo, 0, Math.PI * 2, false);
|
|
425
|
+
const holePath = new THREE.Path();
|
|
426
|
+
holePath.absarc(0, 0, fi, 0, Math.PI * 2, true);
|
|
427
|
+
outerShape.holes.push(holePath);
|
|
428
|
+
geometry = new THREE.ExtrudeGeometry(outerShape, { depth: fh, bevelEnabled: false });
|
|
429
|
+
geometry.center();
|
|
430
|
+
break;
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
case 'washer': {
|
|
434
|
+
const wo = (params.outerDiameter || 20) / 2;
|
|
435
|
+
const wi = (params.innerDiameter || 10) / 2;
|
|
436
|
+
const wt = params.thickness || 2;
|
|
437
|
+
const wShape = new THREE.Shape();
|
|
438
|
+
wShape.absarc(0, 0, wo, 0, Math.PI * 2, false);
|
|
439
|
+
const wHole = new THREE.Path();
|
|
440
|
+
wHole.absarc(0, 0, wi, 0, Math.PI * 2, true);
|
|
441
|
+
wShape.holes.push(wHole);
|
|
442
|
+
geometry = new THREE.ExtrudeGeometry(wShape, { depth: wt, bevelEnabled: false });
|
|
443
|
+
geometry.center();
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
case 'spacer': {
|
|
448
|
+
const so = (params.outerDiameter || params.diameter || 15) / 2;
|
|
449
|
+
const si = (params.innerDiameter || params.holeDiameter || 6) / 2;
|
|
450
|
+
const sh = params.height || params.length || 20;
|
|
451
|
+
const sShape = new THREE.Shape();
|
|
452
|
+
sShape.absarc(0, 0, so, 0, Math.PI * 2, false);
|
|
453
|
+
const sHole = new THREE.Path();
|
|
454
|
+
sHole.absarc(0, 0, si, 0, Math.PI * 2, true);
|
|
455
|
+
sShape.holes.push(sHole);
|
|
456
|
+
geometry = new THREE.ExtrudeGeometry(sShape, { depth: sh, bevelEnabled: false });
|
|
457
|
+
geometry.center();
|
|
458
|
+
break;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
case 'gear': {
|
|
462
|
+
// Simple spur gear approximation
|
|
463
|
+
const gr = params.radius || params.diameter / 2 || 30;
|
|
464
|
+
const gt = params.teeth || 20;
|
|
465
|
+
const gd = params.depth || params.thickness || 10;
|
|
466
|
+
const toothH = gr * 0.15;
|
|
467
|
+
const gShape = new THREE.Shape();
|
|
468
|
+
for (let i = 0; i < gt; i++) {
|
|
469
|
+
const a0 = (i / gt) * Math.PI * 2;
|
|
470
|
+
const a1 = ((i + 0.3) / gt) * Math.PI * 2;
|
|
471
|
+
const a2 = ((i + 0.5) / gt) * Math.PI * 2;
|
|
472
|
+
const a3 = ((i + 0.8) / gt) * Math.PI * 2;
|
|
473
|
+
if (i === 0) gShape.moveTo(Math.cos(a0) * gr, Math.sin(a0) * gr);
|
|
474
|
+
gShape.lineTo(Math.cos(a1) * (gr + toothH), Math.sin(a1) * (gr + toothH));
|
|
475
|
+
gShape.lineTo(Math.cos(a2) * (gr + toothH), Math.sin(a2) * (gr + toothH));
|
|
476
|
+
gShape.lineTo(Math.cos(a3) * gr, Math.sin(a3) * gr);
|
|
477
|
+
}
|
|
478
|
+
gShape.closePath();
|
|
479
|
+
geometry = new THREE.ExtrudeGeometry(gShape, { depth: gd, bevelEnabled: false });
|
|
480
|
+
geometry.center();
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
case 'plate': {
|
|
485
|
+
const pw = params.width || 100;
|
|
486
|
+
const ph = params.height || params.depth || 50;
|
|
487
|
+
const pt = params.thickness || 5;
|
|
488
|
+
geometry = new THREE.BoxGeometry(pw, pt, ph);
|
|
489
|
+
break;
|
|
490
|
+
}
|
|
491
|
+
|
|
400
492
|
default:
|
|
401
|
-
|
|
493
|
+
// Fallback: treat unknown as a box with available dimensions
|
|
494
|
+
console.warn(`Unknown primitive type "${type}" — creating box fallback`);
|
|
495
|
+
geometry = new THREE.BoxGeometry(
|
|
496
|
+
params.width || params.diameter || 50,
|
|
497
|
+
params.height || params.thickness || 50,
|
|
498
|
+
params.depth || params.thickness || 50
|
|
499
|
+
);
|
|
402
500
|
}
|
|
403
501
|
|
|
404
502
|
// Create mesh
|
package/app/js/shortcuts.js
CHANGED
|
@@ -46,7 +46,7 @@ const SHORTCUT_MAP = {
|
|
|
46
46
|
// Display
|
|
47
47
|
'g': { action: 'toggleGrid', label: 'Toggle Grid', category: 'Display' },
|
|
48
48
|
'w': { action: 'toggleWireframe', label: 'Toggle Wireframe', category: 'Display' },
|
|
49
|
-
'
|
|
49
|
+
'h': { action: 'fitAll', label: 'Fit All / Home View', category: 'Display' },
|
|
50
50
|
|
|
51
51
|
// Export & Save
|
|
52
52
|
'ctrl+s': { action: 'save', label: 'Save (Export JSON)', category: 'File' },
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
export function initTokenDashboard() {
|
|
14
14
|
// Create panel HTML
|
|
15
15
|
const panelHTML = `
|
|
16
|
-
<div
|
|
16
|
+
<div style="padding: 12px; overflow-y: auto;">
|
|
17
17
|
<!-- Balance Card -->
|
|
18
18
|
<div style="background: var(--bg-tertiary); border: 1px solid var(--border-color); border-radius: 6px; padding: 12px; margin-bottom: 12px;">
|
|
19
19
|
<div style="font-size: 11px; color: var(--text-secondary); text-transform: uppercase; margin-bottom: 6px;">Balance</div>
|
package/app/js/tree.js
CHANGED
|
@@ -310,7 +310,20 @@ export function onSelect(callback) {
|
|
|
310
310
|
*/
|
|
311
311
|
export function suppressFeature(index) {
|
|
312
312
|
if (index >= 0 && index < treeState.features.length) {
|
|
313
|
-
|
|
313
|
+
const feature = treeState.features[index];
|
|
314
|
+
feature.suppressed = !feature.suppressed;
|
|
315
|
+
// Toggle mesh visibility in 3D scene
|
|
316
|
+
if (feature.mesh) {
|
|
317
|
+
feature.mesh.visible = !feature.suppressed;
|
|
318
|
+
}
|
|
319
|
+
// Also sync with APP.features if available
|
|
320
|
+
const appFeatures = window.APP?.features;
|
|
321
|
+
if (appFeatures && appFeatures[index]) {
|
|
322
|
+
appFeatures[index].suppressed = feature.suppressed;
|
|
323
|
+
if (appFeatures[index].mesh) {
|
|
324
|
+
appFeatures[index].mesh.visible = !feature.suppressed;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
314
327
|
renderTree();
|
|
315
328
|
}
|
|
316
329
|
}
|
package/app/js/viewport.js
CHANGED
|
@@ -521,6 +521,14 @@ export function toggleReferencePlanes(visible, plane = null) {
|
|
|
521
521
|
* @param {boolean} enabled - Enable/disable wireframe mode
|
|
522
522
|
*/
|
|
523
523
|
export function toggleWireframe(enabled) {
|
|
524
|
+
// If no argument, toggle based on current state
|
|
525
|
+
if (enabled === undefined) {
|
|
526
|
+
window._wireframeEnabled = !window._wireframeEnabled;
|
|
527
|
+
enabled = window._wireframeEnabled;
|
|
528
|
+
} else {
|
|
529
|
+
window._wireframeEnabled = !!enabled;
|
|
530
|
+
}
|
|
531
|
+
|
|
524
532
|
if (scene) {
|
|
525
533
|
scene.traverse((obj) => {
|
|
526
534
|
// Skip reference planes and grid
|
package/linkedin-post.md
CHANGED
|
@@ -1,24 +1,29 @@
|
|
|
1
1
|
# cycleCAD Launch Post
|
|
2
2
|
|
|
3
|
-
We just shipped
|
|
3
|
+
**We just shipped the first AI-first CAD modeler that runs in your browser.**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
cycleCAD is open-source, free, and agent-ready. No install. No subscriptions required. Just solid 3D parametric design with a twist: agents can design parts as easily as humans.
|
|
6
6
|
|
|
7
|
-
**The
|
|
7
|
+
**The story:** I was building cycleWASH (a robotic bike washing machine) and every CAD tool cost $500-$4000/year and required Windows. 19 modules, 18,800 lines of JavaScript, and a lot of coffee later — here we are.
|
|
8
8
|
|
|
9
|
-
What
|
|
10
|
-
✨ Browser-native (
|
|
11
|
-
✨
|
|
12
|
-
✨ AI-
|
|
13
|
-
✨
|
|
14
|
-
✨
|
|
15
|
-
✨
|
|
9
|
+
**What you get:**
|
|
10
|
+
✨ Browser-native 3D CAD modeler (sketch → extrude → fillet → export)
|
|
11
|
+
✨ Inventor file parsing (open your .ipt/.iam directly)
|
|
12
|
+
✨ AI co-pilot (Gemini + Groq + local NLP fallback)
|
|
13
|
+
✨ Agent API — 55 commands for autonomous design
|
|
14
|
+
✨ MCP Server + CLI tool (integrate into any AI workflow)
|
|
15
|
+
✨ Model marketplace (publish, earn, collaborate)
|
|
16
|
+
✨ ExplodeView integration (40+ analysis tools: BOM, DIN specs, McMaster-Carr, QR codes, AR)
|
|
16
17
|
|
|
17
|
-
|
|
18
|
+
**Free tier. Pro €49/mo. Enterprise €299/mo.**
|
|
18
19
|
|
|
19
|
-
|
|
20
|
-
**Star us on GitHub:** https://github.com/vvlars-cmd/cyclecad
|
|
20
|
+
We're building the OS of manufacturing where agents design, humans review, and machines build.
|
|
21
21
|
|
|
22
|
-
|
|
22
|
+
**Try it now:**
|
|
23
|
+
→ https://cyclecad.com
|
|
24
|
+
→ npm install cyclecad
|
|
25
|
+
→ https://github.com/vvlars-cmd/cyclecad
|
|
23
26
|
|
|
24
|
-
|
|
27
|
+
Star us. Contribute. Let's build the future of CAD together.
|
|
28
|
+
|
|
29
|
+
#OpenSource #CAD #AI #Manufacturing #Agents #Engineering #3D #Design #BuildingInPublic #FreeCAD
|
package/package.json
CHANGED