floodd commited on
Commit
c062e09
·
verified ·
1 Parent(s): 980d128

Update app.py

Browse files
Files changed (1) hide show
  1. app.py +412 -147
app.py CHANGED
@@ -1,4 +1,6 @@
 
1
 
 
2
  import gradio as gr
3
  import torch
4
  import numpy as np
@@ -10,82 +12,247 @@ from datetime import datetime, timedelta
10
  import csv
11
  import pandas as pd
12
  import threading
 
 
 
 
 
 
 
 
 
 
 
 
 
 
13
 
14
  try:
15
  import google.generativeai as genai
16
  print("✅ Library 'google-generativeai' berhasil diimpor.")
17
  except ImportError:
18
- print("❌ Peringatan: Library 'google-generativeai' tidak ditemukan.")
19
  genai = None
20
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
21
  VISITOR_LOG_FILE = "visitor_log.csv"
22
  file_lock = threading.Lock()
 
 
23
 
 
24
  def initialize_log_file():
25
- """Membuat file log jika belum ada."""
26
  if not os.path.exists(VISITOR_LOG_FILE):
27
  with file_lock:
28
- # Periksa lagi di dalam lock untuk menghindari race condition
29
  if not os.path.exists(VISITOR_LOG_FILE):
30
  with open(VISITOR_LOG_FILE, mode='w', newline='', encoding='utf-8') as f:
31
  writer = csv.writer(f)
32
  writer.writerow(["Timestamp", "IP Address", "User Agent"])
33
  print(f"✅ File log '{VISITOR_LOG_FILE}' berhasil dibuat.")
34
 
35
- # Inisialisasi file log saat script dimulai
36
  initialize_log_file()
37
 
38
- def log_visitor(request: gr.Request):
39
- """Mencatat informasi pengunjung ke dalam file CSV."""
40
- timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
41
- ip_address = request.client.host if request else "N/A"
42
- user_agent = request.headers.get("user-agent", "Unknown") if request else "N/A"
43
-
44
- with file_lock:
45
- with open(VISITOR_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
46
- writer = csv.writer(f)
47
- writer.writerow([timestamp, ip_address, user_agent])
48
- print(f"✅ Pengunjung baru tercatat: IP {ip_address}")
49
 
50
- def update_visitor_monitor(time_filter: str):
51
- """Membaca file log, memproses data, dan mengembalikannya untuk ditampilkan di UI."""
 
 
52
  try:
53
- with file_lock:
54
- if not os.path.exists(VISITOR_LOG_FILE) or os.path.getsize(VISITOR_LOG_FILE) == 0:
55
- return "## 📈 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
56
-
57
- df = pd.read_csv(VISITOR_LOG_FILE)
58
- if df.empty:
59
- return "## 📈 0", pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
60
-
61
- df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce')
62
- df.dropna(subset=['Timestamp'], inplace=True)
63
-
64
- total_overall_visitors = len(df)
65
- total_visitors_formatted = f"## 📈 {total_overall_visitors:,}"
66
-
67
- df['Total Pengunjung'] = np.arange(1, len(df) + 1)
68
-
69
- now = pd.to_datetime('now').tz_localize(None)
70
- if time_filter == "1 Minggu Terakhir":
71
- df_plot = df[df['Timestamp'] >= now - timedelta(weeks=1)]
72
- elif time_filter == "2 Minggu Terakhir":
73
- df_plot = df[df['Timestamp'] >= now - timedelta(weeks=2)]
74
- elif time_filter == "3 Bulan Terakhir":
75
- df_plot = df[df['Timestamp'] >= now - timedelta(days=90)]
76
- else:
77
- df_plot = df
78
-
79
- if df_plot.empty:
80
- return total_visitors_formatted, pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
81
-
82
- return total_visitors_formatted, df_plot
83
-
84
  except Exception as e:
85
- error_message = f"Error saat memperbarui monitor: {e}"
86
- print(f"❌ {error_message}")
87
- return f"## ⚠️ Error", pd.DataFrame({"Error": [error_message]})
88
 
 
89
  class GeminiChat:
90
  def __init__(self):
91
  self.api_keys = []
@@ -97,145 +264,243 @@ class GeminiChat:
97
  if key: self.api_keys.append(key); i += 1
98
  else: break
99
  if self.api_keys:
100
- print(f"✅ Berhasil memuat {len(self.api_keys)} API Key. Sistem rotasi aktif.")
101
  self.is_configured = True
102
  else:
103
- print("❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan di Secrets.")
104
 
105
- def chat(self, message, history):
106
- if not self.is_configured: return "Maaf, chatbot tidak terkonfigurasi."
107
  try:
108
  selected_key = random.choice(self.api_keys)
109
  genai.configure(api_key=selected_key)
110
  model = genai.GenerativeModel('gemini-2.5-flash')
111
- response = model.generate_content(message)
 
 
112
  return response.text
113
  except Exception as e:
114
- print(f"❌ Terjadi error pada salah satu API Key: {e}")
115
- return "Terjadi kesalahan saat menghubungi API Gemini. Silakan coba lagi."
116
 
117
  gemini_bot = GeminiChat()
118
 
119
- device = "cuda" if torch.cuda.is_available() else "cpu"
120
- print(f"➡️ Menggunakan device untuk generator gambar: {device.upper()}")
121
- pipe = DiffusionPipeline.from_pretrained(
122
- "stabilityai/sdxl-turbo", torch_dtype=torch.float16 if device == "cuda" else torch.float32,
123
- variant="fp16" if device == "cuda" else None, use_safetensors=True
124
- ).to(device)
125
- if torch.cuda.is_available(): pipe.enable_xformers_memory_efficient_attention()
126
-
 
 
 
 
127
  def generate_images(prompt, negative_prompt, steps, seed, num_images):
 
128
  if seed == -1: seed = random.randint(0, 2**32 - 1)
129
- generator = torch.manual_seed(seed)
130
- images = pipe(
131
- prompt=prompt, negative_prompt=negative_prompt, generator=generator,
132
- num_inference_steps=steps, guidance_scale=0.0, num_images_per_prompt=num_images
133
- ).images
134
  return images, seed
135
 
136
  def genie_wrapper(prompt, negative_prompt, steps, seed, num_images):
137
- yield gr.update(visible=False), gr.update(visible=True), gr.update(interactive=False), gr.update(visible=False)
138
  start_time = time.time()
139
- for i in range(10):
140
- elapsed_time = time.time() - start_time
141
- loader_html_content = f"""<div class="loader-container"><div class="loader-bar"></div><p class="loader-text">Mempersiapkan model... Waktu berlalu: {elapsed_time:.2f} detik</p></div>"""
142
- yield gr.update(), gr.update(value=loader_html_content), gr.update(), gr.update()
143
- time.sleep(0.1)
144
- images, used_seed = generate_images(prompt, negative_prompt, steps, int(seed), int(num_images))
145
  end_time = time.time()
146
  generation_time = end_time - start_time
147
  info_text = f"Seed yang digunakan: {used_seed}\nTotal waktu generasi: {generation_time:.2f} detik"
148
- yield gr.update(value=images, visible=True), gr.update(visible=False), gr.update(interactive=True), gr.update(value=info_text, visible=True)
149
 
150
  def submit_report(name, email, message):
 
 
 
151
  report_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
152
  report_content = f"--- Laporan Baru ({report_time}) ---\nNama: {name}\nEmail: {email}\nPesan: {message}\n\n"
153
- with open("reports.log", "a", encoding="utf-8") as f:
154
- f.write(report_content)
155
  print("✅ Laporan baru telah disimpan ke reports.log")
156
  return gr.update(value="✅ Terima kasih! Laporan Anda telah kami terima.", visible=True)
157
 
158
- CUSTOM_CSS = """
159
- .loader-container { border: 1px solid #333; border-radius: 8px; padding: 20px; background-color: #1a1a1a; text-align: center; overflow: hidden; position: relative; }
160
- .loader-text { font-family: 'monospace'; font-size: 1.1em; color: #00ff99; text-shadow: 0 0 5px #00ff99; }
161
- .loader-bar { position: absolute; top: 0; left: -10%; width: 10%; height: 100%; background: linear-gradient(90deg, transparent, rgba(0, 255, 153, 0.5), transparent); animation: scan 2s linear infinite; }
162
- @keyframes scan { 0% { left: -10%; } 100% { left: 100%; } }
163
- .footer { text-align: center; margin: 2rem auto; }
164
- """
 
 
 
 
 
 
 
 
 
 
 
 
 
 
165
 
166
- with gr.Blocks(theme=gr.themes.Monochrome(), css=CUSTOM_CSS) as demo:
167
- gr.Markdown("# 🚀 RenXploit's Creative AI Suite 🌌\nSebuah platform lengkap untuk kreativitas Anda, ditenagai oleh AI.")
168
-
169
- with gr.Tabs():
170
- # --- TAB 1: IMAGE GENERATOR ---
171
- with gr.TabItem("🎨 Image Generator", id=0):
172
- with gr.Row(variant='panel'):
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
173
  with gr.Column(scale=1):
174
- gr.Markdown("### 📝 Masukkan Perintah Anda")
175
- prompt_input = gr.Textbox(label="Prompt", placeholder="Contoh: Cinematic photo, seekor rubah merah...", lines=3, info="Jadilah sangat spesifik!")
176
- negative_prompt_input = gr.Textbox(label="Prompt Negatif", placeholder="Contoh: blurry, low quality...", lines=2, info="Hal-hal yang TIDAK Anda inginkan.")
177
  num_images_slider = gr.Slider(minimum=1, maximum=8, value=2, step=1, label="Jumlah Gambar")
178
- generate_btn = gr.Button("✨ Hasilkan Gambar!", variant="primary")
179
- with gr.Accordion("⚙️ Opsi Lanjutan", open=False):
180
- steps_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Langkah Iterasi")
181
  with gr.Row():
182
  seed_input = gr.Number(label="Seed", value=-1, precision=0, info="Gunakan -1 untuk acak.")
183
  random_seed_btn = gr.Button("🎲 Acak")
184
  with gr.Column(scale=2):
185
- gr.Markdown("### 🖼️ Hasil Generasi")
186
  output_gallery = gr.Gallery(label="Hasil Gambar", show_label=False, elem_id="gallery", columns=2, object_fit="contain", height="auto")
187
- loader_html = gr.HTML(visible=False)
188
- info_box = gr.Textbox(label="Informasi Generasi", visible=False, interactive=False)
189
-
190
- # --- TAB 2: CHAT WITH AI ---
191
- with gr.TabItem("💬 Chat with AI", id=1):
192
- gr.Markdown("### 🤖 Asisten AI Flood\nTanyakan apa saja!")
193
- if not gemini_bot.is_configured:
194
- gr.Warning("Fitur Chatbot dinonaktifkan. API Key Gemini tidak terkonfigurasi.")
195
- else:
196
- gr.ChatInterface(gemini_bot.chat, chatbot=gr.Chatbot(height=500, value=[[None, "Halo! Saya adalah asisten AI dari RenXploit. Ada yang bisa saya bantu?"]]), type="tuples")
197
-
198
- # --- TAB 3: VISITOR MONITOR (BARU) ---
199
- with gr.TabItem("📊 Visitor Monitor", id=2):
200
- gr.Markdown("### 📈 Live Visitor Monitor\nPantau jumlah total pengunjung aplikasi Anda secara real-time.")
201
- with gr.Row():
202
- with gr.Column(scale=3):
203
- visitor_count_display = gr.Markdown("## 📈 Memuat data...")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
204
  with gr.Column(scale=2):
205
- time_filter_radio = gr.Radio(["Semua Waktu", "1 Minggu Terakhir", "2 Minggu Terakhir", "3 Bulan Terakhir"], label="Tampilkan data untuk", value="Semua Waktu")
206
- refresh_btn = gr.Button("🔄 Segarkan Manual")
207
- visitor_plot = gr.LinePlot(x="Timestamp", y="Total Pengunjung", title="Grafik Pertumbuhan Pengunjung", tooltip=['Timestamp', 'Total Pengunjung'], height=500, interactive=True)
 
 
 
 
 
 
208
 
209
- # --- TAB-TAB STATIS LAINNYA ---
210
- with gr.TabItem("💡 Panduan Prompting", id=3):
211
- gr.Markdown("## Cara Menjadi \"Art Director\" yang Hebat untuk AI\n\nSelamat datang di panduan prompting! Membuat gambar AI yang bagus adalah seni tersendiri. Anggap diri Anda sebagai sutradara film dan AI sebagai aktor Anda. Semakin jelas instruksi Anda, semakin baik hasilnya.\n\n### 1. **Jadilah Spesifik dan Deskriptif**\n- **Buruk:** `mobil`\n- **Bagus:** `mobil sport merah klasik tahun 1960-an, convertible, melaju di jalan pantai saat matahari terbenam`\n\n### 2. **Gunakan Kata Sifat yang Kuat**\n- Tambahkan kata-kata seperti `sinematik`, `dramatis`, `sureal`, `fotorealistik`, `epik`, `bercahaya`, `mistis` untuk mengatur suasana.\n\n### 3. **Tentukan Gaya Seni**\n- Ingin terlihat seperti apa? Sebutkan!\n- Contoh: `lukisan cat minyak`, `gaya anime studio ghibli`, `seni piksel 16-bit`, `foto hitam putih`, `seni konsep sci-fi`\n\n### 4. **Gunakan Prompt Negatif**\n- Gunakan kolom Prompt Negatif untuk menghilangkan hal-hal yang tidak Anda inginkan. Ini sangat kuat!\n- Contoh: `tangan jelek, buram, kualitas rendah, teks, tanda air, cacat, kusam`\n")
212
-
213
- with gr.TabItem("📖 Blog & Updates", id=4):
214
- gr.Markdown("### Perkembangan Terbaru dari RenXploit's AI Suite\n\n- **v1.0 new (28 Oktober 2025):** Menambahkan tab Visitor Monitor! Sekarang kami dapat melihat seberapa banyak aplikasi ini digunakan\n- **v1.5 (29 Oktober 2025):** Peluncuran awal Creative AI Suite dengan Image Generator SDXL-Turbo dan Asisten AI berbasis Gemini.\n- **Rencana Berikutnya:** Kami sedang menjajaki kemungkinan menambahkan fitur *upscaling* gambar dan model generator gambar yang berbeda. Nantikan!")
215
-
216
- with gr.TabItem("ℹ️ About & Support", id=5):
217
- gr.Markdown("### Tentang Proyek dan Dukungan")
218
- with gr.Accordion("Tentang RenXploit's Creative AI Suite", open=True):
219
- gr.Markdown("**RenXploit's Creative AI Suite** adalah proyek pribadi yang dibuat untuk mengeksplorasi kemampuan AI generatif dalam sebuah antarmuka yang mudah digunakan.\n **Bagian ini belum saya kerjakan dengan selesai** jadi jika anda ingin komplain atau ada hal apapun itu bisa report ke saya lewat website portofolio saya dengan url: https://ngoprek.xyz/contact")
220
- with gr.Accordion("Laporkan Masalah atau Beri Masukan"):
221
- report_name = gr.Textbox(label="Nama Anda")
222
- report_email = gr.Textbox(label="Email Anda (Opsional)")
223
- report_message = gr.Textbox(label="Pesan Anda", lines=5, placeholder="Jelaskan masalah yang Anda temui atau ide yang Anda miliki...")
224
- report_btn = gr.Button("Kirim Laporan", variant="primary")
225
- report_status = gr.Markdown(visible=False)
226
-
227
- gr.Markdown("---\n<div class='footer'><p>Dibuat dengan ❤️ oleh <b>RenXploit</b>.</p></div>", elem_classes="footer")
228
-
229
- # --- Event Handlers untuk AI Suite ---
230
  random_seed_btn.click(lambda: -1, outputs=seed_input)
231
- generate_btn.click(fn=genie_wrapper, inputs=[prompt_input, negative_prompt_input, steps_slider, seed_input, num_images_slider], outputs=[output_gallery, loader_html, generate_btn, info_box])
232
  report_btn.click(fn=submit_report, inputs=[report_name, report_email, report_message], outputs=[report_status])
233
-
234
- # --- Event Handlers untuk Visitor Monitor ---
235
  demo.load(log_visitor, inputs=None, outputs=None)
236
  demo.load(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
237
  refresh_btn.click(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
238
  time_filter_radio.change(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
239
 
 
 
 
 
 
 
 
 
 
 
240
  if __name__ == "__main__":
241
- demo.launch()
 
1
+ # -*- coding: utf-8 -*-
2
 
3
+ # --- IMPORT LIBRARY UTAMA & INTI ---
4
  import gradio as gr
5
  import torch
6
  import numpy as np
 
12
  import csv
13
  import pandas as pd
14
  import threading
15
+ from PIL import Image, ImageEnhance
16
+
17
+ # --- LIBRARY TAMBAHAN UNTUK FITUR BARU ---
18
+ try:
19
+ import psutil
20
+ import platform
21
+ from transformers import Swin2SRForImageSuperResolution, Swin2SRImageProcessor
22
+ print("✅ Library tambahan (psutil, transformers) berhasil diimpor.")
23
+ except ImportError:
24
+ print("❌ Peringatan: Library 'psutil' atau 'transformers' tidak ditemukan. Fitur System Monitor & Upscaler tidak akan berfungsi.")
25
+ psutil = None
26
+ platform = None
27
+ Swin2SRForImageSuperResolution = None
28
+ Swin2SRImageProcessor = None
29
 
30
  try:
31
  import google.generativeai as genai
32
  print("✅ Library 'google-generativeai' berhasil diimpor.")
33
  except ImportError:
34
+ print("❌ Peringatan: Library 'google-generativeai' tidak ditemukan. Fitur Chatbot & Prompt Enhancer tidak akan berfungsi.")
35
  genai = None
36
 
37
+ # --- [UPGRADE] HEAD HTML & CSS BARU (Tampilan Super Canggih) ---
38
+ HEAD_HTML = """
39
+ <head>
40
+ <meta charset="UTF-8">
41
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
42
+ <title>RenXploit's Creative AI Suite</title>
43
+ <link rel="preconnect" href="https://fonts.googleapis.com">
44
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
45
+ <link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Fira+Code:wght@500&display=swap" rel="stylesheet">
46
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.5.1/css/all.min.css">
47
+ <script>
48
+ function typewriterEffect(element, text, speed) {
49
+ let i = 0;
50
+ element.innerHTML = "";
51
+ const cursor = document.createElement('span');
52
+ cursor.className = 'typewriter-cursor';
53
+ cursor.innerHTML = '▋';
54
+ element.appendChild(cursor);
55
+
56
+ function type() {
57
+ if (i < text.length) {
58
+ element.insertBefore(document.createTextNode(text.charAt(i)), cursor);
59
+ i++;
60
+ setTimeout(type, speed);
61
+ } else {
62
+ cursor.style.animation = 'blink 1s step-end infinite';
63
+ }
64
+ }
65
+ type();
66
+ }
67
+
68
+ document.addEventListener("DOMContentLoaded", function() {
69
+ // Efek fade-in untuk seluruh aplikasi
70
+ document.body.style.opacity = '1';
71
+
72
+ // Efek Typewriter untuk judul
73
+ const titleElement = document.querySelector('#main-title h1');
74
+ if (titleElement) {
75
+ const titleText = "RenXploit's Creative AI Suite";
76
+ titleElement.textContent = "";
77
+ setTimeout(() => typewriterEffect(titleElement, titleText, 70), 500);
78
+ }
79
+
80
+ // Trigger periodik untuk monitor sistem
81
+ setInterval(() => {
82
+ const triggerButton = document.querySelector('#system-info-trigger-btn button');
83
+ if (triggerButton) {
84
+ triggerButton.click();
85
+ }
86
+ }, 5000); // Interval 5 detik
87
+ });
88
+ </script>
89
+ <style>
90
+ :root {
91
+ --primary-color: #00aaff;
92
+ --secondary-color: #9400d3;
93
+ --glow-color: rgba(0, 170, 255, 0.5);
94
+ --background-color: #0d1117;
95
+ --content-background: rgba(22, 27, 34, 0.6);
96
+ --glass-border: rgba(255, 255, 255, 0.1);
97
+ --border-color: #30363d;
98
+ --text-color: #c9d1d9;
99
+ --text-muted-color: #8b949e;
100
+ --font-family-main: 'Inter', sans-serif;
101
+ --font-family-mono: 'Fira Code', monospace;
102
+ --border-radius: 16px;
103
+ --shadow-light: 0 4px 14px 0 rgba(0, 170, 255, 0.1);
104
+ --shadow-strong: 0 6px 20px 0 rgba(0, 170, 255, 0.2);
105
+ }
106
+
107
+ /* --- Latar Belakang Aurora & Animasi --- */
108
+ body, .gradio-container {
109
+ font-family: var(--font-family-main);
110
+ color: var(--text-color);
111
+ background-color: var(--background-color);
112
+ background-image:
113
+ radial-gradient(at 20% 20%, hsla(212,100%,50%,0.2) 0px, transparent 50%),
114
+ radial-gradient(at 80% 20%, hsla(289,100%,50%,0.2) 0px, transparent 50%),
115
+ radial-gradient(at 20% 80%, hsla(180,100%,50%,0.2) 0px, transparent 50%),
116
+ radial-gradient(at 80% 80%, hsla(30,100%,50%,0.2) 0px, transparent 50%);
117
+ opacity: 0;
118
+ transition: opacity 1s ease-in-out;
119
+ }
120
+
121
+ /* --- Header & Judul Utama --- */
122
+ #header-container { text-align: center; padding: 3rem 1rem 2rem 1rem; }
123
+ #main-logo { font-size: 3em; color: var(--primary-color); margin-bottom: 1rem; text-shadow: 0 0 15px var(--glow-color); }
124
+ #main-title h1 {
125
+ font-size: 2.8em !important;
126
+ font-weight: 700 !important;
127
+ color: #ffffff;
128
+ background: linear-gradient(90deg, #00aaff, #00ffaa);
129
+ -webkit-background-clip: text;
130
+ -webkit-text-fill-color: transparent;
131
+ display: inline-block;
132
+ margin: 0;
133
+ }
134
+ .typewriter-cursor { color: var(--primary-color); font-weight: bold; animation: blink 1s step-end infinite; }
135
+ @keyframes blink { from, to { opacity: 1 } 50% { opacity: 0 } }
136
+ #main-subtitle p { font-size: 1.2em !important; color: var(--text-muted-color); margin-top: 0.5rem !important; }
137
+
138
+ /* --- Gaya Tab Profesional --- */
139
+ .gradio-tabs > div[role="tablist"] {
140
+ display: flex; justify-content: center; flex-wrap: wrap; gap: 12px;
141
+ border-bottom: 1px solid var(--border-color); padding-bottom: 15px; margin-bottom: 2rem;
142
+ }
143
+ .gradio-tabs > div[role="tablist"] > button {
144
+ background: var(--content-background) !important; color: var(--text-muted-color) !important;
145
+ border: 1px solid var(--glass-border) !important; border-radius: 10px !important;
146
+ font-size: 1em !important; font-weight: 600 !important; padding: 10px 20px !important;
147
+ transition: all 0.3s ease; backdrop-filter: blur(10px);
148
+ }
149
+ .gradio-tabs > div[role="tablist"] > button:hover {
150
+ color: #ffffff !important; background: rgba(0, 170, 255, 0.2) !important;
151
+ border-color: var(--primary-color) !important; transform: translateY(-3px);
152
+ }
153
+ .gradio-tabs > div[role="tablist"] > button.selected {
154
+ color: #ffffff !important;
155
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)) !important;
156
+ border: 1px solid var(--primary-color) !important; box-shadow: 0 0 20px var(--glow-color);
157
+ }
158
+ .gradio-tabs i { margin-right: 8px; }
159
+ .tabitem { background-color: transparent !important; border: none !important; padding: 0 !important; }
160
+
161
+ /* --- Panel Kaca (Glassmorphism) --- */
162
+ .gradio-row.panel, .gradio-accordion {
163
+ background-color: var(--content-background) !important;
164
+ border: 1px solid var(--glass-border) !important;
165
+ border-radius: var(--border-radius) !important;
166
+ padding: 28px !important;
167
+ box-shadow: 0 8px 32px 0 rgba(0, 0, 0, 0.37);
168
+ backdrop-filter: blur(12px);
169
+ -webkit-backdrop-filter: blur(12px);
170
+ transition: all 0.3s ease;
171
+ }
172
+ .gradio-row.panel:hover, .gradio-accordion:hover {
173
+ border: 1px solid rgba(0, 170, 255, 0.5) !important;
174
+ box-shadow: 0 0 25px rgba(0, 170, 255, 0.15);
175
+ }
176
+ .section-header {
177
+ font-size: 1.5em; font-weight: 600; color: #ffffff;
178
+ border-bottom: 1px solid var(--border-color); padding-bottom: 10px; margin-bottom: 20px;
179
+ }
180
+ .section-header i { color: var(--primary-color); margin-right: 12px; }
181
+
182
+ /* --- Tombol & Input --- */
183
+ .gradio-button.primary {
184
+ background: linear-gradient(90deg, var(--primary-color), var(--secondary-color)) !important;
185
+ color: #ffffff !important; font-weight: 600 !important; font-size: 1.1em !important;
186
+ border-radius: 10px !important; border: none !important; padding: 12px 24px !important;
187
+ transition: all 0.3s ease; box-shadow: var(--shadow-light);
188
+ }
189
+ .gradio-button.primary:hover { transform: translateY(-3px) scale(1.03); box-shadow: var(--shadow-strong); }
190
+ .gradio-textbox, .gradio-number, .gradio-image, .gradio-radio {
191
+ background-color: rgba(13, 17, 23, 0.8) !important; border: 1px solid var(--border-color) !important;
192
+ color: var(--text-color) !important; border-radius: 10px !important;
193
+ }
194
+ .gradio-textbox:focus-within, .gradio-number:focus-within {
195
+ border-color: var(--primary-color) !important;
196
+ box-shadow: 0 0 0 3px rgba(0, 170, 255, 0.3);
197
+ }
198
+
199
+ /* --- Galeri & Chatbot --- */
200
+ #gallery { background-color: transparent; }
201
+ #gallery .gallery-item { border-radius: var(--border-radius); border: 2px solid transparent; transition: all 0.3s ease; }
202
+ #gallery .gallery-item:hover { transform: scale(1.05); box-shadow: 0 0 30px var(--glow-color); border-color: var(--primary-color); }
203
+ .gradio-chatbot .message.user { background: linear-gradient(90deg, var(--primary-color), #00c6ff) !important; color: white !important; }
204
+ .gradio-chatbot .message.bot { background-color: var(--content-background) !important; border: 1px solid var(--glass-border) !important; }
205
+
206
+ /* --- Footer --- */
207
+ .footer { text-align: center; padding: 2rem 0; color: var(--text-muted-color); border-top: 1px solid var(--border-color); margin-top: 2rem;}
208
+ .footer b { color: var(--primary-color); font-weight: 600; }
209
+ </style>
210
+ </head>
211
+ """
212
+
213
+ # --- PENGATURAN & LOGIC GLOBAL ---
214
  VISITOR_LOG_FILE = "visitor_log.csv"
215
  file_lock = threading.Lock()
216
+ device = "cuda" if torch.cuda.is_available() else "cpu"
217
+ print(f"➡️ Menggunakan device: {device.upper()}")
218
 
219
+ # --- Inisialisasi File Log Pengunjung ---
220
  def initialize_log_file():
 
221
  if not os.path.exists(VISITOR_LOG_FILE):
222
  with file_lock:
 
223
  if not os.path.exists(VISITOR_LOG_FILE):
224
  with open(VISITOR_LOG_FILE, mode='w', newline='', encoding='utf-8') as f:
225
  writer = csv.writer(f)
226
  writer.writerow(["Timestamp", "IP Address", "User Agent"])
227
  print(f"✅ File log '{VISITOR_LOG_FILE}' berhasil dibuat.")
228
 
 
229
  initialize_log_file()
230
 
231
+ # --- PEMUATAN MODEL-MODEL AI ---
232
+ # 1. Model Generator Gambar (Inti)
233
+ print("➡️ Memuat model SDXL-Turbo...")
234
+ pipe = DiffusionPipeline.from_pretrained(
235
+ "stabilityai/sdxl-turbo", torch_dtype=torch.float16 if device == "cuda" else torch.float32,
236
+ variant="fp16" if device == "cuda" else None, use_safetensors=True
237
+ ).to(device)
238
+ if torch.cuda.is_available(): pipe.enable_xformers_memory_efficient_attention()
239
+ print("✅ Model SDXL-Turbo berhasil dimuat.")
 
 
240
 
241
+ # 2. Model Peningkatan Resolusi (Fitur Baru)
242
+ upscaler_model = None
243
+ upscaler_processor = None
244
+ if Swin2SRForImageSuperResolution:
245
  try:
246
+ print("➡️ Memuat model AI Upscaler (Swin2SR)...")
247
+ upscaler_model = Swin2SRForImageSuperResolution.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr").to(device)
248
+ upscaler_processor = Swin2SRImageProcessor.from_pretrained("caidas/swin2sr-realworld-sr-x4-64-bsrgan-psnr")
249
+ print("✅ Model AI Upscaler berhasil dimuat.")
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
250
  except Exception as e:
251
+ print(f" Gagal memuat model Upscaler: {e}. Fitur upscale akan dinonaktifkan.")
252
+ upscaler_model = None # Pastikan dinonaktifkan jika gagal
253
+ upscaler_processor = None
254
 
255
+ # --- KELAS UNTUK CHATBOT GEMINI ---
256
  class GeminiChat:
257
  def __init__(self):
258
  self.api_keys = []
 
264
  if key: self.api_keys.append(key); i += 1
265
  else: break
266
  if self.api_keys:
267
+ print(f"✅ Berhasil memuat {len(self.api_keys)} API Key Gemini. Sistem rotasi aktif.")
268
  self.is_configured = True
269
  else:
270
+ print("❌ PERINGATAN: Tidak ada API Key Gemini yang ditemukan. Fitur AI Chat & Prompt Enhancer tidak akan berfungsi.")
271
 
272
+ def chat(self, message, history, system_prompt=None):
273
+ if not self.is_configured: return "Maaf, fitur ini tidak terkonfigurasi karena tidak ada API Key yang valid."
274
  try:
275
  selected_key = random.choice(self.api_keys)
276
  genai.configure(api_key=selected_key)
277
  model = genai.GenerativeModel('gemini-2.5-flash')
278
+ full_prompt = message
279
+ if system_prompt: full_prompt = f"{system_prompt}\n\nUser query: {message}"
280
+ response = model.generate_content(full_prompt)
281
  return response.text
282
  except Exception as e:
283
+ print(f"❌ Terjadi error pada API Key Gemini: {e}")
284
+ return "Terjadi kesalahan saat menghubungi API AI. Mungkin salah satu API Key tidak valid, kuota habis, atau ada masalah jaringan. Silakan coba lagi."
285
 
286
  gemini_bot = GeminiChat()
287
 
288
+ # --- FUNGSI-FUNGSI INTI & FITUR ---
289
+ def log_visitor(request: gr.Request):
290
+ if not request: return # Hindari error saat Gradio di-reload
291
+ timestamp = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
292
+ ip_address = request.client.host
293
+ user_agent = request.headers.get("user-agent", "Unknown")
294
+ with file_lock:
295
+ with open(VISITOR_LOG_FILE, mode='a', newline='', encoding='utf-8') as f:
296
+ writer = csv.writer(f)
297
+ writer.writerow([timestamp, ip_address, user_agent])
298
+ print(f"✅ Pengunjung baru tercatat: IP {ip_address}")
299
+
300
  def generate_images(prompt, negative_prompt, steps, seed, num_images):
301
+ if not prompt: raise gr.Error("Prompt tidak boleh kosong!")
302
  if seed == -1: seed = random.randint(0, 2**32 - 1)
303
+ generator = torch.manual_seed(int(seed))
304
+ images = pipe(prompt=prompt, negative_prompt=negative_prompt, generator=generator, num_inference_steps=int(steps), guidance_scale=0.0, num_images_per_prompt=int(num_images)).images
 
 
 
305
  return images, seed
306
 
307
  def genie_wrapper(prompt, negative_prompt, steps, seed, num_images):
308
+ yield gr.update(visible=False), gr.update(interactive=False), gr.update(visible=False)
309
  start_time = time.time()
310
+ images, used_seed = generate_images(prompt, negative_prompt, steps, seed, num_images)
 
 
 
 
 
311
  end_time = time.time()
312
  generation_time = end_time - start_time
313
  info_text = f"Seed yang digunakan: {used_seed}\nTotal waktu generasi: {generation_time:.2f} detik"
314
+ yield gr.update(value=images, visible=True), gr.update(interactive=True), gr.update(value=info_text, visible=True)
315
 
316
  def submit_report(name, email, message):
317
+ if not name or not message:
318
+ gr.Warning("Nama dan Pesan tidak boleh kosong!")
319
+ return gr.update(visible=False)
320
  report_time = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
321
  report_content = f"--- Laporan Baru ({report_time}) ---\nNama: {name}\nEmail: {email}\nPesan: {message}\n\n"
322
+ with open("reports.log", "a", encoding="utf-8") as f: f.write(report_content)
 
323
  print("✅ Laporan baru telah disimpan ke reports.log")
324
  return gr.update(value="✅ Terima kasih! Laporan Anda telah kami terima.", visible=True)
325
 
326
+ def update_visitor_monitor(time_filter: str):
327
+ try:
328
+ with file_lock:
329
+ if not os.path.exists(VISITOR_LOG_FILE) or os.path.getsize(VISITOR_LOG_FILE) < 10: return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
330
+ df = pd.read_csv(VISITOR_LOG_FILE)
331
+ if df.empty: return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
332
+ df['Timestamp'] = pd.to_datetime(df['Timestamp'], errors='coerce'); df.dropna(subset=['Timestamp'], inplace=True)
333
+ if df.empty: return "## <i class='fa-solid fa-users'></i> Total Pengunjung: 0", pd.DataFrame()
334
+ total_overall_visitors = len(df)
335
+ total_visitors_formatted = f"## <i class='fa-solid fa-users'></i> Total Pengunjung: {total_overall_visitors:,}"
336
+ df['Total Pengunjung'] = np.arange(1, len(df) + 1)
337
+ now = pd.to_datetime('now', utc=True).tz_localize(None); df['Timestamp'] = df['Timestamp'].dt.tz_localize(None)
338
+ if time_filter == "1 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=1)]
339
+ elif time_filter == "2 Minggu Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(weeks=2)]
340
+ elif time_filter == "3 Bulan Terakhir": df_plot = df[df['Timestamp'] >= now - timedelta(days=90)]
341
+ else: df_plot = df
342
+ if df_plot.empty: return total_visitors_formatted, pd.DataFrame({"Timestamp": [], "Total Pengunjung": []})
343
+ return total_visitors_formatted, df_plot
344
+ except Exception as e:
345
+ error_message = f"Error saat memperbarui monitor: {e}"; print(f"❌ {error_message}")
346
+ return f"## ⚠️ Error", pd.DataFrame()
347
 
348
+ def enhance_prompt(simple_prompt):
349
+ if not simple_prompt: gr.Warning("Tolong masukkan ide Anda terlebih dahulu."); return ""
350
+ if not gemini_bot.is_configured: gr.Error("Fitur Prompt Enhancer tidak aktif karena API Key belum diatur."); return "Fitur tidak aktif."
351
+ system_instruction = ("Anda adalah ahli prompt engineering untuk AI text-to-image. Ubah ide sederhana dari pengguna menjadi prompt yang kaya, deskriptif, dan artistik. "
352
+ "Fokus pada detail visual: subjek, setting, pencahayaan, gaya seni, komposisi, dan kualitas (hyperdetailed, 4K, masterpiece). "
353
+ "Hasilkan HANYA prompt-nya saja tanpa penjelasan.")
354
+ yield gr.update(value="🧠 AI sedang meracik prompt ajaib...", interactive=False)
355
+ enhanced_prompt = gemini_bot.chat(simple_prompt, [], system_prompt=system_instruction)
356
+ yield gr.update(value=enhanced_prompt, interactive=True)
357
+
358
+ def upscale_image(image_to_upscale, clarity_strength):
359
+ if image_to_upscale is None: raise gr.Error("Silakan unggah gambar terlebih dahulu.")
360
+ if upscaler_model is None or upscaler_processor is None: raise gr.Error("Fitur Upscaler tidak aktif karena model gagal dimuat.")
361
+ yield None, "🚀 Memproses peningkatan resolusi 4x oleh AI..."
362
+ try:
363
+ with torch.no_grad():
364
+ inputs = upscaler_processor(image_to_upscale, return_tensors="pt").to(device)
365
+ outputs = upscaler_model(**inputs)
366
+ output_image = outputs.reconstruction.data.squeeze().float().cpu().clamp_(0, 1).numpy()
367
+ output_image = (np.moveaxis(output_image, 0, -1) * 255.0).round().astype(np.uint8)
368
+ final_image = Image.fromarray(output_image)
369
+ if clarity_strength > 1.0:
370
+ yield final_image, f"✨ Menerapkan peningkatan kejernihan (Strength: {clarity_strength:.2f})..."
371
+ enhancer = ImageEnhance.Sharpness(final_image); final_image = enhancer.enhance(clarity_strength)
372
+ yield final_image, f"✅ Gambar berhasil ditingkatkan! Resolusi akhir: {final_image.width}x{final_image.height}px."
373
+ except Exception as e: print(f"❌ Error saat upscaling: {e}"); yield None, f"⚠️ Terjadi error: {e}"
374
+
375
+ def update_system_info():
376
+ if not psutil or not platform: return "Informasi sistem tidak tersedia (library `psutil` tidak ditemukan)."
377
+ cpu_percent = psutil.cpu_percent(interval=None)
378
+ ram = psutil.virtual_memory()
379
+ gpu_info = "`Tidak terdeteksi (PyTorch tidak menemukan CUDA)`"
380
+ if torch.cuda.is_available():
381
+ gpu_name = torch.cuda.get_device_name(0); gpu_mem_used_gb = torch.cuda.memory_allocated(0)/(1024**3); gpu_mem_total_gb = torch.cuda.get_device_properties(0).total_memory/(1024**3)
382
+ gpu_info = f"**GPU:** `{gpu_name}`\n\n<i class='fa-solid fa-memory'></i> **VRAM:** `{gpu_mem_used_gb:.2f} GB / {gpu_mem_total_gb:.2f} GB`"
383
+ return (f"<i class='fa-solid fa-server'></i> **CPU:** `{cpu_percent:.1f}%`\n\n"
384
+ f"<i class='fa-solid fa-sd-card'></i> **RAM:** `{ram.percent:.1f}% ({ram.used/(1024**3):.2f} GB / {ram.total/(1024**3):.2f} GB)`\n\n"
385
+ f"<i class='fa-solid fa-microchip'></i> {gpu_info}")
386
+
387
+ # --- ANTARMUKA PENGGUNA (GRADIO UI) ---
388
+ with gr.Blocks(theme=gr.themes.Base(primary_hue="sky"), head=HEAD_HTML) as demo:
389
+ with gr.Row(elem_id="header-container"):
390
+ gr.Markdown("<i id='main-logo' class='fa-solid fa-meteor'></i>", elem_id="main-logo-container")
391
+ gr.Markdown("# RenXploit's Creative AI Suite", elem_id="main-title")
392
+ gr.Markdown("Platform All-in-One untuk Imajinasi Tanpa Batas, Didukung oleh Kecerdasan Buatan.", elem_id="main-subtitle")
393
+
394
+ with gr.Tabs() as tabs:
395
+ with gr.TabItem("<i class='fa-solid fa-wand-magic-sparkles'></i> Image Generator", id=0):
396
+ with gr.Row(variant='panel', equal_height=False):
397
  with gr.Column(scale=1):
398
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-pen-ruler'></i>Kontrol Kreasi</h3>")
399
+ prompt_input = gr.Textbox(label="Prompt Utama", placeholder="Contoh: Seekor kucing astronot, potret sinematik...", lines=3, info="Jelaskan visimu sedetail mungkin!")
400
+ negative_prompt_input = gr.Textbox(label="Prompt Negatif", placeholder="Contoh: kualitas buruk, teks, buram...", lines=2, info="Hal yang ingin dihindari.")
401
  num_images_slider = gr.Slider(minimum=1, maximum=8, value=2, step=1, label="Jumlah Gambar")
402
+ generate_btn = gr.Button("✨ Hasilkan Karya!", variant="primary")
403
+ with gr.Accordion("<i class='fa-solid fa-sliders'></i> Opsi Lanjutan", open=False):
404
+ steps_slider = gr.Slider(minimum=1, maximum=5, value=2, step=1, label="Langkah Generasi (Cepat vs Detail)")
405
  with gr.Row():
406
  seed_input = gr.Number(label="Seed", value=-1, precision=0, info="Gunakan -1 untuk acak.")
407
  random_seed_btn = gr.Button("🎲 Acak")
408
  with gr.Column(scale=2):
409
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-image'></i>Galeri Hasil</h3>")
410
  output_gallery = gr.Gallery(label="Hasil Gambar", show_label=False, elem_id="gallery", columns=2, object_fit="contain", height="auto")
411
+ info_box = gr.Textbox(label="Informasi Generasi", visible=False, interactive=False, lines=2)
412
+
413
+ with gr.TabItem("<i class='fa-solid fa-comments'></i> Chat with AI", id=1):
414
+ with gr.Row(variant='panel'):
415
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-robot'></i>Asisten AI Flood</h3>")
416
+ if not gemini_bot.is_configured: gr.Warning("Fitur Chatbot dinonaktifkan. API Key Gemini tidak terkonfigurasi.")
417
+ else: gr.ChatInterface(gemini_bot.chat, chatbot=gr.Chatbot(height=500, label="Flood AI", value=[(None, "Halo! Saya Flood, asisten AI dari RenXploit. Ada yang bisa saya bantu?")]), title=None, description="Tanyakan apa saja, dari coding hingga puisi!")
418
+
419
+ with gr.TabItem("<i class='fa-solid fa-lightbulb'></i> Prompt Enhancer", id=2):
420
+ with gr.Row(variant='panel'):
421
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-brain'></i>Ubah Ide Jadi Prompt Ajaib</h3>")
422
+ gr.Markdown("Cukup tulis ide sederhana, dan biarkan AI menyempurnakannya menjadi prompt yang detail dan artistik untuk hasil gambar terbaik.")
423
+ simple_prompt_input = gr.Textbox(label="Ide Sederhana Anda", placeholder="Contoh: naga di atas gunung berapi", lines=3)
424
+ enhance_btn = gr.Button("Buat Prompt Ajaib!", variant="primary")
425
+ enhanced_prompt_output = gr.Textbox(label="Prompt yang Disempurnakan", lines=5, interactive=True, show_copy_button=True)
426
+ send_to_gen_btn = gr.Button("➡️ Kirim & Pindah ke Generator")
427
+
428
+ with gr.TabItem("<i class='fa-solid fa-expand'></i> AI Image Upscaler", id=3):
429
+ with gr.Row(variant='panel', equal_height=False):
430
+ with gr.Column():
431
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-upload'></i>Unggah & Tingkatkan</h3>")
432
+ gr.Markdown("Tingkatkan resolusi dan detail gambar hingga 4x lipat menggunakan AI canggih.")
433
+ image_to_upscale_input = gr.Image(type="pil", label="Unggah Gambar Anda")
434
+ clarity_slider = gr.Slider(minimum=1.0, maximum=3.0, value=1.0, step=0.1, label="Tingkat Peningkatan Kejernihan", info="Setelah di-upscale 4x, atur kejernihan. 1.0 = Tanpa efek.")
435
+ upscale_btn = gr.Button("Tingkatkan Resolusi!", variant="primary")
436
+ with gr.Column():
437
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-circle-check'></i>Hasil Peningkatan</h3>")
438
+ upscaled_image_output = gr.Image(label="Gambar Hasil Upscale", interactive=False)
439
+ upscale_status_text = gr.Markdown("Status: Menunggu gambar...")
440
+
441
+ with gr.TabItem("<i class='fa-solid fa-chart-line'></i> Visitor Monitor", id=4):
442
+ with gr.Row(variant='panel'):
443
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-satellite-dish'></i>Live Visitor Monitor</h3>")
444
+ with gr.Row():
445
+ with gr.Column(scale=3): visitor_count_display = gr.Markdown("## 📈 Memuat data...")
446
+ with gr.Column(scale=2):
447
+ time_filter_radio = gr.Radio(["Semua Waktu", "1 Minggu Terakhir", "2 Minggu Terakhir", "3 Bulan Terakhir"], label="Filter Waktu", value="Semua Waktu")
448
+ refresh_btn = gr.Button("🔄 Segarkan")
449
+ visitor_plot = gr.LinePlot(x="Timestamp", y="Total Pengunjung", title="Grafik Pertumbuhan Pengunjung", tooltip=['Timestamp', 'Total Pengunjung'], height=450)
450
+
451
+ with gr.TabItem("<i class='fa-solid fa-cogs'></i> System & Settings", id=5):
452
+ with gr.Row(variant='panel'):
453
  with gr.Column(scale=2):
454
+ with gr.Accordion("<i class='fa-solid fa-gauge-high'></i> Live System Monitor", open=True):
455
+ system_info_md = gr.Markdown("Memuat info sistem...")
456
+ system_info_trigger_btn = gr.Button("Trigger System Info", visible=False, elem_id="system-info-trigger-btn")
457
+ with gr.Column(scale=3):
458
+ with gr.Accordion("<i class='fa-solid fa-sliders'></i> Pengaturan Aplikasi", open=True):
459
+ gr.Radio(["FP16 (Cepat, Kualitas Baik)", "FP32 (Lambat, Kualitas Terbaik)"],
460
+ value="FP16 (Cepat, Kualitas Baik)" if device == "cuda" else "FP32 (Lambat, Kualitas Terbaik)",
461
+ label="Presisi Model Generator", interactive=False,
462
+ info="Terkunci. Ditentukan saat startup berdasarkan ketersediaan GPU Anda.")
463
 
464
+ with gr.TabItem("<i class='fa-solid fa-book-open'></i> Panduan", id=6):
465
+ with gr.Row(variant='panel'): gr.Markdown("""<h3 class='section-header'><i class='fa-solid fa-lightbulb'></i>Cara Menjadi "Art Director" yang Hebat untuk AI</h3><p>Konten panduan akan ditambahkan di sini...</p>""")
466
+
467
+ with gr.TabItem("<i class='fa-solid fa-newspaper'></i> Blog & Updates", id=7):
468
+ with gr.Row(variant='panel'): gr.Markdown("""<h3 class='section-header'><i class='fa-solid fa-rocket'></i>Perkembangan Terbaru</h3>- **v3.0 (Pembaruan Desain):** Perombakan total UI/UX dengan tema Glassmorphism, ikon, dan animasi profesional.\n- **v2.4:** Upscaler di-upgrade dengan kontrol kejernihan, Visitor Monitor diperbaiki.\n- **v2.1:** Menambahkan **Prompt Enhancer**, **AI Image Upscaler**, dan **Live System Monitor**.""")
469
+
470
+ with gr.TabItem("<i class='fa-solid fa-info-circle'></i> About & Support", id=8):
471
+ with gr.Row(variant='panel'):
472
+ gr.Markdown("<h3 class='section-header'><i class='fa-solid fa-question-circle'></i>Tentang & Bantuan</h3>")
473
+ with gr.Accordion("Tentang RenXploit's Creative AI Suite", open=True):
474
+ gr.Markdown("**RenXploit's Creative AI Suite** adalah proyek pribadi yang dibuat untuk mengeksplorasi kemampuan AI generatif dalam sebuah antarmuka yang mudah digunakan.\n **Bagian ini belum saya kerjakan dengan selesai** jadi jika anda ingin komplain atau ada hal apapun itu bisa report ke saya lewat website portofolio saya dengan url: https://ngoprek.xyz/contact")
475
+ with gr.Accordion("Laporkan Masalah atau Beri Masukan"):
476
+ report_name = gr.Textbox(label="Nama Anda")
477
+ report_email = gr.Textbox(label="Email Anda (Opsional)")
478
+ report_message = gr.Textbox(label="Pesan Anda", lines=5, placeholder="Jelaskan masalah atau ide Anda...")
479
+ report_btn = gr.Button("Kirim Laporan", variant="primary")
480
+ report_status = gr.Markdown(visible=False)
481
+
482
+ gr.Markdown("<div class='footer'><p>Dibuat dengan <i class='fa-solid fa-heart' style='color: #ff4d4d;'></i> oleh <b>RenXploit</b>.</p></div>")
483
+
484
+ # --- PENANGANAN EVENT (EVENT HANDLERS) ---
485
  random_seed_btn.click(lambda: -1, outputs=seed_input)
486
+ generate_btn.click(fn=genie_wrapper, inputs=[prompt_input, negative_prompt_input, steps_slider, seed_input, num_images_slider], outputs=[output_gallery, generate_btn, info_box])
487
  report_btn.click(fn=submit_report, inputs=[report_name, report_email, report_message], outputs=[report_status])
488
+
489
+ # Event Visitor Monitor
490
  demo.load(log_visitor, inputs=None, outputs=None)
491
  demo.load(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
492
  refresh_btn.click(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
493
  time_filter_radio.change(fn=update_visitor_monitor, inputs=[time_filter_radio], outputs=[visitor_count_display, visitor_plot])
494
 
495
+ # Event Fitur Baru
496
+ enhance_btn.click(fn=enhance_prompt, inputs=[simple_prompt_input], outputs=[enhanced_prompt_output])
497
+ send_to_gen_btn.click(fn=lambda prompt: (prompt, gr.Tabs(selected=0)), inputs=[enhanced_prompt_output], outputs=[prompt_input, tabs])
498
+ upscale_btn.click(fn=upscale_image, inputs=[image_to_upscale_input, clarity_slider], outputs=[upscaled_image_output, upscale_status_text])
499
+
500
+ # Event System Monitor
501
+ system_info_trigger_btn.click(fn=update_system_info, inputs=None, outputs=system_info_md)
502
+ demo.load(fn=update_system_info, inputs=None, outputs=system_info_md)
503
+
504
+ # --- Menjalankan Aplikasi ---
505
  if __name__ == "__main__":
506
+ demo.launch(debug=True)