90
91 <script type="module">
92 import React, { useState, useRef, useCallback } from 'https://esm.sh/react@18.2.0?deps=react@18.2.0';
93 import { createRoot } from 'https://esm.sh/react-dom@18.2.0/client?deps=react@18.2.0,react-dom@18.2.0';
94
95 const App = () => {
292 ] : [];
293
294 return React.createElement('div', { className: 'container mx-auto px-4 py-8 max-w-6xl' },
295 // Header
296 React.createElement('div', { className: 'text-center mb-8' },
297 React.createElement('h1', { className: 'text-4xl font-bold text-gray-900 mb-4' },
298 '๐ Body Measurement System'
299 ),
300 React.createElement('p', { className: 'text-lg text-gray-600 max-w-2xl mx-auto' },
301 'Upload a clear, full-body photo to get comprehensive body measurements using AI-powered pose detection. Perfect for tailoring and clothing sizing.'
302 )
304
305 // Main content
306 React.createElement('div', { className: 'grid grid-cols-1 lg:grid-cols-2 gap-8' },
307 // Left column - Upload and controls
308 React.createElement('div', { className: 'space-y-6' },
309 // Upload area
310 React.createElement('div', { className: 'bg-white rounded-lg shadow-md p-6' },
311 React.createElement('h2', { className: 'text-xl font-semibold mb-4' }, 'Upload Image'),
312
313 !imagePreview ?
314 React.createElement('div', {
315 className: 'upload-area',
316 onClick: () => fileInputRef.current?.click(),
318 onDragOver: handleDragOver
319 },
320 React.createElement('div', { className: 'text-6xl mb-4' }, '๐ท'),
321 React.createElement('p', { className: 'text-lg font-medium text-gray-700 mb-2' },
322 'Click to upload or drag and drop'
323 ),
324 React.createElement('p', { className: 'text-sm text-gray-500' },
325 'JPEG, PNG, WebP up to 10MB'
326 )
327 ) :
328 React.createElement('div', { className: 'text-center' },
329 React.createElement('img', {
330 src: imagePreview,
331 alt: 'Preview',
332 className: 'preview-image mx-auto mb-4'
333 }),
334 React.createElement('button', {
335 onClick: () => fileInputRef.current?.click(),
336 className: 'px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors'
338 ),
339
340 React.createElement('input', {
341 ref: fileInputRef,
342 type: 'file',
348
349 // Settings
350 React.createElement('div', { className: 'bg-white rounded-lg shadow-md p-6' },
351 React.createElement('h2', { className: 'text-xl font-semibold mb-4' }, 'Settings'),
352
353 React.createElement('div', { className: 'space-y-4' },
354 React.createElement('div', null,
355 React.createElement('label', { className: 'block text-sm font-medium text-gray-700 mb-2' },
356 'Reference Height (cm) - Optional'
357 ),
358 React.createElement('input', {
359 type: 'number',
360 value: referenceHeight,
363 className: 'w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500'
364 }),
365 React.createElement('p', { className: 'text-xs text-gray-500 mt-1' },
366 'Providing your actual height improves measurement accuracy'
367 )
368 ),
369
370 React.createElement('div', null,
371 React.createElement('label', { className: 'block text-sm font-medium text-gray-700 mb-2' },
372 'Gender'
373 ),
374 React.createElement('select', {
375 value: gender,
376 onChange: (e) => setGender(e.target.value),
377 className: 'w-full px-3 py-2 border border-gray-300 rounded-md focus:outline-none focus:ring-2 focus:ring-blue-500'
378 },
379 React.createElement('option', { value: 'male' }, 'Male'),
380 React.createElement('option', { value: 'female' }, 'Female')
381 )
382 )
385
386 // Action buttons
387 React.createElement('div', { className: 'space-y-3' },
388 React.createElement('button', {
389 onClick: calculateMeasurements,
390 disabled: !selectedImage || loading,
391 className: `w-full px-6 py-3 bg-blue-600 text-white rounded-md font-medium hover:bg-blue-700 disabled:bg-gray-400 disabled:cursor-not-allowed transition-colors flex items-center justify-center space-x-2`
392 },
393 loading ? React.createElement('div', { className: 'loading-spinner' }) : null,
394 React.createElement('span', null, loading ? 'Processing...' : 'Calculate Measurements')
395 ),
396
397 measurements ? React.createElement('button', {
398 onClick: downloadReport,
399 className: 'w-full px-6 py-3 bg-green-600 text-white rounded-md font-medium hover:bg-green-700 transition-colors'
402
403 // Error display
404 error ? React.createElement('div', { className: 'bg-red-50 border border-red-200 rounded-md p-4' },
405 React.createElement('div', { className: 'flex items-center' },
406 React.createElement('span', { className: 'text-red-600 mr-2' }, 'โ ๏ธ'),
407 React.createElement('span', { className: 'text-red-700' }, error)
408 )
409 ) : null
411
412 // Right column - Results
413 React.createElement('div', { className: 'space-y-6' },
414 measurements ? [
415 // Confidence score
416 React.createElement('div', { key: 'confidence', className: 'bg-white rounded-lg shadow-md p-6' },
417 React.createElement('h2', { className: 'text-xl font-semibold mb-4' }, 'Measurement Confidence'),
418 React.createElement('div', { className: 'flex items-center space-x-3' },
419 React.createElement('div', { className: 'flex-1' },
420 React.createElement('div', { className: 'confidence-bar' },
421 React.createElement('div', {
422 className: `confidence-fill ${getConfidenceColor(confidence)}`,
423 style: { width: `${confidence}%` }
425 )
426 ),
427 React.createElement('span', { className: 'font-semibold text-lg' }, `${confidence}%`)
428 ),
429 React.createElement('p', { className: 'text-sm text-gray-600 mt-2' },
430 confidence >= 80 ? 'High confidence - measurements should be quite accurate' :
431 confidence >= 60 ? 'Medium confidence - measurements are reasonably accurate' :
436 // Measurements
437 ...measurementSections.map((section, index) =>
438 React.createElement('div', { key: index, className: 'measurement-card' },
439 React.createElement('h3', { className: 'text-lg font-semibold mb-3 text-gray-800' }, section.title),
440 React.createElement('div', { className: 'space-y-1' },
441 ...section.items.map((item, itemIndex) =>
442 item.value !== undefined ? React.createElement('div', { key: itemIndex, className: 'measurement-item' },
443 React.createElement('span', { className: 'measurement-label' }, item.label),
444 React.createElement('span', { className: 'measurement-value' }, formatMeasurement(item.value))
445 ) : null
446 )
451 // Female-specific measurements
452 gender === 'female' && measurements.bustCircumference ?
453 React.createElement('div', { key: 'female-specific', className: 'measurement-card' },
454 React.createElement('h3', { className: 'text-lg font-semibold mb-3 text-gray-800' }, 'Additional Measurements'),
455 React.createElement('div', { className: 'space-y-1' },
456 React.createElement('div', { className: 'measurement-item' },
457 React.createElement('span', { className: 'measurement-label' }, 'Bust Circumference'),
458 React.createElement('span', { className: 'measurement-value' }, formatMeasurement(measurements.bustCircumference))
459 ),
460 measurements.underBustCircumference ? React.createElement('div', { className: 'measurement-item' },
461 React.createElement('span', { className: 'measurement-label' }, 'Under-Bust Circumference'),
462 React.createElement('span', { className: 'measurement-value' }, formatMeasurement(measurements.underBustCircumference))
463 ) : null
464 )
465 ) : null
466 ] :
467 React.createElement('div', { className: 'bg-white rounded-lg shadow-md p-8 text-center' },
468 React.createElement('div', { className: 'text-6xl mb-4' }, '๐'),
469 React.createElement('h2', { className: 'text-xl font-semibold text-gray-700 mb-2' }, 'No Measurements Yet'),
470 React.createElement('p', { className: 'text-gray-500' },
471 'Upload an image and click "Calculate Measurements" to get started'
472 )
476
477 // Instructions
478 React.createElement('div', { className: 'mt-12 bg-blue-50 rounded-lg p-6' },
479 React.createElement('h2', { className: 'text-xl font-semibold text-blue-900 mb-4' }, '๐ Instructions for Best Results'),
480 React.createElement('div', { className: 'grid grid-cols-1 md:grid-cols-2 gap-4 text-sm text-blue-800' },
481 React.createElement('div', null,
482 React.createElement('h3', { className: 'font-semibold mb-2' }, 'Photo Requirements:'),
483 React.createElement('ul', { className: 'space-y-1 list-disc list-inside' },
484 React.createElement('li', null, 'Full body visible from head to feet'),
485 React.createElement('li', null, 'Person standing upright and straight'),
486 React.createElement('li', null, 'Arms slightly away from body'),
487 React.createElement('li', null, 'Good lighting and clear image'),
488 React.createElement('li', null, 'Minimal background clutter')
489 )
490 ),
491 React.createElement('div', null,
492 React.createElement('h3', { className: 'font-semibold mb-2' }, 'Tips for Accuracy:'),
493 React.createElement('ul', { className: 'space-y-1 list-disc list-inside' },
494 React.createElement('li', null, 'Provide reference height if known'),
495 React.createElement('li', null, 'Wear form-fitting clothing'),
496 React.createElement('li', null, 'Take photo from about 6-8 feet away'),
497 React.createElement('li', null, 'Camera at chest height'),
498 React.createElement('li', null, 'Front-facing pose works best')
499 )
500 )
505
506 const root = createRoot(document.getElementById('root'));
507 root.render(React.createElement(App));
508 </script>
509</body>