repo_to_md / html_template.html
broadfield-dev's picture
Update html_template.html
caca101 verified
<!DOCTYPE html>
<html>
<head>
<title>Repo & Files to Markdown</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;500&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
transition: background 0.3s, color 0.3s;
}
/* Dark Mode (Default) */
body.dark-mode {
background: linear-gradient(to bottom right, #1A252F, #2C3E50);
color: #E0E6ED;
}
body.dark-mode .input-section,
body.dark-mode .markdown-section,
body.dark-mode .preview-section {
background: #2F4054;
box-shadow: 0 6px 12px rgba(0, 0, 0, 0.3);
}
body.dark-mode input[type="text"],
body.dark-mode input[type="file"] {
background: #3A5063;
color: #FFFFFF;
border: 1px solid #4A6FA5;
}
body.dark-mode input[type="text"]:focus,
body.dark-mode input[type="file"]:focus {
border-color: #A66F3A;
}
body.dark-mode button {
background: linear-gradient(to right, #3498DB, #2C7DB2);
box-shadow: inset 0 0 5px rgba(52, 152, 219, 0.3);
}
body.dark-mode button:hover {
background: linear-gradient(to right, #2C7DB2, #3498DB);
box-shadow: inset 0 0 8px rgba(52, 152, 219, 0.5);
}
body.dark-mode #downloadBtn {
background: linear-gradient(to right, #8B5A2B, #7F4D1A);
box-shadow: inset 0 0 5px rgba(139, 90, 43, 0.3);
}
body.dark-mode #downloadBtn:hover {
background: linear-gradient(to right, #7F4D1A, #8B5A2B);
box-shadow: inset 0 0 8px rgba(139, 90, 43, 0.5);
}
body.dark-mode textarea {
background: #3A5063;
color: #FFFFFF;
border: 1px solid #4A6FA5;
}
body.dark-mode #output {
background: #3A5063;
}
body.dark-mode #output h1, body.dark-mode #output h2, body.dark-mode #output h3 {
color: #FFFFFF;
font-weight: 500;
letter-spacing: 0.5px;
}
body.dark-mode #output code,
body.dark-mode #output pre {
background: #2C3E50;
}
body.dark-mode .spinner {
border: 4px solid #2F4054;
border-top: 4px solid #E0E6ED;
}
/* Light Mode */
body.light-mode {
background: linear-gradient(to bottom right, #f0f4f8, #d9e2ec);
color: #333;
}
body.light-mode .input-section,
body.light-mode .markdown-section,
body.light-mode .preview-section {
background: white;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
}
body.light-mode input[type="text"],
body.light-mode input[type="file"] {
background: white;
color: #333;
border: 1px solid #ccc;
}
body.light-mode input[type="text"]:focus,
body.light-mode input[type="file"]:focus {
border-color: #4CAF50;
}
body.light-mode button {
background: linear-gradient(to right, #4CAF50, #45a049);
}
body.light-mode button:hover {
background: linear-gradient(to right, #45a049, #4CAF50);
}
body.light-mode #downloadBtn {
background: linear-gradient(to right, #3498db, #2980b9);
}
body.light-mode #downloadBtn:hover {
background: linear-gradient(to right, #2980b9, #3498db);
}
body.light-mode textarea {
background: white;
color: #333;
border: 1px solid #ccc;
}
body.light-mode #output {
background: white;
}
body.light-mode #output h1, body.light-mode #output h2, body.light-mode #output h3 {
color: #333;
}
body.light-mode #output code,
body.light-mode #output pre {
background: #f4f4f4;
}
body.light-mode .spinner {
border: 4px solid #f3f3f3;
border-top: 4px solid #3498db;
}
/* Common Styles */
.container {
max-width: 1200px;
margin: 20px auto;
padding: 20px;
position: relative;
}
.input-section, .markdown-section, .preview-section {
border-radius: 10px;
padding: 20px;
margin-bottom: 20px;
transition: background 0.3s, box-shadow 0.3s;
}
input[type="text"], input[type="file"] {
width: 100%;
padding: 10px;
margin: 10px 0;
border-radius: 5px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
transition: border-color 0.3s;
}
button {
padding: 10px 20px;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin: 5px;
transition: background 0.3s, transform 0.1s, box-shadow 0.3s;
}
button:active {
transform: scale(0.97) translateY(2px);
box-shadow: none;
}
.spinner {
display: none;
border-radius: 50%;
width: 30px;
height: 30px;
animation: spin 1s linear infinite;
margin: 20px auto;
}
@keyframes spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
.output-container {
display: flex;
flex-wrap: wrap;
gap: 20px;
}
.markdown-section, .preview-section {
flex: 1;
min-width: 300px;
}
textarea {
width: 100%;
height: 400px;
margin-top: 10px;
padding: 10px;
border-radius: 5px;
box-shadow: inset 0 1px 3px rgba(0, 0, 0, 0.1);
font-family: monospace;
resize: vertical;
transition: background 0.3s, color 0.3s, border 0.3s;
}
#output {
margin-top: 10px;
padding: 10px;
max-height: 600px;
overflow-y: auto;
border-radius: 5px;
border: 1px solid #4A6FA5;
transition: background 0.3s, border 0.3s;
}
#output code {
padding: 2px 4px;
border-radius: 3px;
}
#output pre {
padding: 10px;
border-radius: 5px;
overflow-x: auto;
}
.fade-in {
animation: fadeIn 0.5s ease-in;
}
@keyframes fadeIn {
from { opacity: 0; transform: scale(0.98); }
to { opacity: 1; transform: scale(1.02); }
}
/* Toggle Switch */
.mode-toggle {
position: absolute;
top: 20px;
right: 20px;
display: flex;
align-items: center;
}
.mode-toggle label {
margin-left: 10px;
font-size: 14px;
color: #E0E6ED;
}
.switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.switch input {
opacity: 0;
width: 0;
height: 0;
}
.slider {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: #7F8C8D;
transition: 0.3s;
border-radius: 24px;
}
.slider:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background: #ECF0F1;
transition: 0.3s;
border-radius: 50%;
}
input:checked + .slider {
background: #4CAF50;
}
input:checked + .slider:before {
transform: translateX(26px);
}
/* Copy Button */
.copy-button {
padding: 8px 16px;
background: #3498DB;
color: white;
border: none;
border-radius: 5px;
cursor: pointer;
margin-left: 10px;
transition: background 0.3s, transform 0.1s;
}
.copy-button:hover {
background: #2C7DB2;
}
.copy-button:active {
transform: scale(0.97) translateY(2px);
}
</style>
</head>
<body class="dark-mode">
<div class="container">
<div class="mode-toggle">
<label class="switch">
<input type="checkbox" id="modeToggle" onchange="toggleMode()">
<span class="slider"></span>
</label>
<label for="modeToggle">Light Mode</label>
</div>
<div class="input-section">
<h1>Repository & Files to Markdown Converter</h1>
<p>Enter a GitHub/Hugging Face Space URL (e.g., https://huggingface.co/spaces/username/space)</p>
<input type="text" id="repoUrl" placeholder="Enter GitHub or Hugging Face Space URL">
<p>OR upload files (select multiple files or a folder - folder upload supported in Chrome)</p>
<input type="file" id="fileInput" multiple webkitdirectory>
<br>
<button onclick="processRepo()">Convert URL</button>
<button onclick="processFiles()">Convert Files</button>
<button id="downloadBtn" style="display: none;" onclick="downloadMarkdown()">Download .md</button>
<div id="spinner" class="spinner"></div>
</div>
<div class="output-container">
<div class="markdown-section">
<h2>Markdown Output: <button class="copy-button" onclick="copyToClipboard()">Copy</button></h2>
<textarea id="markdownOutput" readonly></textarea>
</div>
<div class="preview-section">
<h2>Preview:</h2>
<div id="output"></div>
</div>
</div>
</div>
<script>
let currentMarkdown = '';
let currentFilename = '';
function toggleMode() {
const body = document.body;
const toggle = document.getElementById('modeToggle');
body.classList.toggle('dark-mode', !toggle.checked);
body.classList.toggle('light-mode', toggle.checked);
}
async function processRepo() {
const repoUrl = document.getElementById('repoUrl').value;
await processContent('/process', { repo_url: repoUrl });
}
async function processFiles() {
const files = document.getElementById('fileInput').files;
if (files.length === 0) {
alert('Please select at least one file or folder');
return;
}
const formData = new FormData();
for (let file of files) {
formData.append('files[]', file);
}
await processContent('/process', formData, false);
}
async function processContent(url, data, isJson = true) {
const spinner = document.getElementById('spinner');
const buttons = document.querySelectorAll('button');
spinner.style.display = 'block';
buttons.forEach(btn => btn.disabled = true);
try {
const options = {
method: 'POST',
...(isJson ? {
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
} : { body: data })
};
const response = await fetch(url, options);
const result = await response.json();
if (result.error) {
alert(result.error);
return;
}
currentMarkdown = result.markdown;
currentFilename = result.filename;
document.getElementById('markdownOutput').value = result.markdown;
document.getElementById('output').innerHTML = result.html;
document.getElementById('downloadBtn').style.display = 'inline-block';
const markdownOutput = document.getElementById('markdownOutput');
const output = document.getElementById('output');
markdownOutput.classList.add('fade-in');
output.classList.add('fade-in');
setTimeout(() => {
markdownOutput.classList.remove('fade-in');
output.classList.remove('fade-in');
}, 500);
} catch (error) {
alert('An error occurred: ' + error.message);
} finally {
spinner.style.display = 'none';
buttons.forEach(btn => btn.disabled = false);
}
}
async function downloadMarkdown() {
try {
const response = await fetch('/download', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
markdown: currentMarkdown,
filename: currentFilename
})
});
const blob = await response.blob();
const url = window.URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = currentFilename;
document.body.appendChild(a);
a.click();
a.remove();
window.URL.revokeObjectURL(url);
} catch (error) {
alert('Error downloading file: ' + error.message);
}
}
async function copyToClipboard() {
const markdownOutput = document.getElementById('markdownOutput');
try {
await navigator.clipboard.writeText(markdownOutput.value);
alert('Markdown copied to clipboard!');
} catch (err) {
alert('Failed to copy to clipboard: ' + err.message);
}
}
</script>
</body>
</html>