cyclecad 0.1.4 → 0.1.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cyclecad",
3
- "version": "0.1.4",
3
+ "version": "0.1.7",
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
  "scripts": {
@@ -1,233 +0,0 @@
1
- # DUO Inventor Project Manifest
2
-
3
- ## Overview
4
-
5
- A complete JSON manifest of the cycleWASH DUO Inventor project has been generated and is ready for browser-based loading without requiring File System Access API.
6
-
7
- **Files Generated:**
8
- - `/app/duo-manifest.json` — Complete project structure (313 KB)
9
- - `/app/duo-manifest-demo.html` — Interactive viewer (9.3 KB)
10
-
11
- ## Project Statistics
12
-
13
- | Metric | Value |
14
- |--------|-------|
15
- | **Total Files** | 474 |
16
- | **Parts (.ipt)** | 393 |
17
- | **Assemblies (.iam)** | 80 |
18
- | **Custom Parts** | 214 |
19
- | **Standard Parts (DIN/ISO)** | 51 |
20
- | **Vendor/Buyout Parts** | 128 |
21
- | **Main Assembly Size** | 9.07 MB |
22
-
23
- ## Manifest Structure
24
-
25
- ```json
26
- {
27
- "name": "DUO Durchgehend Inventor",
28
- "description": "cycleWASH DUO — Automatic Bicycle Washing Machine",
29
- "ipj": "D-ZBG-DUO-Anlage.ipj",
30
- "workspace": "Workspaces/Arbeitsbereich",
31
- "contentCenter": "Libraries/Content Center Files",
32
- "stats": {
33
- "total": 474,
34
- "parts": 393,
35
- "assemblies": 80,
36
- "custom": 214,
37
- "standard": 51,
38
- "vendor": 128
39
- },
40
- "tree": {
41
- "name": "DUO Durchgehend Inventor",
42
- "type": "folder",
43
- "children": [ /* hierarchical folder structure */ ]
44
- },
45
- "assemblies": [
46
- {
47
- "name": "D-ZBG-DUO-Anlage.iam",
48
- "path": "Workspaces/Arbeitsbereich/Zusatzoptionen/DUOdurch/D-ZBG-DUO-Anlage.iam",
49
- "size": 9500000,
50
- "isMain": true
51
- },
52
- /* ... 79 more assemblies ... */
53
- ],
54
- "parts": [
55
- {
56
- "name": "TrägerHöhe1.ipt",
57
- "path": "Workspaces/Arbeitsbereich/DUO Anlage/Gestell/TrägerHöhe1.ipt",
58
- "category": "custom",
59
- "size": 237568
60
- },
61
- /* ... 392 more parts ... */
62
- ]
63
- }
64
- ```
65
-
66
- ## Usage in Browser
67
-
68
- ### Load the Manifest
69
- ```javascript
70
- // Fetch the manifest in your app
71
- fetch('./duo-manifest.json')
72
- .then(r => r.json())
73
- .then(manifest => {
74
- console.log('Loaded', manifest.stats.parts, 'parts');
75
- console.log('Assemblies:', manifest.assemblies.length);
76
- });
77
- ```
78
-
79
- ### Query the Data
80
- ```javascript
81
- // Find all custom parts
82
- const customParts = manifest.parts.filter(p => p.category === 'custom');
83
-
84
- // Find the main assembly
85
- const mainAssembly = manifest.assemblies.find(a => a.isMain);
86
-
87
- // Get parts in a specific folder
88
- const frameparts = manifest.parts.filter(p =>
89
- p.path.includes('Gestell')
90
- );
91
-
92
- // Sort by size
93
- const largestParts = [...manifest.parts]
94
- .sort((a, b) => b.size - a.size)
95
- .slice(0, 10);
96
- ```
97
-
98
- ## File Categories
99
-
100
- ### Custom Parts (214)
101
- Parts designed for cycleWASH DUO, typically with "D-" prefix:
102
- - `D-TrägerHöhe1.ipt`
103
- - `D-Wanne v2.ipt`
104
- - `D-Deckblech.ipt`
105
-
106
- Location: `Workspaces/Arbeitsbereich/`
107
-
108
- ### Standard Parts (51)
109
- DIN/ISO standard hardware from Inventor's Content Center:
110
- - `ISO 4017 - M6 x 12 - A2.ipt` (hex cap screws)
111
- - `DIN 6912 - M6 x 10 - A2.ipt` (socket cap screws)
112
- - `DIN 912 - M3 x 8 - A2.ipt`
113
-
114
- Location: `Libraries/Content Center Files/de-DE/`
115
-
116
- ### Vendor Parts (128)
117
- Bought-in/standard components from suppliers:
118
- - igus linear guides
119
- - Interroll rollers
120
- - WEG electric motors
121
- - Rittal electrical enclosures
122
- - UNISTAR_2000B components
123
-
124
- Location: `Workspaces/Arbeitsbereich/Zukaufteile/`
125
-
126
- ## Largest Components
127
-
128
- | Part | Size | Category |
129
- |------|------|----------|
130
- | KD-161083.ipt | 21.5 MB | Vendor |
131
- | Rampe NX.ipt | 10.9 MB | Custom |
132
- | 502PU AND MOTOR.ipt | 10.9 MB | Vendor |
133
- | KD-158948.ipt | 9.3 MB | Vendor |
134
- | Große Ecke neuer Beschnitt alleine2.ipt | 5.0 MB | Custom |
135
-
136
- ## Main Assembly
137
-
138
- **Name:** D-ZBG-DUO-Anlage.iam
139
- **Path:** `Workspaces/Arbeitsbereich/Zusatzoptionen/DUOdurch/D-ZBG-DUO-Anlage.iam`
140
- **Size:** 9.07 MB
141
- **Components:** 47 (per parsing)
142
-
143
- ## Folder Structure
144
-
145
- ```
146
- DUO Durchgehend Inventor/
147
- ├── D-ZBG-DUO-Anlage.ipj (project file)
148
- ├── Libraries/
149
- │ └── Content Center Files/
150
- │ └── de-DE/
151
- │ ├── DIN 1587/
152
- │ ├── DIN 580/
153
- │ ├── DIN 6912/
154
- │ ├── DIN 7349 ees/
155
- │ ├── ISO 4017/
156
- │ ├── ISO 4762/
157
- │ └── ... (other DIN/ISO standards)
158
- └── Workspaces/
159
- └── Arbeitsbereich/
160
- ├── DUO Anlage/
161
- │ ├── Gestell/ (frame components)
162
- │ ├── Lenkerhalterung/ (handlebar holder)
163
- │ └── ... (other subsystems)
164
- ├── MiniDuo NX_11/ (smaller variant)
165
- ├── Übernommen/ (legacy parts)
166
- ├── Zukaufteile/ (vendor parts)
167
- │ ├── igus/
168
- │ ├── Interroll/
169
- │ ├── Rittal/
170
- │ ├── WEG/
171
- │ └── UNISTAR_2000B/
172
- └── Zusatzoptionen/ (optional modules)
173
- └── DUOdurch/
174
- ├── Gestell/
175
- ├── Raddreheinheit/
176
- ├── Schiebebürsten/
177
- └── D-ZBG-DUO-Anlage.iam (main assembly)
178
- ```
179
-
180
- ## Integration Points
181
-
182
- ### cyclecad Project Browser
183
- The manifest is designed to integrate with `project-browser.js`:
184
- - Load manifest at startup
185
- - Render folder tree with file type icons
186
- - Filter by category (custom/standard/vendor)
187
- - Search parts by name
188
- - Display statistics dashboard
189
-
190
- ### Assembly Resolver
191
- The manifest enables `assembly-resolver.js` to:
192
- - Quickly reference assembly paths
193
- - Build dependency graphs
194
- - Generate BOMs without parsing binary files
195
- - Map component relationships
196
-
197
- ### Browser UI
198
- No File System Access API needed:
199
- - All 393 parts queryable client-side
200
- - Manifest loads as JSON (standard XHR/Fetch)
201
- - Works on static hosting (GitHub Pages, CDN)
202
- - Progressive loading for large assemblies
203
-
204
- ## Demo Viewer
205
-
206
- Open `duo-manifest-demo.html` in a browser to:
207
- - View project statistics
208
- - Browse all assemblies
209
- - Search parts (first 50 shown)
210
- - Explore folder hierarchy
211
- - Copy file paths for integration
212
-
213
- ## Manifest Generation
214
-
215
- **Source:** `/sessions/sharp-modest-allen/mnt/cyclecad/example/DUO Durchgehend Inventor/`
216
-
217
- **Generated with:**
218
- ```bash
219
- node build-manifest.js > duo-manifest.json
220
- ```
221
-
222
- **Validation:**
223
- - ✅ 393 .ipt files parsed and categorized
224
- - ✅ 80 .iam assemblies indexed
225
- - ✅ Folder hierarchy preserved
226
- - ✅ File sizes included (bytes)
227
- - ✅ Relative paths normalized
228
-
229
- ---
230
-
231
- **Last Updated:** 2026-03-24
232
- **Manifest Size:** 313 KB
233
- **Format:** JSON (UTF-8, no compression)
@@ -1,337 +0,0 @@
1
- <!DOCTYPE html>
2
- <html lang="en">
3
- <head>
4
- <meta charset="UTF-8">
5
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
- <title>DUO Manifest Viewer</title>
7
- <style>
8
- body {
9
- font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
10
- margin: 0;
11
- padding: 20px;
12
- background: #f5f5f5;
13
- color: #333;
14
- }
15
- .container {
16
- max-width: 1200px;
17
- margin: 0 auto;
18
- background: white;
19
- padding: 20px;
20
- border-radius: 8px;
21
- box-shadow: 0 2px 8px rgba(0,0,0,0.1);
22
- }
23
- h1 {
24
- margin-top: 0;
25
- color: #0066cc;
26
- }
27
- .stats {
28
- display: grid;
29
- grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
30
- gap: 20px;
31
- margin: 20px 0;
32
- }
33
- .stat-box {
34
- background: #f9f9f9;
35
- border-left: 4px solid #0066cc;
36
- padding: 15px;
37
- border-radius: 4px;
38
- }
39
- .stat-box h3 {
40
- margin: 0 0 10px 0;
41
- color: #666;
42
- font-size: 12px;
43
- text-transform: uppercase;
44
- letter-spacing: 0.5px;
45
- }
46
- .stat-box .value {
47
- font-size: 32px;
48
- font-weight: bold;
49
- color: #0066cc;
50
- }
51
- .stat-box .sublabel {
52
- font-size: 12px;
53
- color: #999;
54
- margin-top: 5px;
55
- }
56
- .tabs {
57
- display: flex;
58
- gap: 0;
59
- border-bottom: 2px solid #eee;
60
- margin: 20px 0;
61
- }
62
- .tab-btn {
63
- padding: 10px 20px;
64
- background: none;
65
- border: none;
66
- cursor: pointer;
67
- font-size: 14px;
68
- color: #666;
69
- border-bottom: 2px solid transparent;
70
- margin-bottom: -2px;
71
- transition: all 0.2s;
72
- }
73
- .tab-btn:hover {
74
- color: #0066cc;
75
- }
76
- .tab-btn.active {
77
- color: #0066cc;
78
- border-bottom-color: #0066cc;
79
- }
80
- .tab-content {
81
- display: none;
82
- }
83
- .tab-content.active {
84
- display: block;
85
- }
86
- table {
87
- width: 100%;
88
- border-collapse: collapse;
89
- font-size: 13px;
90
- }
91
- th {
92
- background: #f5f5f5;
93
- padding: 10px;
94
- text-align: left;
95
- font-weight: 600;
96
- border-bottom: 1px solid #ddd;
97
- }
98
- td {
99
- padding: 10px;
100
- border-bottom: 1px solid #eee;
101
- }
102
- tr:hover {
103
- background: #fafafa;
104
- }
105
- .category-badge {
106
- display: inline-block;
107
- padding: 2px 8px;
108
- border-radius: 3px;
109
- font-size: 11px;
110
- font-weight: 600;
111
- text-transform: uppercase;
112
- letter-spacing: 0.3px;
113
- }
114
- .category-badge.custom {
115
- background: #e3f2fd;
116
- color: #1976d2;
117
- }
118
- .category-badge.standard {
119
- background: #f3e5f5;
120
- color: #7b1fa2;
121
- }
122
- .category-badge.vendor {
123
- background: #fff3e0;
124
- color: #e65100;
125
- }
126
- .tree-view {
127
- margin: 20px 0;
128
- }
129
- .tree-item {
130
- margin-left: 20px;
131
- padding: 5px 0;
132
- font-size: 13px;
133
- }
134
- .tree-folder {
135
- font-weight: 600;
136
- color: #0066cc;
137
- margin-top: 10px;
138
- }
139
- .file-size {
140
- color: #999;
141
- font-size: 12px;
142
- }
143
- .loading {
144
- text-align: center;
145
- color: #999;
146
- padding: 40px;
147
- }
148
- .error {
149
- background: #ffebee;
150
- color: #c62828;
151
- padding: 15px;
152
- border-radius: 4px;
153
- margin: 20px 0;
154
- }
155
- </style>
156
- </head>
157
- <body>
158
- <div class="container">
159
- <h1>DUO Durchgehend Inventor — Manifest Viewer</h1>
160
- <div class="loading">Loading manifest...</div>
161
- </div>
162
-
163
- <script>
164
- async function loadManifest() {
165
- try {
166
- const response = await fetch('./duo-manifest.json');
167
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
168
-
169
- const manifest = await response.json();
170
- renderManifest(manifest);
171
- } catch (error) {
172
- document.querySelector('.container').innerHTML = `
173
- <h1>DUO Durchgehend Inventor — Manifest Viewer</h1>
174
- <div class="error">Error loading manifest: ${error.message}</div>
175
- `;
176
- }
177
- }
178
-
179
- function formatSize(bytes) {
180
- if (bytes === 0) return '0 B';
181
- const k = 1024;
182
- const sizes = ['B', 'KB', 'MB', 'GB'];
183
- const i = Math.floor(Math.log(bytes) / Math.log(k));
184
- return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
185
- }
186
-
187
- function renderManifest(manifest) {
188
- const s = manifest.stats;
189
- const html = `
190
- <h1>${manifest.name}</h1>
191
- <p><strong>${manifest.description}</strong></p>
192
-
193
- <div class="stats">
194
- <div class="stat-box">
195
- <h3>Total Files</h3>
196
- <div class="value">${s.total}</div>
197
- </div>
198
- <div class="stat-box">
199
- <h3>Parts (.ipt)</h3>
200
- <div class="value">${s.parts}</div>
201
- <div class="sublabel">Custom: ${s.custom} • Standard: ${s.standard} • Vendor: ${s.vendor}</div>
202
- </div>
203
- <div class="stat-box">
204
- <h3>Assemblies (.iam)</h3>
205
- <div class="value">${s.assemblies}</div>
206
- </div>
207
- </div>
208
-
209
- <div class="tabs">
210
- <button class="tab-btn active" data-tab="project">Project Info</button>
211
- <button class="tab-btn" data-tab="assemblies">Assemblies</button>
212
- <button class="tab-btn" data-tab="parts">Parts</button>
213
- <button class="tab-btn" data-tab="tree">Folder Tree</button>
214
- </div>
215
-
216
- <div id="project" class="tab-content active">
217
- <table>
218
- <tr>
219
- <th>Property</th>
220
- <th>Value</th>
221
- </tr>
222
- <tr>
223
- <td>Project File</td>
224
- <td><code>${manifest.ipj}</code></td>
225
- </tr>
226
- <tr>
227
- <td>Workspace</td>
228
- <td><code>${manifest.workspace}</code></td>
229
- </tr>
230
- <tr>
231
- <td>Content Center</td>
232
- <td><code>${manifest.contentCenter}</code></td>
233
- </tr>
234
- <tr>
235
- <td>Total Components</td>
236
- <td>${s.total}</td>
237
- </tr>
238
- <tr>
239
- <td>Main Assembly</td>
240
- <td><code>${manifest.assemblies.find(a => a.isMain)?.path || 'N/A'}</code></td>
241
- </tr>
242
- </table>
243
- </div>
244
-
245
- <div id="assemblies" class="tab-content">
246
- <table>
247
- <tr>
248
- <th>Assembly Name</th>
249
- <th>Path</th>
250
- <th>Size</th>
251
- <th></th>
252
- </tr>
253
- ${manifest.assemblies.map(a => `
254
- <tr>
255
- <td><strong>${a.name}</strong></td>
256
- <td><code style="font-size: 11px">${a.path}</code></td>
257
- <td>${formatSize(a.size)}</td>
258
- <td>${a.isMain ? '<span style="color: #d32f2f; font-weight: 600;">● MAIN</span>' : ''}</td>
259
- </tr>
260
- `).join('')}
261
- </table>
262
- </div>
263
-
264
- <div id="parts" class="tab-content">
265
- <table>
266
- <tr>
267
- <th>Part Name</th>
268
- <th>Category</th>
269
- <th>Path</th>
270
- <th>Size</th>
271
- </tr>
272
- ${manifest.parts.slice(0, 50).map(p => `
273
- <tr>
274
- <td>${p.name}</td>
275
- <td><span class="category-badge ${p.category}">${p.category}</span></td>
276
- <td><code style="font-size: 11px">${p.path}</code></td>
277
- <td>${formatSize(p.size)}</td>
278
- </tr>
279
- `).join('')}
280
- <tr>
281
- <td colspan="4" style="text-align: center; color: #999; font-size: 12px;">
282
- Showing 50 of ${manifest.parts.length} parts
283
- </td>
284
- </tr>
285
- </table>
286
- </div>
287
-
288
- <div id="tree" class="tab-content">
289
- <div class="tree-view">
290
- ${renderTree(manifest.tree, 0)}
291
- </div>
292
- </div>
293
- `;
294
-
295
- document.querySelector('.container').innerHTML = html;
296
-
297
- // Tab switching
298
- document.querySelectorAll('.tab-btn').forEach(btn => {
299
- btn.addEventListener('click', (e) => {
300
- document.querySelectorAll('.tab-btn').forEach(b => b.classList.remove('active'));
301
- document.querySelectorAll('.tab-content').forEach(c => c.classList.remove('active'));
302
- e.target.classList.add('active');
303
- document.getElementById(e.target.dataset.tab).classList.add('active');
304
- });
305
- });
306
- }
307
-
308
- function renderTree(node, depth, limit = 100, count = { n: 0 }) {
309
- if (count.n >= limit) return '';
310
- count.n++;
311
-
312
- if (node.type === 'folder') {
313
- const margin = depth * 20;
314
- const items = (node.children || []).slice(0, 50);
315
- return `
316
- <div class="tree-item" style="margin-left: ${margin}px; font-weight: 600; color: #0066cc;">
317
- 📁 ${node.name}
318
- </div>
319
- ${items.map(child => renderTree(child, depth + 1, limit, count)).join('')}
320
- ${(node.children || []).length > 50 ? `<div class="tree-item" style="margin-left: ${(depth+1)*20}px; color: #999;">... and ${node.children.length - 50} more</div>` : ''}
321
- `;
322
- } else {
323
- const margin = depth * 20;
324
- const badge = node.category ? `<span class="category-badge ${node.category}" style="margin-left: 10px">${node.category}</span>` : '';
325
- return `
326
- <div class="tree-item" style="margin-left: ${margin}px;">
327
- 📄 ${node.name} <span class="file-size">${formatSize(node.size)}</span>${badge}
328
- </div>
329
- `;
330
- }
331
- }
332
-
333
- // Load on page load
334
- loadManifest();
335
- </script>
336
- </body>
337
- </html>