Spaces:
Running
Running
Fix binary/image handling - use blob for preview
Browse files
app.py
CHANGED
|
@@ -629,18 +629,14 @@ def ui():
|
|
| 629 |
try {
|
| 630 |
const res = await fetch(url, opts);
|
| 631 |
const elapsed = Math.round(performance.now() - start);
|
| 632 |
-
const
|
|
|
|
| 633 |
|
| 634 |
// Status
|
| 635 |
const statusEl = document.getElementById('status');
|
| 636 |
statusEl.textContent = res.status + ' ' + res.statusText;
|
| 637 |
statusEl.className = 'status-value ' + (res.ok ? 'status-ok' : (res.status >= 500 ? 'status-err' : 'status-warn'));
|
| 638 |
|
| 639 |
-
// Meta
|
| 640 |
-
document.getElementById('time').textContent = elapsed + 'ms';
|
| 641 |
-
document.getElementById('size').textContent = text.length > 1024 ? (text.length / 1024).toFixed(1) + ' KB' : text.length + ' B';
|
| 642 |
-
document.getElementById('content-type').textContent = res.headers.get('content-type')?.split(';')[0] || '-';
|
| 643 |
-
|
| 644 |
// Response headers
|
| 645 |
const headersHtml = [];
|
| 646 |
res.headers.forEach((value, name) => {
|
|
@@ -648,21 +644,41 @@ def ui():
|
|
| 648 |
});
|
| 649 |
document.getElementById('response-headers').innerHTML = headersHtml.join('') || '<div style="color:#86868b">No headers</div>';
|
| 650 |
|
| 651 |
-
|
| 652 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
| 653 |
|
| 654 |
-
|
| 655 |
-
|
| 656 |
-
|
| 657 |
-
|
| 658 |
-
|
| 659 |
-
}
|
| 660 |
-
|
| 661 |
-
|
| 662 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 663 |
|
| 664 |
-
|
| 665 |
-
|
|
|
|
| 666 |
|
| 667 |
} catch (err) {
|
| 668 |
document.getElementById('status').textContent = 'Error';
|
|
@@ -680,7 +696,26 @@ def ui():
|
|
| 680 |
document.getElementById('url').onkeydown = (e) => { if (e.key === 'Enter') sendRequest(); };
|
| 681 |
sendRequest();
|
| 682 |
|
| 683 |
-
// Preview rendering
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 684 |
function renderPreview(text, contentType) {
|
| 685 |
const container = document.getElementById('preview-container');
|
| 686 |
const ct = contentType.toLowerCase();
|
|
@@ -693,10 +728,6 @@ def ui():
|
|
| 693 |
iframe.srcdoc = text;
|
| 694 |
container.innerHTML = '';
|
| 695 |
container.appendChild(iframe);
|
| 696 |
-
} else if (ct.includes('image/')) {
|
| 697 |
-
// Show image (requires blob URL for binary)
|
| 698 |
-
const url = document.getElementById('url').value;
|
| 699 |
-
container.innerHTML = `<img class="preview-image" src="${url}" alt="Image preview">`;
|
| 700 |
} else if (ct.includes('application/json') || ct.includes('json')) {
|
| 701 |
// Interactive JSON tree
|
| 702 |
try {
|
|
|
|
| 629 |
try {
|
| 630 |
const res = await fetch(url, opts);
|
| 631 |
const elapsed = Math.round(performance.now() - start);
|
| 632 |
+
const contentType = res.headers.get('content-type') || '';
|
| 633 |
+
const isBinary = contentType.includes('image/') || contentType.includes('octet-stream');
|
| 634 |
|
| 635 |
// Status
|
| 636 |
const statusEl = document.getElementById('status');
|
| 637 |
statusEl.textContent = res.status + ' ' + res.statusText;
|
| 638 |
statusEl.className = 'status-value ' + (res.ok ? 'status-ok' : (res.status >= 500 ? 'status-err' : 'status-warn'));
|
| 639 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 640 |
// Response headers
|
| 641 |
const headersHtml = [];
|
| 642 |
res.headers.forEach((value, name) => {
|
|
|
|
| 644 |
});
|
| 645 |
document.getElementById('response-headers').innerHTML = headersHtml.join('') || '<div style="color:#86868b">No headers</div>';
|
| 646 |
|
| 647 |
+
if (isBinary) {
|
| 648 |
+
// Handle binary data
|
| 649 |
+
const blob = await res.blob();
|
| 650 |
+
document.getElementById('time').textContent = elapsed + 'ms';
|
| 651 |
+
document.getElementById('size').textContent = blob.size > 1024 ? (blob.size / 1024).toFixed(1) + ' KB' : blob.size + ' B';
|
| 652 |
+
document.getElementById('content-type').textContent = contentType.split(';')[0] || '-';
|
| 653 |
|
| 654 |
+
document.getElementById('response-raw').textContent = `[Binary data: ${blob.size} bytes]`;
|
| 655 |
+
document.getElementById('response').innerHTML = `<span style="color:#86868b">[Binary data: ${blob.size} bytes, type: ${contentType}]</span>`;
|
| 656 |
+
|
| 657 |
+
// Preview binary
|
| 658 |
+
renderBinaryPreview(blob, contentType, url);
|
| 659 |
+
} else {
|
| 660 |
+
// Handle text data
|
| 661 |
+
const text = await res.text();
|
| 662 |
+
document.getElementById('time').textContent = elapsed + 'ms';
|
| 663 |
+
document.getElementById('size').textContent = text.length > 1024 ? (text.length / 1024).toFixed(1) + ' KB' : text.length + ' B';
|
| 664 |
+
document.getElementById('content-type').textContent = contentType.split(';')[0] || '-';
|
| 665 |
+
|
| 666 |
+
// Raw response
|
| 667 |
+
document.getElementById('response-raw').textContent = text;
|
| 668 |
+
|
| 669 |
+
// Formatted response
|
| 670 |
+
let display;
|
| 671 |
+
try {
|
| 672 |
+
const json = JSON.parse(text);
|
| 673 |
+
display = highlightJSON(JSON.stringify(json, null, 2));
|
| 674 |
+
} catch {
|
| 675 |
+
display = text.replace(/</g, '<').replace(/>/g, '>');
|
| 676 |
+
}
|
| 677 |
+
document.getElementById('response').innerHTML = display;
|
| 678 |
|
| 679 |
+
// Preview
|
| 680 |
+
renderPreview(text, contentType);
|
| 681 |
+
}
|
| 682 |
|
| 683 |
} catch (err) {
|
| 684 |
document.getElementById('status').textContent = 'Error';
|
|
|
|
| 696 |
document.getElementById('url').onkeydown = (e) => { if (e.key === 'Enter') sendRequest(); };
|
| 697 |
sendRequest();
|
| 698 |
|
| 699 |
+
// Preview rendering for binary content
|
| 700 |
+
function renderBinaryPreview(blob, contentType, url) {
|
| 701 |
+
const container = document.getElementById('preview-container');
|
| 702 |
+
const ct = contentType.toLowerCase();
|
| 703 |
+
|
| 704 |
+
if (ct.includes('image/')) {
|
| 705 |
+
const blobUrl = URL.createObjectURL(blob);
|
| 706 |
+
container.innerHTML = `<img class="preview-image" src="${blobUrl}" alt="Image preview" onload="URL.revokeObjectURL(this.src)">`;
|
| 707 |
+
} else {
|
| 708 |
+
// Generic binary
|
| 709 |
+
container.innerHTML = `<div class="preview-text" style="text-align:center;padding:40px;">
|
| 710 |
+
<div style="font-size:32px;margin-bottom:12px;">Binary Data</div>
|
| 711 |
+
<div style="color:#86868b;">${blob.size} bytes</div>
|
| 712 |
+
<div style="color:#86868b;">${contentType}</div>
|
| 713 |
+
<a href="${url}" download style="display:inline-block;margin-top:16px;padding:8px 16px;background:#007aff;color:#fff;border-radius:6px;text-decoration:none;">Download</a>
|
| 714 |
+
</div>`;
|
| 715 |
+
}
|
| 716 |
+
}
|
| 717 |
+
|
| 718 |
+
// Preview rendering for text content
|
| 719 |
function renderPreview(text, contentType) {
|
| 720 |
const container = document.getElementById('preview-container');
|
| 721 |
const ct = contentType.toLowerCase();
|
|
|
|
| 728 |
iframe.srcdoc = text;
|
| 729 |
container.innerHTML = '';
|
| 730 |
container.appendChild(iframe);
|
|
|
|
|
|
|
|
|
|
|
|
|
| 731 |
} else if (ct.includes('application/json') || ct.includes('json')) {
|
| 732 |
// Interactive JSON tree
|
| 733 |
try {
|