cyclecad 3.9.1 → 3.9.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/_test_write ADDED
File without changes
package/app/index.html CHANGED
@@ -1145,8 +1145,10 @@
1145
1145
  </div>
1146
1146
 
1147
1147
  <!-- Viewport -->
1148
- <div id="viewport-container">
1148
+ <div id="viewport-container" style="position:relative;">
1149
1149
  <canvas id="viewport"></canvas>
1150
+ <!-- ViewCube -->
1151
+ <div id="viewcube" style="position:absolute;top:12px;right:12px;width:90px;height:90px;pointer-events:auto;z-index:100;"></div>
1150
1152
  </div>
1151
1153
 
1152
1154
  <!-- Right Panel (Properties) -->
@@ -1306,6 +1308,72 @@
1306
1308
 
1307
1309
  </div>
1308
1310
 
1311
+ <!-- Welcome Splash Screen -->
1312
+ <div id="welcome-panel" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.75);display:flex;align-items:center;justify-content:center;z-index:9999;">
1313
+ <div style="background:var(--bg-secondary,#252526);border:1px solid var(--border-color,#3c3c3c);border-radius:12px;padding:40px 48px;max-width:560px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.5);">
1314
+ <div style="font-size:36px;margin-bottom:4px;">
1315
+ <span style="color:#0284C7;font-weight:700;">cycle</span><span style="color:#e0e0e0;font-weight:300;">CAD</span>
1316
+ </div>
1317
+ <div style="color:#888;font-size:13px;margin-bottom:28px;">Agent-First Parametric 3D CAD Modeler</div>
1318
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;">
1319
+ <button data-action="new-sketch" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;transition:background 0.2s;" onmouseover="this.style.background='#0369a1'" onmouseout="this.style.background='#0284C7'">
1320
+ <div style="font-size:24px;margin-bottom:6px;">✏️</div>
1321
+ New Sketch
1322
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Start with a 2D sketch</div>
1323
+ </button>
1324
+ <button data-action="import" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;transition:background 0.2s;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1325
+ <div style="font-size:24px;margin-bottom:6px;">📂</div>
1326
+ Open / Import
1327
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">STEP, STL, Inventor, JSON</div>
1328
+ </button>
1329
+ <button data-action="ai-generate" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;transition:background 0.2s;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1330
+ <div style="font-size:24px;margin-bottom:6px;">🤖</div>
1331
+ Text-to-CAD
1332
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Describe a part in English</div>
1333
+ </button>
1334
+ <button data-action="load-inventor" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;transition:background 0.2s;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1335
+ <div style="font-size:24px;margin-bottom:6px;">🏭</div>
1336
+ Inventor Project
1337
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Load .ipj / .ipt / .iam</div>
1338
+ </button>
1339
+ </div>
1340
+ <div style="color:#666;font-size:11px;">v0.9.0 &middot; 12 killer features &middot; 46 modules &middot; <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="color:#0284C7;text-decoration:none;">GitHub</a></div>
1341
+ </div>
1342
+ </div>
1343
+
1344
+ <!-- Welcome Splash Screen -->
1345
+ <div id="welcome-panel" style="position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.75);display:flex;align-items:center;justify-content:center;z-index:9999;">
1346
+ <div style="background:var(--bg-secondary,#252526);border:1px solid var(--border-color,#3c3c3c);border-radius:12px;padding:40px 48px;max-width:560px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.5);">
1347
+ <div style="font-size:36px;margin-bottom:4px;">
1348
+ <span style="color:#0284C7;font-weight:700;">cycle</span><span style="color:#e0e0e0;font-weight:300;">CAD</span>
1349
+ </div>
1350
+ <div style="color:#888;font-size:13px;margin-bottom:28px;">Agent-First Parametric 3D CAD Modeler</div>
1351
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;">
1352
+ <button data-action="new-sketch" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;" onmouseover="this.style.background='#0369a1'" onmouseout="this.style.background='#0284C7'">
1353
+ <div style="font-size:24px;margin-bottom:6px;">&#9999;&#65039;</div>
1354
+ New Sketch
1355
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Start with a 2D sketch</div>
1356
+ </button>
1357
+ <button data-action="import" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1358
+ <div style="font-size:24px;margin-bottom:6px;">&#128194;</div>
1359
+ Open / Import
1360
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">STEP, STL, Inventor, JSON</div>
1361
+ </button>
1362
+ <button data-action="ai-generate" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1363
+ <div style="font-size:24px;margin-bottom:6px;">&#129302;</div>
1364
+ Text-to-CAD
1365
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Describe a part in English</div>
1366
+ </button>
1367
+ <button data-action="load-inventor" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;" onmouseover="this.style.background='#4b5563'" onmouseout="this.style.background='#374151'">
1368
+ <div style="font-size:24px;margin-bottom:6px;">&#127981;</div>
1369
+ Inventor Project
1370
+ <div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Load .ipj / .ipt / .iam</div>
1371
+ </button>
1372
+ </div>
1373
+ <div style="color:#666;font-size:11px;">v0.9.0 &middot; 12 killer features &middot; 46 modules &middot; <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="color:#0284C7;text-decoration:none;">GitHub</a></div>
1374
+ </div>
1375
+ </div>
1376
+
1309
1377
  <!-- Modal Dialogs -->
1310
1378
  <div id="dialog-overlay" class="modal-overlay">
1311
1379
  <div class="modal-dialog">
@@ -1447,22 +1515,6 @@
1447
1515
  const grid = new THREE.GridHelper(500, 50, 0x444444, 0x333333);
1448
1516
  scene.add(grid);
1449
1517
 
1450
- // Demo geometry — a sample part so viewport isn't empty
1451
- const boxGeo = new THREE.BoxGeometry(60, 40, 80);
1452
- const boxMat = new THREE.MeshStandardMaterial({ color: 0x0284C7, metalness: 0.3, roughness: 0.6 });
1453
- const box = new THREE.Mesh(boxGeo, boxMat);
1454
- box.position.y = 20;
1455
- box.castShadow = true;
1456
- box.receiveShadow = true;
1457
- scene.add(box);
1458
-
1459
- const cylGeo = new THREE.CylinderGeometry(12, 12, 50, 32);
1460
- const cylMat = new THREE.MeshStandardMaterial({ color: 0x10b981, metalness: 0.4, roughness: 0.5 });
1461
- const cyl = new THREE.Mesh(cylGeo, cylMat);
1462
- cyl.position.set(0, 45, 0);
1463
- cyl.castShadow = true;
1464
- scene.add(cyl);
1465
-
1466
1518
  // Ground plane
1467
1519
  const groundGeo = new THREE.PlaneGeometry(500, 500);
1468
1520
  const groundMat = new THREE.ShadowMaterial({ opacity: 0.15 });
@@ -1475,11 +1527,89 @@
1475
1527
  new ResizeObserver(resizeViewport).observe(viewportEl);
1476
1528
  resizeViewport(); // Call initial resize to set canvas dimensions
1477
1529
 
1530
+ // ===== ViewCube =====
1531
+ const vcContainer = document.getElementById('viewcube');
1532
+ const vcScene = new THREE.Scene();
1533
+ const vcCamera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
1534
+ vcCamera.position.set(0, 0, 3.5);
1535
+ const vcRenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
1536
+ vcRenderer.setSize(90, 90);
1537
+ vcRenderer.setPixelRatio(window.devicePixelRatio);
1538
+ vcContainer.appendChild(vcRenderer.domElement);
1539
+
1540
+ // Cube faces
1541
+ const faceLabels = ['Right', 'Left', 'Top', 'Bottom', 'Front', 'Back'];
1542
+ const faceColors = [0x3b82f6, 0x3b82f6, 0x10b981, 0x10b981, 0x0284C7, 0x0284C7];
1543
+ const cubeMats = faceLabels.map((label, i) => {
1544
+ const canvas = document.createElement('canvas');
1545
+ canvas.width = 128; canvas.height = 128;
1546
+ const ctx = canvas.getContext('2d');
1547
+ ctx.fillStyle = '#' + faceColors[i].toString(16).padStart(6, '0');
1548
+ ctx.fillRect(0, 0, 128, 128);
1549
+ ctx.strokeStyle = '#1e293b';
1550
+ ctx.lineWidth = 3;
1551
+ ctx.strokeRect(1, 1, 126, 126);
1552
+ ctx.fillStyle = '#fff';
1553
+ ctx.font = 'bold 22px Arial';
1554
+ ctx.textAlign = 'center';
1555
+ ctx.textBaseline = 'middle';
1556
+ ctx.fillText(label, 64, 64);
1557
+ const tex = new THREE.CanvasTexture(canvas);
1558
+ return new THREE.MeshBasicMaterial({ map: tex });
1559
+ });
1560
+ const vcCube = new THREE.Mesh(new THREE.BoxGeometry(1.6, 1.6, 1.6), cubeMats);
1561
+ vcScene.add(vcCube);
1562
+
1563
+ // Axis lines on viewcube
1564
+ const axLen = 1.3;
1565
+ const axMat = (c) => new THREE.LineBasicMaterial({ color: c });
1566
+ const axGeo = (v) => new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(), v]);
1567
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(axLen, 0, 0)), axMat(0xff4444)));
1568
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(0, axLen, 0)), axMat(0x44ff44)));
1569
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(0, 0, axLen)), axMat(0x4444ff)));
1570
+
1571
+ // ViewCube click → snap camera to face
1572
+ const vcViews = {
1573
+ Right: { x: 3, y: 0, z: 0 },
1574
+ Left: { x: -3, y: 0, z: 0 },
1575
+ Top: { x: 0, y: 3, z: 0 },
1576
+ Bottom: { x: 0, y: -3, z: 0 },
1577
+ Front: { x: 0, y: 0, z: 3 },
1578
+ Back: { x: 0, y: 0, z: -3 }
1579
+ };
1580
+ const vcRay = new THREE.Raycaster();
1581
+ const vcMouse = new THREE.Vector2();
1582
+ vcRenderer.domElement.addEventListener('click', (e) => {
1583
+ const rect = vcRenderer.domElement.getBoundingClientRect();
1584
+ vcMouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
1585
+ vcMouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
1586
+ vcRay.setFromCamera(vcMouse, vcCamera);
1587
+ const hits = vcRay.intersectObject(vcCube);
1588
+ if (hits.length > 0) {
1589
+ const fi = hits[0].face.materialIndex;
1590
+ const label = faceLabels[fi];
1591
+ const v = vcViews[label];
1592
+ if (v) {
1593
+ const dist = camera.position.length();
1594
+ const s = dist / 3;
1595
+ camera.position.set(v.x * s, v.y * s, v.z * s);
1596
+ camera.lookAt(controls.target);
1597
+ controls.update();
1598
+ }
1599
+ }
1600
+ });
1601
+ vcRenderer.domElement.style.cursor = 'pointer';
1602
+
1478
1603
  // Animation loop
1479
1604
  function animate() {
1480
1605
  requestAnimationFrame(animate);
1481
1606
  controls.update();
1482
1607
  renderer.render(scene, camera);
1608
+ // Sync ViewCube with main camera
1609
+ const dir = new THREE.Vector3();
1610
+ camera.getWorldDirection(dir);
1611
+ vcCube.quaternion.copy(camera.quaternion).invert();
1612
+ vcRenderer.render(vcScene, vcCamera);
1483
1613
  }
1484
1614
  animate();
1485
1615
 
@@ -1513,6 +1643,34 @@
1513
1643
  const statusBar = document.getElementById('status-bar');
1514
1644
  const timelineContent = document.getElementById('timeline-content');
1515
1645
 
1646
+ // ===== Welcome Splash =====
1647
+ const welcomePanel = document.getElementById('welcome-panel');
1648
+ if (welcomePanel) {
1649
+ const dismissWelcome = () => { welcomePanel.style.display = 'none'; };
1650
+ const wb1 = welcomePanel.querySelector('[data-action="new-sketch"]');
1651
+ if (wb1) wb1.onclick = () => { dismissWelcome(); handleMenuAction('sketch-new'); };
1652
+ const wb2 = welcomePanel.querySelector('[data-action="import"]');
1653
+ if (wb2) wb2.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
1654
+ const wb3 = welcomePanel.querySelector('[data-action="ai-generate"]');
1655
+ if (wb3) wb3.onclick = () => { dismissWelcome(); handleMenuAction('tools-text-to-cad'); };
1656
+ const wb4 = welcomePanel.querySelector('[data-action="load-inventor"]');
1657
+ if (wb4) wb4.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
1658
+ }
1659
+
1660
+ // ===== Welcome Splash =====
1661
+ const welcomePanel = document.getElementById('welcome-panel');
1662
+ if (welcomePanel) {
1663
+ const dismissWelcome = () => { welcomePanel.style.display = 'none'; };
1664
+ const wb1 = welcomePanel.querySelector('[data-action="new-sketch"]');
1665
+ if (wb1) wb1.onclick = () => { dismissWelcome(); handleMenuAction('sketch-new'); };
1666
+ const wb2 = welcomePanel.querySelector('[data-action="import"]');
1667
+ if (wb2) wb2.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
1668
+ const wb3 = welcomePanel.querySelector('[data-action="ai-generate"]');
1669
+ if (wb3) wb3.onclick = () => { dismissWelcome(); handleMenuAction('tools-text-to-cad'); };
1670
+ const wb4 = welcomePanel.querySelector('[data-action="load-inventor"]');
1671
+ if (wb4) wb4.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
1672
+ }
1673
+
1516
1674
  // ===== Menu Actions Handler =====
1517
1675
  function handleMenuAction(action) {
1518
1676
  switch (action) {
@@ -1562,99 +1720,87 @@
1562
1720
  break;
1563
1721
  case 'tools-text-to-cad':
1564
1722
  if (window.CycleCAD && window.CycleCAD.TextToCAD) {
1565
- const ttcPanel = window.CycleCAD.TextToCAD.getUI();
1566
1723
  showDialog('Text-to-CAD (AI)', '');
1567
- const dlg = document.querySelector('.dialog-content');
1568
- if (dlg) { dlg.innerHTML = ''; dlg.appendChild(ttcPanel); dlg.style.maxHeight = '500px'; dlg.style.overflow = 'auto'; }
1569
- }
1724
+ const db1 = document.getElementById('dialog-body');
1725
+ if (db1) { db1.innerHTML = ''; db1.appendChild(window.CycleCAD.TextToCAD.getUI()); db1.style.maxHeight = '500px'; db1.style.overflow = 'auto'; }
1726
+ } else { showToast('TextToCAD module not loaded', 'error'); }
1570
1727
  break;
1571
1728
  case 'tools-photo-to-cad':
1572
1729
  if (window.CycleCAD && window.CycleCAD.PhotoToCAD) {
1573
- const ptcPanel = window.CycleCAD.PhotoToCAD.getUI();
1574
1730
  showDialog('Photo-to-CAD', '');
1575
- const dlg2 = document.querySelector('.dialog-content');
1576
- if (dlg2) { dlg2.innerHTML = ''; dlg2.appendChild(ptcPanel); dlg2.style.maxHeight = '500px'; dlg2.style.overflow = 'auto'; }
1577
- }
1731
+ const db2 = document.getElementById('dialog-body');
1732
+ if (db2) { db2.innerHTML = ''; db2.appendChild(window.CycleCAD.PhotoToCAD.getUI()); db2.style.maxHeight = '500px'; db2.style.overflow = 'auto'; }
1733
+ } else { showToast('PhotoToCAD module not loaded', 'error'); }
1578
1734
  break;
1579
1735
  case 'tools-dfm':
1580
1736
  if (window.CycleCAD && window.CycleCAD.Manufacturability) {
1581
- const dfmPanel = window.CycleCAD.Manufacturability.getUI();
1582
1737
  showDialog('Manufacturability Check', '');
1583
- const dlg3 = document.querySelector('.dialog-content');
1584
- if (dlg3) { dlg3.innerHTML = ''; dlg3.appendChild(dfmPanel); dlg3.style.maxHeight = '500px'; dlg3.style.overflow = 'auto'; }
1585
- }
1738
+ const db3 = document.getElementById('dialog-body');
1739
+ if (db3) { db3.innerHTML = ''; db3.appendChild(window.CycleCAD.Manufacturability.getUI()); db3.style.maxHeight = '500px'; db3.style.overflow = 'auto'; }
1740
+ } else { showToast('Manufacturability module not loaded', 'error'); }
1586
1741
  break;
1587
1742
  case 'tools-generative':
1588
1743
  if (window.CycleCAD && window.CycleCAD.GenerativeDesign) {
1589
- const genPanel = window.CycleCAD.GenerativeDesign.getUI();
1590
1744
  showDialog('Generative Design', '');
1591
- const dlg4 = document.querySelector('.dialog-content');
1592
- if (dlg4) { dlg4.innerHTML = ''; dlg4.appendChild(genPanel); dlg4.style.maxHeight = '500px'; dlg4.style.overflow = 'auto'; }
1593
- }
1745
+ const db4 = document.getElementById('dialog-body');
1746
+ if (db4) { db4.innerHTML = ''; db4.appendChild(window.CycleCAD.GenerativeDesign.getUI()); db4.style.maxHeight = '500px'; db4.style.overflow = 'auto'; }
1747
+ } else { showToast('GenerativeDesign module not loaded', 'error'); }
1594
1748
  break;
1595
1749
  case 'tools-physics':
1596
1750
  if (window.CycleCAD && window.CycleCAD.MultiPhysics) {
1597
- const physPanel = window.CycleCAD.MultiPhysics.getUI();
1598
1751
  showDialog('Multi-Physics Simulation', '');
1599
- const dlg5 = document.querySelector('.dialog-content');
1600
- if (dlg5) { dlg5.innerHTML = ''; dlg5.appendChild(physPanel); dlg5.style.maxHeight = '500px'; dlg5.style.overflow = 'auto'; }
1601
- }
1752
+ const db5 = document.getElementById('dialog-body');
1753
+ if (db5) { db5.innerHTML = ''; db5.appendChild(window.CycleCAD.MultiPhysics.getUI()); db5.style.maxHeight = '500px'; db5.style.overflow = 'auto'; }
1754
+ } else { showToast('MultiPhysics module not loaded', 'error'); }
1602
1755
  break;
1603
1756
  case 'tools-parts':
1604
1757
  if (window.CycleCAD && window.CycleCAD.SmartParts) {
1605
- const partsPanel = window.CycleCAD.SmartParts.getUI();
1606
1758
  showDialog('Smart Parts Library', '');
1607
- const dlg6 = document.querySelector('.dialog-content');
1608
- if (dlg6) { dlg6.innerHTML = ''; dlg6.appendChild(partsPanel); dlg6.style.maxHeight = '500px'; dlg6.style.overflow = 'auto'; }
1609
- }
1759
+ const db6 = document.getElementById('dialog-body');
1760
+ if (db6) { db6.innerHTML = ''; db6.appendChild(window.CycleCAD.SmartParts.getUI()); db6.style.maxHeight = '500px'; db6.style.overflow = 'auto'; }
1761
+ } else { showToast('SmartParts module not loaded', 'error'); }
1610
1762
  break;
1611
1763
  case 'tools-smart-assembly':
1612
1764
  if (window.CycleCAD && window.CycleCAD.SmartAssembly) {
1613
- const saPanel = window.CycleCAD.SmartAssembly.getUI();
1614
1765
  showDialog('Smart Assembly Mating', '');
1615
- const dlg7 = document.querySelector('.dialog-content');
1616
- if (dlg7) { dlg7.innerHTML = ''; dlg7.appendChild(saPanel); dlg7.style.maxHeight = '500px'; dlg7.style.overflow = 'auto'; }
1617
- }
1766
+ const db7 = document.getElementById('dialog-body');
1767
+ if (db7) { db7.innerHTML = ''; db7.appendChild(window.CycleCAD.SmartAssembly.getUI()); db7.style.maxHeight = '500px'; db7.style.overflow = 'auto'; }
1768
+ } else { showToast('SmartAssembly module not loaded', 'error'); }
1618
1769
  break;
1619
1770
  case 'tools-digital-twin':
1620
1771
  if (window.CycleCAD && window.CycleCAD.DigitalTwin) {
1621
- const dtPanel = window.CycleCAD.DigitalTwin.getUI();
1622
1772
  showDialog('Digital Twin — Live Data', '');
1623
- const dlg8 = document.querySelector('.dialog-content');
1624
- if (dlg8) { dlg8.innerHTML = ''; dlg8.appendChild(dtPanel); dlg8.style.maxHeight = '500px'; dlg8.style.overflow = 'auto'; }
1625
- }
1773
+ const db8 = document.getElementById('dialog-body');
1774
+ if (db8) { db8.innerHTML = ''; db8.appendChild(window.CycleCAD.DigitalTwin.getUI()); db8.style.maxHeight = '500px'; db8.style.overflow = 'auto'; }
1775
+ } else { showToast('DigitalTwin module not loaded', 'error'); }
1626
1776
  break;
1627
1777
  case 'tools-machine':
1628
1778
  if (window.CycleCAD && window.CycleCAD.MachineControl) {
1629
- const mcPanel = window.CycleCAD.MachineControl.getUI();
1630
1779
  showDialog('Machine Control — CNC / 3D Printer', '');
1631
- const dlg9 = document.querySelector('.dialog-content');
1632
- if (dlg9) { dlg9.innerHTML = ''; dlg9.appendChild(mcPanel); dlg9.style.maxHeight = '500px'; dlg9.style.overflow = 'auto'; }
1633
- }
1780
+ const db9 = document.getElementById('dialog-body');
1781
+ if (db9) { db9.innerHTML = ''; db9.appendChild(window.CycleCAD.MachineControl.getUI()); db9.style.maxHeight = '500px'; db9.style.overflow = 'auto'; }
1782
+ } else { showToast('MachineControl module not loaded', 'error'); }
1634
1783
  break;
1635
1784
  case 'tools-notebook':
1636
1785
  if (window.CycleCAD && window.CycleCAD.EngineeringNotebook) {
1637
- const nbPanel = window.CycleCAD.EngineeringNotebook.getUI();
1638
1786
  showDialog('Engineering Notebook', '');
1639
- const dlg10 = document.querySelector('.dialog-content');
1640
- if (dlg10) { dlg10.innerHTML = ''; dlg10.appendChild(nbPanel); dlg10.style.maxHeight = '500px'; dlg10.style.overflow = 'auto'; }
1641
- }
1787
+ const db10 = document.getElementById('dialog-body');
1788
+ if (db10) { db10.innerHTML = ''; db10.appendChild(window.CycleCAD.EngineeringNotebook.getUI()); db10.style.maxHeight = '500px'; db10.style.overflow = 'auto'; }
1789
+ } else { showToast('EngineeringNotebook module not loaded', 'error'); }
1642
1790
  break;
1643
1791
  case 'tools-auto-assembly':
1644
1792
  if (window.CycleCAD && window.CycleCAD.AutoAssembly) {
1645
- const aaPanel = window.CycleCAD.AutoAssembly.getUI();
1646
1793
  showDialog('Auto-Assemble Parts', '');
1647
- const dlg11 = document.querySelector('.dialog-content');
1648
- if (dlg11) { dlg11.innerHTML = ''; dlg11.appendChild(aaPanel); dlg11.style.maxHeight = '500px'; dlg11.style.overflow = 'auto'; }
1649
- }
1794
+ const db11 = document.getElementById('dialog-body');
1795
+ if (db11) { db11.innerHTML = ''; db11.appendChild(window.CycleCAD.AutoAssembly.getUI()); db11.style.maxHeight = '500px'; db11.style.overflow = 'auto'; }
1796
+ } else { showToast('AutoAssembly module not loaded', 'error'); }
1650
1797
  break;
1651
1798
  case 'tools-parametric':
1652
1799
  if (window.CycleCAD && window.CycleCAD.ParametricFromExample) {
1653
- const pfPanel = window.CycleCAD.ParametricFromExample.getUI();
1654
1800
  showDialog('Parametric from Example', '');
1655
- const dlg12 = document.querySelector('.dialog-content');
1656
- if (dlg12) { dlg12.innerHTML = ''; dlg12.appendChild(pfPanel); dlg12.style.maxHeight = '500px'; dlg12.style.overflow = 'auto'; }
1657
- }
1801
+ const db12 = document.getElementById('dialog-body');
1802
+ if (db12) { db12.innerHTML = ''; db12.appendChild(window.CycleCAD.ParametricFromExample.getUI()); db12.style.maxHeight = '500px'; db12.style.overflow = 'auto'; }
1803
+ } else { showToast('ParametricFromExample module not loaded', 'error'); }
1658
1804
  break;
1659
1805
  default:
1660
1806
  showToast(`${action}`, 'success');
@@ -212,7 +212,7 @@
212
212
  if (/add|with|plus/.test(lower)) return 'add';
213
213
  if (/(fillet|chamfer|pattern|shell|subtract|cut)/.test(lower)) return 'modify';
214
214
  if (/combine|merge|join|union/.test(lower)) return 'combine';
215
- if /(array|repeat|pattern)/.test(lower)) return 'pattern';
215
+ if (/(array|repeat|pattern)/.test(lower)) return 'pattern';
216
216
  if (/export|save|output/.test(lower)) return 'export';
217
217
  return 'create';
218
218
  }
Binary file
@@ -0,0 +1,181 @@
1
+ #!/bin/bash
2
+ # cycleCAD Fix: Welcome Splash + ViewCube + Dialog Selectors + Remove Demo Geometry
3
+ # Run from ~/cyclecad: bash fix-splash-viewcube.sh
4
+
5
+ set -e
6
+ cd "$(dirname "$0")"
7
+
8
+ echo "=== Fix 1: text-to-cad.js syntax error (line 215) ==="
9
+ sed -i.bak 's/ if \/(array|repeat|pattern)\/.test(lower)) return '\''pattern'\'';/ if (\/(array|repeat|pattern)\/.test(lower)) return '\''pattern'\'';/' app/js/modules/text-to-cad.js
10
+ rm -f app/js/modules/text-to-cad.js.bak
11
+
12
+ echo "=== Fix 2: Replace .dialog-content with #dialog-body in all 12 tool handlers ==="
13
+ sed -i.bak "s/document.querySelector('.dialog-content')/document.getElementById('dialog-body')/g" app/index.html
14
+ rm -f app/index.html.bak
15
+
16
+ echo "=== Fix 3: Remove demo geometry (box + cylinder) ==="
17
+ # Remove the demo geometry block between "Demo geometry" and "Ground plane"
18
+ python3 -c "
19
+ import re
20
+ with open('app/index.html', 'r') as f:
21
+ content = f.read()
22
+
23
+ # Remove demo geometry block
24
+ content = re.sub(
25
+ r' // Demo geometry.*?scene\.add\(cyl\);\n\n // Ground plane',
26
+ ' // Ground plane',
27
+ content,
28
+ flags=re.DOTALL
29
+ )
30
+ with open('app/index.html', 'w') as f:
31
+ f.write(content)
32
+ print('Demo geometry removed')
33
+ "
34
+
35
+ echo "=== Fix 4: Add welcome splash panel ==="
36
+ python3 -c "
37
+ with open('app/index.html', 'r') as f:
38
+ content = f.read()
39
+
40
+ splash = '''<!-- Welcome Splash Screen -->
41
+ <div id=\"welcome-panel\" style=\"position:fixed;top:0;left:0;right:0;bottom:0;background:rgba(0,0,0,0.75);display:flex;align-items:center;justify-content:center;z-index:9999;\">
42
+ <div style=\"background:var(--bg-secondary,#252526);border:1px solid var(--border-color,#3c3c3c);border-radius:12px;padding:40px 48px;max-width:560px;width:90%;text-align:center;box-shadow:0 20px 60px rgba(0,0,0,0.5);\">
43
+ <div style=\"font-size:36px;margin-bottom:4px;\">
44
+ <span style=\"color:#0284C7;font-weight:700;\">cycle</span><span style=\"color:#e0e0e0;font-weight:300;\">CAD</span>
45
+ </div>
46
+ <div style=\"color:#888;font-size:13px;margin-bottom:28px;\">Agent-First Parametric 3D CAD Modeler</div>
47
+ <div style=\"display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;\">
48
+ <button data-action=\"new-sketch\" style=\"background:#0284C7;color:#fff;border:none;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;\" onmouseover=\"this.style.background='#0369a1'\" onmouseout=\"this.style.background='#0284C7'\">
49
+ <div style=\"font-size:24px;margin-bottom:6px;\">&#9999;&#65039;</div>
50
+ New Sketch
51
+ <div style=\"font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;\">Start with a 2D sketch</div>
52
+ </button>
53
+ <button data-action=\"import\" style=\"background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;\" onmouseover=\"this.style.background='#4b5563'\" onmouseout=\"this.style.background='#374151'\">
54
+ <div style=\"font-size:24px;margin-bottom:6px;\">&#128194;</div>
55
+ Open / Import
56
+ <div style=\"font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;\">STEP, STL, Inventor, JSON</div>
57
+ </button>
58
+ <button data-action=\"ai-generate\" style=\"background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;\" onmouseover=\"this.style.background='#4b5563'\" onmouseout=\"this.style.background='#374151'\">
59
+ <div style=\"font-size:24px;margin-bottom:6px;\">&#129302;</div>
60
+ Text-to-CAD
61
+ <div style=\"font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;\">Describe a part in English</div>
62
+ </button>
63
+ <button data-action=\"load-inventor\" style=\"background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;\" onmouseover=\"this.style.background='#4b5563'\" onmouseout=\"this.style.background='#374151'\">
64
+ <div style=\"font-size:24px;margin-bottom:6px;\">&#127981;</div>
65
+ Inventor Project
66
+ <div style=\"font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;\">Load .ipj / .ipt / .iam</div>
67
+ </button>
68
+ </div>
69
+ <div style=\"color:#666;font-size:11px;\">v0.9.0 &middot; 12 killer features &middot; 46 modules &middot; <a href=\"https://github.com/vvlars-cmd/cyclecad\" target=\"_blank\" style=\"color:#0284C7;text-decoration:none;\">GitHub</a></div>
70
+ </div>
71
+ </div>
72
+
73
+ '''
74
+
75
+ # Insert before modal dialogs
76
+ content = content.replace('<!-- Modal Dialogs -->', splash + '<!-- Modal Dialogs -->')
77
+ with open('app/index.html', 'w') as f:
78
+ f.write(content)
79
+ print('Welcome splash added')
80
+ "
81
+
82
+ echo "=== Fix 5: Add ViewCube to viewport ==="
83
+ python3 -c "
84
+ with open('app/index.html', 'r') as f:
85
+ content = f.read()
86
+
87
+ # Add position:relative and viewcube div to viewport-container
88
+ content = content.replace(
89
+ '<div id=\"viewport-container\">',
90
+ '<div id=\"viewport-container\" style=\"position:relative;\">'
91
+ )
92
+ content = content.replace(
93
+ '</canvas>\n </div>\n\n <!-- Right Panel',
94
+ '</canvas>\n <div id=\"viewcube\" style=\"position:absolute;top:12px;right:12px;width:90px;height:90px;pointer-events:auto;z-index:100;\"></div>\n </div>\n\n <!-- Right Panel'
95
+ )
96
+
97
+ # Add ViewCube JS before animation loop
98
+ viewcube_js = ''' // ===== ViewCube =====
99
+ const vcContainer = document.getElementById('viewcube');
100
+ const vcScene = new THREE.Scene();
101
+ const vcCamera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
102
+ vcCamera.position.set(0, 0, 3.5);
103
+ const vcRenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
104
+ vcRenderer.setSize(90, 90);
105
+ vcRenderer.setPixelRatio(window.devicePixelRatio);
106
+ vcContainer.appendChild(vcRenderer.domElement);
107
+ const faceLabels = ['Right', 'Left', 'Top', 'Bottom', 'Front', 'Back'];
108
+ const faceColors = [0x3b82f6, 0x3b82f6, 0x10b981, 0x10b981, 0x0284C7, 0x0284C7];
109
+ const cubeMats = faceLabels.map((label, i) => {
110
+ const canvas = document.createElement('canvas');
111
+ canvas.width = 128; canvas.height = 128;
112
+ const ctx = canvas.getContext('2d');
113
+ ctx.fillStyle = '#' + faceColors[i].toString(16).padStart(6, '0');
114
+ ctx.fillRect(0, 0, 128, 128);
115
+ ctx.strokeStyle = '#1e293b'; ctx.lineWidth = 3; ctx.strokeRect(1, 1, 126, 126);
116
+ ctx.fillStyle = '#fff'; ctx.font = 'bold 22px Arial'; ctx.textAlign = 'center'; ctx.textBaseline = 'middle';
117
+ ctx.fillText(label, 64, 64);
118
+ return new THREE.MeshBasicMaterial({ map: new THREE.CanvasTexture(canvas) });
119
+ });
120
+ const vcCube = new THREE.Mesh(new THREE.BoxGeometry(1.6, 1.6, 1.6), cubeMats);
121
+ vcScene.add(vcCube);
122
+ const axLen = 1.3;
123
+ const axMat = (c) => new THREE.LineBasicMaterial({ color: c });
124
+ const axGeo = (v) => new THREE.BufferGeometry().setFromPoints([new THREE.Vector3(), v]);
125
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(axLen, 0, 0)), axMat(0xff4444)));
126
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(0, axLen, 0)), axMat(0x44ff44)));
127
+ vcScene.add(new THREE.Line(axGeo(new THREE.Vector3(0, 0, axLen)), axMat(0x4444ff)));
128
+ const vcViews = { Right:{x:3,y:0,z:0}, Left:{x:-3,y:0,z:0}, Top:{x:0,y:3,z:0}, Bottom:{x:0,y:-3,z:0}, Front:{x:0,y:0,z:3}, Back:{x:0,y:0,z:-3} };
129
+ const vcRay = new THREE.Raycaster(); const vcMouse = new THREE.Vector2();
130
+ vcRenderer.domElement.addEventListener('click', (e) => {
131
+ const rect = vcRenderer.domElement.getBoundingClientRect();
132
+ vcMouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
133
+ vcMouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
134
+ vcRay.setFromCamera(vcMouse, vcCamera);
135
+ const hits = vcRay.intersectObject(vcCube);
136
+ if (hits.length > 0) {
137
+ const v = vcViews[faceLabels[hits[0].face.materialIndex]];
138
+ if (v) { const s = camera.position.length() / 3; camera.position.set(v.x*s, v.y*s, v.z*s); camera.lookAt(controls.target); controls.update(); }
139
+ }
140
+ });
141
+ vcRenderer.domElement.style.cursor = 'pointer';
142
+
143
+ '''
144
+
145
+ content = content.replace(
146
+ ' // Animation loop\n function animate() {\n requestAnimationFrame(animate);\n controls.update();\n renderer.render(scene, camera);\n }',
147
+ viewcube_js + ' // Animation loop\n function animate() {\n requestAnimationFrame(animate);\n controls.update();\n renderer.render(scene, camera);\n vcCube.quaternion.copy(camera.quaternion).invert();\n vcRenderer.render(vcScene, vcCamera);\n }'
148
+ )
149
+
150
+ # Add welcome splash wiring before menu handler
151
+ splash_js = ''' // ===== Welcome Splash =====
152
+ const welcomePanel = document.getElementById('welcome-panel');
153
+ if (welcomePanel) {
154
+ const dismissWelcome = () => { welcomePanel.style.display = 'none'; };
155
+ const wb1 = welcomePanel.querySelector('[data-action=\"new-sketch\"]');
156
+ if (wb1) wb1.onclick = () => { dismissWelcome(); handleMenuAction('sketch-new'); };
157
+ const wb2 = welcomePanel.querySelector('[data-action=\"import\"]');
158
+ if (wb2) wb2.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
159
+ const wb3 = welcomePanel.querySelector('[data-action=\"ai-generate\"]');
160
+ if (wb3) wb3.onclick = () => { dismissWelcome(); handleMenuAction('tools-text-to-cad'); };
161
+ const wb4 = welcomePanel.querySelector('[data-action=\"load-inventor\"]');
162
+ if (wb4) wb4.onclick = () => { dismissWelcome(); handleMenuAction('file-import'); };
163
+ }
164
+
165
+ '''
166
+
167
+ content = content.replace(' // ===== Menu Actions Handler =====', splash_js + ' // ===== Menu Actions Handler =====')
168
+
169
+ with open('app/index.html', 'w') as f:
170
+ f.write(content)
171
+ print('ViewCube + welcome wiring added')
172
+ "
173
+
174
+ echo ""
175
+ echo "=== All fixes applied! ==="
176
+ echo ""
177
+ echo "Now run:"
178
+ echo " git add app/index.html app/js/modules/text-to-cad.js"
179
+ echo " git commit -m 'Fix: welcome splash, ViewCube, dialog selectors, remove demo geometry'"
180
+ echo " git push origin main"
181
+ echo " npm version patch && npm publish"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyclecad",
3
- "version": "3.9.1",
3
+ "version": "3.9.2",
4
4
  "description": "Browser-based parametric 3D CAD modeler with AI-powered tools, native Inventor file parsing, and smart assembly management. No install required.",
5
5
  "main": "index.html",
6
6
  "bin": {