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/CLAUDE.md +20 -9
- package/app/index.html +451 -3
- package/app/js/advanced-ops.js +762 -0
- package/app/js/assembly.js +1102 -0
- package/app/js/constraint-solver.js +1046 -0
- package/app/js/dxf-export.js +1173 -0
- package/app/js/viewport.js +83 -0
- package/app/mobile.html +1276 -0
- package/package.json +1 -1
- package/DUO-MANIFEST-README.md +0 -233
- package/app/duo-manifest-demo.html +0 -337
- package/app/duo-manifest.json +0 -7375
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cyclecad",
|
|
3
|
-
"version": "0.1.
|
|
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": {
|
package/DUO-MANIFEST-README.md
DELETED
|
@@ -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>
|