render-markdown / index.html
tatht's picture
Chỗ này đâu có dùng react-markdown đâu - Follow Up Deployment
550be8d verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>JSON Markdown Renderer</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.7.0/styles/github.min.css">
<script src="https://unpkg.com/highlight.js@11.7.0/lib/core.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/js/all.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/showdown/2.1.0/showdown.min.js"></script>
<style>
.markdown-body {
box-sizing: border-box;
min-width: 200px;
max-width: 980px;
margin: 0 auto;
padding: 45px;
}
@media (max-width: 767px) {
.markdown-body {
padding: 15px;
}
}
.hidden {
display: none;
}
.dropzone {
border: 2px dashed #6b7280;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #3b82f6;
background-color: rgba(59, 130, 246, 0.05);
}
pre {
border-radius: 6px;
overflow-x: auto;
padding: 16px;
background-color: #f6f8fa;
}
code {
font-family: ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<div class="max-w-4xl mx-auto">
<header class="text-center mb-10">
<h1 class="text-3xl font-bold text-gray-800 mb-2">JSON Markdown Renderer</h1>
<p class="text-gray-600">Upload a JSON file containing markdown content and see it rendered beautifully</p>
</header>
<div class="bg-white rounded-xl shadow-md overflow-hidden mb-8 transition-all duration-300">
<div id="upload-container" class="p-8">
<div id="dropzone" class="dropzone rounded-lg p-12 text-center cursor-pointer mb-6">
<div class="mx-auto w-16 h-16 text-blue-500 mb-4">
<i class="fas fa-cloud-upload-alt text-5xl"></i>
</div>
<h3 class="text-lg font-medium text-gray-700 mb-1">Drag & drop your JSON file here</h3>
<p class="text-sm text-gray-500 mb-4">or</p>
<label for="file-upload" class="px-4 py-2 bg-blue-500 text-white rounded-md hover:bg-blue-600 transition-colors cursor-pointer">
<span class="font-medium">Browse Files</span>
<input id="file-upload" type="file" accept=".json,application/json" class="hidden">
</label>
<p class="text-xs text-gray-500 mt-4">Supports single JSON file with markdown content</p>
</div>
<div class="text-center">
<p class="text-gray-500 mb-2">Don't have a JSON file? Try with our sample:</p>
<button id="sample-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 transition-colors">
<i class="fas fa-file-alt mr-2"></i> Load Sample JSON
</button>
</div>
</div>
<div id="error-container" class="hidden p-4 bg-red-50 text-red-700 rounded-md mb-4">
<div class="flex items-center">
<i class="fas fa-exclamation-circle mr-2"></i>
<span id="error-message"></span>
</div>
</div>
</div>
<div id="result-container" class="hidden bg-white rounded-xl shadow-md overflow-hidden transition-all duration-300">
<div class="border-b border-gray-200 px-6 py-4 flex justify-between items-center bg-gray-50">
<h2 class="text-lg font-medium text-gray-800">Rendered Markdown</h2>
<button id="back-btn" class="px-3 py-1 bg-gray-200 text-gray-700 rounded-md text-sm hover:bg-gray-300 transition-colors">
<i class="fas fa-arrow-left mr-1"></i> Back
</button>
</div>
<div id="markdown-content" class="markdown-body p-6">
<!-- Markdown content will be rendered here -->
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Get DOM elements
const dropzone = document.getElementById('dropzone');
const fileUpload = document.getElementById('file-upload');
const uploadContainer = document.getElementById('upload-container');
const resultContainer = document.getElementById('result-container');
const markdownContent = document.getElementById('markdown-content');
const backBtn = document.getElementById('back-btn');
const sampleBtn = document.getElementById('sample-btn');
const errorContainer = document.getElementById('error-container');
const errorMessage = document.getElementById('error-message');
// Handle drag and drop events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropzone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropzone.classList.add('active');
}
function unhighlight() {
dropzone.classList.remove('active');
}
// Handle file drop
dropzone.addEventListener('drop', handleDrop, false);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
if (files.length > 0) {
handleFiles(files);
}
}
// Handle file selection via input
fileUpload.addEventListener('change', function() {
if (this.files.length > 0) {
handleFiles(this.files);
}
});
// Handle back button
backBtn.addEventListener('click', function() {
resultContainer.classList.add('hidden');
uploadContainer.classList.remove('hidden');
markdownContent.innerHTML = '';
fileUpload.value = '';
});
// Handle sample button
sampleBtn.addEventListener('click', renderSample);
// Process uploaded files
function handleFiles(files) {
const file = files[0];
if (file.type !== 'application/json' && !file.name.endsWith('.json')) {
showError('Please upload a valid JSON file.');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
try {
const jsonData = JSON.parse(e.target.result);
processJsonData(jsonData);
} catch (error) {
showError('Invalid JSON content: ' + error.message);
}
};
reader.onerror = function() {
showError('Error reading the file.');
};
reader.readAsText(file);
}
// Process JSON data and render markdown
function processJsonData(jsonData) {
hideError();
// Find the first markdown string in the JSON
let markdownText = findFirstMarkdownString(jsonData);
if (!markdownText) {
showError('No markdown content found in the JSON file.');
return;
}
// Show loading state
markdownContent.innerHTML = '<div class="text-center py-8"><i class="fas fa-spinner fa-spin text-3xl text-blue-500"></i><p class="mt-4 text-gray-600">Rendering markdown...</p></div>';
// Process markdown with Showdown.js
const converter = new showdown.Converter();
const html = converter.makeHtml(markdownText);
markdownContent.innerHTML = html;
// Switch to result view
uploadContainer.classList.add('hidden');
resultContainer.classList.remove('hidden');
}
// Recursively find the first string that looks like markdown
function findFirstMarkdownString(obj) {
if (typeof obj === 'string') {
// Check if this looks like markdown
if (obj.includes('##') || obj.includes('*') || obj.includes('`') || obj.includes('![')) {
return obj;
}
} else if (Array.isArray(obj)) {
for (let item of obj) {
const result = findFirstMarkdownString(item);
if (result) return result;
}
} else if (typeof obj === 'object' && obj !== null) {
for (let key in obj) {
const result = findFirstMarkdownString(obj[key]);
if (result) return result;
}
}
return null;
}
// Display sample markdown
function renderSample() {
const sampleJson = {
"document": {
"title": "Sample Markdown",
"content": `# Welcome to JSON Markdown Renderer
This is a **sample markdown** content rendered from JSON.
## Features
- Render markdown from JSON files
- Drag and drop interface
- Syntax highlighting for code blocks
- Mobile responsive design
### Code Example
\`\`\`javascript
function greet(name) {
return 'Hello, ' + name + '!';
}
console.log(greet('World'));
\`\`\`
## Usage
1. Upload a JSON file containing markdown content
2. The system will extract and render the markdown
3. Enjoy beautifully formatted content!
[Learn more about Markdown](https://www.markdownguide.org)`,
"metadata": {
"author": "John Doe",
"created": "2023-06-01"
}
}
};
processJsonData(sampleJson);
}
// Show error message
function showError(message) {
errorMessage.textContent = message;
errorContainer.classList.remove('hidden');
}
// Hide error message
function hideError() {
errorContainer.classList.add('hidden');
}
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=tatht/render-markdown" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>