Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -399,6 +399,17 @@ HTML = r"""<!DOCTYPE html>
|
|
| 399 |
::-webkit-scrollbar-track { background: transparent; }
|
| 400 |
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
|
| 401 |
iframe { border: none; width: 100%; height: 100%; }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 402 |
</style>
|
| 403 |
</head>
|
| 404 |
<body class="bg-gray-100 flex justify-center h-[100dvh] overflow-hidden text-gray-800">
|
|
@@ -447,7 +458,12 @@ HTML = r"""<!DOCTYPE html>
|
|
| 447 |
</div>
|
| 448 |
</header>
|
| 449 |
|
| 450 |
-
<!--
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 451 |
<main class="flex-1 min-h-0 relative bg-gray-50 flex flex-col overflow-hidden">
|
| 452 |
|
| 453 |
<!-- βββ CHAT PAGE βββ -->
|
|
@@ -674,6 +690,7 @@ HTML = r"""<!DOCTYPE html>
|
|
| 674 |
loopCount: 0,
|
| 675 |
toastMsg: '',
|
| 676 |
_pollTimer: null,
|
|
|
|
| 677 |
|
| 678 |
init() {
|
| 679 |
this.$watch('tab', val => {
|
|
@@ -789,16 +806,41 @@ HTML = r"""<!DOCTYPE html>
|
|
| 789 |
}
|
| 790 |
},
|
| 791 |
|
| 792 |
-
/* βββ Preview (Eruda
|
| 793 |
|
| 794 |
updatePreview() {
|
| 795 |
const iframe = document.getElementById('preview-frame');
|
| 796 |
if (!iframe) return;
|
| 797 |
|
|
|
|
|
|
|
|
|
|
|
|
|
| 798 |
// Render index.html first, fallback to first file
|
| 799 |
let html = this.files['index.html'] || Object.values(this.files)[0] || '';
|
| 800 |
|
| 801 |
-
//
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 802 |
const erudaSnippet = [
|
| 803 |
'<script src="https://cdn.jsdelivr.net/npm/eruda"><\/script>',
|
| 804 |
'<script>eruda.init();<\/script>'
|
|
|
|
| 399 |
::-webkit-scrollbar-track { background: transparent; }
|
| 400 |
::-webkit-scrollbar-thumb { background: #cbd5e1; border-radius: 10px; }
|
| 401 |
iframe { border: none; width: 100%; height: 100%; }
|
| 402 |
+
@keyframes loadingSlide {
|
| 403 |
+
0% { transform: translateX(-150%); }
|
| 404 |
+
100% { transform: translateX(300%); }
|
| 405 |
+
}
|
| 406 |
+
.loading-fill {
|
| 407 |
+
width: 45%;
|
| 408 |
+
height: 100%;
|
| 409 |
+
background: linear-gradient(90deg, #818cf8, #6366f1, #4f46e5);
|
| 410 |
+
border-radius: 999px;
|
| 411 |
+
animation: loadingSlide 1.1s cubic-bezier(.4,0,.6,1) infinite;
|
| 412 |
+
}
|
| 413 |
</style>
|
| 414 |
</head>
|
| 415 |
<body class="bg-gray-100 flex justify-center h-[100dvh] overflow-hidden text-gray-800">
|
|
|
|
| 458 |
</div>
|
| 459 |
</header>
|
| 460 |
|
| 461 |
+
<!-- Loading bar (visible on all tabs saat polling) -->
|
| 462 |
+
<div x-show="isProcessing"
|
| 463 |
+
class="flex-shrink-0 h-1 bg-indigo-100 overflow-hidden"
|
| 464 |
+
style="display:none">
|
| 465 |
+
<div class="loading-fill"></div>
|
| 466 |
+
</div>
|
| 467 |
<main class="flex-1 min-h-0 relative bg-gray-50 flex flex-col overflow-hidden">
|
| 468 |
|
| 469 |
<!-- βββ CHAT PAGE βββ -->
|
|
|
|
| 690 |
loopCount: 0,
|
| 691 |
toastMsg: '',
|
| 692 |
_pollTimer: null,
|
| 693 |
+
_blobUrls: [],
|
| 694 |
|
| 695 |
init() {
|
| 696 |
this.$watch('tab', val => {
|
|
|
|
| 806 |
}
|
| 807 |
},
|
| 808 |
|
| 809 |
+
/* βββ Preview (Eruda + Blob URLs for CSS/JS) βββ */
|
| 810 |
|
| 811 |
updatePreview() {
|
| 812 |
const iframe = document.getElementById('preview-frame');
|
| 813 |
if (!iframe) return;
|
| 814 |
|
| 815 |
+
// Revoke blob URLs dari render sebelumnya
|
| 816 |
+
this._blobUrls.forEach(u => URL.revokeObjectURL(u));
|
| 817 |
+
this._blobUrls = [];
|
| 818 |
+
|
| 819 |
// Render index.html first, fallback to first file
|
| 820 |
let html = this.files['index.html'] || Object.values(this.files)[0] || '';
|
| 821 |
|
| 822 |
+
// Buat blob URL untuk setiap file .css dan .js, lalu ganti referensinya di HTML
|
| 823 |
+
for (const [fname, content] of Object.entries(this.files)) {
|
| 824 |
+
if (fname === 'index.html') continue;
|
| 825 |
+
let mimeType = null;
|
| 826 |
+
if (fname.endsWith('.css')) mimeType = 'text/css';
|
| 827 |
+
else if (fname.endsWith('.js')) mimeType = 'application/javascript';
|
| 828 |
+
if (!mimeType) continue;
|
| 829 |
+
|
| 830 |
+
const blob = new Blob([content], { type: mimeType });
|
| 831 |
+
const blobUrl = URL.createObjectURL(blob);
|
| 832 |
+
this._blobUrls.push(blobUrl);
|
| 833 |
+
|
| 834 |
+
// Escape nama file untuk regex (titik, dll)
|
| 835 |
+
const escaped = fname.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
| 836 |
+
// Ganti href="fname" / href='fname' dan src="fname" / src='fname'
|
| 837 |
+
html = html.replace(
|
| 838 |
+
new RegExp(`((?:href|src)=["'])${escaped}(["'])`, 'g'),
|
| 839 |
+
`$1${blobUrl}$2`
|
| 840 |
+
);
|
| 841 |
+
}
|
| 842 |
+
|
| 843 |
+
// Inject Eruda debugger
|
| 844 |
const erudaSnippet = [
|
| 845 |
'<script src="https://cdn.jsdelivr.net/npm/eruda"><\/script>',
|
| 846 |
'<script>eruda.init();<\/script>'
|