@cluesmith/codev 1.4.10 → 1.4.12
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.
|
@@ -28,11 +28,10 @@ if (isNaN(port) || port < 1 || port > 65535) {
|
|
|
28
28
|
const fullFilePath = filePath;
|
|
29
29
|
const displayPath = path.basename(filePath);
|
|
30
30
|
/**
|
|
31
|
-
* Find
|
|
32
|
-
*
|
|
31
|
+
* Find a template file
|
|
32
|
+
* Templates are bundled with agent-farm package in templates/ directory
|
|
33
33
|
*/
|
|
34
|
-
function findTemplatePath() {
|
|
35
|
-
const filename = 'open.html';
|
|
34
|
+
function findTemplatePath(filename) {
|
|
36
35
|
// Templates are at package root: packages/codev/templates/
|
|
37
36
|
// From compiled: dist/agent-farm/servers/ -> ../../../templates/
|
|
38
37
|
// From source: src/agent-farm/servers/ -> ../../../templates/
|
|
@@ -41,7 +40,7 @@ function findTemplatePath() {
|
|
|
41
40
|
return pkgPath;
|
|
42
41
|
throw new Error(`Template not found: ${filename}`);
|
|
43
42
|
}
|
|
44
|
-
const templatePath = findTemplatePath();
|
|
43
|
+
const templatePath = findTemplatePath('open.html');
|
|
45
44
|
// Validate file exists
|
|
46
45
|
if (!fs.existsSync(fullFilePath)) {
|
|
47
46
|
console.error(`File not found: ${fullFilePath}`);
|
|
@@ -63,6 +62,9 @@ const isImage = imageExtensions.includes(ext);
|
|
|
63
62
|
// Video detection
|
|
64
63
|
const videoExtensions = ['webm', 'mp4', 'mov', 'avi'];
|
|
65
64
|
const isVideo = videoExtensions.includes(ext);
|
|
65
|
+
// STL (3D model) detection
|
|
66
|
+
const isSTL = ext === 'stl';
|
|
67
|
+
const stlTemplatePath = isSTL ? findTemplatePath('stl-viewer.html') : null;
|
|
66
68
|
// MIME type mapping for images
|
|
67
69
|
const imageMimeTypes = {
|
|
68
70
|
png: 'image/png',
|
|
@@ -90,7 +92,23 @@ const server = http.createServer((req, res) => {
|
|
|
90
92
|
res.end();
|
|
91
93
|
return;
|
|
92
94
|
}
|
|
93
|
-
// Serve
|
|
95
|
+
// Serve STL viewer for STL files
|
|
96
|
+
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html') && isSTL && stlTemplatePath) {
|
|
97
|
+
try {
|
|
98
|
+
let template = fs.readFileSync(stlTemplatePath, 'utf-8');
|
|
99
|
+
// Replace placeholders
|
|
100
|
+
template = template.replace(/\{\{FILE_PATH\}\}/g, fullFilePath);
|
|
101
|
+
template = template.replace(/\{\{FILE\}\}/g, displayPath);
|
|
102
|
+
res.writeHead(200, { 'Content-Type': 'text/html; charset=utf-8' });
|
|
103
|
+
res.end(template);
|
|
104
|
+
}
|
|
105
|
+
catch (err) {
|
|
106
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
107
|
+
res.end('Error loading STL viewer: ' + err.message);
|
|
108
|
+
}
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
// Serve annotation viewer for other files
|
|
94
112
|
if (req.method === 'GET' && (req.url === '/' || req.url === '/index.html')) {
|
|
95
113
|
try {
|
|
96
114
|
let template = fs.readFileSync(templatePath, 'utf-8');
|
|
@@ -193,6 +211,28 @@ const server = http.createServer((req, res) => {
|
|
|
193
211
|
}
|
|
194
212
|
return;
|
|
195
213
|
}
|
|
214
|
+
// Handle STL content (GET /api/stl)
|
|
215
|
+
if (req.method === 'GET' && req.url?.startsWith('/api/stl')) {
|
|
216
|
+
if (!isSTL) {
|
|
217
|
+
res.writeHead(400, { 'Content-Type': 'text/plain' });
|
|
218
|
+
res.end('Not an STL file');
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
try {
|
|
222
|
+
const stlData = fs.readFileSync(fullFilePath);
|
|
223
|
+
res.writeHead(200, {
|
|
224
|
+
'Content-Type': 'model/stl',
|
|
225
|
+
'Content-Length': stlData.length,
|
|
226
|
+
'Cache-Control': 'no-cache'
|
|
227
|
+
});
|
|
228
|
+
res.end(stlData);
|
|
229
|
+
}
|
|
230
|
+
catch (err) {
|
|
231
|
+
res.writeHead(500, { 'Content-Type': 'text/plain' });
|
|
232
|
+
res.end('Error reading STL: ' + err.message);
|
|
233
|
+
}
|
|
234
|
+
return;
|
|
235
|
+
}
|
|
196
236
|
// Handle file save
|
|
197
237
|
if (req.method === 'POST' && req.url === '/save') {
|
|
198
238
|
let body = '';
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"open-server.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/open-server.ts"],"names":[],"mappings":";AAEA;;;GAGG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AAEpC,MAAM,UAAU,GAAG,aAAa,CAAC,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;AAClD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,CAAC;AAE3C,iCAAiC;AACjC,MAAM,OAAO,GAAG,IAAI,OAAO,EAAE;KAC1B,IAAI,CAAC,aAAa,CAAC;KACnB,WAAW,CAAC,0CAA0C,CAAC;KACvD,QAAQ,CAAC,QAAQ,EAAE,mBAAmB,CAAC;KACvC,QAAQ,CAAC,YAAY,EAAE,mBAAmB,CAAC;KAC3C,KAAK,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAEvB,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;AAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AACnC,MAAM,QAAQ,GAAG,IAAI,CAAC,CAAC,CAAC,CAAC;AAEzB,IAAI,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,GAAG,CAAC,IAAI,IAAI,GAAG,KAAK,EAAE,CAAC;IAC5C,OAAO,CAAC,KAAK,CAAC,wBAAwB,IAAI,CAAC,CAAC,CAAC,0CAA0C,CAAC,CAAC;IACzF,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC;AAED,QAAQ;AACR,MAAM,YAAY,GAAG,QAAQ,CAAC;AAC9B,MAAM,WAAW,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AAE5C;;;GAGG;AACH,SAAS,gBAAgB
|
|
1
|
+
{"version":3,"file":"open-server.js","sourceRoot":"","sources":["../../../src/agent-farm/servers/open-server.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -30,6 +30,7 @@ af start [options]
|
|
|
30
30
|
- `-c, --cmd <command>` - Command to run in architect terminal
|
|
31
31
|
- `-p, --port <port>` - Port for architect terminal
|
|
32
32
|
- `--no-role` - Skip loading architect role prompt
|
|
33
|
+
- `--allow-insecure-remote` - Bind to 0.0.0.0 for remote access (see below)
|
|
33
34
|
|
|
34
35
|
**Description:**
|
|
35
36
|
|
|
@@ -51,7 +52,59 @@ af start -p 4300
|
|
|
51
52
|
|
|
52
53
|
# Start with specific command
|
|
53
54
|
af start -c "claude --model opus"
|
|
55
|
+
|
|
56
|
+
# Start with remote access enabled
|
|
57
|
+
af start --allow-insecure-remote
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
#### Remote Access
|
|
61
|
+
|
|
62
|
+
By default, Agent Farm binds to `localhost` (127.0.0.1), making it accessible only from the local machine. To access the dashboard from another device (e.g., a tablet, phone, or another computer on your network):
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
af start --allow-insecure-remote
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This binds the server to `0.0.0.0`, making it accessible from any network interface.
|
|
69
|
+
|
|
70
|
+
**Finding your machine's IP:**
|
|
71
|
+
```bash
|
|
72
|
+
# macOS
|
|
73
|
+
ipconfig getifaddr en0 # WiFi
|
|
74
|
+
ipconfig getifaddr en1 # Ethernet
|
|
75
|
+
|
|
76
|
+
# Linux
|
|
77
|
+
hostname -I | awk '{print $1}'
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
**Accessing remotely:**
|
|
54
81
|
```
|
|
82
|
+
http://<your-ip>:4200
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
**⚠️ Security Warning:**
|
|
86
|
+
|
|
87
|
+
The `--allow-insecure-remote` flag provides **no authentication**. Anyone on your network can:
|
|
88
|
+
- View and interact with all terminals
|
|
89
|
+
- Execute commands as your user
|
|
90
|
+
- Access and modify your code
|
|
91
|
+
|
|
92
|
+
**Only use this on trusted networks** (home WiFi, isolated development networks). Never use on:
|
|
93
|
+
- Public WiFi (coffee shops, airports)
|
|
94
|
+
- Shared office networks without VPN
|
|
95
|
+
- Any network with untrusted users
|
|
96
|
+
|
|
97
|
+
**For secure remote access**, consider:
|
|
98
|
+
1. **SSH tunneling** (recommended):
|
|
99
|
+
```bash
|
|
100
|
+
# On remote machine, tunnel to your dev machine
|
|
101
|
+
ssh -L 4200:localhost:4200 user@your-dev-machine
|
|
102
|
+
# Then open http://localhost:4200 on the remote machine
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
2. **VPN** - Access your home/office network securely
|
|
106
|
+
|
|
107
|
+
3. **Tailscale/ZeroTier** - Mesh VPN for secure device-to-device connections
|
|
55
108
|
|
|
56
109
|
---
|
|
57
110
|
|
|
@@ -0,0 +1,473 @@
|
|
|
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>{{FILE}} - STL Viewer</title>
|
|
7
|
+
<style>
|
|
8
|
+
* {
|
|
9
|
+
margin: 0;
|
|
10
|
+
padding: 0;
|
|
11
|
+
box-sizing: border-box;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
body {
|
|
15
|
+
background: #1a1a2e;
|
|
16
|
+
color: #e0e0e0;
|
|
17
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
18
|
+
overflow: hidden;
|
|
19
|
+
height: 100vh;
|
|
20
|
+
display: flex;
|
|
21
|
+
flex-direction: column;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
#header {
|
|
25
|
+
background: #16213e;
|
|
26
|
+
padding: 8px 16px;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
justify-content: space-between;
|
|
30
|
+
border-bottom: 1px solid #0f3460;
|
|
31
|
+
flex-shrink: 0;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
#filename {
|
|
35
|
+
font-size: 14px;
|
|
36
|
+
color: #94a3b8;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
#info {
|
|
40
|
+
font-size: 12px;
|
|
41
|
+
color: #64748b;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
#controls {
|
|
45
|
+
display: flex;
|
|
46
|
+
gap: 8px;
|
|
47
|
+
align-items: center;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
.control-group {
|
|
51
|
+
display: flex;
|
|
52
|
+
gap: 4px;
|
|
53
|
+
padding: 0 8px;
|
|
54
|
+
border-right: 1px solid #0f3460;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
.control-group:last-child {
|
|
58
|
+
border-right: none;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
.control-label {
|
|
62
|
+
font-size: 10px;
|
|
63
|
+
color: #64748b;
|
|
64
|
+
margin-right: 4px;
|
|
65
|
+
align-self: center;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
button {
|
|
69
|
+
background: #0f3460;
|
|
70
|
+
color: #e0e0e0;
|
|
71
|
+
border: 1px solid #1e4976;
|
|
72
|
+
padding: 4px 8px;
|
|
73
|
+
border-radius: 4px;
|
|
74
|
+
cursor: pointer;
|
|
75
|
+
font-size: 11px;
|
|
76
|
+
transition: background 0.2s;
|
|
77
|
+
min-width: 28px;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
button:hover {
|
|
81
|
+
background: #1e4976;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
button.active {
|
|
85
|
+
background: #2563eb;
|
|
86
|
+
border-color: #3b82f6;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
button.view-btn {
|
|
90
|
+
font-family: monospace;
|
|
91
|
+
font-weight: bold;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
button.view-btn.positive {
|
|
95
|
+
color: #4ade80;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
button.view-btn.negative {
|
|
99
|
+
color: #f87171;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
#canvas-container {
|
|
103
|
+
flex: 1;
|
|
104
|
+
position: relative;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
#canvas {
|
|
108
|
+
display: block;
|
|
109
|
+
width: 100%;
|
|
110
|
+
height: 100%;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
#loading {
|
|
114
|
+
position: absolute;
|
|
115
|
+
top: 50%;
|
|
116
|
+
left: 50%;
|
|
117
|
+
transform: translate(-50%, -50%);
|
|
118
|
+
text-align: center;
|
|
119
|
+
color: #94a3b8;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
#loading .spinner {
|
|
123
|
+
width: 40px;
|
|
124
|
+
height: 40px;
|
|
125
|
+
border: 3px solid #0f3460;
|
|
126
|
+
border-top-color: #3b82f6;
|
|
127
|
+
border-radius: 50%;
|
|
128
|
+
animation: spin 1s linear infinite;
|
|
129
|
+
margin: 0 auto 12px;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
@keyframes spin {
|
|
133
|
+
to { transform: rotate(360deg); }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
#error {
|
|
137
|
+
position: absolute;
|
|
138
|
+
top: 50%;
|
|
139
|
+
left: 50%;
|
|
140
|
+
transform: translate(-50%, -50%);
|
|
141
|
+
text-align: center;
|
|
142
|
+
color: #ef4444;
|
|
143
|
+
display: none;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.hidden {
|
|
147
|
+
display: none !important;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/* Axes legend */
|
|
151
|
+
#axes-legend {
|
|
152
|
+
position: absolute;
|
|
153
|
+
bottom: 16px;
|
|
154
|
+
left: 16px;
|
|
155
|
+
background: rgba(22, 33, 62, 0.9);
|
|
156
|
+
padding: 8px 12px;
|
|
157
|
+
border-radius: 4px;
|
|
158
|
+
font-size: 11px;
|
|
159
|
+
font-family: monospace;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
#axes-legend div {
|
|
163
|
+
margin: 2px 0;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
.axis-x { color: #ef4444; }
|
|
167
|
+
.axis-y { color: #22c55e; }
|
|
168
|
+
.axis-z { color: #3b82f6; }
|
|
169
|
+
</style>
|
|
170
|
+
</head>
|
|
171
|
+
<body>
|
|
172
|
+
<div id="header">
|
|
173
|
+
<div>
|
|
174
|
+
<span id="filename">{{FILE}}</span>
|
|
175
|
+
<span id="info"></span>
|
|
176
|
+
</div>
|
|
177
|
+
<div id="controls">
|
|
178
|
+
<div class="control-group">
|
|
179
|
+
<span class="control-label">View:</span>
|
|
180
|
+
<button id="viewTop" class="view-btn positive" title="Top (+Y)">+Y</button>
|
|
181
|
+
<button id="viewBottom" class="view-btn negative" title="Bottom (-Y)">-Y</button>
|
|
182
|
+
<button id="viewFront" class="view-btn positive" title="Front (+Z)">+Z</button>
|
|
183
|
+
<button id="viewBack" class="view-btn negative" title="Back (-Z)">-Z</button>
|
|
184
|
+
<button id="viewRight" class="view-btn positive" title="Right (+X)">+X</button>
|
|
185
|
+
<button id="viewLeft" class="view-btn negative" title="Left (-X)">-X</button>
|
|
186
|
+
</div>
|
|
187
|
+
<div class="control-group">
|
|
188
|
+
<button id="viewIso" title="Isometric view">Iso</button>
|
|
189
|
+
<button id="resetBtn" title="Fit model to view">Fit</button>
|
|
190
|
+
</div>
|
|
191
|
+
<div class="control-group">
|
|
192
|
+
<button id="wireframeBtn" title="Toggle wireframe mode">Wire</button>
|
|
193
|
+
<button id="axesBtn" class="active" title="Toggle axes">Axes</button>
|
|
194
|
+
<button id="gridBtn" class="active" title="Toggle grid">Grid</button>
|
|
195
|
+
</div>
|
|
196
|
+
</div>
|
|
197
|
+
</div>
|
|
198
|
+
|
|
199
|
+
<div id="canvas-container">
|
|
200
|
+
<canvas id="canvas"></canvas>
|
|
201
|
+
<div id="loading">
|
|
202
|
+
<div class="spinner"></div>
|
|
203
|
+
<div>Loading STL...</div>
|
|
204
|
+
</div>
|
|
205
|
+
<div id="error">
|
|
206
|
+
<div style="font-size: 48px; margin-bottom: 12px;">⚠️</div>
|
|
207
|
+
<div id="error-message">Failed to load STL file</div>
|
|
208
|
+
</div>
|
|
209
|
+
<div id="axes-legend">
|
|
210
|
+
<div class="axis-x">X → Right</div>
|
|
211
|
+
<div class="axis-y">Y → Up</div>
|
|
212
|
+
<div class="axis-z">Z → Front</div>
|
|
213
|
+
</div>
|
|
214
|
+
</div>
|
|
215
|
+
|
|
216
|
+
<!-- Three.js from CDN (using r128 which has global builds) -->
|
|
217
|
+
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/build/three.min.js"></script>
|
|
218
|
+
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/loaders/STLLoader.js"></script>
|
|
219
|
+
<script src="https://cdn.jsdelivr.net/npm/three@0.128.0/examples/js/controls/OrbitControls.js"></script>
|
|
220
|
+
|
|
221
|
+
<script>
|
|
222
|
+
// Configuration
|
|
223
|
+
const FILE_PATH = '{{FILE_PATH}}';
|
|
224
|
+
const FILE_NAME = '{{FILE}}';
|
|
225
|
+
|
|
226
|
+
// Three.js setup
|
|
227
|
+
let scene, camera, renderer, controls, mesh;
|
|
228
|
+
let axesHelper, gridHelper;
|
|
229
|
+
let wireframeMode = false;
|
|
230
|
+
let showAxes = true;
|
|
231
|
+
let showGrid = true;
|
|
232
|
+
let modelCenter = new THREE.Vector3();
|
|
233
|
+
let cameraDistance = 100;
|
|
234
|
+
|
|
235
|
+
// DOM elements
|
|
236
|
+
const canvas = document.getElementById('canvas');
|
|
237
|
+
const container = document.getElementById('canvas-container');
|
|
238
|
+
const loading = document.getElementById('loading');
|
|
239
|
+
const error = document.getElementById('error');
|
|
240
|
+
const errorMessage = document.getElementById('error-message');
|
|
241
|
+
const info = document.getElementById('info');
|
|
242
|
+
|
|
243
|
+
// Buttons
|
|
244
|
+
const resetBtn = document.getElementById('resetBtn');
|
|
245
|
+
const wireframeBtn = document.getElementById('wireframeBtn');
|
|
246
|
+
const axesBtn = document.getElementById('axesBtn');
|
|
247
|
+
const gridBtn = document.getElementById('gridBtn');
|
|
248
|
+
const viewTop = document.getElementById('viewTop');
|
|
249
|
+
const viewBottom = document.getElementById('viewBottom');
|
|
250
|
+
const viewFront = document.getElementById('viewFront');
|
|
251
|
+
const viewBack = document.getElementById('viewBack');
|
|
252
|
+
const viewRight = document.getElementById('viewRight');
|
|
253
|
+
const viewLeft = document.getElementById('viewLeft');
|
|
254
|
+
const viewIso = document.getElementById('viewIso');
|
|
255
|
+
|
|
256
|
+
function init() {
|
|
257
|
+
// Scene
|
|
258
|
+
scene = new THREE.Scene();
|
|
259
|
+
scene.background = new THREE.Color(0x1a1a2e);
|
|
260
|
+
|
|
261
|
+
// Camera
|
|
262
|
+
camera = new THREE.PerspectiveCamera(
|
|
263
|
+
45,
|
|
264
|
+
container.clientWidth / container.clientHeight,
|
|
265
|
+
0.1,
|
|
266
|
+
10000
|
|
267
|
+
);
|
|
268
|
+
camera.position.set(100, 100, 100);
|
|
269
|
+
|
|
270
|
+
// Renderer
|
|
271
|
+
renderer = new THREE.WebGLRenderer({ canvas, antialias: true });
|
|
272
|
+
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
273
|
+
renderer.setPixelRatio(window.devicePixelRatio);
|
|
274
|
+
|
|
275
|
+
// Controls
|
|
276
|
+
controls = new THREE.OrbitControls(camera, renderer.domElement);
|
|
277
|
+
controls.enableDamping = true;
|
|
278
|
+
controls.dampingFactor = 0.1;
|
|
279
|
+
controls.screenSpacePanning = true;
|
|
280
|
+
|
|
281
|
+
// Lighting
|
|
282
|
+
const ambientLight = new THREE.AmbientLight(0x404040, 0.5);
|
|
283
|
+
scene.add(ambientLight);
|
|
284
|
+
|
|
285
|
+
const directionalLight1 = new THREE.DirectionalLight(0xffffff, 0.8);
|
|
286
|
+
directionalLight1.position.set(1, 1, 1);
|
|
287
|
+
scene.add(directionalLight1);
|
|
288
|
+
|
|
289
|
+
const directionalLight2 = new THREE.DirectionalLight(0xffffff, 0.4);
|
|
290
|
+
directionalLight2.position.set(-1, -1, -1);
|
|
291
|
+
scene.add(directionalLight2);
|
|
292
|
+
|
|
293
|
+
// Grid
|
|
294
|
+
gridHelper = new THREE.GridHelper(200, 20, 0x0f3460, 0x0f3460);
|
|
295
|
+
scene.add(gridHelper);
|
|
296
|
+
|
|
297
|
+
// Axes helper (X=red, Y=green, Z=blue)
|
|
298
|
+
axesHelper = new THREE.AxesHelper(50);
|
|
299
|
+
scene.add(axesHelper);
|
|
300
|
+
|
|
301
|
+
// Load STL
|
|
302
|
+
loadSTL();
|
|
303
|
+
|
|
304
|
+
// Event listeners
|
|
305
|
+
window.addEventListener('resize', onResize);
|
|
306
|
+
|
|
307
|
+
// Control buttons
|
|
308
|
+
resetBtn.addEventListener('click', fitToView);
|
|
309
|
+
wireframeBtn.addEventListener('click', toggleWireframe);
|
|
310
|
+
axesBtn.addEventListener('click', toggleAxes);
|
|
311
|
+
gridBtn.addEventListener('click', toggleGrid);
|
|
312
|
+
|
|
313
|
+
// View buttons
|
|
314
|
+
viewTop.addEventListener('click', () => setView(0, 1, 0));
|
|
315
|
+
viewBottom.addEventListener('click', () => setView(0, -1, 0));
|
|
316
|
+
viewFront.addEventListener('click', () => setView(0, 0, 1));
|
|
317
|
+
viewBack.addEventListener('click', () => setView(0, 0, -1));
|
|
318
|
+
viewRight.addEventListener('click', () => setView(1, 0, 0));
|
|
319
|
+
viewLeft.addEventListener('click', () => setView(-1, 0, 0));
|
|
320
|
+
viewIso.addEventListener('click', () => setView(1, 1, 1));
|
|
321
|
+
|
|
322
|
+
// Animation loop
|
|
323
|
+
animate();
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
function loadSTL() {
|
|
327
|
+
const loader = new THREE.STLLoader();
|
|
328
|
+
|
|
329
|
+
// Load from API endpoint
|
|
330
|
+
loader.load(
|
|
331
|
+
'/api/stl',
|
|
332
|
+
(geometry) => {
|
|
333
|
+
// Center geometry
|
|
334
|
+
geometry.computeBoundingBox();
|
|
335
|
+
const center = new THREE.Vector3();
|
|
336
|
+
geometry.boundingBox.getCenter(center);
|
|
337
|
+
geometry.translate(-center.x, -center.y, -center.z);
|
|
338
|
+
|
|
339
|
+
// Move to sit on grid (optional - comment out to keep centered)
|
|
340
|
+
const minY = geometry.boundingBox.min.y - center.y;
|
|
341
|
+
geometry.translate(0, -minY, 0);
|
|
342
|
+
|
|
343
|
+
// Recalculate bounding box after translation
|
|
344
|
+
geometry.computeBoundingBox();
|
|
345
|
+
|
|
346
|
+
// Material
|
|
347
|
+
const material = new THREE.MeshPhongMaterial({
|
|
348
|
+
color: 0x3b82f6,
|
|
349
|
+
specular: 0x111111,
|
|
350
|
+
shininess: 30,
|
|
351
|
+
flatShading: false
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
mesh = new THREE.Mesh(geometry, material);
|
|
355
|
+
scene.add(mesh);
|
|
356
|
+
|
|
357
|
+
// Calculate model center and size
|
|
358
|
+
const box = new THREE.Box3().setFromObject(mesh);
|
|
359
|
+
box.getCenter(modelCenter);
|
|
360
|
+
const size = box.getSize(new THREE.Vector3());
|
|
361
|
+
cameraDistance = Math.max(size.x, size.y, size.z) * 2;
|
|
362
|
+
|
|
363
|
+
// Scale grid and axes to model size
|
|
364
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
365
|
+
const gridSize = Math.ceil(maxDim * 2 / 10) * 10; // Round up to nearest 10
|
|
366
|
+
scene.remove(gridHelper);
|
|
367
|
+
gridHelper = new THREE.GridHelper(gridSize, gridSize / 5, 0x0f3460, 0x0f3460);
|
|
368
|
+
scene.add(gridHelper);
|
|
369
|
+
|
|
370
|
+
scene.remove(axesHelper);
|
|
371
|
+
axesHelper = new THREE.AxesHelper(gridSize / 2);
|
|
372
|
+
scene.add(axesHelper);
|
|
373
|
+
|
|
374
|
+
// Fit camera to model
|
|
375
|
+
fitToView();
|
|
376
|
+
|
|
377
|
+
// Update info
|
|
378
|
+
const triangles = geometry.attributes.position.count / 3;
|
|
379
|
+
info.textContent = ` — ${triangles.toLocaleString()} triangles`;
|
|
380
|
+
|
|
381
|
+
// Hide loading
|
|
382
|
+
loading.classList.add('hidden');
|
|
383
|
+
},
|
|
384
|
+
(progress) => {
|
|
385
|
+
// Progress callback (optional)
|
|
386
|
+
},
|
|
387
|
+
(err) => {
|
|
388
|
+
console.error('Error loading STL:', err);
|
|
389
|
+
loading.classList.add('hidden');
|
|
390
|
+
error.style.display = 'block';
|
|
391
|
+
errorMessage.textContent = 'Failed to load STL file: ' + (err.message || 'Unknown error');
|
|
392
|
+
}
|
|
393
|
+
);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function fitToView() {
|
|
397
|
+
if (!mesh) return;
|
|
398
|
+
|
|
399
|
+
const box = new THREE.Box3().setFromObject(mesh);
|
|
400
|
+
const size = box.getSize(new THREE.Vector3());
|
|
401
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
402
|
+
|
|
403
|
+
const maxDim = Math.max(size.x, size.y, size.z);
|
|
404
|
+
const fov = camera.fov * (Math.PI / 180);
|
|
405
|
+
cameraDistance = maxDim / (2 * Math.tan(fov / 2)) * 1.5;
|
|
406
|
+
|
|
407
|
+
// Isometric view
|
|
408
|
+
setView(1, 1, 1);
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function setView(x, y, z) {
|
|
412
|
+
if (!mesh) return;
|
|
413
|
+
|
|
414
|
+
const box = new THREE.Box3().setFromObject(mesh);
|
|
415
|
+
const center = box.getCenter(new THREE.Vector3());
|
|
416
|
+
|
|
417
|
+
// Normalize direction
|
|
418
|
+
const dir = new THREE.Vector3(x, y, z).normalize();
|
|
419
|
+
|
|
420
|
+
// Set camera position
|
|
421
|
+
camera.position.copy(center).add(dir.multiplyScalar(cameraDistance));
|
|
422
|
+
|
|
423
|
+
// Set up vector (handle top/bottom views)
|
|
424
|
+
if (Math.abs(y) > 0.9) {
|
|
425
|
+
camera.up.set(0, 0, y > 0 ? -1 : 1);
|
|
426
|
+
} else {
|
|
427
|
+
camera.up.set(0, 1, 0);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
// Look at center
|
|
431
|
+
controls.target.copy(center);
|
|
432
|
+
camera.lookAt(center);
|
|
433
|
+
controls.update();
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function toggleWireframe() {
|
|
437
|
+
wireframeMode = !wireframeMode;
|
|
438
|
+
if (mesh) {
|
|
439
|
+
mesh.material.wireframe = wireframeMode;
|
|
440
|
+
}
|
|
441
|
+
wireframeBtn.classList.toggle('active', wireframeMode);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function toggleAxes() {
|
|
445
|
+
showAxes = !showAxes;
|
|
446
|
+
axesHelper.visible = showAxes;
|
|
447
|
+
axesBtn.classList.toggle('active', showAxes);
|
|
448
|
+
document.getElementById('axes-legend').style.display = showAxes ? 'block' : 'none';
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
function toggleGrid() {
|
|
452
|
+
showGrid = !showGrid;
|
|
453
|
+
gridHelper.visible = showGrid;
|
|
454
|
+
gridBtn.classList.toggle('active', showGrid);
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
function onResize() {
|
|
458
|
+
camera.aspect = container.clientWidth / container.clientHeight;
|
|
459
|
+
camera.updateProjectionMatrix();
|
|
460
|
+
renderer.setSize(container.clientWidth, container.clientHeight);
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
function animate() {
|
|
464
|
+
requestAnimationFrame(animate);
|
|
465
|
+
controls.update();
|
|
466
|
+
renderer.render(scene, camera);
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Start
|
|
470
|
+
init();
|
|
471
|
+
</script>
|
|
472
|
+
</body>
|
|
473
|
+
</html>
|