Reshama's picture
Upload 3 files
852dc09 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>ThreadStory - Fashion History Explorer</title>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
font-family: 'Georgia', serif;
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
min-height: 100vh;
padding: 20px;
}
.container {
max-width: 1000px;
margin: 0 auto;
background: white;
border-radius: 15px;
box-shadow: 0 10px 30px rgba(0,0,0,0.1);
overflow: hidden;
}
.header {
background: linear-gradient(45deg, #8B4513, #A0522D);
color: white;
padding: 30px;
text-align: center;
}
.header h1 { font-size: 2.5rem; margin-bottom: 10px; }
.content { padding: 30px; }
.step {
margin-bottom: 30px;
padding: 20px;
border: 2px solid #D2B48C;
border-radius: 10px;
background: #f9f9f9;
}
.step h2 { color: #8B4513; margin-bottom: 15px; }
.upload-area {
border: 2px dashed #8B4513;
padding: 40px;
text-align: center;
border-radius: 10px;
cursor: pointer;
transition: background 0.3s;
}
.upload-area:hover { background: #f0f0f0; }
.upload-area.dragover { background: #e8f4f8; }
input[type="file"] { display: none; }
.btn {
background: linear-gradient(45deg, #8B4513, #A0522D);
color: white;
padding: 12px 25px;
border: none;
border-radius: 25px;
cursor: pointer;
font-size: 1rem;
margin: 10px 5px;
transition: transform 0.2s;
}
.btn:hover { transform: translateY(-2px); }
.btn:disabled { opacity: 0.6; cursor: not-allowed; }
/* Interactive Image Styles */
.image-container {
position: relative;
display: inline-block;
max-width: 100%;
margin: 20px auto;
}
.interactive-image {
max-width: 100%;
height: auto;
display: block;
border-radius: 10px;
}
.hotspot {
position: absolute;
width: 40px;
height: 40px;
background: rgba(139, 69, 19, 0.8);
border: 3px solid white;
border-radius: 50%;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-weight: bold;
font-size: 18px;
transform: translate(-50%, -50%);
transition: all 0.3s;
box-shadow: 0 4px 10px rgba(0,0,0,0.3);
z-index: 10;
}
.hotspot:hover {
transform: translate(-50%, -50%) scale(1.2);
background: rgba(160, 82, 45, 0.9);
}
.hotspot.active {
background: rgba(160, 82, 45, 1);
animation: pulse 1s infinite;
}
@keyframes pulse {
0%, 100% { transform: translate(-50%, -50%) scale(1); }
50% { transform: translate(-50%, -50%) scale(1.15); }
}
.popup-bubble {
position: fixed;
background: white;
border: 3px solid #8B4513;
border-radius: 15px;
padding: 15px;
min-width: 200px;
max-width: 300px;
box-shadow: 0 8px 20px rgba(0,0,0,0.2);
z-index: 1000;
display: none;
}
.popup-bubble.show {
display: block;
animation: popIn 0.3s ease-out;
}
@keyframes popIn {
from { opacity: 0; transform: scale(0.8); }
to { opacity: 1; transform: scale(1); }
}
.popup-bubble h4 {
color: #8B4513;
margin-bottom: 10px;
font-size: 1.1rem;
}
.popup-bubble p {
color: #555;
margin-bottom: 10px;
font-size: 0.9rem;
}
.popup-bubble button {
width: 100%;
background: #8B4513;
color: white;
border: none;
padding: 8px;
border-radius: 8px;
cursor: pointer;
font-size: 0.9rem;
}
.popup-bubble button:hover {
background: #A0522D;
}
.popup-close {
position: absolute;
top: 5px;
right: 10px;
background: none;
border: none;
font-size: 20px;
cursor: pointer;
color: #999;
width: auto !important;
padding: 0 !important;
}
.popup-close:hover {
color: #333;
background: none !important;
}
.explanation {
background: #f8f9fa;
padding: 20px;
border-radius: 10px;
border-left: 4px solid #8B4513;
white-space: pre-line;
line-height: 1.6;
}
.loading { text-align: center; color: #8B4513; font-style: italic; }
.error {
background: #f8d7da;
color: #721c24;
padding: 15px;
border-radius: 8px;
border-left: 4px solid #dc3545;
}
.hidden { display: none; }
.center { text-align: center; }
</style>
</head>
<body>
<div class="container">
<div class="header">
<h1>🧵 ThreadStory</h1>
<p>Upload an image to discover the history of fashion items</p>
</div>
<div class="content">
<!-- Step 1: Upload Image -->
<div class="step" id="step1">
<h2>Step 1: Upload Image</h2>
<div class="upload-area" onclick="document.getElementById('imageInput').click()">
<p>📸 Click here or drag & drop an image</p>
<input type="file" id="imageInput" accept="image/*" onchange="uploadImage()">
</div>
<div id="uploadStatus"></div>
</div>
<!-- Step 2: Interactive Image with Hotspots -->
<div class="step hidden" id="step2">
<h2>Step 2: Click on Items in the Image</h2>
<p>Click on the numbered circles to learn about each fashion item:</p>
<div class="center">
<div class="image-container" id="imageContainer">
<img id="uploadedImage" class="interactive-image" src="" alt="Uploaded fashion item">
<!-- Hotspots will be added here dynamically -->
</div>
</div>
<!-- Popup bubble template -->
<div class="popup-bubble" id="popupBubble">
<button class="popup-close" onclick="closePopup()">×</button>
<h4 id="popupTitle"></h4>
<p id="popupDescription">Click "Learn More" for detailed history</p>
<button onclick="learnMore()">Learn More</button>
</div>
</div>
<!-- Step 3: View Detailed Explanation -->
<div class="step hidden" id="step3">
<h2>Step 3: Historical Explanation</h2>
<div id="explanation"></div>
<button class="btn" onclick="goBackToImage()">← Back to Image</button>
</div>
</div>
</div>
<script>
let currentItems = [];
let currentItemIndex = null;
let imageFilename = '';
function uploadImage() {
const file = document.getElementById('imageInput').files[0];
if (!file) return;
const status = document.getElementById('uploadStatus');
status.innerHTML = '<div class="loading">Analyzing image and detecting items...</div>';
const formData = new FormData();
formData.append('image', file);
fetch('/upload', {
method: 'POST',
body: formData
})
.then(response => response.json())
.then(data => {
if (data.success) {
currentItems = data.items;
imageFilename = data.image_filename;
displayInteractiveImage();
document.getElementById('step1').classList.add('hidden');
document.getElementById('step2').classList.remove('hidden');
} else {
status.innerHTML = `<div class="error">${data.error}</div>`;
}
})
.catch(error => {
status.innerHTML = '<div class="error">Error uploading image</div>';
});
}
function displayInteractiveImage() {
const img = document.getElementById('uploadedImage');
const container = document.getElementById('imageContainer');
// Set image source
img.src = `/uploads/${imageFilename}`;
// Wait for image to load before adding hotspots
img.onload = function() {
// Remove any existing hotspots
const existingHotspots = container.querySelectorAll('.hotspot');
existingHotspots.forEach(h => h.remove());
// Add hotspots for each item
currentItems.forEach((item, index) => {
const coords = item.coords.split(',');
const x = parseFloat(coords[0]);
const y = parseFloat(coords[1]);
const hotspot = document.createElement('div');
hotspot.className = 'hotspot';
hotspot.textContent = index + 1;
hotspot.style.left = x + '%';
hotspot.style.top = y + '%';
hotspot.onclick = (e) => showPopup(index, e);
container.appendChild(hotspot);
});
};
}
function showPopup(itemIndex, event) {
currentItemIndex = itemIndex;
const item = currentItems[itemIndex];
const popup = document.getElementById('popupBubble');
const title = document.getElementById('popupTitle');
title.textContent = item.name;
// Position popup near the clicked hotspot
const container = document.getElementById('imageContainer');
const containerRect = container.getBoundingClientRect();
const hotspot = event.target;
const hotspotRect = hotspot.getBoundingClientRect();
// Position popup near the clicked hotspot (using fixed positioning)
const x = hotspotRect.left + hotspotRect.width / 2;
const y = hotspotRect.top - 20;
popup.style.left = x + 'px';
popup.style.top = y + 'px';
popup.style.transform = 'translate(-50%, -100%)';
popup.classList.add('show');
// Highlight active hotspot
document.querySelectorAll('.hotspot').forEach(h => h.classList.remove('active'));
hotspot.classList.add('active');
}
function closePopup() {
document.getElementById('popupBubble').classList.remove('show');
document.querySelectorAll('.hotspot').forEach(h => h.classList.remove('active'));
}
function learnMore() {
closePopup();
const explanation = document.getElementById('explanation');
explanation.innerHTML = '<div class="loading">Getting detailed historical explanation...</div>';
fetch('/explain', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ item_index: currentItemIndex })
})
.then(response => response.json())
.then(data => {
if (data.success) {
explanation.innerHTML = `
<h3>${data.item}</h3>
<div class="explanation">${data.explanation}</div>
`;
document.getElementById('step2').classList.add('hidden');
document.getElementById('step3').classList.remove('hidden');
} else {
explanation.innerHTML = `<div class="error">${data.error}</div>`;
}
})
.catch(error => {
explanation.innerHTML = '<div class="error">Error getting explanation</div>';
});
}
function goBackToImage() {
document.getElementById('step3').classList.add('hidden');
document.getElementById('step2').classList.remove('hidden');
}
// Drag and drop functionality
const uploadArea = document.querySelector('.upload-area');
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
const files = e.dataTransfer.files;
if (files.length > 0) {
document.getElementById('imageInput').files = files;
uploadImage();
}
});
// Close popup when clicking outside
document.addEventListener('click', (e) => {
const popup = document.getElementById('popupBubble');
const hotspots = document.querySelectorAll('.hotspot');
let clickedHotspot = false;
hotspots.forEach(h => {
if (h.contains(e.target)) clickedHotspot = true;
});
if (!popup.contains(e.target) && !clickedHotspot && popup.classList.contains('show')) {
closePopup();
}
});
</script>
</body>
</html>