Spaces:
Runtime error
Runtime error
Update index.html
Browse files- index.html +3 -133
index.html
CHANGED
|
@@ -152,13 +152,8 @@
|
|
| 152 |
.tool-badge.missing { background: rgba(255,68,85,0.1); color: var(--danger); border: 1px solid rgba(255,68,85,0.3); }
|
| 153 |
|
| 154 |
/* URL UPLOAD */
|
| 155 |
-
.url-upload-wrap {
|
| 156 |
-
.url-
|
| 157 |
-
.url-label { font-family: 'Share Tech Mono', monospace; font-size: 11px; color: var(--text-dim); letter-spacing: 1px; white-space: nowrap; }
|
| 158 |
-
.url-input { flex: 1; background: var(--bg); border: 1px solid var(--border-bright); color: var(--text-bright); font-family: 'Share Tech Mono', monospace; font-size: 12px; padding: 7px 10px; border-radius: 3px; outline: none; transition: border-color 0.2s; }
|
| 159 |
-
.url-input:focus { border-color: var(--accent); }
|
| 160 |
-
.url-input::placeholder { color: var(--text-dim); }
|
| 161 |
-
.url-btn { padding: 7px 16px; font-size: 12px; white-space: nowrap; }
|
| 162 |
|
| 163 |
/* TOAST */
|
| 164 |
.toast { position: fixed; bottom: 30px; right: 30px; background: var(--panel); border: 1px solid var(--accent); color: var(--text-bright); padding: 12px 20px; border-radius: 4px; font-size: 14px; font-weight: 600; letter-spacing: 1px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); transform: translateY(80px); opacity: 0; transition: all 0.3s cubic-bezier(0.34,1.56,0.64,1); z-index: 1000; }
|
|
@@ -184,7 +179,7 @@
|
|
| 184 |
|
| 185 |
<!-- Steps -->
|
| 186 |
<div class="steps">
|
| 187 |
-
<div class="step active" id="step1"><span class="step-num">01</span>
|
| 188 |
<div class="step" id="step2"><span class="step-num">02</span> Edit Characters</div>
|
| 189 |
<div class="step" id="step3"><span class="step-num">03</span> Export Patched APK</div>
|
| 190 |
</div>
|
|
@@ -192,22 +187,6 @@
|
|
| 192 |
<!-- Tool status -->
|
| 193 |
<div class="tool-status" id="toolStatus"></div>
|
| 194 |
|
| 195 |
-
<!-- Upload (hidden β APK loaded from server) -->
|
| 196 |
-
<div class="upload-zone" id="uploadZone" style="display:none;">
|
| 197 |
-
<div class="upload-icon">π¦</div>
|
| 198 |
-
<div class="upload-title">Drop SuperCity.apk here</div>
|
| 199 |
-
<div class="upload-sub">or click to browse Β· .apk files only Β· max 200 MB</div>
|
| 200 |
-
<input type="file" id="apkInput" accept=".apk">
|
| 201 |
-
</div>
|
| 202 |
-
<!-- URL Upload (hidden) -->
|
| 203 |
-
<div class="url-upload-wrap" id="urlUploadWrap" style="display:none;">
|
| 204 |
-
<div class="url-upload-inner">
|
| 205 |
-
<span class="url-label">or load from URL</span>
|
| 206 |
-
<input class="url-input" type="text" id="apkUrlInput" placeholder="https://example.com/SuperCity.apk">
|
| 207 |
-
<button class="btn btn-secondary url-btn" onclick="uploadFromUrl()">β¬ Load URL</button>
|
| 208 |
-
</div>
|
| 209 |
-
</div>
|
| 210 |
-
|
| 211 |
<div class="progress-wrap" id="progressWrap">
|
| 212 |
<div class="progress-label" id="progressLabel">Uploadingβ¦</div>
|
| 213 |
<div class="progress-bar-outer"><div class="progress-bar-inner" id="progressBar"></div></div>
|
|
@@ -367,115 +346,6 @@ fetch('/tools').then(r => r.json()).then(tools => {
|
|
| 367 |
});
|
| 368 |
})();
|
| 369 |
|
| 370 |
-
// ββ UPLOAD βββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 371 |
-
const uploadZone = document.getElementById('uploadZone');
|
| 372 |
-
const apkInput = document.getElementById('apkInput');
|
| 373 |
-
|
| 374 |
-
uploadZone.addEventListener('click', () => apkInput.click());
|
| 375 |
-
uploadZone.addEventListener('dragover', e => { e.preventDefault(); uploadZone.classList.add('drag-over'); });
|
| 376 |
-
uploadZone.addEventListener('dragleave', () => uploadZone.classList.remove('drag-over'));
|
| 377 |
-
uploadZone.addEventListener('drop', e => { e.preventDefault(); uploadZone.classList.remove('drag-over'); if (e.dataTransfer.files[0]) uploadAPK(e.dataTransfer.files[0]); });
|
| 378 |
-
apkInput.addEventListener('change', () => { if (apkInput.files[0]) uploadAPK(apkInput.files[0]); });
|
| 379 |
-
|
| 380 |
-
function uploadAPK(file) {
|
| 381 |
-
if (!file.name.endsWith('.apk')) { showToast('Only .apk files', true); return; }
|
| 382 |
-
|
| 383 |
-
const wrap = document.getElementById('progressWrap');
|
| 384 |
-
const bar = document.getElementById('progressBar');
|
| 385 |
-
const label = document.getElementById('progressLabel');
|
| 386 |
-
wrap.style.display = 'block';
|
| 387 |
-
bar.style.width = '0%';
|
| 388 |
-
label.textContent = 'Uploadingβ¦';
|
| 389 |
-
|
| 390 |
-
const form = new FormData();
|
| 391 |
-
form.append('apk', file);
|
| 392 |
-
|
| 393 |
-
const xhr = new XMLHttpRequest();
|
| 394 |
-
xhr.open('POST', '/upload');
|
| 395 |
-
|
| 396 |
-
xhr.upload.onprogress = e => {
|
| 397 |
-
if (e.lengthComputable) {
|
| 398 |
-
const pct = Math.round(e.loaded / e.total * 80);
|
| 399 |
-
bar.style.width = pct + '%';
|
| 400 |
-
label.textContent = `Uploading⦠${pct}%`;
|
| 401 |
-
}
|
| 402 |
-
};
|
| 403 |
-
|
| 404 |
-
xhr.onload = () => {
|
| 405 |
-
bar.style.width = '100%';
|
| 406 |
-
label.textContent = 'Parsing charactersβ¦';
|
| 407 |
-
if (xhr.status === 200) {
|
| 408 |
-
const data = JSON.parse(xhr.responseText);
|
| 409 |
-
sessionToken = data.token;
|
| 410 |
-
originalRaw = data.characters;
|
| 411 |
-
parseCharacters(data.characters);
|
| 412 |
-
wrap.style.display = 'none';
|
| 413 |
-
document.getElementById('fileInfoBar').style.display = 'flex';
|
| 414 |
-
document.getElementById('fileInfoBar').innerHTML = `
|
| 415 |
-
<div class="file-info">π¦ <span class="file-info-name">${file.name}</span>
|
| 416 |
-
Β· ${characters.length} characters loaded Β· session: ${sessionToken.split('_')[0]}</div>`;
|
| 417 |
-
document.getElementById('editorLayout').style.display = 'grid';
|
| 418 |
-
document.getElementById('urlUploadWrap').style.display = 'none';
|
| 419 |
-
setStep(2);
|
| 420 |
-
showToast(`β Loaded ${characters.length} characters`);
|
| 421 |
-
} else {
|
| 422 |
-
wrap.style.display = 'none';
|
| 423 |
-
const err = JSON.parse(xhr.responseText);
|
| 424 |
-
showToast('Error: ' + err.error, true);
|
| 425 |
-
}
|
| 426 |
-
};
|
| 427 |
-
|
| 428 |
-
xhr.onerror = () => { wrap.style.display = 'none'; showToast('Upload failed', true); };
|
| 429 |
-
xhr.send(form);
|
| 430 |
-
}
|
| 431 |
-
|
| 432 |
-
function uploadFromUrl() {
|
| 433 |
-
const url = document.getElementById('apkUrlInput').value.trim();
|
| 434 |
-
if (!url) { showToast('Enter a URL first', true); return; }
|
| 435 |
-
if (!url.toLowerCase().endsWith('.apk')) { showToast('URL must point to a .apk file', true); return; }
|
| 436 |
-
|
| 437 |
-
const wrap = document.getElementById('progressWrap');
|
| 438 |
-
const bar = document.getElementById('progressBar');
|
| 439 |
-
const label = document.getElementById('progressLabel');
|
| 440 |
-
wrap.style.display = 'block';
|
| 441 |
-
bar.style.width = '10%';
|
| 442 |
-
label.textContent = 'Fetching APK from URLβ¦';
|
| 443 |
-
|
| 444 |
-
fetch('/upload-url', {
|
| 445 |
-
method: 'POST',
|
| 446 |
-
headers: { 'Content-Type': 'application/json' },
|
| 447 |
-
body: JSON.stringify({ url }),
|
| 448 |
-
})
|
| 449 |
-
.then(res => {
|
| 450 |
-
bar.style.width = '80%';
|
| 451 |
-
label.textContent = 'Parsing charactersβ¦';
|
| 452 |
-
if (!res.ok) return res.json().then(e => { throw new Error(e.error); });
|
| 453 |
-
return res.json();
|
| 454 |
-
})
|
| 455 |
-
.then(data => {
|
| 456 |
-
bar.style.width = '100%';
|
| 457 |
-
sessionToken = data.token;
|
| 458 |
-
originalRaw = data.characters;
|
| 459 |
-
parseCharacters(data.characters);
|
| 460 |
-
wrap.style.display = 'none';
|
| 461 |
-
const fname = url.split('/').pop();
|
| 462 |
-
document.getElementById('fileInfoBar').style.display = 'flex';
|
| 463 |
-
document.getElementById('fileInfoBar').innerHTML = `
|
| 464 |
-
<div class="file-info">π <span class="file-info-name">${fname}</span>
|
| 465 |
-
Β· ${characters.length} characters loaded Β· session: ${sessionToken.split('_')[0]}</div>`;
|
| 466 |
-
document.getElementById('editorLayout').style.display = 'grid';
|
| 467 |
-
document.getElementById('urlUploadWrap').style.display = 'none';
|
| 468 |
-
document.getElementById('uploadZone').style.display = 'none';
|
| 469 |
-
setStep(2);
|
| 470 |
-
showToast(`β Loaded ${characters.length} characters from URL`);
|
| 471 |
-
})
|
| 472 |
-
.catch(err => {
|
| 473 |
-
wrap.style.display = 'none';
|
| 474 |
-
bar.style.width = '0%';
|
| 475 |
-
showToast('Error: ' + err.message, true);
|
| 476 |
-
});
|
| 477 |
-
}
|
| 478 |
-
|
| 479 |
// ββ PARSE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 480 |
function parseCharacters(raw) {
|
| 481 |
characters = [];
|
|
|
|
| 152 |
.tool-badge.missing { background: rgba(255,68,85,0.1); color: var(--danger); border: 1px solid rgba(255,68,85,0.3); }
|
| 153 |
|
| 154 |
/* URL UPLOAD */
|
| 155 |
+
.url-upload-wrap { display: none; }
|
| 156 |
+
.url-btn { display: none; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 157 |
|
| 158 |
/* TOAST */
|
| 159 |
.toast { position: fixed; bottom: 30px; right: 30px; background: var(--panel); border: 1px solid var(--accent); color: var(--text-bright); padding: 12px 20px; border-radius: 4px; font-size: 14px; font-weight: 600; letter-spacing: 1px; box-shadow: 0 4px 20px rgba(0,0,0,0.5); transform: translateY(80px); opacity: 0; transition: all 0.3s cubic-bezier(0.34,1.56,0.64,1); z-index: 1000; }
|
|
|
|
| 179 |
|
| 180 |
<!-- Steps -->
|
| 181 |
<div class="steps">
|
| 182 |
+
<div class="step active" id="step1"><span class="step-num">01</span> Load APK</div>
|
| 183 |
<div class="step" id="step2"><span class="step-num">02</span> Edit Characters</div>
|
| 184 |
<div class="step" id="step3"><span class="step-num">03</span> Export Patched APK</div>
|
| 185 |
</div>
|
|
|
|
| 187 |
<!-- Tool status -->
|
| 188 |
<div class="tool-status" id="toolStatus"></div>
|
| 189 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 190 |
<div class="progress-wrap" id="progressWrap">
|
| 191 |
<div class="progress-label" id="progressLabel">Uploadingβ¦</div>
|
| 192 |
<div class="progress-bar-outer"><div class="progress-bar-inner" id="progressBar"></div></div>
|
|
|
|
| 346 |
});
|
| 347 |
})();
|
| 348 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 349 |
// ββ PARSE ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 350 |
function parseCharacters(raw) {
|
| 351 |
characters = [];
|