cyclecad 1.0.2 → 1.0.4

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 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.1</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.4</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');
@@ -4008,7 +4011,13 @@
4008
4011
  });
4009
4012
 
4010
4013
  // ========== New Module Toolbar Buttons ==========
4011
- function toggleModulePanel(moduleKey, panelId) {
4014
+ // Map of ES modules that need dynamic import to initialize
4015
+ const _moduleImportMap = {
4016
+ copilot: { path: './js/ai-copilot.js', initFn: 'initCopilot' },
4017
+ materials: { path: './js/material-library.js', initFn: 'initMaterialLibrary' },
4018
+ };
4019
+
4020
+ async function toggleModulePanel(moduleKey, panelId) {
4012
4021
  let panel = document.getElementById(panelId);
4013
4022
  if (panel) {
4014
4023
  panel.style.display = panel.style.display === 'none' ? 'flex' : 'none';
@@ -4017,7 +4026,7 @@
4017
4026
  // Create floating panel
4018
4027
  panel = document.createElement('div');
4019
4028
  panel.id = panelId;
4020
- panel.style.cssText = 'position:fixed;top:60px;right:320px;width:400px;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);';
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);';
4021
4030
 
4022
4031
  const header = document.createElement('div');
4023
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;';
@@ -4049,12 +4058,24 @@
4049
4058
  const body = document.createElement('div');
4050
4059
  body.style.cssText = 'padding:14px;flex:1;min-height:0;overflow-y:auto;font-size:12px;';
4051
4060
 
4052
- const mod = window.cycleCAD?.[moduleKey];
4061
+ // Try dynamic import for ES modules that export init functions
4062
+ let mod = window.cycleCAD?.[moduleKey];
4063
+ if (!mod && _moduleImportMap[moduleKey]) {
4064
+ try {
4065
+ const m = await import(_moduleImportMap[moduleKey].path);
4066
+ if (m[_moduleImportMap[moduleKey].initFn]) {
4067
+ m[_moduleImportMap[moduleKey].initFn](body);
4068
+ panel.appendChild(body);
4069
+ document.body.appendChild(panel);
4070
+ return;
4071
+ }
4072
+ } catch(e) { console.warn('[Module Import]', moduleKey, e); }
4073
+ mod = window.cycleCAD?.[moduleKey]; // re-check after import
4074
+ }
4075
+
4053
4076
  if (mod?.init) {
4054
- // Use init() which sets innerHTML AND attaches event listeners
4055
4077
  mod.init(body);
4056
4078
  } else if (mod?.getUI) {
4057
- // Strip position:fixed and z-index from module HTML — our wrapper handles positioning
4058
4079
  let html = mod.getUI();
4059
4080
  html = html.replace(/position:\s*fixed;?/gi, 'position:relative;')
4060
4081
  .replace(/z-index:\s*\d+;?/gi, '')
@@ -4063,18 +4084,13 @@
4063
4084
  .replace(/height:\s*calc\([^)]+\);?/gi, 'height:auto;')
4064
4085
  .replace(/width:\s*400px;?/gi, 'width:100%;');
4065
4086
  body.innerHTML = html;
4066
- // Wire up tab switching for modules that use .tab-btn
4067
4087
  body.querySelectorAll('.tab-btn').forEach(btn => {
4068
4088
  btn.addEventListener('click', () => {
4069
4089
  const tab = btn.dataset.tab;
4070
4090
  body.querySelectorAll('.tab-btn').forEach(b => {
4071
- b.style.color = '#888';
4072
- b.style.borderBottom = 'none';
4073
- b.classList.remove('active');
4091
+ b.style.color = '#888'; b.style.borderBottom = 'none'; b.classList.remove('active');
4074
4092
  });
4075
- btn.style.color = '#58a6ff';
4076
- btn.style.borderBottom = '2px solid #58a6ff';
4077
- btn.classList.add('active');
4093
+ btn.style.color = '#58a6ff'; btn.style.borderBottom = '2px solid #58a6ff'; btn.classList.add('active');
4078
4094
  body.querySelectorAll('.tab-content').forEach(c => c.style.display = 'none');
4079
4095
  const target = body.querySelector(`.tab-content.${tab}`);
4080
4096
  if (target) target.style.display = 'block';
@@ -4083,6 +4099,12 @@
4083
4099
  } else if (mod?.getStatus) {
4084
4100
  const status = mod.getStatus();
4085
4101
  body.innerHTML = `<div style="color:var(--text-secondary);line-height:1.8;">${JSON.stringify(status, null, 2).replace(/\n/g, '<br>')}</div>`;
4102
+ } else if (mod?.analyze || mod?.generate || mod?.search || mod?.execute) {
4103
+ // Module has methods but no UI — build a quick command panel
4104
+ const methods = Object.keys(mod).filter(k => typeof mod[k] === 'function');
4105
+ body.innerHTML = `<div style="margin-bottom:12px;"><input id="_mod_input_${moduleKey}" placeholder="Enter command or query..." style="width:100%;padding:8px 10px;border:1px solid var(--border-color);border-radius:5px;background:var(--bg-primary);color:var(--text-primary);font-size:12px;box-sizing:border-box;"></div>
4106
+ <div style="display:flex;flex-wrap:wrap;gap:6px;margin-bottom:12px;">${methods.slice(0,8).map(m => `<button onclick="(async()=>{const r=await window.cycleCAD.${moduleKey}.${m}(document.getElementById('_mod_input_${moduleKey}').value);document.getElementById('_mod_out_${moduleKey}').textContent=JSON.stringify(r,null,2)})()" style="padding:4px 10px;border-radius:4px;border:1px solid var(--border-color);background:var(--bg-tertiary);color:var(--text-primary);cursor:pointer;font-size:11px;">${m}()</button>`).join('')}</div>
4107
+ <pre id="_mod_out_${moduleKey}" style="background:var(--bg-primary);border:1px solid var(--border-color);border-radius:5px;padding:10px;font-size:11px;color:var(--text-secondary);white-space:pre-wrap;max-height:300px;overflow-y:auto;margin:0;">Ready. Click a function above.</pre>`;
4086
4108
  } else {
4087
4109
  body.innerHTML = `<div style="color:var(--accent-green);padding:12px;text-align:center;"><p style="font-weight:600;margin-bottom:8px;">Module Loaded</p><p style="color:var(--text-secondary);">Access via console:<br><code style="background:var(--bg-tertiary);padding:2px 6px;border-radius:3px;">window.cycleCAD.${moduleKey}</code></p></div>`;
4088
4110
  }
@@ -5221,6 +5243,6 @@
5221
5243
  </div>
5222
5244
  </div>
5223
5245
 
5224
- <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.1</span>
5246
+ <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.4</span>
5225
5247
  </body>
5226
5248
  </html>
@@ -938,20 +938,29 @@ export async function executeTextCommand(prompt) {
938
938
  return { ok: false };
939
939
  }
940
940
 
941
- // Execute via Agent API
942
- if (window.cycleCAD && window.cycleCAD.execute) {
943
- const results = [];
944
- for (const cmd of commands) {
945
- const result = await window.cycleCAD.execute(cmd);
946
- results.push(result);
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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyclecad",
3
- "version": "1.0.2",
3
+ "version": "1.0.4",
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": {