mlbench123 commited on
Commit
e267df1
Β·
verified Β·
1 Parent(s): d0e1b15

Create index.html

Browse files
Files changed (1) hide show
  1. index.html +468 -0
index.html ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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>DXF Canvas Composer API [TEST v2]</title>
7
+ <style>
8
+ * {
9
+ margin: 0;
10
+ padding: 0;
11
+ box-sizing: border-box;
12
+ }
13
+ body {
14
+ font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
15
+ background: linear-gradient(135deg, #e05d44 0%, #a02020 100%);
16
+ min-height: 100vh;
17
+ padding: 20px;
18
+ }
19
+ .container {
20
+ max-width: 1200px;
21
+ margin: 0 auto;
22
+ }
23
+ .header {
24
+ background: white;
25
+ padding: 30px;
26
+ border-radius: 15px;
27
+ box-shadow: 0 10px 30px rgba(0,0,0,0.2);
28
+ margin-bottom: 20px;
29
+ }
30
+ .header h1 {
31
+ color: #333;
32
+ font-size: 32px;
33
+ margin-bottom: 10px;
34
+ }
35
+ .header p {
36
+ color: #666;
37
+ font-size: 16px;
38
+ }
39
+ .status-card {
40
+ background: white;
41
+ padding: 25px;
42
+ border-radius: 15px;
43
+ box-shadow: 0 5px 20px rgba(0,0,0,0.15);
44
+ margin-bottom: 20px;
45
+ }
46
+ .status-indicator {
47
+ display: inline-flex;
48
+ align-items: center;
49
+ gap: 10px;
50
+ padding: 10px 20px;
51
+ border-radius: 25px;
52
+ font-weight: 600;
53
+ font-size: 14px;
54
+ margin-bottom: 15px;
55
+ }
56
+ .status-running {
57
+ background: #10b981;
58
+ color: white;
59
+ }
60
+ .status-error {
61
+ background: #ef4444;
62
+ color: white;
63
+ }
64
+ .status-checking {
65
+ background: #f59e0b;
66
+ color: white;
67
+ }
68
+ .pulse {
69
+ width: 12px;
70
+ height: 12px;
71
+ background: white;
72
+ border-radius: 50%;
73
+ animation: pulse 2s infinite;
74
+ }
75
+ @keyframes pulse {
76
+ 0%, 100% { opacity: 1; }
77
+ 50% { opacity: 0.4; }
78
+ }
79
+ .info-grid {
80
+ display: grid;
81
+ grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
82
+ gap: 15px;
83
+ margin-top: 20px;
84
+ }
85
+ .info-item {
86
+ padding: 15px;
87
+ background: #f8fafc;
88
+ border-radius: 10px;
89
+ border-left: 4px solid #667eea;
90
+ }
91
+ .info-item label {
92
+ display: block;
93
+ font-size: 12px;
94
+ color: #64748b;
95
+ text-transform: uppercase;
96
+ letter-spacing: 0.5px;
97
+ margin-bottom: 5px;
98
+ }
99
+ .info-item .value {
100
+ font-size: 18px;
101
+ font-weight: 600;
102
+ color: #1e293b;
103
+ }
104
+ .test-section {
105
+ background: white;
106
+ padding: 25px;
107
+ border-radius: 15px;
108
+ box-shadow: 0 5px 20px rgba(0,0,0,0.15);
109
+ margin-bottom: 20px;
110
+ }
111
+ .test-section h2 {
112
+ color: #333;
113
+ margin-bottom: 20px;
114
+ font-size: 24px;
115
+ }
116
+ textarea {
117
+ width: 100%;
118
+ min-height: 300px;
119
+ padding: 15px;
120
+ border: 2px solid #e2e8f0;
121
+ border-radius: 10px;
122
+ font-family: 'Courier New', monospace;
123
+ font-size: 13px;
124
+ resize: vertical;
125
+ margin-bottom: 15px;
126
+ }
127
+ textarea:focus {
128
+ outline: none;
129
+ border-color: #667eea;
130
+ }
131
+ .btn-group {
132
+ display: flex;
133
+ gap: 10px;
134
+ flex-wrap: wrap;
135
+ }
136
+ button {
137
+ padding: 12px 24px;
138
+ border: none;
139
+ border-radius: 8px;
140
+ font-size: 14px;
141
+ font-weight: 600;
142
+ cursor: pointer;
143
+ transition: all 0.3s;
144
+ }
145
+ .btn-primary {
146
+ background: #667eea;
147
+ color: white;
148
+ }
149
+ .btn-primary:hover {
150
+ background: #5568d3;
151
+ transform: translateY(-2px);
152
+ box-shadow: 0 5px 15px rgba(102, 126, 234, 0.4);
153
+ }
154
+ .btn-secondary {
155
+ background: #e2e8f0;
156
+ color: #475569;
157
+ }
158
+ .btn-secondary:hover {
159
+ background: #cbd5e1;
160
+ }
161
+ .btn-primary:disabled {
162
+ background: #94a3b8;
163
+ cursor: not-allowed;
164
+ transform: none;
165
+ }
166
+ .response-section {
167
+ background: white;
168
+ padding: 25px;
169
+ border-radius: 15px;
170
+ box-shadow: 0 5px 20px rgba(0,0,0,0.15);
171
+ }
172
+ .response-section h2 {
173
+ color: #333;
174
+ margin-bottom: 15px;
175
+ font-size: 24px;
176
+ }
177
+ .response-content {
178
+ background: #1e293b;
179
+ color: #e2e8f0;
180
+ padding: 20px;
181
+ border-radius: 10px;
182
+ font-family: 'Courier New', monospace;
183
+ font-size: 13px;
184
+ max-height: 500px;
185
+ overflow-y: auto;
186
+ white-space: pre-wrap;
187
+ word-wrap: break-word;
188
+ }
189
+ .loading {
190
+ display: inline-block;
191
+ width: 14px;
192
+ height: 14px;
193
+ border: 2px solid white;
194
+ border-top-color: transparent;
195
+ border-radius: 50%;
196
+ animation: spin 0.8s linear infinite;
197
+ margin-right: 8px;
198
+ }
199
+ @keyframes spin {
200
+ to { transform: rotate(360deg); }
201
+ }
202
+ .footer {
203
+ text-align: center;
204
+ color: white;
205
+ margin-top: 30px;
206
+ padding: 20px;
207
+ font-size: 14px;
208
+ opacity: 0.9;
209
+ }
210
+ .link-output {
211
+ margin-top: 15px;
212
+ padding: 15px;
213
+ background: #f0fdf4;
214
+ border: 2px solid #10b981;
215
+ border-radius: 8px;
216
+ }
217
+ .link-output a {
218
+ color: #059669;
219
+ font-weight: 600;
220
+ text-decoration: none;
221
+ word-break: break-all;
222
+ }
223
+ .link-output a:hover {
224
+ text-decoration: underline;
225
+ }
226
+ </style>
227
+ </head>
228
+ <body>
229
+ <div class="container">
230
+ <div class="header">
231
+ <h1>πŸ§ͺ DXF Canvas Composer API [TEST v2]</h1>
232
+ <p>Industrial-scale tool layout composition system with AWS S3 integration</p>
233
+ </div>
234
+
235
+ <div class="status-card">
236
+ <div class="status-indicator status-checking" id="statusIndicator">
237
+ <span class="pulse"></span>
238
+ <span id="statusText">Checking API Status...</span>
239
+ </div>
240
+
241
+ <div class="info-grid" id="infoGrid">
242
+ <div class="info-item">
243
+ <label>S3 Available</label>
244
+ <div class="value" id="s3Status">-</div>
245
+ </div>
246
+ <div class="info-item">
247
+ <label>AWS Region</label>
248
+ <div class="value" id="awsRegion">-</div>
249
+ </div>
250
+ <div class="info-item">
251
+ <label>S3 Bucket</label>
252
+ <div class="value" id="s3Bucket">-</div>
253
+ </div>
254
+ <div class="info-item">
255
+ <label>Cache Directory</label>
256
+ <div class="value" id="cacheDir">-</div>
257
+ </div>
258
+ </div>
259
+ </div>
260
+
261
+ <div class="test-section">
262
+ <h2>Image β†’ DXF Converter</h2>
263
+ <p style="color:#666;margin-bottom:20px;">Upload an original image and its segmentation mask. The DXF will download automatically.</p>
264
+
265
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:20px;margin-bottom:20px;">
266
+ <div>
267
+ <label style="display:block;font-weight:600;margin-bottom:8px;color:#333;">Original Image <span style="color:#ef4444">*</span></label>
268
+ <input type="file" id="imageFile" accept="image/*"
269
+ style="width:100%;padding:10px;border:2px dashed #667eea;border-radius:8px;cursor:pointer;">
270
+ <div id="imagePreview" style="margin-top:10px;display:none;">
271
+ <img id="imagePreviewImg" style="max-width:100%;max-height:150px;border-radius:6px;border:1px solid #e2e8f0;">
272
+ </div>
273
+ </div>
274
+ <div>
275
+ <label style="display:block;font-weight:600;margin-bottom:8px;color:#333;">Segmentation Mask <span style="color:#ef4444">*</span></label>
276
+ <input type="file" id="maskFile" accept="image/*"
277
+ style="width:100%;padding:10px;border:2px dashed #10b981;border-radius:8px;cursor:pointer;">
278
+ <div id="maskPreview" style="margin-top:10px;display:none;">
279
+ <img id="maskPreviewImg" style="max-width:100%;max-height:150px;border-radius:6px;border:1px solid #e2e8f0;">
280
+ </div>
281
+ </div>
282
+ </div>
283
+
284
+ <div style="display:grid;grid-template-columns:1fr 1fr 1fr;gap:15px;margin-bottom:20px;">
285
+ <div>
286
+ <label style="display:block;font-weight:600;margin-bottom:6px;color:#333;">Length</label>
287
+ <input type="number" id="lengthVal" value="8.5" step="0.01" min="0.01"
288
+ style="width:100%;padding:10px;border:2px solid #e2e8f0;border-radius:8px;font-size:15px;">
289
+ </div>
290
+ <div>
291
+ <label style="display:block;font-weight:600;margin-bottom:6px;color:#333;">Depth</label>
292
+ <input type="number" id="depthVal" value="0.5" step="0.01" min="0"
293
+ style="width:100%;padding:10px;border:2px solid #e2e8f0;border-radius:8px;font-size:15px;">
294
+ </div>
295
+ <div>
296
+ <label style="display:block;font-weight:600;margin-bottom:6px;color:#333;">Unit</label>
297
+ <select id="unitVal" style="width:100%;padding:10px;border:2px solid #e2e8f0;border-radius:8px;font-size:15px;">
298
+ <option value="inches">inches</option>
299
+ <option value="mm">mm</option>
300
+ </select>
301
+ </div>
302
+ </div>
303
+
304
+ <div class="btn-group">
305
+ <button class="btn-primary" id="convertBtn" onclick="convertImageToDxf()">
306
+ <span id="convertBtnText">⬇ Convert & Download DXF</span>
307
+ </button>
308
+ <button class="btn-secondary" onclick="clearConvertResponse()">Clear</button>
309
+ </div>
310
+ </div>
311
+
312
+ <div class="response-section">
313
+ <h2>Status</h2>
314
+ <div class="response-content" id="responseContent">Ready. Select images and click Convert.</div>
315
+ <div id="previewSection" style="display:none;margin-top:20px;">
316
+ <h3 style="margin-bottom:12px;color:#333;">Preview Images (from S3)</h3>
317
+ <div style="display:grid;grid-template-columns:1fr 1fr;gap:15px;">
318
+ <div>
319
+ <p style="font-weight:600;margin-bottom:6px;color:#64748b;font-size:13px;">ORIGINAL IMAGE</p>
320
+ <img id="previewOriginal" src="" style="width:100%;border-radius:8px;border:1px solid #e2e8f0;">
321
+ </div>
322
+ <div>
323
+ <p style="font-weight:600;margin-bottom:6px;color:#64748b;font-size:13px;">CONTOUR PREVIEW</p>
324
+ <img id="previewContour" src="" style="width:100%;border-radius:8px;border:1px solid #e2e8f0;">
325
+ </div>
326
+ </div>
327
+ </div>
328
+ </div>
329
+
330
+ <div class="footer">
331
+ Powered by Hugging Face β€’ AWS S3 β€’ ezdxf [TEST v2 β€” mask input + auto-download]
332
+ </div>
333
+ </div>
334
+
335
+ <script>
336
+ const HEALTH_ENDPOINT = '/test/api/health';
337
+ const IMG2DXF_ENDPOINT = '/test/api/image-to-dxf';
338
+
339
+ // ── Image preview helpers ──────────────────────────────────────────────
340
+ document.getElementById('imageFile').addEventListener('change', function() {
341
+ showPreview(this, 'imagePreview', 'imagePreviewImg');
342
+ });
343
+ document.getElementById('maskFile').addEventListener('change', function() {
344
+ showPreview(this, 'maskPreview', 'maskPreviewImg');
345
+ });
346
+
347
+ function showPreview(input, containerId, imgId) {
348
+ const container = document.getElementById(containerId);
349
+ const img = document.getElementById(imgId);
350
+ if (input.files && input.files[0]) {
351
+ const reader = new FileReader();
352
+ reader.onload = e => {
353
+ img.src = e.target.result;
354
+ container.style.display = 'block';
355
+ };
356
+ reader.readAsDataURL(input.files[0]);
357
+ }
358
+ }
359
+
360
+ // ── Health check ───────────────────────────────────────────────────────
361
+ async function checkHealth() {
362
+ try {
363
+ const response = await fetch(HEALTH_ENDPOINT);
364
+ const data = await response.json();
365
+ const indicator = document.getElementById('statusIndicator');
366
+ const text = document.getElementById('statusText');
367
+
368
+ if (data.status === 'healthy') {
369
+ indicator.className = 'status-indicator status-running';
370
+ text.textContent = 'API Running';
371
+ } else {
372
+ indicator.className = 'status-indicator status-error';
373
+ text.textContent = 'API Error';
374
+ }
375
+ document.getElementById('s3Status').textContent = data.s3_available ? 'Yes' : 'No';
376
+ document.getElementById('awsRegion').textContent = data.aws_region || '-';
377
+ document.getElementById('s3Bucket').textContent = data.s3_bucket || '-';
378
+ document.getElementById('cacheDir').textContent = data.cache_dir || '-';
379
+ } catch (error) {
380
+ document.getElementById('statusIndicator').className = 'status-indicator status-error';
381
+ document.getElementById('statusText').textContent = 'Connection Failed';
382
+ }
383
+ }
384
+
385
+ // ── Convert image + mask β†’ DXF download ───────────────────────────────
386
+ async function convertImageToDxf() {
387
+ const btn = document.getElementById('convertBtn');
388
+ const btnText = document.getElementById('convertBtnText');
389
+ const status = document.getElementById('responseContent');
390
+
391
+ const imageFile = document.getElementById('imageFile').files[0];
392
+ const maskFile = document.getElementById('maskFile').files[0];
393
+
394
+ if (!imageFile) { alert('Please select an original image.'); return; }
395
+ if (!maskFile) { alert('Please select a segmentation mask.'); return; }
396
+
397
+ const length = document.getElementById('lengthVal').value;
398
+ const depth = document.getElementById('depthVal').value;
399
+ const unit = document.getElementById('unitVal').value;
400
+
401
+ btn.disabled = true;
402
+ btnText.innerHTML = '<span class="loading"></span>Processing...';
403
+ status.textContent = 'Uploading images and generating DXF...\nThis may take 10–30 seconds.';
404
+ document.getElementById('previewSection').style.display = 'none';
405
+
406
+ try {
407
+ const formData = new FormData();
408
+ formData.append('image', imageFile);
409
+ formData.append('mask', maskFile);
410
+ formData.append('length', length);
411
+ formData.append('depth', depth);
412
+ formData.append('unit', unit);
413
+
414
+ const response = await fetch(IMG2DXF_ENDPOINT, {
415
+ method: 'POST',
416
+ body: formData
417
+ });
418
+
419
+ if (!response.ok) {
420
+ // Error returned as JSON
421
+ const errData = await response.json();
422
+ status.textContent = `❌ Error ${response.status}: ${errData.error || 'Unknown error'}`;
423
+ return;
424
+ }
425
+
426
+ // ── Trigger browser download of the DXF blob ───────────────
427
+ const dxfFilename = response.headers.get('X-DXF-Filename') || 'tool_output.dxf';
428
+ const blob = await response.blob();
429
+ const url = URL.createObjectURL(blob);
430
+ const a = document.createElement('a');
431
+ a.href = url;
432
+ a.download = dxfFilename;
433
+ document.body.appendChild(a);
434
+ a.click();
435
+ document.body.removeChild(a);
436
+ URL.revokeObjectURL(url);
437
+
438
+ // ── Show preview images if S3 URLs were returned ───────────
439
+ const origUrl = response.headers.get('X-Original-Image-URL');
440
+ const contourUrl = response.headers.get('X-Contour-Image-URL');
441
+
442
+ status.textContent = `βœ… DXF downloaded: ${dxfFilename}\n\nOriginal image URL: ${origUrl || '(not available)'}\nContour image URL: ${contourUrl || '(not available)'}`;
443
+
444
+ if (origUrl || contourUrl) {
445
+ if (origUrl) document.getElementById('previewOriginal').src = origUrl;
446
+ if (contourUrl) document.getElementById('previewContour').src = contourUrl;
447
+ document.getElementById('previewSection').style.display = 'block';
448
+ }
449
+
450
+ } catch (error) {
451
+ status.textContent = `❌ Request failed: ${error.message}`;
452
+ } finally {
453
+ btn.disabled = false;
454
+ btnText.innerHTML = '⬇ Convert & Download DXF';
455
+ }
456
+ }
457
+
458
+ function clearConvertResponse() {
459
+ document.getElementById('responseContent').textContent = 'Ready. Select images and click Convert.';
460
+ document.getElementById('previewSection').style.display = 'none';
461
+ }
462
+
463
+ // Health check on load and every 30s
464
+ checkHealth();
465
+ setInterval(checkHealth, 30000);
466
+ </script>
467
+ </body>
468
+ </html>