cyclecad 1.0.3 → 1.0.5
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/app/index.html +30 -3
- package/app/js/ai-copilot.js +21 -12
- package/app/js/operations.js +51 -30
- package/package.json +1 -1
package/app/index.html
CHANGED
|
@@ -1419,7 +1419,7 @@
|
|
|
1419
1419
|
<span class="splash-logo-cycle">cycle</span><span class="splash-logo-cad">CAD</span>
|
|
1420
1420
|
</div>
|
|
1421
1421
|
<p class="splash-subtitle">Parametric 3D CAD Modeler for the Mechanical Designer</p>
|
|
1422
|
-
<p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v1.0.
|
|
1422
|
+
<p style="display:inline-block; color:#0066cc; font-size:1rem; margin:12px 0 0 0; letter-spacing:2px; font-family:monospace; font-weight:700; background:rgba(0,102,204,0.08); border:1.5px solid rgba(0,102,204,0.25); border-radius:8px; padding:5px 20px;">v1.0.5</p>
|
|
1423
1423
|
</div>
|
|
1424
1424
|
<div class="splash-options">
|
|
1425
1425
|
<button class="splash-button splash-button-primary" id="btn-empty-project" style="grid-column: 1 / -1;">
|
|
@@ -2885,6 +2885,9 @@
|
|
|
2885
2885
|
}
|
|
2886
2886
|
}
|
|
2887
2887
|
|
|
2888
|
+
// Expose globally so Copilot + Agent API + tutorials can create geometry
|
|
2889
|
+
window._executeParsedPrompt = function(prompt) { executeParsedPrompt(prompt); };
|
|
2890
|
+
|
|
2888
2891
|
function executeParsedPrompt(prompt) {
|
|
2889
2892
|
try {
|
|
2890
2893
|
const splash = document.getElementById('welcome-splash');
|
|
@@ -4026,7 +4029,31 @@
|
|
|
4026
4029
|
panel.style.cssText = 'position:fixed;top:60px;right:320px;width:420px;max-height:80vh;overflow-y:auto;background:var(--bg-secondary);border:1px solid var(--border-color);border-radius:8px;z-index:500;display:flex;flex-direction:column;box-shadow:var(--shadow-lg);';
|
|
4027
4030
|
|
|
4028
4031
|
const header = document.createElement('div');
|
|
4029
|
-
header.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:8px 8px 0 0;cursor:move;';
|
|
4032
|
+
header.style.cssText = 'display:flex;justify-content:space-between;align-items:center;padding:10px 14px;border-bottom:1px solid var(--border-color);background:var(--bg-tertiary);border-radius:8px 8px 0 0;cursor:move;user-select:none;';
|
|
4033
|
+
|
|
4034
|
+
// Make panel draggable by header
|
|
4035
|
+
let isDragging = false;
|
|
4036
|
+
let dragOffsetX = 0;
|
|
4037
|
+
let dragOffsetY = 0;
|
|
4038
|
+
|
|
4039
|
+
header.addEventListener('mousedown', (e) => {
|
|
4040
|
+
isDragging = true;
|
|
4041
|
+
const rect = panel.getBoundingClientRect();
|
|
4042
|
+
dragOffsetX = e.clientX - rect.left;
|
|
4043
|
+
dragOffsetY = e.clientY - rect.top;
|
|
4044
|
+
panel.style.zIndex = '9999'; // Bring to front
|
|
4045
|
+
});
|
|
4046
|
+
|
|
4047
|
+
document.addEventListener('mousemove', (e) => {
|
|
4048
|
+
if (!isDragging || !panel.parentElement) return;
|
|
4049
|
+
panel.style.left = (e.clientX - dragOffsetX) + 'px';
|
|
4050
|
+
panel.style.top = (e.clientY - dragOffsetY) + 'px';
|
|
4051
|
+
panel.style.right = 'auto'; // Remove right constraint when dragging
|
|
4052
|
+
});
|
|
4053
|
+
|
|
4054
|
+
document.addEventListener('mouseup', () => {
|
|
4055
|
+
isDragging = false;
|
|
4056
|
+
});
|
|
4030
4057
|
|
|
4031
4058
|
const titles = {
|
|
4032
4059
|
'copilot-panel': '✨ AI Copilot',
|
|
@@ -5240,6 +5267,6 @@
|
|
|
5240
5267
|
</div>
|
|
5241
5268
|
</div>
|
|
5242
5269
|
|
|
5243
|
-
<span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v1.0.
|
|
5270
|
+
<span id="version-badge" style="position:fixed;bottom:42px;left:50%;transform:translateX(-50%);z-index:999;font-size:0.9rem;color:rgba(255,255,255,0.9);letter-spacing:0.1em;white-space:nowrap;padding:6px 16px;user-select:all;pointer-events:auto;font-family:monospace;font-weight:700;background:rgba(0,0,0,0.7);border:1px solid rgba(88,166,255,0.4);border-radius:6px;text-shadow:0 1px 3px rgba(0,0,0,0.5);" title="cycleCAD version">cycleCAD v1.0.5</span>
|
|
5244
5271
|
</body>
|
|
5245
5272
|
</html>
|
package/app/js/ai-copilot.js
CHANGED
|
@@ -938,20 +938,29 @@ export async function executeTextCommand(prompt) {
|
|
|
938
938
|
return { ok: false };
|
|
939
939
|
}
|
|
940
940
|
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
941
|
+
addMessage('ai', `Got it! ${preview}. Creating now...`);
|
|
942
|
+
|
|
943
|
+
// Execute: try direct geometry creation first (fastest path)
|
|
944
|
+
const results = [];
|
|
945
|
+
for (const cmd of commands) {
|
|
946
|
+
try {
|
|
947
|
+
// Convert Agent API format {method: 'shape.cylinder', params: {}} to executeParsedPrompt format {type: 'cylinder', params: {}}
|
|
948
|
+
if (window._executeParsedPrompt) {
|
|
949
|
+
const method = cmd.method || '';
|
|
950
|
+
const type = method.replace('shape.', '').replace('feature.', '');
|
|
951
|
+
window._executeParsedPrompt({ type, params: cmd.params || {} });
|
|
952
|
+
results.push({ ok: true, method });
|
|
953
|
+
} else if (window.cycleCAD && window.cycleCAD.execute) {
|
|
954
|
+
const result = await window.cycleCAD.execute(cmd);
|
|
955
|
+
results.push(result);
|
|
956
|
+
}
|
|
957
|
+
} catch (e) {
|
|
958
|
+
console.warn('[Copilot] Command failed:', cmd, e);
|
|
959
|
+
results.push({ ok: false, error: e.message });
|
|
947
960
|
}
|
|
948
|
-
|
|
949
|
-
addMessage('ai', `Got it! ${preview}. Creating now...`);
|
|
950
|
-
return { ok: true, results, commands };
|
|
951
|
-
} else {
|
|
952
|
-
addMessage('ai', 'Agent API not available. Try initializing Agent API first.');
|
|
953
|
-
return { ok: false };
|
|
954
961
|
}
|
|
962
|
+
|
|
963
|
+
return { ok: true, results, commands };
|
|
955
964
|
} catch (error) {
|
|
956
965
|
console.error('[Copilot] Execute error:', error);
|
|
957
966
|
addMessage('ai', `Error: ${error.message || 'Something went wrong'}`);
|
package/app/js/operations.js
CHANGED
|
@@ -349,12 +349,17 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
349
349
|
const { material = 'steel' } = options;
|
|
350
350
|
let geometry;
|
|
351
351
|
|
|
352
|
+
// Scale factor: AI copilot provides dimensions in millimeters, but we render at 1:1 scene units
|
|
353
|
+
// For a 100mm cube to look reasonable in viewport, scale down by 1.0 (1:1 scale)
|
|
354
|
+
// Most CAD systems use 1 scene unit = 1 mm
|
|
355
|
+
const SCALE = 1.0;
|
|
356
|
+
|
|
352
357
|
switch (type) {
|
|
353
358
|
case 'box':
|
|
354
359
|
geometry = new THREE.BoxGeometry(
|
|
355
|
-
params.width ||
|
|
356
|
-
params.height ||
|
|
357
|
-
params.depth ||
|
|
360
|
+
(params.width || 10) * SCALE,
|
|
361
|
+
(params.height || 10) * SCALE,
|
|
362
|
+
(params.depth || 10) * SCALE,
|
|
358
363
|
params.widthSegments || 1,
|
|
359
364
|
params.heightSegments || 1,
|
|
360
365
|
params.depthSegments || 1
|
|
@@ -363,9 +368,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
363
368
|
|
|
364
369
|
case 'cylinder':
|
|
365
370
|
geometry = new THREE.CylinderGeometry(
|
|
366
|
-
params.radius ||
|
|
367
|
-
params.radius ||
|
|
368
|
-
params.height ||
|
|
371
|
+
(params.radius || 10) * SCALE,
|
|
372
|
+
(params.radius || 10) * SCALE,
|
|
373
|
+
(params.height || 20) * SCALE,
|
|
369
374
|
params.segments || 32,
|
|
370
375
|
1,
|
|
371
376
|
params.openEnded || false
|
|
@@ -374,7 +379,7 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
374
379
|
|
|
375
380
|
case 'sphere':
|
|
376
381
|
geometry = new THREE.SphereGeometry(
|
|
377
|
-
params.radius ||
|
|
382
|
+
(params.radius || 10) * SCALE,
|
|
378
383
|
params.segments || 32,
|
|
379
384
|
params.segments || 32
|
|
380
385
|
);
|
|
@@ -382,26 +387,42 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
382
387
|
|
|
383
388
|
case 'cone':
|
|
384
389
|
geometry = new THREE.ConeGeometry(
|
|
385
|
-
params.bottomRadius ||
|
|
386
|
-
params.height ||
|
|
390
|
+
(params.bottomRadius || 10) * SCALE,
|
|
391
|
+
(params.height || 20) * SCALE,
|
|
387
392
|
params.segments || 32
|
|
388
393
|
);
|
|
389
394
|
break;
|
|
390
395
|
|
|
391
396
|
case 'torus':
|
|
392
397
|
geometry = new THREE.TorusGeometry(
|
|
393
|
-
params.radius ||
|
|
394
|
-
params.tube ||
|
|
398
|
+
(params.radius || 10) * SCALE,
|
|
399
|
+
(params.tube || 4) * SCALE,
|
|
395
400
|
params.radialSegments || 16,
|
|
396
401
|
params.tubeSegments || 100
|
|
397
402
|
);
|
|
398
403
|
break;
|
|
399
404
|
|
|
405
|
+
case 'hole': {
|
|
406
|
+
// Create a cylinder for hole preview (visual aid — actual hole subtraction happens in boolean operations)
|
|
407
|
+
// Used by feature.hole commands from AI copilot
|
|
408
|
+
const holeRadius = (params.radius || 5) * SCALE;
|
|
409
|
+
const holeDepth = (params.depth || 20) * SCALE;
|
|
410
|
+
geometry = new THREE.CylinderGeometry(
|
|
411
|
+
holeRadius,
|
|
412
|
+
holeRadius,
|
|
413
|
+
holeDepth,
|
|
414
|
+
16,
|
|
415
|
+
1,
|
|
416
|
+
false
|
|
417
|
+
);
|
|
418
|
+
break;
|
|
419
|
+
}
|
|
420
|
+
|
|
400
421
|
case 'bracket': {
|
|
401
422
|
// 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;
|
|
423
|
+
const bw = (params.width || 80) * SCALE;
|
|
424
|
+
const bh = (params.height || 40) * SCALE;
|
|
425
|
+
const bt = (params.thickness || 5) * SCALE;
|
|
405
426
|
const shape = new THREE.Shape();
|
|
406
427
|
shape.moveTo(0, 0);
|
|
407
428
|
shape.lineTo(bw, 0);
|
|
@@ -417,9 +438,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
417
438
|
|
|
418
439
|
case 'flange': {
|
|
419
440
|
// 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;
|
|
441
|
+
const fo = ((params.outerDiameter || params.outerRadius * 2 || 60) / 2) * SCALE;
|
|
442
|
+
const fi = ((params.innerDiameter || params.innerRadius * 2 || 20) / 2) * SCALE;
|
|
443
|
+
const fh = (params.height || params.thickness || 10) * SCALE;
|
|
423
444
|
const outerShape = new THREE.Shape();
|
|
424
445
|
outerShape.absarc(0, 0, fo, 0, Math.PI * 2, false);
|
|
425
446
|
const holePath = new THREE.Path();
|
|
@@ -431,9 +452,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
431
452
|
}
|
|
432
453
|
|
|
433
454
|
case 'washer': {
|
|
434
|
-
const wo = (params.outerDiameter || 20) / 2;
|
|
435
|
-
const wi = (params.innerDiameter || 10) / 2;
|
|
436
|
-
const wt = params.thickness || 2;
|
|
455
|
+
const wo = ((params.outerDiameter || 20) / 2) * SCALE;
|
|
456
|
+
const wi = ((params.innerDiameter || 10) / 2) * SCALE;
|
|
457
|
+
const wt = (params.thickness || 2) * SCALE;
|
|
437
458
|
const wShape = new THREE.Shape();
|
|
438
459
|
wShape.absarc(0, 0, wo, 0, Math.PI * 2, false);
|
|
439
460
|
const wHole = new THREE.Path();
|
|
@@ -445,9 +466,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
445
466
|
}
|
|
446
467
|
|
|
447
468
|
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;
|
|
469
|
+
const so = (((params.outerDiameter || params.diameter || 15) / 2) * SCALE);
|
|
470
|
+
const si = (((params.innerDiameter || params.holeDiameter || 6) / 2) * SCALE);
|
|
471
|
+
const sh = (params.height || params.length || 20) * SCALE;
|
|
451
472
|
const sShape = new THREE.Shape();
|
|
452
473
|
sShape.absarc(0, 0, so, 0, Math.PI * 2, false);
|
|
453
474
|
const sHole = new THREE.Path();
|
|
@@ -460,9 +481,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
460
481
|
|
|
461
482
|
case 'gear': {
|
|
462
483
|
// Simple spur gear approximation
|
|
463
|
-
const gr = params.radius || params.diameter / 2 || 30;
|
|
484
|
+
const gr = ((params.radius || params.diameter / 2 || 30) * SCALE);
|
|
464
485
|
const gt = params.teeth || 20;
|
|
465
|
-
const gd = params.depth || params.thickness || 10;
|
|
486
|
+
const gd = (params.depth || params.thickness || 10) * SCALE;
|
|
466
487
|
const toothH = gr * 0.15;
|
|
467
488
|
const gShape = new THREE.Shape();
|
|
468
489
|
for (let i = 0; i < gt; i++) {
|
|
@@ -482,9 +503,9 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
482
503
|
}
|
|
483
504
|
|
|
484
505
|
case 'plate': {
|
|
485
|
-
const pw = params.width || 100;
|
|
486
|
-
const ph = params.height || params.depth || 50;
|
|
487
|
-
const pt = params.thickness || 5;
|
|
506
|
+
const pw = (params.width || 100) * SCALE;
|
|
507
|
+
const ph = (params.height || params.depth || 50) * SCALE;
|
|
508
|
+
const pt = (params.thickness || 5) * SCALE;
|
|
488
509
|
geometry = new THREE.BoxGeometry(pw, pt, ph);
|
|
489
510
|
break;
|
|
490
511
|
}
|
|
@@ -493,8 +514,8 @@ export function createPrimitive(type, params = {}, options = {}) {
|
|
|
493
514
|
// Fallback: treat unknown as a box with available dimensions
|
|
494
515
|
console.warn(`Unknown primitive type "${type}" — creating box fallback`);
|
|
495
516
|
geometry = new THREE.BoxGeometry(
|
|
496
|
-
params.width || params.diameter || 50,
|
|
497
|
-
params.height || params.thickness || 50,
|
|
517
|
+
(params.width || params.diameter || 50) * SCALE,
|
|
518
|
+
(params.height || params.thickness || 50) * SCALE,
|
|
498
519
|
params.depth || params.thickness || 50
|
|
499
520
|
);
|
|
500
521
|
}
|
package/package.json
CHANGED