Spaces:
Running
Running
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>Advanced Course Creator Tool</title> | |
<script src="https://cdn.tailwindcss.com"></script> | |
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/mammoth/1.4.0/mammoth.browser.min.js"></script> | |
<style> | |
.file-drop-area { | |
border: 2px dashed #3b82f6; | |
transition: all 0.3s ease; | |
} | |
.file-drop-area.active { | |
border-color: #10b981; | |
background-color: rgba(16, 185, 129, 0.05); | |
} | |
.course-item { | |
transition: all 0.2s ease; | |
} | |
.course-item:hover { | |
transform: translateY(-2px); | |
} | |
/* Custom scrollbar */ | |
::-webkit-scrollbar { | |
width: 8px; | |
} | |
::-webkit-scrollbar-track { | |
background: #f1f1f1; | |
} | |
::-webkit-scrollbar-thumb { | |
background: #3b82f6; | |
border-radius: 4px; | |
} | |
/* Modal animation */ | |
.modal-enter { | |
opacity: 0; | |
transform: translateY(-10px); | |
} | |
.modal-enter-active { | |
opacity: 1; | |
transform: translateY(0); | |
transition: all 0.3s ease; | |
} | |
.loader { | |
border-top-color: #3b82f6; | |
animation: spin 1s linear infinite; | |
} | |
@keyframes spin { | |
0% { transform: rotate(0deg); } | |
100% { transform: rotate(360deg); } | |
} | |
</style> | |
</head> | |
<body class="bg-gray-50 min-h-screen"> | |
<div class="container mx-auto px-4 py-8"> | |
<header class="text-center mb-12"> | |
<h1 class="text-4xl font-bold text-gray-800 mb-2">Advanced Course Creator</h1> | |
<p class="text-lg text-gray-600">Transform your documents into structured online courses</p> | |
<div class="w-24 h-1 bg-blue-500 mx-auto mt-4 rounded-full"></div> | |
</header> | |
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8"> | |
<!-- Upload Section --> | |
<div class="lg:col-span-1 bg-white rounded-xl shadow-md overflow-hidden"> | |
<div class="p-6"> | |
<div class="flex items-center mb-4"> | |
<div class="bg-blue-100 p-2 rounded-full mr-3"> | |
<i class="fas fa-file-upload text-blue-500 text-xl"></i> | |
</div> | |
<h2 class="text-xl font-semibold text-gray-800">Upload Documents</h2> | |
</div> | |
<div id="fileDropArea" class="file-drop-area rounded-lg p-8 text-center cursor-pointer mb-4"> | |
<i class="fas fa-cloud-upload-alt text-4xl text-blue-400 mb-3"></i> | |
<p class="font-medium text-gray-600 mb-2">Drag & drop your files here</p> | |
<p class="text-sm text-gray-500 mb-4">or</p> | |
<label for="fileInput" class="inline-block px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition cursor-pointer"> | |
<input id="fileInput" type="file" accept=".txt,.pdf,.docx" multiple class="hidden"> | |
<span>Browse Files</span> | |
</label> | |
<p class="text-xs text-gray-500 mt-3">Supported formats: TXT, PDF, DOCX</p> | |
</div> | |
<div class="mt-6"> | |
<h3 class="font-medium text-gray-700 mb-3">File Processing Options</h3> | |
<div class="space-y-3"> | |
<label class="flex items-center"> | |
<input type="checkbox" id="autoSplitChapters" class="form-checkbox h-4 w-4 text-blue-500" checked> | |
<span class="ml-2 text-gray-700">Auto-split into chapters</span> | |
</label> | |
<label class="flex items-center"> | |
<input type="checkbox" id="detectHeadings" class="form-checkbox h-4 w-4 text-blue-500" checked> | |
<span class="ml-2 text-gray-700">Detect headings as sections</span> | |
</label> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Course Structure Section --> | |
<div class="lg:col-span-2 bg-white rounded-xl shadow-md overflow-hidden"> | |
<div class="p-6"> | |
<div class="flex items-center mb-4"> | |
<div class="bg-purple-100 p-2 rounded-full mr-3"> | |
<i class="fas fa-graduation-cap text-purple-500 text-xl"></i> | |
</div> | |
<h2 class="text-xl font-semibold text-gray-800">Course Structure</h2> | |
</div> | |
<div class="mb-6"> | |
<input type="text" id="courseTitle" placeholder="Enter course title" class="w-full px-4 py-3 border border-gray-300 rounded-lg focus:ring-2 focus:ring-blue-500 focus:border-blue-500"> | |
</div> | |
<div id="courseOutline" class="space-y-3"> | |
<!-- Example content that will be replaced --> | |
<div class="p-4 bg-gray-100 rounded-lg text-center text-gray-500"> | |
<p>Upload documents to see the generated course outline here</p> | |
</div> | |
</div> | |
<div class="mt-6 flex justify-between items-center"> | |
<div class="flex items-center"> | |
<div id="processingLoader" class="loader ease-linear rounded-full border-4 border-t-4 border-gray-200 h-6 w-6 mr-2 hidden"></div> | |
<span id="processingStatus" class="text-sm text-gray-500">Ready to process</span> | |
</div> | |
<button id="generateCourseBtn" class="px-6 py-2 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition flex items-center disabled:opacity-50"> | |
<i class="fas fa-magic mr-2"></i> | |
Generate Course | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Preview Section --> | |
<div id="previewSection" class="mt-12 bg-white rounded-xl shadow-md overflow-hidden hidden"> | |
<div class="p-6"> | |
<div class="flex items-center mb-4"> | |
<div class="bg-green-100 p-2 rounded-full mr-3"> | |
<i class="fas fa-eye text-green-500 text-xl"></i> | |
</div> | |
<h2 class="text-xl font-semibold text-gray-800">Course Preview</h2> | |
</div> | |
<div class="border border-gray-200 rounded-lg p-6"> | |
<div id="coursePreview" class="prose max-w-none"> | |
<!-- Preview content will be inserted here --> | |
</div> | |
</div> | |
<div class="mt-6 flex justify-end space-x-3"> | |
<button id="exportPdfBtn" class="px-4 py-2 border border-gray-300 hover:bg-gray-50 text-gray-700 font-medium rounded-lg transition flex items-center"> | |
<i class="fas fa-file-pdf mr-2 text-red-500"></i> | |
Export PDF | |
</button> | |
<button id="exportHtmlBtn" class="px-4 py-2 border border-gray-300 hover:bg-gray-50 text-gray-700 font-medium rounded-lg transition flex items-center"> | |
<i class="fas fa-file-code mr-2 text-blue-500"></i> | |
Export HTML | |
</button> | |
<button id="publishBtn" class="px-6 py-2 bg-green-500 hover:bg-green-600 text-white font-medium rounded-lg transition flex items-center"> | |
<i class="fas fa-rocket mr-2"></i> | |
Publish | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<!-- Modal --> | |
<div id="successModal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 hidden"> | |
<div class="bg-white rounded-xl p-6 max-w-md w-full transform transition-all modal-enter"> | |
<div class="text-center"> | |
<div class="mx-auto flex items-center justify-center h-12 w-12 rounded-full bg-green-100 mb-4"> | |
<i class="fas fa-check text-green-500 text-xl"></i> | |
</div> | |
<h3 class="text-lg font-medium text-gray-900 mb-2" id="modalTitle">Success!</h3> | |
<p class="text-sm text-gray-500" id="modalMessage">Your course has been successfully generated.</p> | |
<div class="mt-5"> | |
<button id="modalCloseBtn" type="button" class="px-4 py-2 bg-blue-500 hover:bg-blue-600 text-white font-medium rounded-lg transition"> | |
Continue | |
</button> | |
</div> | |
</div> | |
</div> | |
</div> | |
<script> | |
// Set PDF.js worker path | |
pdfjsLib.GlobalWorkerOptions.workerSrc = 'https://cdnjs.cloudflare.com/ajax/libs/pdf.js/2.11.338/pdf.worker.min.js'; | |
document.addEventListener('DOMContentLoaded', function() { | |
// Elements | |
const fileDropArea = document.getElementById('fileDropArea'); | |
const fileInput = document.getElementById('fileInput'); | |
const courseOutline = document.getElementById('courseOutline'); | |
const generateCourseBtn = document.getElementById('generateCourseBtn'); | |
const previewSection = document.getElementById('previewSection'); | |
const processingLoader = document.getElementById('processingLoader'); | |
const processingStatus = document.getElementById('processingStatus'); | |
const coursePreview = document.getElementById('coursePreview'); | |
const successModal = document.getElementById('successModal'); | |
const modalCloseBtn = document.getElementById('modalCloseBtn'); | |
const autoSplitChapters = document.getElementById('autoSplitChapters'); | |
const detectHeadings = document.getElementById('detectHeadings'); | |
const courseTitle = document.getElementById('courseTitle'); | |
// File handling | |
fileDropArea.addEventListener('dragover', (e) => { | |
e.preventDefault(); | |
fileDropArea.classList.add('active'); | |
}); | |
fileDropArea.addEventListener('dragleave', () => { | |
fileDropArea.classList.remove('active'); | |
}); | |
fileDropArea.addEventListener('drop', (e) => { | |
e.preventDefault(); | |
fileDropArea.classList.remove('active'); | |
handleFiles(e.dataTransfer.files); | |
}); | |
fileInput.addEventListener('change', () => { | |
if (fileInput.files.length > 0) { | |
handleFiles(fileInput.files); | |
} | |
}); | |
// Initialize empty course data | |
let courseData = { | |
title: '', | |
chapters: [] | |
}; | |
// Process uploaded files | |
async function handleFiles(files) { | |
processingLoader.classList.remove('hidden'); | |
processingStatus.textContent = 'Processing files...'; | |
generateCourseBtn.disabled = true; | |
courseData = { | |
title: courseTitle.value || 'New Course', | |
chapters: [] | |
}; | |
for (let file of files) { | |
try { | |
const content = await extractTextFromFile(file); | |
const chapterTitle = file.name.replace(/\.[^/.]+$/, ''); // Remove extension | |
courseData.chapters.push({ | |
title: chapterTitle, | |
content: content, | |
sections: autoSplitChapters.checked && detectHeadings.checked ? | |
splitIntoSections(content) : | |
[{ title: 'Content', content: content }] | |
}); | |
} catch (error) { | |
console.error('Error processing file:', error); | |
} | |
} | |
renderCourseOutline(); | |
processingLoader.classList.add('hidden'); | |
processingStatus.textContent = `${files.length} file(s) processed`; | |
generateCourseBtn.disabled = false; | |
} | |
// Extract text from different file types | |
async function extractTextFromFile(file) { | |
return new Promise((resolve, reject) => { | |
const reader = new FileReader(); | |
reader.onload = async (e) => { | |
try { | |
if (file.type === 'application/pdf') { | |
const pdf = await pdfjsLib.getDocument(e.target.result).promise; | |
let text = ''; | |
for (let i = 1; i <= pdf.numPages; i++) { | |
const page = await pdf.getPage(i); | |
const content = await page.getTextContent(); | |
const strings = content.items.map(item => item.str); | |
text += strings.join(' ') + '\n'; | |
} | |
resolve(text); | |
} else if (file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { | |
const arrayBuffer = e.target.result; | |
const result = await mammoth.extractRawText({ arrayBuffer }); | |
resolve(result.value); | |
} else { // Plain text | |
resolve(e.target.result); | |
} | |
} catch (error) { | |
reject(error); | |
} | |
}; | |
if (file.type === 'application/pdf' || file.type === 'application/vnd.openxmlformats-officedocument.wordprocessingml.document') { | |
reader.readAsArrayBuffer(file); | |
} else { | |
reader.readAsText(file); | |
} | |
}); | |
} | |
// Split content into sections based on headings | |
function splitIntoSections(content) { | |
// Simple implementation - can be enhanced with NLP | |
const lines = content.split('\n'); | |
const sections = []; | |
let currentSection = { title: 'Introduction', content: '' }; | |
for (const line of lines) { | |
if (line.match(/^(#+ )|([A-Z][^\.!?]+:\s*$)|(Chapter \d+:|Section \d+:)/)) { | |
if (currentSection.content.trim()) { | |
sections.push(currentSection); | |
} | |
currentSection = { title: line.trim(), content: '' }; | |
} else { | |
currentSection.content += line + '\n'; | |
} | |
} | |
if (currentSection.content.trim()) { | |
sections.push(currentSection); | |
} | |
return sections.length > 0 ? sections : [{ title: 'Content', content }]; | |
} | |
// Render the course outline | |
function renderCourseOutline() { | |
if (courseData.chapters.length === 0) { | |
courseOutline.innerHTML = ` | |
<div class="p-4 bg-gray-100 rounded-lg text-center text-gray-500"> | |
<p>Upload documents to see the generated course outline here</p> | |
</div> | |
`; | |
return; | |
} | |
let html = '<div class="space-y-4">'; | |
courseData.chapters.forEach((chapter, chapterIndex) => { | |
html += ` | |
<div class="course-item bg-white border border-gray-200 rounded-lg overflow-hidden"> | |
<div class="px-4 py-3 bg-gray-50 border-b border-gray-200 flex justify-between items-center"> | |
<h3 class="font-medium text-gray-800 flex items-center"> | |
<i class="far fa-folder-open text-blue-400 mr-2"></i> | |
${chapter.title} | |
</h3> | |
<button class="text-gray-400 hover:text-gray-600" onclick="toggleChapterSections(${chapterIndex})"> | |
<i class="fas fa-chevron-down"></i> | |
</button> | |
</div> | |
<div id="chapter-${chapterIndex}-sections" class="divide-y divide-gray-200"> | |
`; | |
chapter.sections.forEach((section, sectionIndex) => { | |
html += ` | |
<div class="px-4 py-3 flex items-start"> | |
<div class="mr-3 mt-1"> | |
<i class="far fa-file-alt text-gray-400"></i> | |
</div> | |
<div class="flex-1"> | |
<h4 class="font-medium text-gray-700">${section.title}</h4> | |
<p class="text-sm text-gray-500 line-clamp-2">${section.content.substring(0, 100)}...</p> | |
</div> | |
<div class="flex space-x-2 ml-2"> | |
<button class="text-gray-400 hover:text-blue-500" title="Edit" onclick="editSection(${chapterIndex}, ${sectionIndex})"> | |
<i class="fas fa-pencil-alt text-sm"></i> | |
</button> | |
<button class="text-gray-400 hover:text-red-500" title="Delete" onclick="deleteSection(${chapterIndex}, ${sectionIndex})"> | |
<i class="fas fa-trash-alt text-sm"></i> | |
</button> | |
</div> | |
</div> | |
`; | |
}); | |
html += ` | |
</div> | |
</div> | |
`; | |
}); | |
html += ` | |
<button onclick="addNewChapter()" class="w-full p-3 border-2 border-dashed border-gray-300 hover:border-blue-400 rounded-lg text-gray-500 hover:text-blue-500 transition flex items-center justify-center"> | |
<i class="fas fa-plus mr-2"></i> | |
Add New Chapter | |
</button> | |
</div> | |
`; | |
courseOutline.innerHTML = html; | |
} | |
// Generate the final course | |
generateCourseBtn.addEventListener('click', function() { | |
processingLoader.classList.remove('hidden'); | |
processingStatus.textContent = 'Generating course...'; | |
// Update course title if changed | |
courseData.title = courseTitle.value || 'New Course'; | |
// Generate preview | |
setTimeout(() => { | |
renderCoursePreview(); | |
previewSection.classList.remove('hidden'); | |
processingLoader.classList.add('hidden'); | |
processingStatus.textContent = 'Course generated'; | |
// Scroll to preview | |
previewSection.scrollIntoView({ behavior: 'smooth' }); | |
// Show success modal | |
showModal('Course Generated', 'Your course has been successfully created from your documents.'); | |
}, 1000); | |
}); | |
// Render the course preview | |
function renderCoursePreview() { | |
let html = ` | |
<h1 class="text-3xl font-bold mb-6 text-center">${courseData.title}</h1> | |
<div class="space-y-8"> | |
`; | |
courseData.chapters.forEach(chapter => { | |
html += ` | |
<div class="chapter"> | |
<h2 class="text-2xl font-semibold mb-4 flex items-center"> | |
<i class="far fa-folder-open text-blue-500 mr-3"></i> | |
${chapter.title} | |
</h2> | |
<div class="ml-8 space-y-6"> | |
`; | |
chapter.sections.forEach(section => { | |
html += ` | |
<div class="section"> | |
<h3 class="text-xl font-medium mb-3">${section.title}</h3> | |
<div class="prose prose-sm max-w-none"> | |
<p>${section.content.split('\n').join('</p><p>')}</p> | |
</div> | |
</div> | |
`; | |
}); | |
html += ` | |
</div> | |
</div> | |
`; | |
}); | |
html += `</div>`; | |
coursePreview.innerHTML = html; | |
} | |
// Show modal | |
function showModal(title, message) { | |
document.getElementById('modalTitle').textContent = title; | |
document.getElementById('modalMessage').textContent = message; | |
successModal.classList.remove('hidden'); | |
} | |
// Close modal | |
modalCloseBtn.addEventListener('click', function() { | |
successModal.classList.add('hidden'); | |
}); | |
// Export functions | |
document.getElementById('exportPdfBtn').addEventListener('click', function() { | |
showModal('Export Feature', 'PDF export would be implemented here with a library like jsPDF.'); | |
}); | |
document.getElementById('exportHtmlBtn').addEventListener('click', function() { | |
showModal('Export Feature', 'HTML export would be implemented here, generating a downloadable HTML file.'); | |
}); | |
document.getElementById('publishBtn').addEventListener('click', function() { | |
showModal('Publish Feature', 'In a real application, this would publish your course to a learning management system.'); | |
}); | |
// Global functions accessible from inline handlers | |
window.toggleChapterSections = function(chapterIndex) { | |
const sectionsDiv = document.getElementById(`chapter-${chapterIndex}-sections`); | |
const toggleBtn = sectionsDiv.previousElementSibling.querySelector('button'); | |
if (sectionsDiv.classList.contains('hidden')) { | |
sectionsDiv.classList.remove('hidden'); | |
toggleBtn.innerHTML = '<i class="fas fa-chevron-down"></i>'; | |
} else { | |
sectionsDiv.classList.add('hidden'); | |
toggleBtn.innerHTML = '<i class="fas fa-chevron-right"></i>'; | |
} | |
}; | |
window.editSection = function(chapterIndex, sectionIndex) { | |
const section = courseData.chapters[chapterIndex].sections[sectionIndex]; | |
showModal('Edit Section', `Editing feature would be implemented here for: "${section.title}"`); | |
}; | |
window.deleteSection = function(chapterIndex, sectionIndex) { | |
if (confirm('Are you sure you want to delete this section?')) { | |
courseData.chapters[chapterIndex].sections.splice(sectionIndex, 1); | |
renderCourseOutline(); | |
} | |
}; | |
window.addNewChapter = function() { | |
const chapterTitle = prompt('Enter chapter title:', 'New Chapter'); | |
if (chapterTitle) { | |
courseData.chapters.push({ | |
title: chapterTitle, | |
content: '', | |
sections: [{ title: 'New Section', content: '' }] | |
}); | |
renderCourseOutline(); | |
} | |
}; | |
}); | |
</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=zoeboy/criador-de-cursos" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body> | |
</html> |