Spaces:
Sleeping
Sleeping
| <html lang="ru"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>ForgeBuilder — ZIP → JAR</title> | |
| <link rel="preconnect" href="https://fonts.googleapis.com"> | |
| <link href="https://fonts.googleapis.com/css2?family=JetBrains+Mono:wght@400;600;700&family=Unbounded:wght@400;700;900&display=swap" rel="stylesheet"> | |
| <style> | |
| *, *::before, *::after { box-sizing: border-box; margin: 0; padding: 0; } | |
| :root { | |
| --bg: #0a0a0c; | |
| --surface: #111116; | |
| --surface2: #18181f; | |
| --border: #2a2a35; | |
| --border2: #3a3a48; | |
| --accent: #e8572a; | |
| --accent2: #ff7a4d; | |
| --green: #2ecc71; | |
| --yellow: #f39c12; | |
| --red: #e74c3c; | |
| --text: #e8e8f0; | |
| --muted: #6e6e85; | |
| --mono: 'JetBrains Mono', monospace; | |
| --display: 'Unbounded', sans-serif; | |
| } | |
| html { scroll-behavior: smooth; } | |
| body { | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: var(--mono); | |
| font-size: 14px; | |
| line-height: 1.6; | |
| min-height: 100vh; | |
| } | |
| /* ── NOISE OVERLAY ── */ | |
| body::before { | |
| content: ''; | |
| position: fixed; | |
| inset: 0; | |
| background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 200 200' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)' opacity='0.04'/%3E%3C/svg%3E"); | |
| pointer-events: none; | |
| z-index: 0; | |
| opacity: 0.6; | |
| } | |
| /* ── LAYOUT ── */ | |
| .wrap { max-width: 860px; margin: 0 auto; padding: 0 20px; position: relative; z-index: 1; } | |
| /* ── HEADER ── */ | |
| header { | |
| padding: 48px 0 40px; | |
| border-bottom: 1px solid var(--border); | |
| margin-bottom: 48px; | |
| } | |
| .logo { | |
| font-family: var(--display); | |
| font-size: clamp(28px, 6vw, 48px); | |
| font-weight: 900; | |
| letter-spacing: -0.03em; | |
| line-height: 1; | |
| margin-bottom: 12px; | |
| } | |
| .logo span { color: var(--accent); } | |
| .tagline { | |
| color: var(--muted); | |
| font-size: 13px; | |
| letter-spacing: 0.08em; | |
| text-transform: uppercase; | |
| } | |
| /* ── CARD ── */ | |
| .card { | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: 12px; | |
| padding: 32px; | |
| margin-bottom: 20px; | |
| } | |
| .card-title { | |
| font-family: var(--display); | |
| font-size: 11px; | |
| font-weight: 700; | |
| letter-spacing: 0.15em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| margin-bottom: 20px; | |
| display: flex; | |
| align-items: center; | |
| gap: 8px; | |
| } | |
| .card-title::before { | |
| content: ''; | |
| display: inline-block; | |
| width: 3px; | |
| height: 14px; | |
| background: var(--accent); | |
| border-radius: 2px; | |
| } | |
| /* ── VERSION SELECTOR ── */ | |
| .version-grid { | |
| display: grid; | |
| grid-template-columns: 1fr 1fr; | |
| gap: 12px; | |
| } | |
| .version-btn { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 16px 20px; | |
| cursor: pointer; | |
| transition: all 0.15s ease; | |
| text-align: left; | |
| color: var(--text); | |
| font-family: var(--mono); | |
| } | |
| .version-btn:hover { | |
| border-color: var(--accent); | |
| background: #1a1016; | |
| } | |
| .version-btn.active { | |
| border-color: var(--accent); | |
| background: #1a1016; | |
| box-shadow: 0 0 0 1px var(--accent); | |
| } | |
| .version-btn .ver-num { | |
| font-family: var(--display); | |
| font-size: 20px; | |
| font-weight: 700; | |
| color: var(--accent2); | |
| display: block; | |
| margin-bottom: 4px; | |
| } | |
| .version-btn .ver-label { | |
| font-size: 11px; | |
| color: var(--muted); | |
| text-transform: uppercase; | |
| letter-spacing: 0.08em; | |
| } | |
| .version-btn.active .ver-label { color: var(--accent); } | |
| /* ── DROP ZONE ── */ | |
| .dropzone { | |
| border: 2px dashed var(--border2); | |
| border-radius: 10px; | |
| padding: 48px 24px; | |
| text-align: center; | |
| cursor: pointer; | |
| transition: all 0.2s ease; | |
| position: relative; | |
| background: var(--surface2); | |
| } | |
| .dropzone:hover, .dropzone.drag-over { | |
| border-color: var(--accent); | |
| background: #14100e; | |
| } | |
| .dropzone input[type=file] { | |
| position: absolute; | |
| inset: 0; | |
| opacity: 0; | |
| cursor: pointer; | |
| width: 100%; | |
| height: 100%; | |
| } | |
| .drop-icon { | |
| font-size: 40px; | |
| margin-bottom: 12px; | |
| display: block; | |
| filter: grayscale(0.3); | |
| } | |
| .drop-title { | |
| font-family: var(--display); | |
| font-size: 16px; | |
| font-weight: 700; | |
| margin-bottom: 6px; | |
| } | |
| .drop-sub { color: var(--muted); font-size: 12px; } | |
| .file-selected { | |
| display: none; | |
| align-items: center; | |
| gap: 12px; | |
| background: var(--surface2); | |
| border: 1px solid var(--green); | |
| border-radius: 8px; | |
| padding: 14px 18px; | |
| margin-top: 12px; | |
| } | |
| .file-selected.show { display: flex; } | |
| .file-icon { color: var(--green); font-size: 20px; } | |
| .file-name { font-weight: 600; flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; } | |
| .file-size { color: var(--muted); font-size: 12px; } | |
| /* ── BUILD BUTTON ── */ | |
| .btn-build { | |
| width: 100%; | |
| padding: 18px; | |
| background: var(--accent); | |
| color: #fff; | |
| border: none; | |
| border-radius: 10px; | |
| font-family: var(--display); | |
| font-size: 15px; | |
| font-weight: 700; | |
| letter-spacing: 0.05em; | |
| cursor: pointer; | |
| transition: all 0.15s ease; | |
| margin-top: 8px; | |
| text-transform: uppercase; | |
| } | |
| .btn-build:hover:not(:disabled) { | |
| background: var(--accent2); | |
| transform: translateY(-1px); | |
| } | |
| .btn-build:disabled { | |
| opacity: 0.4; | |
| cursor: not-allowed; | |
| transform: none; | |
| } | |
| /* ── PROGRESS ── */ | |
| #progress-section { display: none; } | |
| #progress-section.show { display: block; } | |
| .log-box { | |
| background: #06060a; | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 20px; | |
| height: 280px; | |
| overflow-y: auto; | |
| font-size: 12px; | |
| line-height: 1.8; | |
| } | |
| .log-box::-webkit-scrollbar { width: 4px; } | |
| .log-box::-webkit-scrollbar-track { background: transparent; } | |
| .log-box::-webkit-scrollbar-thumb { background: var(--border2); border-radius: 2px; } | |
| .log-line { display: flex; gap: 12px; } | |
| .log-time { color: var(--muted); min-width: 60px; } | |
| .log-msg { flex: 1; } | |
| .log-msg.ok { color: var(--green); } | |
| .log-msg.err { color: var(--red); } | |
| .log-msg.warn { color: var(--yellow); } | |
| .log-msg.info { color: #89b4fa; } | |
| .progress-bar-wrap { | |
| background: var(--surface2); | |
| border-radius: 4px; | |
| height: 6px; | |
| margin: 16px 0; | |
| overflow: hidden; | |
| } | |
| .progress-bar { | |
| height: 100%; | |
| background: linear-gradient(90deg, var(--accent), var(--accent2)); | |
| border-radius: 4px; | |
| width: 0%; | |
| transition: width 0.5s ease; | |
| } | |
| .progress-label { | |
| display: flex; | |
| justify-content: space-between; | |
| font-size: 12px; | |
| color: var(--muted); | |
| margin-bottom: 8px; | |
| } | |
| /* ── RESULT ── */ | |
| #result-section { display: none; } | |
| #result-section.show { display: block; } | |
| .result-success { | |
| background: #0a1a0f; | |
| border: 1px solid var(--green); | |
| border-radius: 10px; | |
| padding: 28px; | |
| text-align: center; | |
| } | |
| .result-icon { font-size: 48px; margin-bottom: 12px; display: block; } | |
| .result-title { | |
| font-family: var(--display); | |
| font-size: 22px; | |
| font-weight: 900; | |
| color: var(--green); | |
| margin-bottom: 8px; | |
| } | |
| .result-sub { color: var(--muted); font-size: 13px; margin-bottom: 24px; } | |
| .btn-download { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 10px; | |
| padding: 14px 28px; | |
| background: var(--green); | |
| color: #06060a; | |
| border: none; | |
| border-radius: 8px; | |
| font-family: var(--display); | |
| font-size: 14px; | |
| font-weight: 700; | |
| cursor: pointer; | |
| text-decoration: none; | |
| transition: opacity 0.15s; | |
| } | |
| .btn-download:hover { opacity: 0.85; } | |
| .result-error { | |
| background: #1a0a0a; | |
| border: 1px solid var(--red); | |
| border-radius: 10px; | |
| padding: 28px; | |
| text-align: center; | |
| } | |
| .result-error .result-title { color: var(--red); } | |
| .btn-retry { | |
| display: inline-flex; | |
| align-items: center; | |
| gap: 8px; | |
| padding: 12px 24px; | |
| background: transparent; | |
| color: var(--text); | |
| border: 1px solid var(--border2); | |
| border-radius: 8px; | |
| font-family: var(--mono); | |
| font-size: 13px; | |
| cursor: pointer; | |
| margin-top: 16px; | |
| transition: border-color 0.15s; | |
| } | |
| .btn-retry:hover { border-color: var(--accent); } | |
| /* ── INFO BOXES ── */ | |
| .info-grid { | |
| display: grid; | |
| grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); | |
| gap: 12px; | |
| margin-top: 8px; | |
| } | |
| .info-item { | |
| background: var(--surface2); | |
| border: 1px solid var(--border); | |
| border-radius: 8px; | |
| padding: 16px; | |
| } | |
| .info-label { | |
| font-size: 10px; | |
| text-transform: uppercase; | |
| letter-spacing: 0.12em; | |
| color: var(--muted); | |
| margin-bottom: 6px; | |
| } | |
| .info-val { | |
| font-size: 13px; | |
| font-weight: 600; | |
| color: var(--accent2); | |
| } | |
| /* ── FOOTER ── */ | |
| footer { | |
| border-top: 1px solid var(--border); | |
| margin-top: 60px; | |
| padding: 32px 0; | |
| color: var(--muted); | |
| font-size: 12px; | |
| display: flex; | |
| justify-content: space-between; | |
| align-items: center; | |
| flex-wrap: wrap; | |
| gap: 12px; | |
| } | |
| /* ── SPINNER ── */ | |
| .spinner { | |
| display: inline-block; | |
| width: 14px; | |
| height: 14px; | |
| border: 2px solid rgba(255,255,255,0.2); | |
| border-top-color: #fff; | |
| border-radius: 50%; | |
| animation: spin 0.7s linear infinite; | |
| vertical-align: middle; | |
| margin-right: 6px; | |
| } | |
| @keyframes spin { to { transform: rotate(360deg); } } | |
| /* ── WARNING BADGE ── */ | |
| .warn-badge { | |
| background: #1a1400; | |
| border: 1px solid var(--yellow); | |
| border-radius: 6px; | |
| padding: 10px 14px; | |
| font-size: 12px; | |
| color: var(--yellow); | |
| margin-top: 12px; | |
| display: flex; | |
| gap: 8px; | |
| align-items: flex-start; | |
| } | |
| @media (max-width: 500px) { | |
| .version-grid { grid-template-columns: 1fr; } | |
| .card { padding: 20px; } | |
| header { padding: 32px 0 28px; } | |
| } | |
| </style> | |
| </head> | |
| <body> | |
| <div class="wrap"> | |
| <header> | |
| <div class="logo">Forge<span>Builder</span></div> | |
| <div class="tagline">ZIP исходников → готовый JAR мод // Forge 1.12.2 & 1.20.1</div> | |
| </header> | |
| <!-- STEP 1: VERSION --> | |
| <div class="card"> | |
| <div class="card-title">Шаг 1 — Версия Minecraft</div> | |
| <div class="version-grid"> | |
| <button class="version-btn active" data-ver="1.12.2" onclick="selectVersion(this)"> | |
| <span class="ver-num">1.12.2</span> | |
| <span class="ver-label">Forge 14.23.5.2860 · JDK 8</span> | |
| </button> | |
| <button class="version-btn" data-ver="1.20.1" onclick="selectVersion(this)"> | |
| <span class="ver-num">1.20.1</span> | |
| <span class="ver-label">Forge 47.2.0 · JDK 17</span> | |
| </button> | |
| </div> | |
| </div> | |
| <!-- STEP 2: UPLOAD --> | |
| <div class="card"> | |
| <div class="card-title">Шаг 2 — Загрузи ZIP с исходниками</div> | |
| <div class="dropzone" id="dropzone"> | |
| <input type="file" id="fileInput" accept=".zip" onchange="handleFile(this.files[0])"> | |
| <span class="drop-icon">📦</span> | |
| <div class="drop-title">Перетащи ZIP или нажми</div> | |
| <div class="drop-sub">Максимум 50 MB · Должен содержать build.gradle + src/</div> | |
| </div> | |
| <div class="file-selected" id="fileInfo"> | |
| <span class="file-icon">✓</span> | |
| <span class="file-name" id="fileName">—</span> | |
| <span class="file-size" id="fileSize">—</span> | |
| </div> | |
| <div class="warn-badge"> | |
| ⚠ Загружай только свой код. Вредоносные файлы блокируются и IP банится автоматически. | |
| </div> | |
| </div> | |
| <!-- STEP 3: BUILD --> | |
| <div class="card"> | |
| <div class="card-title">Шаг 3 — Сборка</div> | |
| <div class="info-grid"> | |
| <div class="info-item"> | |
| <div class="info-label">Выбранная версия</div> | |
| <div class="info-val" id="infoVer">1.12.2 Forge</div> | |
| </div> | |
| <div class="info-item"> | |
| <div class="info-label">Ожидаемое время</div> | |
| <div class="info-val">3 — 8 минут</div> | |
| </div> | |
| <div class="info-item"> | |
| <div class="info-label">Сервер</div> | |
| <div class="info-val">Hugging Face Spaces</div> | |
| </div> | |
| <div class="info-item"> | |
| <div class="info-label">JAR хранится</div> | |
| <div class="info-val">1 час после сборки</div> | |
| </div> | |
| </div> | |
| <button class="btn-build" id="buildBtn" onclick="startBuild()" disabled> | |
| Собрать JAR | |
| </button> | |
| </div> | |
| <!-- PROGRESS --> | |
| <div class="card" id="progress-section"> | |
| <div class="card-title">Сборка...</div> | |
| <div class="progress-label"> | |
| <span id="progressStep">Запуск контейнера</span> | |
| <span id="progressPct">0%</span> | |
| </div> | |
| <div class="progress-bar-wrap"> | |
| <div class="progress-bar" id="progressBar"></div> | |
| </div> | |
| <div class="log-box" id="logBox"></div> | |
| </div> | |
| <!-- RESULT --> | |
| <div id="result-section"></div> | |
| <footer> | |
| <span>ForgeBuilder v1.0 — бесплатно для всех</span> | |
| <span>Сервер: Hugging Face Spaces · 16 GB RAM</span> | |
| </footer> | |
| </div> | |
| <script> | |
| let selectedVersion = '1.12.2'; | |
| let selectedFile = null; | |
| let buildJobId = null; | |
| let pollInterval = null; | |
| // ── Version select ────────────────────────────────────────────────────────── | |
| function selectVersion(btn) { | |
| document.querySelectorAll('.version-btn').forEach(b => b.classList.remove('active')); | |
| btn.classList.add('active'); | |
| selectedVersion = btn.dataset.ver; | |
| document.getElementById('infoVer').textContent = selectedVersion + ' Forge'; | |
| checkReady(); | |
| } | |
| // ── File handling ─────────────────────────────────────────────────────────── | |
| function handleFile(file) { | |
| if (!file) return; | |
| if (!file.name.endsWith('.zip')) { | |
| alert('Нужен ZIP файл!'); | |
| return; | |
| } | |
| if (file.size > 50 * 1024 * 1024) { | |
| alert('Файл слишком большой. Максимум 50 MB.'); | |
| return; | |
| } | |
| selectedFile = file; | |
| document.getElementById('fileName').textContent = file.name; | |
| document.getElementById('fileSize').textContent = formatSize(file.size); | |
| document.getElementById('fileInfo').classList.add('show'); | |
| checkReady(); | |
| } | |
| function formatSize(bytes) { | |
| if (bytes < 1024) return bytes + ' B'; | |
| if (bytes < 1024*1024) return (bytes/1024).toFixed(1) + ' KB'; | |
| return (bytes/(1024*1024)).toFixed(1) + ' MB'; | |
| } | |
| // Drag and drop | |
| const dz = document.getElementById('dropzone'); | |
| dz.addEventListener('dragover', e => { e.preventDefault(); dz.classList.add('drag-over'); }); | |
| dz.addEventListener('dragleave', () => dz.classList.remove('drag-over')); | |
| dz.addEventListener('drop', e => { | |
| e.preventDefault(); | |
| dz.classList.remove('drag-over'); | |
| handleFile(e.dataTransfer.files[0]); | |
| }); | |
| // ── Build readiness ───────────────────────────────────────────────────────── | |
| function checkReady() { | |
| document.getElementById('buildBtn').disabled = !selectedFile; | |
| } | |
| // ── Build ─────────────────────────────────────────────────────────────────── | |
| async function startBuild() { | |
| if (!selectedFile) return; | |
| // Show progress section | |
| document.getElementById('progress-section').classList.add('show'); | |
| document.getElementById('result-section').classList.remove('show'); | |
| document.getElementById('result-section').innerHTML = ''; | |
| document.getElementById('buildBtn').disabled = true; | |
| document.getElementById('buildBtn').innerHTML = '<span class="spinner"></span>Собирается...'; | |
| document.getElementById('logBox').innerHTML = ''; | |
| setProgress(0, 'Загрузка файла...'); | |
| // Upload | |
| const form = new FormData(); | |
| form.append('file', selectedFile); | |
| form.append('version', selectedVersion); | |
| try { | |
| addLog('Отправка ZIP на сервер...', 'info'); | |
| const res = await fetch('/build', { method: 'POST', body: form }); | |
| if (!res.ok) { | |
| const err = await res.json().catch(() => ({ error: 'Ошибка сервера' })); | |
| throw new Error(err.error || 'Ошибка ' + res.status); | |
| } | |
| const data = await res.json(); | |
| buildJobId = data.job_id; | |
| addLog('Job ID: ' + buildJobId, 'info'); | |
| setProgress(10, 'Запуск Docker контейнера...'); | |
| pollBuild(); | |
| } catch (e) { | |
| showError(e.message); | |
| } | |
| } | |
| // ── Polling ───────────────────────────────────────────────────────────────── | |
| function pollBuild() { | |
| pollInterval = setInterval(async () => { | |
| try { | |
| const res = await fetch('/status/' + buildJobId); | |
| const data = await res.json(); | |
| // Add new log lines | |
| if (data.logs && data.logs.length > 0) { | |
| data.logs.forEach(line => { | |
| const cls = line.includes('ERROR') ? 'err' | |
| : line.includes('WARN') ? 'warn' | |
| : line.includes('BUILD SUCCESSFUL') ? 'ok' | |
| : ''; | |
| addLog(line, cls); | |
| }); | |
| } | |
| // Update progress | |
| updateProgressFromStatus(data.status, data.progress || 0); | |
| if (data.status === 'done') { | |
| clearInterval(pollInterval); | |
| showSuccess(data.jar_url, data.jar_name, data.build_time); | |
| } else if (data.status === 'error') { | |
| clearInterval(pollInterval); | |
| showError(data.error || 'Ошибка сборки. Проверь build.gradle.'); | |
| } | |
| } catch (e) { | |
| addLog('Ошибка связи с сервером: ' + e.message, 'err'); | |
| } | |
| }, 2000); | |
| } | |
| function updateProgressFromStatus(status, pct) { | |
| const steps = { | |
| 'uploading': [10, 'Загрузка файла...'], | |
| 'extracting': [20, 'Распаковка ZIP...'], | |
| 'validating': [25, 'Проверка структуры проекта...'], | |
| 'container': [30, 'Запуск Docker контейнера...'], | |
| 'deps': [50, 'Загрузка зависимостей Forge...'], | |
| 'compiling': [75, 'Компиляция Java...'], | |
| 'packaging': [90, 'Создание JAR...'], | |
| 'done': [100,'Готово!'], | |
| }; | |
| const s = steps[status] || [pct, status]; | |
| setProgress(s[0], s[1]); | |
| } | |
| // ── UI helpers ────────────────────────────────────────────────────────────── | |
| function setProgress(pct, label) { | |
| document.getElementById('progressBar').style.width = pct + '%'; | |
| document.getElementById('progressPct').textContent = pct + '%'; | |
| document.getElementById('progressStep').textContent = label; | |
| } | |
| function addLog(msg, cls) { | |
| const box = document.getElementById('logBox'); | |
| const now = new Date(); | |
| const t = now.getHours().toString().padStart(2,'0') + ':' | |
| + now.getMinutes().toString().padStart(2,'0') + ':' | |
| + now.getSeconds().toString().padStart(2,'0'); | |
| const line = document.createElement('div'); | |
| line.className = 'log-line'; | |
| line.innerHTML = `<span class="log-time">${t}</span><span class="log-msg ${cls||''}">${escHtml(msg)}</span>`; | |
| box.appendChild(line); | |
| box.scrollTop = box.scrollHeight; | |
| } | |
| function escHtml(s) { | |
| return s.replace(/&/g,'&').replace(/</g,'<').replace(/>/g,'>'); | |
| } | |
| function showSuccess(url, name, time) { | |
| document.getElementById('buildBtn').disabled = false; | |
| document.getElementById('buildBtn').textContent = 'Собрать JAR'; | |
| const sec = document.getElementById('result-section'); | |
| sec.innerHTML = ` | |
| <div class="card"> | |
| <div class="result-success"> | |
| <span class="result-icon">✅</span> | |
| <div class="result-title">BUILD SUCCESSFUL</div> | |
| <div class="result-sub">Время сборки: ${time || '—'} · Файл удалится через 1 час</div> | |
| <a class="btn-download" href="${url}" download="${name}"> | |
| ⬇ Скачать ${name} | |
| </a> | |
| </div> | |
| </div>`; | |
| sec.classList.add('show'); | |
| sec.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| } | |
| function showError(msg) { | |
| document.getElementById('buildBtn').disabled = false; | |
| document.getElementById('buildBtn').textContent = 'Собрать JAR'; | |
| const sec = document.getElementById('result-section'); | |
| sec.innerHTML = ` | |
| <div class="card"> | |
| <div class="result-error"> | |
| <span class="result-icon">❌</span> | |
| <div class="result-title">BUILD FAILED</div> | |
| <div class="result-sub">${escHtml(msg)}</div> | |
| <button class="btn-retry" onclick="resetBuild()">↺ Попробовать снова</button> | |
| </div> | |
| </div>`; | |
| sec.classList.add('show'); | |
| sec.scrollIntoView({ behavior: 'smooth', block: 'center' }); | |
| } | |
| function resetBuild() { | |
| document.getElementById('result-section').classList.remove('show'); | |
| document.getElementById('result-section').innerHTML = ''; | |
| document.getElementById('progress-section').classList.remove('show'); | |
| } | |
| </script> | |
| </body> | |
| </html> | |