text2vector / frontend /index.html
emilbm's picture
init project
5a5e912
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Text2Vector | Text to Vector Conversion</title>
<link rel="icon" type="image/x-icon" href="/static/favicon.ico">
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://unpkg.com/feather-icons"></script>
<script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
<style>
.text-input-group:hover .remove-text-btn {
display: block !important;
}
.gradient-bg {
background: linear-gradient(135deg, #6e8efb 0%, #a777e3 100%);
}
.embed-card {
backdrop-filter: blur(10px);
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
}
.text-area {
min-height: 150px;
}
.vector-display {
font-family: monospace;
white-space: pre-wrap;
overflow-x: auto;
}
#vanta-bg {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
z-index: -1;
}
</style>
</head>
<body class="min-h-screen text-gray-100">
<div id="vanta-bg"></div>
<div class="container mx-auto px-4 py-12">
<!-- Header -->
<header class="text-center mb-12">
<h1 class="text-4xl md:text-5xl font-bold mb-4">Text2Vector ⚡</h1>
<p class="text-xl opacity-80">Transform your text into powerful vector embeddings</p>
</header>
<!-- Main Content -->
<main class="max-w-4xl mx-auto">
<div class="grid md:grid-cols-2 gap-8">
<!-- Input Section -->
<div class="embed-card rounded-xl p-6 shadow-lg">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<i data-feather="edit-3" class="mr-2"></i>
<h2 class="text-xl font-semibold">Input Texts</h2>
</div>
<button id="add-text-btn" class="px-3 py-1 gradient-bg hover:opacity-90 rounded-lg transition flex items-center text-sm">
<i data-feather="plus" class="mr-1"></i>
Add Field
</button>
</div>
<div id="text-inputs-container">
<div class="text-input-group mb-3 relative">
<textarea class="w-full text-area bg-gray-800 bg-opacity-50 rounded-lg p-4 text-white border border-gray-600 focus:border-purple-400 focus:ring-1 focus:ring-purple-400 transition" placeholder="Enter your text here..."></textarea>
<button class="remove-text-btn absolute top-1 right-1 p-1 bg-gray-700 hover:bg-gray-600 rounded-full transition" style="display: none;">
<i data-feather="x" class="w-4 h-4"></i>
</button>
</div>
</div>
<div class="flex justify-between mt-4">
<button id="clear-btn" class="px-4 py-2 bg-gray-700 hover:bg-gray-600 rounded-lg transition flex items-center">
<i data-feather="trash-2" class="mr-2"></i>
Clear All
</button>
<button id="generate-btn" class="px-6 py-2 gradient-bg hover:opacity-90 rounded-lg transition flex items-center">
<i data-feather="zap" class="mr-2"></i>
Generate Embeddings
</button>
</div>
</div>
<!-- Output Section -->
<div class="embed-card rounded-xl p-6 shadow-lg">
<div class="flex items-center justify-between mb-4">
<div class="flex items-center">
<i data-feather="list" class="mr-2"></i>
<h2 class="text-xl font-semibold">Vector Embeddings</h2>
</div>
<button id="copy-btn" class="px-3 py-1 bg-gray-700 hover:bg-gray-600 rounded-lg transition flex items-center text-sm" disabled>
<i data-feather="copy" class="mr-1"></i>
Copy
</button>
</div>
<div id="output-container" class="vector-display bg-gray-800 bg-opacity-50 rounded-lg p-4 h-64 overflow-auto hidden">
<pre id="output-vector" class="text-sm"></pre>
</div>
<div id="placeholder" class="bg-gray-800 bg-opacity-30 rounded-lg p-8 text-center h-64 flex items-center justify-center">
<div class="opacity-60">
<i data-feather="wind" class="w-12 h-12 mx-auto mb-4"></i>
<p>Your embeddings will appear here</p>
</div>
</div>
<button id="download-btn" class="w-full mt-4 px-4 py-2 gradient-bg hover:opacity-90 rounded-lg transition flex items-center justify-center hidden">
<i data-feather="download" class="mr-2"></i>
Download as JSON
</button>
</div>
</div>
<!-- Info Section -->
<div class="embed-card rounded-xl p-6 mt-8 shadow-lg">
<div class="flex items-center mb-4">
<i data-feather="info" class="mr-2"></i>
<h2 class="text-xl font-semibold">About Text2Vector</h2>
</div>
<p class="mb-4">Text2Vector transforms your text into high-dimensional vector representations using Microsoft's multilingual-e5-large model, capturing semantic meaning across multiple languages.</p>
<div class="grid md:grid-cols-3 gap-4">
<div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg">
<div class="flex items-center mb-2">
<i data-feather="hash" class="mr-2 text-purple-300"></i>
<h3 class="font-medium">Dimensionality</h3>
</div>
<p class="text-sm opacity-80">1024-dimensional vectors</p>
</div>
<div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg">
<div class="flex items-center mb-2">
<i data-feather="cpu" class="mr-2 text-purple-300"></i>
<h3 class="font-medium">Model</h3>
</div>
<p class="text-sm opacity-80">Microsoft's multilingual-e5-large</p>
</div>
<div class="bg-gray-800 bg-opacity-30 p-4 rounded-lg">
<div class="flex items-center mb-2">
<i data-feather="code" class="mr-2 text-purple-300"></i>
<h3 class="font-medium">API</h3>
</div>
<p class="text-sm opacity-80">Simple REST integration</p>
</div>
</div>
</div>
</main>
</div>
<!-- Footer -->
<footer class="text-center py-8 opacity-70 text-sm">
<p>© 2023 Text2Vector ⚡ | Powered by multilingual-e5-large embeddings</p>
<p class="mt-2" id="api-status">API Status: <span class="text-red-500">Checking...</span></p>
</footer>
<!-- Scripts -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/three.js/r121/three.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vanta@latest/dist/vanta.globe.min.js"></script>
<script>
// Initialize Vanta.js background
VANTA.GLOBE({
el: "#vanta-bg",
mouseControls: true,
touchControls: true,
gyroControls: false,
minHeight: 200.00,
minWidth: 200.00,
scale: 1.00,
scaleMobile: 1.00,
color: 0x6e8efb,
backgroundColor: 0x0,
size: 0.8
});
// Initialize Feather Icons
feather.replace();
// DOM Elements
const textInputsContainer = document.getElementById('text-inputs-container');
const generateBtn = document.getElementById('generate-btn');
const clearBtn = document.getElementById('clear-btn');
const copyBtn = document.getElementById('copy-btn');
const downloadBtn = document.getElementById('download-btn');
const outputContainer = document.getElementById('output-container');
const outputVector = document.getElementById('output-vector');
const placeholder = document.getElementById('placeholder');
const addTextBtn = document.getElementById('add-text-btn');
// Add new text input field
addTextBtn.addEventListener('click', () => {
const newInputGroup = document.createElement('div');
newInputGroup.className = 'text-input-group mb-3 relative';
newInputGroup.innerHTML = `
<textarea class="w-full text-area bg-gray-800 bg-opacity-50 rounded-lg p-4 text-white border border-gray-600 focus:border-purple-400 focus:ring-1 focus:ring-purple-400 transition" placeholder="Enter your text here..."></textarea>
<button class="remove-text-btn absolute top-1 right-1 p-1 bg-gray-700 hover:bg-gray-600 rounded-full transition">
<i data-feather="x" class="w-4 h-4"></i>
</button>
`;
textInputsContainer.appendChild(newInputGroup);
feather.replace();
setupRemoveButtons();
});
// Setup remove buttons for all input fields
function setupRemoveButtons() {
document.querySelectorAll('.remove-text-btn').forEach(btn => {
btn.addEventListener('click', (e) => {
if (document.querySelectorAll('.text-input-group').length > 1) {
e.target.closest('.text-input-group').remove();
}
});
});
}
// Show remove buttons when hovering over input groups
document.addEventListener('mouseover', (e) => {
if (e.target.closest('.text-input-group')) {
const group = e.target.closest('.text-input-group');
if (document.querySelectorAll('.text-input-group').length > 1) {
group.querySelector('.remove-text-btn').style.display = 'block';
}
}
});
document.addEventListener('mouseout', (e) => {
if (e.target.closest('.text-input-group')) {
const group = e.target.closest('.text-input-group');
group.querySelector('.remove-text-btn').style.display = 'none';
}
});
setupRemoveButtons();
// API Configuration
const API_URL = '/embed';
// Check API health
async function checkApiHealth() {
try {
const response = await fetch('/health');
if (response.ok) {
document.getElementById('api-status').innerHTML =
'API Status: <span class="text-green-500">Online</span>';
} else {
throw new Error('API not responding');
}
} catch (error) {
document.getElementById('api-status').innerHTML =
'API Status: <span class="text-red-500">Offline</span>';
console.error('API health check failed:', error);
}
}
// Initial health check
checkApiHealth();
setInterval(checkApiHealth, 30000); // Check every 30 seconds
// Event Listeners
generateBtn.addEventListener('click', async () => {
const textInputs = Array.from(document.querySelectorAll('.text-input-group textarea'));
const texts = textInputs
.map(input => input.value.trim())
.filter(text => text.length > 0);
if (texts.length === 0) {
alert('Please enter at least one text input');
return;
}
// Show loading state
generateBtn.disabled = true;
generateBtn.innerHTML = '<i data-feather="loader" class="animate-spin mr-2"></i> Processing...';
feather.replace();
// Make API call to backend
try {
const response = await fetch('/embed', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
texts: texts
})
});
if (!response.ok) {
throw new Error(`API Error: ${response.status}`);
}
const data = await response.json();
const embeddings = texts.map((text, index) => ({
text: text,
vector: data.embeddings[index],
model: "multilingual-e5-large",
timestamp: new Date().toISOString()
}));
// Display the embeddings
outputVector.textContent = JSON.stringify(embeddings.length === 1 ? embeddings[0] : embeddings, null, 2);
placeholder.classList.add('hidden');
outputContainer.classList.remove('hidden');
copyBtn.disabled = false;
downloadBtn.classList.remove('hidden');
} catch (error) {
alert(`Failed to generate embeddings: ${error.message}`);
console.error(error);
}
// Reset button
generateBtn.disabled = false;
generateBtn.innerHTML = '<i data-feather="zap" class="mr-2"></i> Generate Embeddings';
feather.replace();
});
clearBtn.addEventListener('click', () => {
document.querySelectorAll('.text-input-group textarea').forEach(input => {
input.value = '';
});
outputVector.textContent = '';
outputContainer.classList.add('hidden');
placeholder.classList.remove('hidden');
copyBtn.disabled = true;
downloadBtn.classList.add('hidden');
});
copyBtn.addEventListener('click', () => {
navigator.clipboard.writeText(outputVector.textContent)
.then(() => {
copyBtn.innerHTML = '<i data-feather="check" class="mr-1"></i> Copied!';
feather.replace();
setTimeout(() => {
copyBtn.innerHTML = '<i data-feather="copy" class="mr-1"></i> Copy';
feather.replace();
}, 2000);
});
});
downloadBtn.addEventListener('click', () => {
const blob = new Blob([outputVector.textContent], { type: 'application/json' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `embedding-${new Date().getTime()}.json`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
});
</script>
</body>
</html>