Spaces:
Running
Running
| <html lang="en"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Markdown/HTML Converter | PDF & DOCX</title> | |
| <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.0-alpha1/dist/css/bootstrap.min.css" rel="stylesheet"> | |
| <script src="https://unpkg.com/vue@3/dist/vue.global.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
| <script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script> | |
| <style> | |
| :root { | |
| --primary-color: #437494; | |
| --secondary-color: #29b98b; | |
| --accent-color: #7b7ac7; | |
| --background-color: #ecf0f1; | |
| --text-color: #2c3e50; | |
| --card-background: #ffffff; | |
| } | |
| body { | |
| background-color: var(--background-color); | |
| color: var(--text-color); | |
| } | |
| .card { | |
| background-color: var(--card-background); | |
| border: none; | |
| border-radius: 15px; | |
| box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); | |
| } | |
| .btn-primary { | |
| background-color: var(--primary-color); | |
| border-color: var(--primary-color); | |
| } | |
| .btn-primary:hover { | |
| background-color: var(--secondary-color); | |
| border-color: var(--secondary-color); | |
| } | |
| .btn-success { | |
| background-color: var(--secondary-color); | |
| border-color: var(--secondary-color); | |
| } | |
| .btn-success:hover { | |
| background-color: var(--primary-color); | |
| border-color: var(--primary-color); | |
| } | |
| .btn-warning { | |
| background-color: var(--accent-color); | |
| border-color: var(--accent-color); | |
| color: white; | |
| } | |
| .btn-warning:hover { | |
| background-color: #c0392b; | |
| border-color: #c0392b; | |
| color: white; | |
| } | |
| pre { | |
| background-color: #f7f9fa; | |
| padding: 15px; | |
| border-radius: 10px; | |
| border: 1px solid #e0e0e0; | |
| } | |
| .form-control:focus { | |
| border-color: var(--primary-color); | |
| box-shadow: 0 0 0 0.2rem rgba(52, 152, 219, 0.25); | |
| } | |
| .fade-enter-active, .fade-leave-active { | |
| transition: opacity 0.5s; | |
| } | |
| .fade-enter-from, .fade-leave-to { | |
| opacity: 0; | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div id="app" class="container py-5"> | |
| <div class="card p-4 mx-auto mb-5" style="max-width: 800px;"> | |
| <h1 class="text-center mb-4" style="color: var(--primary-color);">Markdown/HTML Converter</h1> | |
| <label for="inputType" class="form-label mb-0">Input Type:</label> | |
| <div class="d-flex align-items-center mb-3"> | |
| <div class="me-3 flex-grow-1"> | |
| <select id="inputType" v-model="inputType" class="form-select" style="width: 100%;"> | |
| <option value="markdown">Markdown</option> | |
| <option value="html">HTML</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="btn btn-primary mb-0"> | |
| <i class="fas fa-upload me-2"></i> Upload {{ inputType.toUpperCase() }} File | |
| <input type="file" @change="handleFileUpload" :accept="inputType === 'markdown' ? '.md,.markdown' : '.html,.htm'" class="d-none"> | |
| </label> | |
| </div> | |
| </div> | |
| <textarea v-model="inputContent" @input="updateCharCount" :placeholder="`Enter your ${inputType} here or upload a file`" class="form-control mb-3" rows="8"></textarea> | |
| <div class="text-end text-muted small mb-3">{{ charCount }} / 20000</div> | |
| <div v-if="fileName" class="text-center text-muted fst-italic mb-3">{{ fileName }}</div> | |
| <div class="d-flex justify-content-center mb-3"> | |
| <button @click="convertToPdf" :disabled="isConverting || !inputContent" class="btn btn-success me-2"> | |
| <i class="fas fa-file-pdf me-2"></i> Convert to PDF | |
| </button> | |
| <button @click="convertToDocx" :disabled="isConverting || !inputContent" class="btn btn-primary"> | |
| <i class="fas fa-file-word me-2"></i> Convert to DOCX | |
| </button> | |
| </div> | |
| <transition name="fade"> | |
| <p v-if="statusMessage" :class="{'text-danger': error, 'text-success': success, 'text-primary': isConverting}" class="mt-3 text-center fw-bold"> | |
| {{ statusMessage }} | |
| </p> | |
| </transition> | |
| </div> | |
| <!-- API Documentation --> | |
| <div class="card p-4 mx-auto mt-5" style="max-width: 800px;"> | |
| <h2 class="text-center mb-4" style="color: var(--primary-color);">API Documentation</h2> | |
| <button class="btn btn-primary mb-3" @click="toggleApiDocs"> | |
| {{ showApiDocs ? 'Hide API Docs' : 'Show API Docs' }} | |
| </button> | |
| <transition name="fade"> | |
| <div v-if="showApiDocs"> | |
| <h3 class="mt-4 mb-3">Convert HTML to PDF/DOCX</h3> | |
| <p>Endpoint: | |
| <code class="bg-light p-1 rounded">{{ selectedApi === 'pdf' ? 'https://pvanand-web-scraping.hf.space/html_to_pdf' : 'https://pvanand-web-scraping.hf.space/convert' }}</code> | |
| </p> | |
| <p>Method: <code class="bg-light p-1 rounded">POST</code></p> | |
| <p>Content-Type: <code class="bg-light p-1 rounded">application/json</code></p> | |
| <p>Request Body: | |
| <code class="bg-light p-1 rounded">{{ selectedApi === 'pdf' ? '{ "html_content": "Your HTML string here" }' : '{ "html": "Your HTML string here" }' }}</code> | |
| </p> | |
| <div class="mb-3"> | |
| <label for="apiSelect" class="form-label">Select API Example:</label> | |
| <select id="apiSelect" v-model="selectedApi" class="form-select"> | |
| <option value="pdf">PDF</option> | |
| <option value="docx">DOCX</option> | |
| </select> | |
| </div> | |
| <div class="mb-3"> | |
| <label for="languageSelect" class="form-label">Select Language:</label> | |
| <select id="languageSelect" v-model="selectedLanguage" class="form-select"> | |
| <option value="python">Python</option> | |
| <option value="curl">cURL</option> | |
| <option value="javascript">JavaScript</option> | |
| </select> | |
| </div> | |
| <pre v-if="selectedLanguage === 'python'"> | |
| import requests | |
| import json | |
| url = "{{ selectedApi === 'pdf' ? 'https://pvanand-web-scraping.hf.space/html_to_pdf' : 'https://pvanand-web-scraping.hf.space/convert' }}" | |
| payload = json.dumps({ | |
| "{{ selectedApi === 'pdf' ? 'html_content' : 'html' }}": "<h1>Hello, World!</h1>" | |
| }) | |
| headers = { | |
| 'Content-Type': 'application/json' | |
| } | |
| response = requests.post(url, headers=headers, data=payload) | |
| with open('output.{{ selectedApi }}', 'wb') as f: | |
| f.write(response.content) | |
| </pre> | |
| <pre v-if="selectedLanguage === 'curl'"> | |
| curl -X POST '{{ selectedApi === 'pdf' ? 'https://pvanand-web-scraping.hf.space/html_to_pdf' : 'https://pvanand-web-scraping.hf.space/convert' }}' \ | |
| -H 'Content-Type: application/json' \ | |
| -d '{"{{ selectedApi === 'pdf' ? 'html_content' : 'html' }}": "<h1>Hello, World!</h1>"}' \ | |
| --output output.{{ selectedApi }} | |
| </pre> | |
| <pre v-if="selectedLanguage === 'javascript'"> | |
| fetch('{{ selectedApi === 'pdf' ? 'https://pvanand-web-scraping.hf.space/html_to_pdf' : 'https://pvanand-web-scraping.hf.space/convert' }}', { | |
| method: 'POST', | |
| headers: { | |
| 'Content-Type': 'application/json', | |
| }, | |
| body: JSON.stringify({ | |
| {{ selectedApi === 'pdf' ? 'html_content' : 'html' }}: '<h1>Hello, World!</h1>' | |
| }), | |
| }) | |
| .then(response => response.blob()) | |
| .then(blob => { | |
| const url = window.URL.createObjectURL(blob); | |
| const a = document.createElement('a'); | |
| a.style.display = 'none'; | |
| a.href = url; | |
| a.download = 'output.{{ selectedApi }}'; | |
| document.body.appendChild(a); | |
| a.click(); | |
| window.URL.revokeObjectURL(url); | |
| }) | |
| .catch((error) => console.error('Error:', error)); | |
| </pre> | |
| </div> | |
| </transition> | |
| </div> | |
| </div> | |
| <script> | |
| const { createApp, ref, computed } = Vue; | |
| createApp({ | |
| setup() { | |
| const inputContent = ref(''); | |
| const inputType = ref('markdown'); | |
| const error = ref(false); | |
| const success = ref(false); | |
| const isConverting = ref(false); | |
| const fileName = ref(''); | |
| const showApiDocs = ref(false); | |
| const selectedApi = ref('pdf'); | |
| const selectedLanguage = ref('python'); | |
| const charCount = computed(() => inputContent.value.length); | |
| const statusMessage = ref(''); | |
| const updateCharCount = () => { | |
| if (inputContent.value.length > 20000) { | |
| inputContent.value = inputContent.value.slice(0, 20000); | |
| } | |
| }; | |
| const handleFileUpload = (event) => { | |
| const file = event.target.files[0]; | |
| if (file) { | |
| fileName.value = file.name; | |
| const reader = new FileReader(); | |
| reader.onload = (e) => { | |
| inputContent.value = e.target.result; | |
| updateCharCount(); | |
| }; | |
| reader.readAsText(file); | |
| } | |
| }; | |
| const convertToPdf = async () => { | |
| await convert('pdf'); | |
| }; | |
| const convertToDocx = async () => { | |
| await convert('docx'); | |
| }; | |
| const convert = async (type) => { | |
| error.value = false; | |
| success.value = false; | |
| isConverting.value = true; | |
| statusMessage.value = 'Converting... Please wait.'; | |
| const url = type === 'pdf' | |
| ? 'https://pvanand-web-scraping.hf.space/html_to_pdf' | |
| : 'https://pvanand-web-scraping.hf.space/convert'; | |
| const paramName = type === 'pdf' ? 'html_content' : 'html'; | |
| const filename = `converted.${type}`; | |
| const mimeType = type === 'pdf' | |
| ? 'application/pdf' | |
| : 'application/vnd.openxmlformats-officedocument.wordprocessingml.document'; | |
| try { | |
| let htmlContent = inputContent.value; | |
| if (inputType.value === 'markdown') { | |
| // Use marked.parse() to convert Markdown to HTML | |
| htmlContent = marked.parse(inputContent.value); | |
| } | |
| const response = await axios.post(url, { | |
| [paramName]: htmlContent | |
| }, { responseType: 'blob' }); | |
| const blob = new Blob([response.data], { type: mimeType }); | |
| const link = document.createElement('a'); | |
| link.href = URL.createObjectURL(blob); | |
| link.download = filename; | |
| link.click(); | |
| URL.revokeObjectURL(link.href); | |
| success.value = true; | |
| statusMessage.value = `Successfully converted to ${type.toUpperCase()}!`; | |
| } catch (e) { | |
| error.value = true; | |
| statusMessage.value = `Error converting to ${type.toUpperCase()}. Please try again.`; | |
| console.error('Error:', e); | |
| } finally { | |
| isConverting.value = false; | |
| } | |
| }; | |
| const toggleApiDocs = () => { | |
| showApiDocs.value = !showApiDocs.value; | |
| }; | |
| return { | |
| inputContent, | |
| inputType, | |
| charCount, | |
| fileName, | |
| statusMessage, | |
| isConverting, | |
| error, | |
| success, | |
| showApiDocs, | |
| selectedApi, | |
| selectedLanguage, | |
| updateCharCount, | |
| handleFileUpload, | |
| convertToPdf, | |
| convertToDocx, | |
| toggleApiDocs | |
| }; | |
| } | |
| }).mount('#app'); | |
| </script> | |
| <script src="https://kit.fontawesome.com/your-fontawesome-kit.js" crossorigin="anonymous"></script> | |
| </body> | |
| </html> |