Spaces:
Paused
Paused
Update app.py
Browse files
app.py
CHANGED
|
@@ -246,7 +246,8 @@ def apply_tools_call(files: dict, ai_response: str) -> dict:
|
|
| 246 |
raise ValueError(f"Tool tidak dikenal '{name}' pada blok ke-{i+1}. Gunakan write_file atau edit_file.")
|
| 247 |
|
| 248 |
action_label = f"Diperbarui ({success_count} operasi){' β¨ Auto-Fix' if is_fuzzy else ''}"
|
| 249 |
-
|
|
|
|
| 250 |
|
| 251 |
|
| 252 |
# βββββββββββββββββββββββββββββββββββββββββββββ
|
|
@@ -405,18 +406,44 @@ HTML = r"""<!DOCTYPE html>
|
|
| 405 |
<div x-data="aiEditor()" class="w-full max-w-md h-full bg-white shadow-2xl relative flex flex-col">
|
| 406 |
|
| 407 |
<!-- Header -->
|
| 408 |
-
<header class="flex-shrink-0 bg-indigo-600 text-white
|
| 409 |
<h1 class="text-lg font-bold flex items-center gap-2">
|
| 410 |
<i class="fa-solid fa-wand-magic-sparkles"></i> AI Editor
|
| 411 |
</h1>
|
| 412 |
-
<
|
| 413 |
-
|
| 414 |
-
|
|
|
|
|
|
|
|
|
|
| 415 |
</button>
|
| 416 |
-
<
|
| 417 |
-
|
| 418 |
-
|
| 419 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 420 |
</div>
|
| 421 |
</header>
|
| 422 |
|
|
@@ -435,6 +462,17 @@ HTML = r"""<!DOCTYPE html>
|
|
| 435 |
? 'bg-indigo-600 text-white rounded-tr-none'
|
| 436 |
: 'bg-white text-gray-800 border border-gray-200 rounded-tl-none'">
|
| 437 |
<p class="whitespace-pre-wrap leading-relaxed" x-text="msg.content"></p>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 438 |
<template x-if="msg.action">
|
| 439 |
<div class="mt-2 pt-2 border-t border-gray-100/30 flex items-center gap-1">
|
| 440 |
<span class="text-[10px] px-2 py-0.5 rounded-full font-bold flex items-center gap-1 bg-emerald-100 text-emerald-700">
|
|
@@ -570,13 +608,13 @@ HTML = r"""<!DOCTYPE html>
|
|
| 570 |
|
| 571 |
<!-- βββ PREVIEW PAGE βββ -->
|
| 572 |
<div x-show="tab === 'preview'" class="h-full flex flex-col bg-white" x-transition.opacity style="display:none">
|
| 573 |
-
<div class="flex-shrink-0 bg-gray-
|
| 574 |
-
<span class="
|
| 575 |
-
<i class="fa-solid fa-
|
| 576 |
-
<span class="text-gray-400
|
| 577 |
</span>
|
| 578 |
-
<button @click="updatePreview" class="hover:text-indigo-600 transition flex items-center gap-1">
|
| 579 |
-
<i class="fa-solid fa-rotate-right"></i>
|
| 580 |
</button>
|
| 581 |
</div>
|
| 582 |
<div class="flex-1 min-h-0 w-full relative">
|
|
@@ -622,6 +660,7 @@ HTML = r"""<!DOCTYPE html>
|
|
| 622 |
document.addEventListener('alpine:init', () => {
|
| 623 |
Alpine.data('aiEditor', () => ({
|
| 624 |
tab: 'chat',
|
|
|
|
| 625 |
files: {
|
| 626 |
'index.html': `<!DOCTYPE html>\n<html>\n<head>\n <title>Halo Dunia</title>\n</head>\n<body>\n <h1>Halo Dunia!</h1>\n <button onclick="showAlert()">Klik Saya</button>\n <script>\n function showAlert() {\n alert('Halo!');\n }\n <\/script>\n</body>\n</html>`
|
| 627 |
},
|
|
@@ -728,6 +767,28 @@ HTML = r"""<!DOCTYPE html>
|
|
| 728 |
}
|
| 729 |
},
|
| 730 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 731 |
/* βββ Preview (Eruda only) βββ */
|
| 732 |
|
| 733 |
updatePreview() {
|
|
@@ -806,6 +867,7 @@ HTML = r"""<!DOCTYPE html>
|
|
| 806 |
role: 'ai',
|
| 807 |
content: data.result.message,
|
| 808 |
action: data.result.action_label,
|
|
|
|
| 809 |
});
|
| 810 |
} else {
|
| 811 |
this.chatHistory.push({
|
|
@@ -835,4 +897,4 @@ def index():
|
|
| 835 |
|
| 836 |
|
| 837 |
if __name__ == "__main__":
|
| 838 |
-
app.run(host="0.0.0.0", port=7860)
|
|
|
|
| 246 |
raise ValueError(f"Tool tidak dikenal '{name}' pada blok ke-{i+1}. Gunakan write_file atau edit_file.")
|
| 247 |
|
| 248 |
action_label = f"Diperbarui ({success_count} operasi){' β¨ Auto-Fix' if is_fuzzy else ''}"
|
| 249 |
+
reasons = [t.get('reason', '') for t in tools if t.get('reason')]
|
| 250 |
+
return {"files": temp_files, "message": respond_text, "action_label": action_label, "reasons": reasons}
|
| 251 |
|
| 252 |
|
| 253 |
# βββββββββββββββββββββββββββββββββββββββββββββ
|
|
|
|
| 406 |
<div x-data="aiEditor()" class="w-full max-w-md h-full bg-white shadow-2xl relative flex flex-col">
|
| 407 |
|
| 408 |
<!-- Header -->
|
| 409 |
+
<header class="flex-shrink-0 bg-indigo-600 text-white px-4 py-3 flex justify-between items-center shadow-md z-10">
|
| 410 |
<h1 class="text-lg font-bold flex items-center gap-2">
|
| 411 |
<i class="fa-solid fa-wand-magic-sparkles"></i> AI Editor
|
| 412 |
</h1>
|
| 413 |
+
<!-- Hamburger Menu -->
|
| 414 |
+
<div class="relative">
|
| 415 |
+
<button @click="showMenu = !showMenu"
|
| 416 |
+
class="bg-indigo-500 hover:bg-indigo-400 w-9 h-9 rounded flex items-center justify-center transition shadow"
|
| 417 |
+
:class="showMenu ? 'bg-indigo-400' : ''">
|
| 418 |
+
<i class="fa-solid fa-bars text-base"></i>
|
| 419 |
</button>
|
| 420 |
+
<!-- Dropdown -->
|
| 421 |
+
<div x-show="showMenu"
|
| 422 |
+
@click.away="showMenu = false"
|
| 423 |
+
x-transition:enter="transition ease-out duration-100"
|
| 424 |
+
x-transition:enter-start="opacity-0 scale-95"
|
| 425 |
+
x-transition:enter-end="opacity-100 scale-100"
|
| 426 |
+
x-transition:leave="transition ease-in duration-75"
|
| 427 |
+
x-transition:leave-start="opacity-100 scale-100"
|
| 428 |
+
x-transition:leave-end="opacity-0 scale-95"
|
| 429 |
+
class="absolute right-0 top-full mt-2 w-44 bg-white rounded-xl shadow-2xl border border-gray-100 overflow-hidden z-50 text-gray-700"
|
| 430 |
+
style="display:none">
|
| 431 |
+
<button @click="downloadZip(); showMenu=false"
|
| 432 |
+
class="w-full text-left px-4 py-3 text-sm hover:bg-indigo-50 flex items-center gap-3 transition border-b border-gray-100">
|
| 433 |
+
<i class="fa-solid fa-file-zipper text-indigo-500 w-4"></i>
|
| 434 |
+
<span class="font-medium">Download ZIP</span>
|
| 435 |
+
</button>
|
| 436 |
+
<button @click="downloadCode(); showMenu=false"
|
| 437 |
+
class="w-full text-left px-4 py-3 text-sm hover:bg-indigo-50 flex items-center gap-3 transition border-b border-gray-100">
|
| 438 |
+
<i class="fa-solid fa-file-arrow-down text-emerald-500 w-4"></i>
|
| 439 |
+
<span class="font-medium">Simpan File</span>
|
| 440 |
+
</button>
|
| 441 |
+
<label class="w-full text-left px-4 py-3 text-sm hover:bg-indigo-50 flex items-center gap-3 transition cursor-pointer">
|
| 442 |
+
<i class="fa-solid fa-file-arrow-up text-orange-500 w-4"></i>
|
| 443 |
+
<span class="font-medium">Upload File</span>
|
| 444 |
+
<input type="file" class="hidden" @change="uploadFile($event); showMenu=false">
|
| 445 |
+
</label>
|
| 446 |
+
</div>
|
| 447 |
</div>
|
| 448 |
</header>
|
| 449 |
|
|
|
|
| 462 |
? 'bg-indigo-600 text-white rounded-tr-none'
|
| 463 |
: 'bg-white text-gray-800 border border-gray-200 rounded-tl-none'">
|
| 464 |
<p class="whitespace-pre-wrap leading-relaxed" x-text="msg.content"></p>
|
| 465 |
+
<!-- Reasons dari tool calls -->
|
| 466 |
+
<template x-if="msg.reasons && msg.reasons.length">
|
| 467 |
+
<div class="mt-2 space-y-1">
|
| 468 |
+
<template x-for="(r, ri) in msg.reasons" :key="ri">
|
| 469 |
+
<div class="text-xs bg-indigo-50 border border-indigo-100 rounded-lg px-2 py-1.5 text-indigo-700 flex items-start gap-1.5">
|
| 470 |
+
<i class="fa-solid fa-pencil text-indigo-400 mt-0.5 flex-shrink-0 text-[10px]"></i>
|
| 471 |
+
<span x-text="r" class="leading-snug"></span>
|
| 472 |
+
</div>
|
| 473 |
+
</template>
|
| 474 |
+
</div>
|
| 475 |
+
</template>
|
| 476 |
<template x-if="msg.action">
|
| 477 |
<div class="mt-2 pt-2 border-t border-gray-100/30 flex items-center gap-1">
|
| 478 |
<span class="text-[10px] px-2 py-0.5 rounded-full font-bold flex items-center gap-1 bg-emerald-100 text-emerald-700">
|
|
|
|
| 608 |
|
| 609 |
<!-- βββ PREVIEW PAGE βββ -->
|
| 610 |
<div x-show="tab === 'preview'" class="h-full flex flex-col bg-white" x-transition.opacity style="display:none">
|
| 611 |
+
<div class="flex-shrink-0 bg-gray-100 text-gray-500 px-3 py-1.5 flex justify-between items-center border-b border-gray-200">
|
| 612 |
+
<span class="text-xs flex items-center gap-1.5">
|
| 613 |
+
<i class="fa-solid fa-bug text-indigo-400"></i>
|
| 614 |
+
<span class="text-gray-400">Eruda</span>
|
| 615 |
</span>
|
| 616 |
+
<button @click="updatePreview" class="text-xs hover:text-indigo-600 transition flex items-center gap-1 px-2 py-1 rounded hover:bg-white">
|
| 617 |
+
<i class="fa-solid fa-rotate-right"></i>
|
| 618 |
</button>
|
| 619 |
</div>
|
| 620 |
<div class="flex-1 min-h-0 w-full relative">
|
|
|
|
| 660 |
document.addEventListener('alpine:init', () => {
|
| 661 |
Alpine.data('aiEditor', () => ({
|
| 662 |
tab: 'chat',
|
| 663 |
+
showMenu: false,
|
| 664 |
files: {
|
| 665 |
'index.html': `<!DOCTYPE html>\n<html>\n<head>\n <title>Halo Dunia</title>\n</head>\n<body>\n <h1>Halo Dunia!</h1>\n <button onclick="showAlert()">Klik Saya</button>\n <script>\n function showAlert() {\n alert('Halo!');\n }\n <\/script>\n</body>\n</html>`
|
| 666 |
},
|
|
|
|
| 767 |
}
|
| 768 |
},
|
| 769 |
|
| 770 |
+
async downloadZip() {
|
| 771 |
+
try {
|
| 772 |
+
const JSZip = (await import('https://cdn.jsdelivr.net/npm/jszip@3.10.1/+esm')).default;
|
| 773 |
+
const zip = new JSZip();
|
| 774 |
+
for (const [fname, content] of Object.entries(this.files)) {
|
| 775 |
+
zip.file(fname, content);
|
| 776 |
+
}
|
| 777 |
+
const blob = await zip.generateAsync({ type: 'blob' });
|
| 778 |
+
const url = URL.createObjectURL(blob);
|
| 779 |
+
const a = document.createElement('a');
|
| 780 |
+
a.href = url;
|
| 781 |
+
a.download = 'project.zip';
|
| 782 |
+
document.body.appendChild(a);
|
| 783 |
+
a.click();
|
| 784 |
+
document.body.removeChild(a);
|
| 785 |
+
URL.revokeObjectURL(url);
|
| 786 |
+
this.showToast(`ZIP berhasil diunduh (${Object.keys(this.files).length} file)!`);
|
| 787 |
+
} catch (e) {
|
| 788 |
+
this.showToast('Gagal membuat ZIP.');
|
| 789 |
+
}
|
| 790 |
+
},
|
| 791 |
+
|
| 792 |
/* βββ Preview (Eruda only) βββ */
|
| 793 |
|
| 794 |
updatePreview() {
|
|
|
|
| 867 |
role: 'ai',
|
| 868 |
content: data.result.message,
|
| 869 |
action: data.result.action_label,
|
| 870 |
+
reasons: data.result.reasons || [],
|
| 871 |
});
|
| 872 |
} else {
|
| 873 |
this.chatHistory.push({
|
|
|
|
| 897 |
|
| 898 |
|
| 899 |
if __name__ == "__main__":
|
| 900 |
+
app.run(host="0.0.0.0", port=7860)
|