pvanand's picture
Upload 7 files
68ca1f1 verified
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"/>
<title>HTML Compressor for LLM</title>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/themes/prism.min.css">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/plugins/toolbar/prism-toolbar.min.css">
<style>
:root {
--primary-color: #007bff;
--secondary-color: #6c757d;
--success-color: #28a745;
--border-color: #dee2e6;
--background-color: #f8f9fa;
}
body {
font-family: system-ui, -apple-system, sans-serif;
line-height: 1.6;
margin: 0;
padding: 20px;
background: var(--background-color);
}
.container {
max-width: 1200px;
margin: 0 auto;
background: white;
padding: 30px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
textarea {
width: 100%;
height: 200px;
padding: 12px;
border: 1px solid var(--border-color);
border-radius: 4px;
font-family: 'Monaco', 'Menlo', monospace;
font-size: 14px;
resize: vertical;
margin-bottom: 15px;
}
.options-container {
background: var(--background-color);
padding: 20px;
border-radius: 8px;
margin: 20px 0;
}
.option-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
}
.option-item {
display: flex;
align-items: center;
gap: 10px;
}
.button-group {
display: flex;
gap: 10px;
margin: 15px 0;
}
button {
background: var(--primary-color);
color: white;
padding: 8px 16px;
border: none;
border-radius: 4px;
cursor: pointer;
transition: background 0.2s;
}
button:hover {
background: #0056b3;
}
.results-container {
margin-top: 30px;
}
.results-tabs {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.tab {
padding: 8px 16px;
cursor: pointer;
border: 1px solid var(--border-color);
border-radius: 4px;
transition: all 0.2s;
}
.tab.active {
background: var(--primary-color);
color: white;
}
.result-panel {
border: 1px solid var(--border-color);
border-radius: 4px;
overflow: hidden;
}
.result-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
background: var(--background-color);
border-bottom: 1px solid var(--border-color);
}
.result-content {
padding: 15px;
overflow: auto;
max-height: 500px;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 15px;
margin: 20px 0;
}
.stat-item {
background: white;
padding: 15px;
border-radius: 4px;
border: 1px solid var(--border-color);
}
.stat-value {
font-size: 1.2em;
font-weight: bold;
color: var(--primary-color);
}
.copy-feedback {
position: fixed;
bottom: 20px;
right: 20px;
background: var(--success-color);
color: white;
padding: 10px 20px;
border-radius: 4px;
display: none;
}
.operation-status {
margin: 20px 0;
padding: 15px;
border: 1px solid var(--border-color);
border-radius: 4px;
}
.status-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
gap: 10px;
margin-top: 10px;
}
.status-item {
display: flex;
align-items: center;
gap: 8px;
padding: 8px;
border-radius: 4px;
background: var(--background-color);
}
.status-icon {
width: 20px;
height: 20px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
color: white;
font-size: 12px;
}
.status-success {
background: var(--success-color);
}
.status-error {
background: #dc3545;
}
.status-message {
font-size: 0.9em;
color: #666;
margin-top: 4px;
}
pre {
margin: 0;
border-radius: 4px;
}
code {
font-family: 'Monaco', 'Menlo', monospace;
font-size: 14px;
}
</style>
</head>
<body>
<div class="container">
<h1>HTML Compressor for LLM</h1>
<p>Compress HTML content for optimal LLM processing while preserving essential structure.</p>
<form id="compressorForm">
<textarea
name="html"
id="htmlInput"
placeholder="Paste your HTML here or upload a file..."
></textarea>
<div class="options-container">
<h3>Compression Options</h3>
<div class="option-grid">
<div class="option-item">
<input type="checkbox" id="cleanHead" name="cleanHead" checked>
<label for="cleanHead">Clean head section</label>
</div>
<div class="option-item">
<input type="checkbox" id="removeScripts" name="removeScripts" checked>
<label for="removeScripts">Remove scripts</label>
</div>
<div class="option-item">
<input type="checkbox" id="removeStyles" name="removeStyles" checked>
<label for="removeStyles">Remove styles</label>
</div>
<div class="option-item">
<input type="checkbox" id="handleRepeatingElements" name="handleRepeatingElements" checked>
<label for="handleRepeatingElements">Handle repeating elements</label>
</div>
<div class="option-item">
<input type="checkbox" id="truncateText" name="truncateText" checked>
<label for="truncateText">Truncate text</label>
</div>
<div class="option-item">
<label for="truncateLength">Max text length:</label>
<input type="number" id="truncateLength" name="truncateLength" value="100" min="10" max="1000">
</div>
<div class="option-item">
<input type="checkbox" id="minifyHtml" name="minifyHtml" checked>
<label for="minifyHtml">Minify HTML</label>
</div>
<div class="option-item">
<input type="checkbox" id="removeMedia" name="removeMedia" checked>
<label for="removeMedia">Remove media</label>
</div>
</div>
</div>
<div class="button-group">
<input type="file" accept=".html,.htm" id="fileInput">
<button type="submit">Process HTML</button>
</div>
</form>
<div id="operationStatus" class="operation-status" style="display: none;">
<h3>Operation Status</h3>
<div class="status-grid"></div>
</div>
<div id="stats" class="stats-grid" style="display: none;"></div>
<div class="results-container" style="display: none;">
<div class="results-tabs">
<div class="tab active" data-view="html">Compressed HTML</div>
<div class="tab" data-view="json">JSON Structure</div>
</div>
<div class="result-panel" id="htmlView">
<div class="result-header">
<h3>HTML Output</h3>
<div class="button-group">
<button onclick="copyResult('html')">Copy</button>
<button onclick="downloadResult('html')">Download</button>
</div>
</div>
<div class="result-content">
<pre><code class="language-html" id="htmlOutput"></code></pre>
</div>
</div>
<div class="result-panel" id="jsonView" style="display: none;">
<div class="result-header">
<h3>JSON Structure</h3>
<div class="button-group">
<button onclick="copyResult('json')">Copy</button>
<button onclick="downloadResult('json')">Download</button>
</div>
</div>
<div class="result-content">
<pre><code class="language-json" id="jsonOutput"></code></pre>
</div>
</div>
</div>
<div class="copy-feedback">Copied to clipboard!</div>
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/prism.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-markup.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.24.1/components/prism-json.min.js"></script>
<script>
const form = document.getElementById('compressorForm');
const fileInput = document.getElementById('fileInput');
const htmlInput = document.getElementById('htmlInput');
const resultsContainer = document.querySelector('.results-container');
const statsContainer = document.getElementById('stats');
const copyFeedback = document.querySelector('.copy-feedback');
// Tab switching
document.querySelectorAll('.tab').forEach(tab => {
tab.addEventListener('click', () => {
// Update tabs
document.querySelectorAll('.tab').forEach(t => t.classList.remove('active'));
tab.classList.add('active');
// Update views
const view = tab.dataset.view;
document.getElementById('htmlView').style.display = view === 'html' ? 'block' : 'none';
document.getElementById('jsonView').style.display = view === 'json' ? 'block' : 'none';
});
});
// File input handler
fileInput.addEventListener('change', (e) => {
const file = e.target.files[0];
if (file) {
const reader = new FileReader();
reader.onload = (e) => htmlInput.value = e.target.result;
reader.readAsText(file);
}
});
// Form submission
form.addEventListener('submit', async (e) => {
e.preventDefault();
const formData = new FormData(form);
// Add checkbox states
document.querySelectorAll('input[type="checkbox"]').forEach(checkbox => {
formData.set(checkbox.name, checkbox.checked);
});
try {
const response = await fetch('/process', {
method: 'POST',
body: formData,
});
const data = await response.json();
if (data.error) {
alert(data.error);
return;
}
// Display operation status
const statusContainer = document.querySelector('#operationStatus');
const statusGrid = statusContainer.querySelector('.status-grid');
statusContainer.style.display = 'block';
statusGrid.innerHTML = Object.entries(data.operationStatus)
.map(([operation, status]) => `
<div class="status-item">
<div class="status-icon ${status.success ? 'status-success' : 'status-error'}">
${status.success ? '✓' : '✗'}
</div>
<div>
<div>${formatLabel(operation)}</div>
${status.error ? `<div class="status-message">Error: ${status.error}</div>` : ''}
</div>
</div>
`).join('');
// Display stats
statsContainer.style.display = 'grid';
statsContainer.innerHTML = Object.entries(data.stats)
.map(([key, value]) => `
<div class="stat-item">
<div class="stat-label">${formatLabel(key)}</div>
<div class="stat-value">${value}</div>
</div>
`).join('');
// Show results container
resultsContainer.style.display = 'block';
// Update outputs with syntax highlighting
document.getElementById('htmlOutput').textContent = data.result.html;
document.getElementById('jsonOutput').textContent = data.result.json;
// Trigger Prism highlighting
Prism.highlightAll();
} catch (err) {
alert('Error processing HTML: ' + err.message);
}
});
// Utility functions
function formatLabel(key) {
return key
.replace(/([A-Z])/g, ' $1')
.replace(/([a-z])([A-Z])/g, '$1 $2')
.toLowerCase()
.replace(/^./, str => str.toUpperCase())
.replace('Html', 'HTML');
}
async function copyResult(type) {
const content = document.getElementById(`${type}Output`).textContent;
try {
await navigator.clipboard.writeText(content);
showCopyFeedback();
} catch (err) {
alert('Failed to copy to clipboard');
}
}
function downloadResult(type) {
const content = document.getElementById(`${type}Output`).textContent;
const blob = new Blob([content], { type: 'text/plain' });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `compressed.${type}`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}
function showCopyFeedback() {
copyFeedback.style.display = 'block';
setTimeout(() => {
copyFeedback.style.display = 'none';
}, 2000);
}
</script>
</body>
</html>