cyclecad 3.9.3 → 3.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 +165 -1
- package/app/index.html +221 -52
- package/package.json +1 -1
package/CLAUDE.md
CHANGED
|
@@ -953,5 +953,169 @@ rm -f ~/explodeview/.git/index.lock ~/explodeview/.git/HEAD.lock && cd ~/explode
|
|
|
953
953
|
- [ ] Build Photo-to-CAD Reverse Engineering
|
|
954
954
|
- [ ] Build Instant Manufacturability Feedback
|
|
955
955
|
|
|
956
|
+
## Session 2026-04-01 — Critical Bug Fixes + Sketch Wiring + cycleWASH Prospects
|
|
957
|
+
|
|
958
|
+
### Problem Evolution (Chronological)
|
|
959
|
+
1. **cycleWASH prospect spreadsheet** — Built `cycleWASH-NRW-Prospects.xlsx` with 36 leads (bike shops/rentals in NRW), email templates (DE+EN), follow-up tracker. Created with openpyxl, 3 sheets, color-coded priorities.
|
|
960
|
+
2. **TextToCAD syntax error** — `app/js/modules/text-to-cad.js` line 215 had missing `(` after `if` in regex test. Fixed, pushed.
|
|
961
|
+
3. **Splash screen missing** — App launched showing demo geometry (blue box + green cylinder) instead of welcome splash. Built splash with 4 buttons (New Sketch, Open/Import, Text-to-CAD, Inventor Project).
|
|
962
|
+
4. **Splash buttons not responding (3 attempts)** — Root cause: `<script type="module">` creates its own scope. Functions inside cannot be called from inline HTML `onclick`. Solution: regular `<script>` IIFE with `addEventListener` on `id`-based buttons.
|
|
963
|
+
5. **VM mount is copy-on-write** — CRITICAL LESSON: Files edited via Edit tool exist only in VM overlay, NOT on user's Mac. `git status` on Mac shows "nothing to commit". Only reliable delivery: `write_clipboard` → user pastes in Terminal.
|
|
964
|
+
6. **Dialog selectors wrong** — All 12 Tools menu handlers used `.dialog-content` (doesn't exist). Correct selector: `#dialog-body`. Fixed via clipboard Python command.
|
|
965
|
+
7. **Demo geometry removal** — Removed hardcoded `BoxGeometry(60,40,80)` + `CylinderGeometry(20,20,60)`.
|
|
966
|
+
8. **ViewCube click-only** — Only had click-to-snap-to-face. Added drag-to-rotate with pointer events (pointerdown/pointermove/pointerup), spherical coordinate rotation of main camera. Enlarged 90px → 120px.
|
|
967
|
+
9. **Hard Reset button missing** — Added to Help menu (red text). Clears Service Workers, Cache API, localStorage, sessionStorage, IndexedDB. Works in Safari/Chrome/Firefox.
|
|
968
|
+
10. **GitHub Pages CDN cache** — All fixes deployed to GitHub but Pages CDN served stale version. Added `<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">` to prevent future caching.
|
|
969
|
+
11. **Sketch tools not wired** — Sketch menu items (Line/Circle/Rectangle/Arc/Polyline) existed in UI but `handleMenuAction` only showed toasts. `sketch.js` module was never imported. Wired all tools with auto-enter sketch mode on XY plane.
|
|
970
|
+
12. **CRITICAL: Wrong import names killed entire module** — Imported `entitiesToShape` and `getSketchEntities` from `sketch.js`, but actual exports are `entitiesToGeometry` and `getEntities`. This silently crashed the ENTIRE `<script type="module">` block — meaning NO app logic worked (no buttons, no menus, no ViewCube, nothing). Fixed import names.
|
|
971
|
+
|
|
972
|
+
### Key Technical Discoveries
|
|
973
|
+
|
|
974
|
+
**ES Module Silent Failures:**
|
|
975
|
+
When an `import { wrongName } from './module.js'` fails, the entire `<script type="module">` block dies silently — NO console error in Chrome. The page renders HTML/CSS normally but zero JS functionality works. Diagnosis: check `window._scene` (set by module script) — if undefined, module crashed.
|
|
976
|
+
|
|
977
|
+
**How to diagnose:**
|
|
978
|
+
```javascript
|
|
979
|
+
// Dynamic import catches the real error
|
|
980
|
+
import('./js/sketch.js').then(m => console.log(Object.keys(m))).catch(e => console.error(e));
|
|
981
|
+
```
|
|
982
|
+
|
|
983
|
+
**sketch.js actual exports (v0.9.0):**
|
|
984
|
+
`canvasToWorld, clearSketch, endSketch, entitiesToGeometry, getEntities, setGridSize, setSnapEnabled, setTool, startSketch, undo, worldToCanvas`
|
|
985
|
+
|
|
986
|
+
**VM ↔ Mac File Delivery:**
|
|
987
|
+
- Edit tool → VM overlay only (Mac never sees changes)
|
|
988
|
+
- `write_clipboard` → user pastes in Terminal → changes on Mac → git push works
|
|
989
|
+
- Always use Python heredoc scripts for complex multi-line fixes
|
|
990
|
+
- Pattern: `cd ~/cyclecad && python3 << 'PYEOF'\n...\nPYEOF\n&& git add ... && git commit ... && git push`
|
|
991
|
+
|
|
992
|
+
**GitHub Pages Cache:**
|
|
993
|
+
- Raw GitHub (`raw.githubusercontent.com`) updates instantly
|
|
994
|
+
- GitHub Pages CDN (`cyclecad.com`) can lag 5-10 minutes
|
|
995
|
+
- Cache-busting: `?bust=timestamp` in URL forces fresh fetch
|
|
996
|
+
- Meta tags help browser cache but NOT CDN cache
|
|
997
|
+
- Check source of truth: `https://raw.githubusercontent.com/vvlars-cmd/cyclecad/main/app/index.html`
|
|
998
|
+
|
|
999
|
+
### What Was Built/Fixed This Session
|
|
1000
|
+
|
|
1001
|
+
| File | Action | What |
|
|
1002
|
+
|------|--------|------|
|
|
1003
|
+
| `cycleWASH-NRW-Prospects.xlsx` | NEW | 36 prospects, email templates DE+EN, follow-up tracker |
|
|
1004
|
+
| `app/js/modules/text-to-cad.js` | FIXED | Missing `(` after `if` on line 215 |
|
|
1005
|
+
| `app/index.html` | MAJOR | Splash screen, sketch wiring, ViewCube drag, Hard Reset, no-cache meta, dialog selector fixes, demo geometry removal |
|
|
1006
|
+
| `app/js/sketch.js` | EXISTING | Now imported and wired to menu actions |
|
|
1007
|
+
|
|
1008
|
+
### index.html Changes Detail
|
|
1009
|
+
|
|
1010
|
+
**Sketch module import (line ~1447):**
|
|
1011
|
+
```javascript
|
|
1012
|
+
import { startSketch, endSketch, setTool, getEntities, clearSketch, entitiesToGeometry } from './js/sketch.js';
|
|
1013
|
+
```
|
|
1014
|
+
|
|
1015
|
+
**Sketch menu actions wired (in handleMenuAction switch):**
|
|
1016
|
+
- `sketch-new` → `startSketch('XY', camera, controls)`, shows sketch tools toolbar, activates Sketch tab
|
|
1017
|
+
- `sketch-line` → auto-enters sketch mode + `setTool('line')`
|
|
1018
|
+
- `sketch-circle` → auto-enters sketch mode + `setTool('circle')`
|
|
1019
|
+
- `sketch-rectangle` → auto-enters sketch mode + `setTool('rectangle')`
|
|
1020
|
+
- `sketch-arc` → auto-enters sketch mode + `setTool('arc')`
|
|
1021
|
+
- `sketch-polyline` → auto-enters sketch mode + `setTool('polyline')`
|
|
1022
|
+
- `sketch-finish` → `endSketch()`, converts entities to Three.js wireframe, adds to feature tree
|
|
1023
|
+
|
|
1024
|
+
**ViewCube (line ~1558):**
|
|
1025
|
+
- Pointer events for drag-to-rotate (uses THREE.Spherical for orbit)
|
|
1026
|
+
- Click-to-snap still works (detects drag vs click via `vcDragMoved` flag)
|
|
1027
|
+
- 120px size, grab/grabbing cursors, `touchAction: none` for Safari
|
|
1028
|
+
|
|
1029
|
+
**Hard Reset (Help menu):**
|
|
1030
|
+
- `data-action="help-hard-reset"` with `style="color:#ef4444"`
|
|
1031
|
+
- Handler clears: Service Workers, Cache API, localStorage, sessionStorage, IndexedDB
|
|
1032
|
+
- Reloads with `?reset=timestamp` cache buster
|
|
1033
|
+
|
|
1034
|
+
**Workspace tab wiring:**
|
|
1035
|
+
- Clicking "Sketch" tab auto-enters sketch mode
|
|
1036
|
+
- Clicking any other tab auto-finishes sketch and returns to that workspace
|
|
1037
|
+
|
|
1038
|
+
### Bugs Still Present (Known Issues)
|
|
1039
|
+
|
|
1040
|
+
1. **Version badge hardcoded** — Status bar shows `v0.9.0` but npm is at different version. Need to dynamically read from package.json or update on each publish.
|
|
1041
|
+
2. **Sketch drawing untested end-to-end** — Import fix just pushed. Need to verify: click Circle → click center in viewport → drag radius → see circle drawn on 2D canvas overlay.
|
|
1042
|
+
3. **ViewCube not visible sometimes** — The 120px ViewCube renders in top-right of `#viewport-container`. If the container has wrong dimensions or the renderer size doesn't match, it may not show.
|
|
1043
|
+
4. **Three.js scene renders black/empty** — No demo geometry anymore. Until user draws something, viewport shows only grid lines (which may be hard to see on dark background).
|
|
1044
|
+
5. **Safari private window caching** — Even with no-cache meta tags, Safari aggressively caches. Users must use `?bust=xxx` or Cmd+Shift+R.
|
|
1045
|
+
|
|
1046
|
+
### Git State
|
|
1047
|
+
- All changes committed and pushed to `main` branch
|
|
1048
|
+
- Latest commits:
|
|
1049
|
+
- "Fix sketch.js import names (entitiesToShape->entitiesToGeometry)" ← **PENDING USER RUN** (clipboard copied)
|
|
1050
|
+
- "Wire sketch tools (line/circle/rect/arc) + no-cache meta tags"
|
|
1051
|
+
- "Fix ViewCube drag rotation + add Hard Reset button to Help menu"
|
|
1052
|
+
- Various splash screen fixes
|
|
1053
|
+
|
|
1054
|
+
### npm State
|
|
1055
|
+
- **cyclecad**: Last published unknown version. User needs to run `cd ~/cyclecad && npm version patch && npm publish`
|
|
1056
|
+
- **explodeview**: v1.0.18 bumped locally
|
|
1057
|
+
|
|
1058
|
+
### Collaboration Pattern Refinements (This Session)
|
|
1059
|
+
|
|
1060
|
+
| Pattern | Detail |
|
|
1061
|
+
|---------|--------|
|
|
1062
|
+
| **Clipboard delivery** | ALL code changes must go via `write_clipboard` → user pastes in Terminal. VM edits NEVER reach Mac. |
|
|
1063
|
+
| **Python heredoc scripts** | Best format for complex fixes: `python3 << 'PYEOF'\n...\nPYEOF` avoids quoting issues |
|
|
1064
|
+
| **Always verify on GitHub raw** | Before blaming "fix didn't work", check `raw.githubusercontent.com` to confirm push succeeded |
|
|
1065
|
+
| **Module import debugging** | Wrong import names silently kill entire module. Always verify with `import('./file.js').then(m => Object.keys(m))` |
|
|
1066
|
+
| **Cache frustration** | User gets frustrated when fixes are pushed but browser shows old version. Always provide cache-busting URL |
|
|
1067
|
+
| **Short messages = action needed** | "npm" = run npm publish. "done" = command was pasted and run. Don't ask, just provide the command. |
|
|
1068
|
+
| **User tests in Safari private** | Sachin tests in Safari private window. This has the most aggressive caching. Always consider Safari. |
|
|
1069
|
+
|
|
1070
|
+
### Critical Architecture Notes
|
|
1071
|
+
|
|
1072
|
+
**app/index.html structure:**
|
|
1073
|
+
```
|
|
1074
|
+
<head> — meta tags, CSS styles
|
|
1075
|
+
<div id="menu-bar"> — File/Edit/Sketch/Solid/Surface/Assembly/Drawing/Render/Animation/Inspect/Tools/Help menus
|
|
1076
|
+
<div id="container"> — Main layout
|
|
1077
|
+
<div id="viewport-container"> — Three.js canvas + ViewCube
|
|
1078
|
+
<div id="left-panel"> — Model tree / Assembly / Search tabs
|
|
1079
|
+
<div id="right-panel"> — Properties / Parameters / Material tabs
|
|
1080
|
+
<div id="welcome-panel"> — Splash screen (z-index: 9999)
|
|
1081
|
+
<script> IIFE — Splash button wiring (MUST be regular script, NOT module)
|
|
1082
|
+
<div id="dialog-overlay"> — Modal dialogs (z-index: 10000)
|
|
1083
|
+
<script type="module"> — ALL app logic:
|
|
1084
|
+
- Three.js imports + scene setup
|
|
1085
|
+
- sketch.js import
|
|
1086
|
+
- killer-features.js import
|
|
1087
|
+
- Camera, renderer, lights, grid
|
|
1088
|
+
- ViewCube (separate scene + renderer)
|
|
1089
|
+
- Animation loop
|
|
1090
|
+
- handleMenuAction() switch — ALL menu/toolbar actions
|
|
1091
|
+
- Workspace tab switching
|
|
1092
|
+
- Keyboard shortcuts
|
|
1093
|
+
- Event delegation for menu/toolbar clicks
|
|
1094
|
+
```
|
|
1095
|
+
|
|
1096
|
+
**z-index hierarchy:**
|
|
1097
|
+
- `dialog-overlay`: 10000
|
|
1098
|
+
- `welcome-panel`: 9999
|
|
1099
|
+
- `toast-container`: 9999
|
|
1100
|
+
- ViewCube: 100
|
|
1101
|
+
- Sketch canvas: 30
|
|
1102
|
+
|
|
1103
|
+
### Pending Tasks (Updated 2026-04-01)
|
|
1104
|
+
- [ ] **IMMEDIATE**: User needs to paste clipboard command fixing import names (entitiesToShape→entitiesToGeometry)
|
|
1105
|
+
- [ ] npm publish (user runs `cd ~/cyclecad && npm version patch && npm publish`)
|
|
1106
|
+
- [ ] Verify sketch circle drawing works end-to-end after import fix
|
|
1107
|
+
- [ ] Test ViewCube drag in Safari
|
|
1108
|
+
- [ ] Test Hard Reset button in Safari
|
|
1109
|
+
- [ ] Update version badge to read dynamically from package.json
|
|
1110
|
+
- [ ] Wire splash "New Sketch" button to actually trigger sketch-new action (currently only dismisses)
|
|
1111
|
+
- [ ] Wire splash "Open/Import" button to trigger file-import action
|
|
1112
|
+
- [ ] Wire splash "Text-to-CAD" button to trigger text-to-cad dialog
|
|
1113
|
+
- [ ] Wire splash "Inventor Project" button to trigger inventor import
|
|
1114
|
+
- [ ] ExplodeView route redirects push
|
|
1115
|
+
- [ ] Run test agents in Chrome and fix failures
|
|
1116
|
+
- [ ] Test STEP import with OpenCascade.js WASM
|
|
1117
|
+
- [ ] Build Text-to-CAD with Live Preview
|
|
1118
|
+
- [ ] Build Photo-to-CAD Reverse Engineering
|
|
1119
|
+
|
|
956
1120
|
# currentDate
|
|
957
|
-
Today's date is 2026-
|
|
1121
|
+
Today's date is 2026-04-01.
|
package/app/index.html
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
<!DOCTYPE html>
|
|
2
2
|
<html lang="en">
|
|
3
3
|
<head>
|
|
4
|
+
<meta http-equiv="Cache-Control" content="no-cache, no-store, must-revalidate">
|
|
5
|
+
<meta http-equiv="Pragma" content="no-cache">
|
|
6
|
+
<meta http-equiv="Expires" content="0">
|
|
4
7
|
<meta charset="UTF-8">
|
|
5
8
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
9
|
<title>cycleCAD - Fusion 360 Clone - Parametric 3D CAD Modeler</title>
|
|
@@ -989,6 +992,8 @@
|
|
|
989
992
|
<button class="menu-item-link" data-action="help-docs">Documentation</button>
|
|
990
993
|
<button class="menu-item-link" data-action="help-shortcuts">Keyboard Shortcuts (?)</button>
|
|
991
994
|
<button class="menu-item-link" data-action="help-about">About cycleCAD</button>
|
|
995
|
+
<div style="border-top:1px solid #444;margin:4px 0;"></div>
|
|
996
|
+
<button class="menu-item-link" data-action="help-hard-reset" style="color:#ef4444;">Hard Reset (Clear Cache)</button>
|
|
992
997
|
</div>
|
|
993
998
|
</div>
|
|
994
999
|
</div>
|
|
@@ -1148,7 +1153,7 @@
|
|
|
1148
1153
|
<div id="viewport-container" style="position:relative;">
|
|
1149
1154
|
<canvas id="viewport"></canvas>
|
|
1150
1155
|
<!-- ViewCube -->
|
|
1151
|
-
<div id="viewcube" style="position:absolute;top:12px;right:12px;width:
|
|
1156
|
+
<div id="viewcube" style="position:absolute;top:12px;right:12px;width:120px;height:120px;pointer-events:auto;z-index:100;"></div>
|
|
1152
1157
|
</div>
|
|
1153
1158
|
|
|
1154
1159
|
<!-- Right Panel (Properties) -->
|
|
@@ -1308,40 +1313,12 @@
|
|
|
1308
1313
|
|
|
1309
1314
|
</div>
|
|
1310
1315
|
|
|
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:#252526;border:1px solid #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 onclick="window._dismissSplash('sketch')" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;">
|
|
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 onclick="window._dismissSplash('import')" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;">
|
|
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 onclick="window._dismissSplash('textcad')" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;">
|
|
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 onclick="window._dismissSplash('inventor')" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;">
|
|
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 · 12 killer features · 46 modules · <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="color:#0284C7;text-decoration:none;">GitHub</a></div>
|
|
1341
|
-
</div>
|
|
1342
|
-
</div>
|
|
1343
1316
|
<script>
|
|
1344
1317
|
window._dismissSplash = function(action) {
|
|
1318
|
+
// Update splash version from status bar
|
|
1319
|
+
var sv = document.getElementById('splash-version');
|
|
1320
|
+
var sb = document.querySelector('[class*="status"]');
|
|
1321
|
+
if (sv && sb) { var m = sb.textContent.match(/v[\d.]+/); if (m) sv.textContent = m[0]; }
|
|
1345
1322
|
document.getElementById('welcome-panel').style.display = 'none';
|
|
1346
1323
|
if (action === 'sketch') {
|
|
1347
1324
|
// Will be handled by module script once loaded
|
|
@@ -1354,6 +1331,30 @@ window._dismissSplash = function(action) {
|
|
|
1354
1331
|
};
|
|
1355
1332
|
</script>
|
|
1356
1333
|
|
|
1334
|
+
<!-- Welcome Splash Screen -->
|
|
1335
|
+
<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;">
|
|
1336
|
+
<div style="background:#252526;border:1px solid #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);">
|
|
1337
|
+
<div style="font-size:36px;margin-bottom:4px;"><span style="color:#0284C7;font-weight:700;">cycle</span><span style="color:#e0e0e0;font-weight:300;">CAD</span></div>
|
|
1338
|
+
<div style="color:#888;font-size:13px;margin-bottom:28px;">Agent-First Parametric 3D CAD Modeler</div>
|
|
1339
|
+
<div style="display:grid;grid-template-columns:1fr 1fr;gap:12px;margin-bottom:20px;">
|
|
1340
|
+
<button id="splash-sketch" style="background:#0284C7;color:#fff;border:none;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;"><div style="font-size:24px;margin-bottom:6px;">✏️</div>New Sketch<div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Start with a 2D sketch</div></button>
|
|
1341
|
+
<button id="splash-import" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;"><div style="font-size:24px;margin-bottom:6px;">📂</div>Open / Import<div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">STEP, STL, Inventor, JSON</div></button>
|
|
1342
|
+
<button id="splash-textcad" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;"><div style="font-size:24px;margin-bottom:6px;">🤖</div>Text-to-CAD<div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Describe a part in English</div></button>
|
|
1343
|
+
<button id="splash-inventor" style="background:#374151;color:#fff;border:1px solid #4b5563;border-radius:8px;padding:18px 16px;cursor:pointer;font-size:14px;font-weight:600;"><div style="font-size:24px;margin-bottom:6px;">🏭</div>Inventor Project<div style="font-size:11px;font-weight:400;color:rgba(255,255,255,0.7);margin-top:4px;">Load .ipj / .ipt / .iam</div></button>
|
|
1344
|
+
</div>
|
|
1345
|
+
<div style="color:#666;font-size:11px;">12 killer features · 46 modules · <a href="https://github.com/vvlars-cmd/cyclecad" target="_blank" style="color:#0284C7;text-decoration:none;">GitHub</a></div>
|
|
1346
|
+
</div>
|
|
1347
|
+
</div>
|
|
1348
|
+
<script>
|
|
1349
|
+
(function() {
|
|
1350
|
+
function dismiss() { document.getElementById("welcome-panel").style.display = "none"; }
|
|
1351
|
+
document.getElementById("splash-sketch").addEventListener("click", dismiss);
|
|
1352
|
+
document.getElementById("splash-import").addEventListener("click", dismiss);
|
|
1353
|
+
document.getElementById("splash-textcad").addEventListener("click", dismiss);
|
|
1354
|
+
document.getElementById("splash-inventor").addEventListener("click", dismiss);
|
|
1355
|
+
})();
|
|
1356
|
+
</script>
|
|
1357
|
+
|
|
1357
1358
|
<!-- Modal Dialogs -->
|
|
1358
1359
|
<div id="dialog-overlay" class="modal-overlay">
|
|
1359
1360
|
<div class="modal-dialog">
|
|
@@ -1447,6 +1448,7 @@ window._dismissSplash = function(action) {
|
|
|
1447
1448
|
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
|
|
1448
1449
|
import { GridHelper } from 'three';
|
|
1449
1450
|
import { KillerFeatures } from './js/killer-features.js';
|
|
1451
|
+
import { startSketch, endSketch, setTool, getEntities, clearSketch, entitiesToGeometry } from './js/sketch.js';
|
|
1450
1452
|
|
|
1451
1453
|
// ===== Three.js Viewport Setup =====
|
|
1452
1454
|
const scene = new THREE.Scene();
|
|
@@ -1513,7 +1515,7 @@ window._dismissSplash = function(action) {
|
|
|
1513
1515
|
const vcCamera = new THREE.PerspectiveCamera(40, 1, 0.1, 100);
|
|
1514
1516
|
vcCamera.position.set(0, 0, 3.5);
|
|
1515
1517
|
const vcRenderer = new THREE.WebGLRenderer({ alpha: true, antialias: true });
|
|
1516
|
-
vcRenderer.setSize(
|
|
1518
|
+
vcRenderer.setSize(120, 120);
|
|
1517
1519
|
vcRenderer.setPixelRatio(window.devicePixelRatio);
|
|
1518
1520
|
vcContainer.appendChild(vcRenderer.domElement);
|
|
1519
1521
|
|
|
@@ -1559,26 +1561,63 @@ window._dismissSplash = function(action) {
|
|
|
1559
1561
|
};
|
|
1560
1562
|
const vcRay = new THREE.Raycaster();
|
|
1561
1563
|
const vcMouse = new THREE.Vector2();
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1564
|
+
// ViewCube drag-to-rotate + click-to-snap
|
|
1565
|
+
let vcDragging = false, vcDragStart = null, vcDragMoved = false;
|
|
1566
|
+
const vcEl = vcRenderer.domElement;
|
|
1567
|
+
vcEl.addEventListener('pointerdown', (e) => {
|
|
1568
|
+
vcDragging = true; vcDragMoved = false;
|
|
1569
|
+
vcDragStart = { x: e.clientX, y: e.clientY };
|
|
1570
|
+
vcEl.setPointerCapture(e.pointerId);
|
|
1571
|
+
e.preventDefault(); e.stopPropagation();
|
|
1572
|
+
});
|
|
1573
|
+
vcEl.addEventListener('pointermove', (e) => {
|
|
1574
|
+
if (!vcDragging || !vcDragStart) return;
|
|
1575
|
+
const dx = e.clientX - vcDragStart.x;
|
|
1576
|
+
const dy = e.clientY - vcDragStart.y;
|
|
1577
|
+
if (Math.abs(dx) > 2 || Math.abs(dy) > 2) vcDragMoved = true;
|
|
1578
|
+
if (!vcDragMoved) return;
|
|
1579
|
+
const speed = 0.01;
|
|
1580
|
+
const spherical = new THREE.Spherical().setFromVector3(
|
|
1581
|
+
camera.position.clone().sub(controls.target)
|
|
1582
|
+
);
|
|
1583
|
+
spherical.theta -= dx * speed;
|
|
1584
|
+
spherical.phi -= dy * speed;
|
|
1585
|
+
spherical.phi = Math.max(0.05, Math.min(Math.PI - 0.05, spherical.phi));
|
|
1586
|
+
camera.position.copy(
|
|
1587
|
+
new THREE.Vector3().setFromSpherical(spherical).add(controls.target)
|
|
1588
|
+
);
|
|
1589
|
+
camera.lookAt(controls.target);
|
|
1590
|
+
controls.update();
|
|
1591
|
+
vcDragStart = { x: e.clientX, y: e.clientY };
|
|
1592
|
+
e.preventDefault(); e.stopPropagation();
|
|
1593
|
+
});
|
|
1594
|
+
vcEl.addEventListener('pointerup', (e) => {
|
|
1595
|
+
if (vcDragging && !vcDragMoved) {
|
|
1596
|
+
const rect = vcEl.getBoundingClientRect();
|
|
1597
|
+
vcMouse.x = ((e.clientX - rect.left) / rect.width) * 2 - 1;
|
|
1598
|
+
vcMouse.y = -((e.clientY - rect.top) / rect.height) * 2 + 1;
|
|
1599
|
+
vcRay.setFromCamera(vcMouse, vcCamera);
|
|
1600
|
+
const hits = vcRay.intersectObject(vcCube);
|
|
1601
|
+
if (hits.length > 0) {
|
|
1602
|
+
const fi = hits[0].face.materialIndex;
|
|
1603
|
+
const label = faceLabels[fi];
|
|
1604
|
+
const v = vcViews[label];
|
|
1605
|
+
if (v) {
|
|
1606
|
+
const dist = camera.position.length();
|
|
1607
|
+
const s = dist / 3;
|
|
1608
|
+
camera.position.set(v.x * s, v.y * s, v.z * s);
|
|
1609
|
+
camera.lookAt(controls.target);
|
|
1610
|
+
controls.update();
|
|
1611
|
+
}
|
|
1578
1612
|
}
|
|
1579
1613
|
}
|
|
1614
|
+
vcDragging = false; vcDragStart = null; vcDragMoved = false;
|
|
1615
|
+
e.preventDefault(); e.stopPropagation();
|
|
1580
1616
|
});
|
|
1581
|
-
|
|
1617
|
+
vcEl.style.cursor = 'grab';
|
|
1618
|
+
vcEl.addEventListener('pointerdown', () => { vcEl.style.cursor = 'grabbing'; });
|
|
1619
|
+
vcEl.addEventListener('pointerup', () => { vcEl.style.cursor = 'grab'; });
|
|
1620
|
+
vcEl.style.touchAction = 'none';
|
|
1582
1621
|
|
|
1583
1622
|
// Animation loop
|
|
1584
1623
|
function animate() {
|
|
@@ -1615,6 +1654,23 @@ window._dismissSplash = function(action) {
|
|
|
1615
1654
|
// ===== DOM Queries =====
|
|
1616
1655
|
const menuBar = document.getElementById('menu-bar');
|
|
1617
1656
|
const workspaceTabs = document.querySelectorAll('.workspace-tab');
|
|
1657
|
+
// Wire workspace tabs to enter/exit sketch mode
|
|
1658
|
+
workspaceTabs.forEach(tab => {
|
|
1659
|
+
tab.addEventListener('click', () => {
|
|
1660
|
+
const ws = tab.getAttribute('data-workspace');
|
|
1661
|
+
workspaceTabs.forEach(t => t.classList.remove('active'));
|
|
1662
|
+
tab.classList.add('active');
|
|
1663
|
+
// Show/hide workspace-specific toolbars
|
|
1664
|
+
document.querySelectorAll('.workspace-tools').forEach(t => t.style.display = 'none');
|
|
1665
|
+
const wsTools = document.getElementById(ws + '-tools');
|
|
1666
|
+
if (wsTools) wsTools.style.display = 'flex';
|
|
1667
|
+
if (ws === 'sketch' && !appState.isSketchMode) {
|
|
1668
|
+
handleMenuAction('sketch-new');
|
|
1669
|
+
} else if (ws !== 'sketch' && appState.isSketchMode) {
|
|
1670
|
+
handleMenuAction('sketch-finish');
|
|
1671
|
+
}
|
|
1672
|
+
});
|
|
1673
|
+
});
|
|
1618
1674
|
const leftPanelTabs = document.querySelectorAll('.left-panel-tab');
|
|
1619
1675
|
const rightPanelTabs = document.querySelectorAll('.right-panel-tab');
|
|
1620
1676
|
const viewportContainer = document.getElementById('viewport-container');
|
|
@@ -1649,8 +1705,98 @@ window._dismissSplash = function(action) {
|
|
|
1649
1705
|
case 'edit-redo':
|
|
1650
1706
|
showToast('Redo', 'success');
|
|
1651
1707
|
break;
|
|
1708
|
+
case 'sketch-new':
|
|
1709
|
+
if (!appState.isSketchMode) {
|
|
1710
|
+
appState.isSketchMode = true;
|
|
1711
|
+
startSketch('XY', camera, controls);
|
|
1712
|
+
showToast('Sketch mode — draw on XY plane', 'success');
|
|
1713
|
+
// Show sketch tools, activate Sketch tab
|
|
1714
|
+
var st = document.getElementById('sketch-tools');
|
|
1715
|
+
if (st) st.style.display = 'flex';
|
|
1716
|
+
document.querySelectorAll('.workspace-tab').forEach(t => t.classList.remove('active'));
|
|
1717
|
+
var skTab = document.querySelector('[data-workspace="sketch"]');
|
|
1718
|
+
if (skTab) skTab.classList.add('active');
|
|
1719
|
+
}
|
|
1720
|
+
break;
|
|
1652
1721
|
case 'sketch-line':
|
|
1653
|
-
if (appState.isSketchMode)
|
|
1722
|
+
if (!appState.isSketchMode) { handleMenuAction('sketch-new'); }
|
|
1723
|
+
setTool('line');
|
|
1724
|
+
showToast('Line tool — click to place points', 'success');
|
|
1725
|
+
break;
|
|
1726
|
+
case 'sketch-rectangle':
|
|
1727
|
+
if (!appState.isSketchMode) { handleMenuAction('sketch-new'); }
|
|
1728
|
+
setTool('rectangle');
|
|
1729
|
+
showToast('Rectangle tool — click two corners', 'success');
|
|
1730
|
+
break;
|
|
1731
|
+
case 'sketch-circle':
|
|
1732
|
+
if (!appState.isSketchMode) { handleMenuAction('sketch-new'); }
|
|
1733
|
+
setTool('circle');
|
|
1734
|
+
showToast('Circle tool — click center, then radius', 'success');
|
|
1735
|
+
break;
|
|
1736
|
+
case 'sketch-arc':
|
|
1737
|
+
if (!appState.isSketchMode) { handleMenuAction('sketch-new'); }
|
|
1738
|
+
setTool('arc');
|
|
1739
|
+
showToast('Arc tool — click 3 points', 'success');
|
|
1740
|
+
break;
|
|
1741
|
+
case 'sketch-polyline':
|
|
1742
|
+
if (!appState.isSketchMode) { handleMenuAction('sketch-new'); }
|
|
1743
|
+
setTool('polyline');
|
|
1744
|
+
showToast('Polyline tool — click points, double-click to finish', 'success');
|
|
1745
|
+
break;
|
|
1746
|
+
case 'sketch-finish':
|
|
1747
|
+
if (appState.isSketchMode) {
|
|
1748
|
+
const entities = endSketch();
|
|
1749
|
+
appState.isSketchMode = false;
|
|
1750
|
+
// Hide sketch tools, back to Design tab
|
|
1751
|
+
var st2 = document.getElementById('sketch-tools');
|
|
1752
|
+
if (st2) st2.style.display = 'none';
|
|
1753
|
+
document.querySelectorAll('.workspace-tab').forEach(t => t.classList.remove('active'));
|
|
1754
|
+
var dTab = document.querySelector('[data-workspace="design"]');
|
|
1755
|
+
if (dTab) dTab.classList.add('active');
|
|
1756
|
+
if (entities.length > 0) {
|
|
1757
|
+
// Convert sketch entities to 3D wireframe in scene
|
|
1758
|
+
try {
|
|
1759
|
+
const shape = entitiesToGeometry(entities);
|
|
1760
|
+
if (shape) {
|
|
1761
|
+
const mat = new THREE.LineBasicMaterial({ color: 0x00ff00 });
|
|
1762
|
+
const edges = new THREE.EdgesGeometry(shape);
|
|
1763
|
+
const wireframe = new THREE.LineSegments(edges, mat);
|
|
1764
|
+
wireframe.name = 'Sketch' + (appState.featureTree.length + 1);
|
|
1765
|
+
scene.add(wireframe);
|
|
1766
|
+
appState.featureTree.push({ name: wireframe.name, type: 'Sketch', visible: true, mesh: wireframe });
|
|
1767
|
+
showToast('Sketch finished — ' + entities.length + ' entities', 'success');
|
|
1768
|
+
} else {
|
|
1769
|
+
// Just add lines directly
|
|
1770
|
+
const lineMat = new THREE.LineBasicMaterial({ color: 0x00ccff });
|
|
1771
|
+
entities.forEach((ent, idx) => {
|
|
1772
|
+
if (ent.points && ent.points.length >= 2) {
|
|
1773
|
+
const pts = ent.points.map(p => new THREE.Vector3(p.x, p.y, 0));
|
|
1774
|
+
if (ent.type === 'circle' && ent.center && ent.radius) {
|
|
1775
|
+
const curve = new THREE.EllipseCurve(ent.center.x, ent.center.y, ent.radius, ent.radius, 0, Math.PI * 2, false, 0);
|
|
1776
|
+
const cPts = curve.getPoints(64).map(p => new THREE.Vector3(p.x, p.y, 0));
|
|
1777
|
+
const geo = new THREE.BufferGeometry().setFromPoints(cPts);
|
|
1778
|
+
const line = new THREE.Line(geo, lineMat);
|
|
1779
|
+
line.name = 'SketchCircle' + (idx + 1);
|
|
1780
|
+
scene.add(line);
|
|
1781
|
+
} else {
|
|
1782
|
+
const geo = new THREE.BufferGeometry().setFromPoints(pts);
|
|
1783
|
+
const line = new THREE.Line(geo, lineMat);
|
|
1784
|
+
line.name = 'SketchLine' + (idx + 1);
|
|
1785
|
+
scene.add(line);
|
|
1786
|
+
}
|
|
1787
|
+
}
|
|
1788
|
+
});
|
|
1789
|
+
appState.featureTree.push({ name: 'Sketch' + (appState.featureTree.length + 1), type: 'Sketch', visible: true });
|
|
1790
|
+
showToast('Sketch finished — ' + entities.length + ' entities added', 'success');
|
|
1791
|
+
}
|
|
1792
|
+
} catch(e) {
|
|
1793
|
+
console.warn('Sketch to shape error:', e);
|
|
1794
|
+
showToast('Sketch finished (wireframe only)', 'warning');
|
|
1795
|
+
}
|
|
1796
|
+
} else {
|
|
1797
|
+
showToast('Empty sketch discarded', 'warning');
|
|
1798
|
+
}
|
|
1799
|
+
}
|
|
1654
1800
|
break;
|
|
1655
1801
|
case 'solid-extrude':
|
|
1656
1802
|
showToast('Extrude tool active', 'success');
|
|
@@ -1667,6 +1813,29 @@ window._dismissSplash = function(action) {
|
|
|
1667
1813
|
case 'help-docs':
|
|
1668
1814
|
window.open('https://cyclecad.com/docs', '_blank');
|
|
1669
1815
|
break;
|
|
1816
|
+
case 'help-hard-reset':
|
|
1817
|
+
if (confirm('This will clear ALL cached data and reload. Continue?')) {
|
|
1818
|
+
(async () => {
|
|
1819
|
+
try {
|
|
1820
|
+
if ('serviceWorker' in navigator) {
|
|
1821
|
+
const regs = await navigator.serviceWorker.getRegistrations();
|
|
1822
|
+
for (const r of regs) await r.unregister();
|
|
1823
|
+
}
|
|
1824
|
+
if ('caches' in window) {
|
|
1825
|
+
const keys = await caches.keys();
|
|
1826
|
+
for (const k of keys) await caches.delete(k);
|
|
1827
|
+
}
|
|
1828
|
+
try { localStorage.clear(); } catch(e) {}
|
|
1829
|
+
try { sessionStorage.clear(); } catch(e) {}
|
|
1830
|
+
if (indexedDB.databases) {
|
|
1831
|
+
const dbs = await indexedDB.databases();
|
|
1832
|
+
for (const db of dbs) indexedDB.deleteDatabase(db.name);
|
|
1833
|
+
}
|
|
1834
|
+
window.location.href = window.location.pathname + '?reset=' + Date.now();
|
|
1835
|
+
} catch(e) { window.location.reload(true); }
|
|
1836
|
+
})();
|
|
1837
|
+
}
|
|
1838
|
+
break;
|
|
1670
1839
|
case 'help-about':
|
|
1671
1840
|
showDialog('About cycleCAD', 'cycleCAD v0.9.0 - Fusion 360 Clone<br>Open-source parametric 3D CAD modeler<br><br>Built with Three.js, supporting STEP/IGES import, full parametric modeling, and AI-powered design assistance.');
|
|
1672
1841
|
break;
|
package/package.json
CHANGED