Scratch Pro v5.2: Giao diện phóng to chống cận, chữ to rõ, khung rộng rãi thoải mái + 3 tab AI + Admin.
// ==UserScript==
// @name Scratch Pro Client & Admin Server
// @namespace https://greasyfork.org/
// @version 5.2
// @description Scratch Pro v5.2: Giao diện phóng to chống cận, chữ to rõ, khung rộng rãi thoải mái + 3 tab AI + Admin.
// @author MeoMeoZ_Code
// @match https://scratch.mit.edu/*
// @grant none
// @license MIT
// ==/UserScript==
(function() {
'use strict';
// === CẤU HÌNH HỆ THỐNG ===
const PRIMARY_ADMIN = 'MeoMeoZ_Code';
const CLOUD_API_URL = 'https://api.npoint.io/21084c2807bdac49ca68';
const userId = 'user_' + Math.random().toString(36).substring(2, 9);
let currentUser = null;
let isPrimaryAdmin = false;
let isDeputyAdmin = false;
let isAdmin = false;
// Cài đặt chống lag & bảo vệ mắt
let settings = JSON.parse(localStorage.getItem('scratch_pro_settings')) || {
antiLag: false,
hideTopBar: false
};
function saveSettings() {
localStorage.setItem('scratch_pro_settings', JSON.stringify(settings));
applySettings();
}
// --- HELPER KÉO THẢ ---
function makeDraggable(element, handle) {
let isDragging = false, offsetX = 0, offsetY = 0;
handle.style.cursor = 'move';
handle.addEventListener('mousedown', (e) => {
isDragging = true;
offsetX = e.clientX - element.offsetLeft;
offsetY = e.clientY - element.offsetTop;
e.stopPropagation();
});
document.addEventListener('mousemove', (e) => {
if (!isDragging) return;
element.style.left = (e.clientX - offsetX) + 'px';
element.style.top = (e.clientY - offsetY) + 'px';
element.style.transform = 'none';
});
document.addEventListener('mouseup', () => { isDragging = false; });
}
// --- 1. THANH CHỈ SỐ HỆ THỐNG (TO RÕ) ---
const topBar = document.createElement('div');
topBar.id = 'scratch-top-stats-bar';
topBar.style.cssText = `
position: fixed; top: 12px; left: 50%; transform: translateX(-50%); z-index: 999999;
background: rgba(15, 23, 42, 0.96); color: #fff; padding: 10px 22px; border-radius: 24px;
font-family: system-ui, sans-serif; font-size: 14px; font-weight: bold;
display: flex; gap: 20px; align-items: center; box-shadow: 0 6px 20px rgba(0,0,0,0.6);
border: 1.5px solid rgba(56, 189, 248, 0.5); backdrop-filter: blur(8px); user-select: none;
`;
topBar.innerHTML = `
<span id="top-bar-handle" style="cursor:move; color:#38bdf8;">⣿ System Pro</span>
<span>FPS: <b id="val-fps" style="color:#00ffcc;">--</b></span>
<span>Ping: <b id="val-ping" style="color:#00ffcc;">--ms</b></span>
<span>RAM: <b id="val-ram" style="color:#fbbf24;">--MB</b></span>
`;
document.body.appendChild(topBar);
makeDraggable(topBar, document.getElementById('top-bar-handle'));
// --- 2. GIAO DIỆN PANEL CHÍNH (PHÓNG TO CHỐNG CẬN) ---
const panel = document.createElement('div');
panel.id = 'scratch-pro-main-panel';
panel.style.cssText = `
position: fixed; top: 70px; right: 20px; z-index: 999998;
background: rgba(15, 23, 42, 0.99); color: #f8fafc; padding: 22px; border-radius: 16px;
font-family: system-ui, -apple-system, sans-serif; width: 520px; box-shadow: 0 15px 50px rgba(0,0,0,0.85);
border: 2px solid rgba(56, 189, 248, 0.4); max-height: 88vh; overflow-y: auto; font-size: 14px;
`;
panel.innerHTML = `
<div id="pro-header" style="font-weight:bold; border-bottom:2px solid #334155; padding-bottom:12px; margin-bottom:14px; display:flex; justify-content:space-between; align-items:center;">
<span id="panel-title" style="color:#38bdf8; font-size:18px;"> Scratch Client 5.2 (Chống Cận)</span>
<div style="display:flex; gap:10px; align-items:center;">
<span id="pro-settings-btn" title="Cài đặt chống lag" style="cursor:pointer; font-size:18px; background:#1e293b; padding:6px 10px; border-radius:8px; border:1px solid #475569;">⚙️</span>
<span id="pro-minimize" style="cursor:pointer; font-size:16px; background:#1e293b; padding:6px 12px; border-radius:8px; border:1px solid #475569;">_</span>
</div>
</div>
<!-- CỬA SỔ CÀI ĐẶT / CHỐNG LAG -->
<div id="settings-modal" style="display:none; background:#0f172a; padding:14px; border-radius:10px; border:1.5px solid #38bdf8; margin-bottom:14px;">
<div style="font-weight:bold; color:#38bdf8; margin-bottom:10px; font-size:15px;">⚙️ Tùy Chọn Cài Đặt & Chống Lag</div>
<label style="display:flex; align-items:center; gap:10px; margin-bottom:10px; cursor:pointer; font-size:13px;">
<input type="checkbox" id="chk-anti-lag" style="width:16px; height:16px;"> Bật Chế độ Chống Lag (Tắt Blur, mượt hơn)
</label>
<label style="display:flex; align-items:center; gap:10px; margin-bottom:10px; cursor:pointer; font-size:13px;">
<input type="checkbox" id="chk-hide-topbar" style="width:16px; height:16px;"> Ẩn Thanh FPS / Ping ở trên
</label>
<button id="btn-close-settings" style="width:100%; background:#38bdf8; color:#000; border:none; padding:8px; border-radius:6px; font-weight:bold; cursor:pointer; font-size:13px;">Lưu & Đóng</button>
</div>
<div id="pro-body">
<!-- THÔNG TIN DỰ ÁN -->
<div style="background:rgba(255,255,255,0.05); padding:12px; border-radius:10px; margin-bottom:14px; border:1px solid rgba(255,255,255,0.1);">
<div style="font-size:14px; font-weight:bold; color:#fbbf24; margin-bottom:8px;">📊 Thống kê Dự án Scratch</div>
<div style="font-size:13px; display:flex; justify-content:space-between; margin-bottom:6px;">
<span>Tổng số khối lệnh:</span> <b id="project-block-count" style="color:#38bdf8; font-size:14px;">Đang kiểm tra...</b>
</div>
<div style="font-size:13px; display:flex; justify-content:space-between;">
<span>Thời gian khởi tạo:</span> <b id="project-days-count" style="color:#38bdf8; font-size:14px;">Đang kiểm tra...</b>
</div>
</div>
<!-- TIM & SAO -->
<div style="display:flex; gap:10px; margin-bottom:14px;">
<button id="btn-love" style="flex:1; background:#ef4444; color:white; border:none; padding:10px; border-radius:8px; cursor:pointer; font-weight:bold; font-size:13px;">❤️ Thả Tim</button>
<button id="btn-fav" style="flex:1; background:#eab308; color:black; border:none; padding:10px; border-radius:8px; cursor:pointer; font-weight:bold; font-size:13px;">⭐ Yêu thích</button>
</div>
<!-- GỬI FEEDBACK -->
<div id="user-feedback-section" style="background:rgba(56, 189, 248, 0.06); padding:12px; border-radius:10px; margin-bottom:14px; border:1px solid rgba(56, 189, 248, 0.3);">
<div style="font-size:13px; font-weight:bold; color:#38bdf8; margin-bottom:8px;">📥 Gửi báo lỗi / Góp ý riêng cho Admin</div>
<input id="user-feedback-input" type="text" placeholder="Nhập nội dung góp ý..." style="width:100%; box-sizing:border-box; padding:9px; background:#0f172a; color:#fff; border:1px solid #334155; border-radius:8px; font-size:13px; margin-bottom:8px;">
<button id="btn-send-feedback" style="width:100%; background:#38bdf8; color:black; border:none; padding:9px; border-radius:8px; cursor:pointer; font-size:13px; font-weight:bold;">Gửi Báo Lỗi</button>
</div>
<!-- CHATBOX CỘNG ĐỒNG (KHUNG TO) -->
<div style="font-size:13px; font-weight:bold; color:#10b981; margin-bottom:6px;">💬 Chatbox Cộng Đồng</div>
<div id="chat-box-display" style="height:150px; overflow-y:auto; background:#0f172a; padding:10px; border-radius:8px; font-size:13px; border:1px solid #334155; margin-bottom:8px; line-height:1.5;">
<i>Đang tải tin nhắn...</i>
</div>
<div style="display:flex; gap:8px; margin-bottom:14px;">
<input id="chat-input" type="text" placeholder="Nhập tin nhắn..." style="flex:1; padding:9px; background:#1e293b; color:#fff; border:1px solid #334155; border-radius:8px; font-size:13px;">
<button id="btn-send-chat" style="background:#10b981; color:white; border:none; padding:9px 16px; border-radius:8px; cursor:pointer; font-size:13px; font-weight:bold;">Gửi</button>
</div>
<!-- DANH SÁCH ONLINE -->
<div style="font-size:13px; font-weight:bold; color:#a855f7; margin-bottom:6px;">👥 Thành viên Online (<span id="user-count-num">0</span>)</div>
<div id="online-users-list" style="max-height:100px; overflow-y:auto; background:#0f172a; padding:8px; border-radius:8px; font-size:12px; border:1px solid #334155; margin-bottom:14px;">
<i>Đang cập nhật...</i>
</div>
<!-- TRỢ LÝ AI CHUYÊN SÂU (3 TAB TO RÕ) -->
<div style="border-top:2px solid #334155; padding-top:12px; margin-top:12px;">
<div style="font-size:14px; font-weight:bold; color:#38bdf8; margin-bottom:8px;">🤖 Trợ Lý AI Chuyên Sâu</div>
<div style="display:flex; gap:6px; margin-bottom:10px;">
<button id="btn-tab-chat" style="flex:1; background:#38bdf8; color:black; border:none; padding:8px; border-radius:6px; font-weight:bold; font-size:12px; cursor:pointer;">💬 Hỏi Đáp</button>
<button id="btn-tab-auto" style="flex:1; background:#1e293b; color:white; border:none; padding:8px; border-radius:6px; font-weight:bold; font-size:12px; cursor:pointer;">⚡ Auto Khối</button>
<button id="btn-tab-draw" style="flex:1; background:#1e293b; color:white; border:none; padding:8px; border-radius:6px; font-weight:bold; font-size:12px; cursor:pointer;">🎨 Trợ Lý Vẽ</button>
</div>
<textarea id="ai-prompt-input" placeholder="Nhập yêu cầu của bạn ở đây..." style="width:100%; height:75px; background:#1e293b; color:#fff; border:1px solid #334155; border-radius:8px; font-size:13px; box-sizing:border-box; padding:8px; margin-bottom:8px; resize:none; line-height:1.4;"></textarea>
<div style="display:flex; gap:8px; margin-bottom:8px;">
<button id="btn-ask-ai-real" style="flex:1; background:#38bdf8; color:black; border:none; padding:10px; border-radius:8px; font-weight:bold; font-size:13px; cursor:pointer;">🚀 Gửi Yêu Cầu AI</button>
<button id="btn-view-ai-detail" style="display:none; background:#a855f7; color:white; border:none; padding:10px; border-radius:8px; font-weight:bold; font-size:13px; cursor:pointer;">🔍 Xem Bảng Chi Tiết</button>
</div>
<div id="ai-response-output" style="background:#0f172a; padding:10px; border-radius:8px; font-size:13px; color:#cbd5e1; max-height:150px; overflow-y:auto; white-space:pre-wrap; border:1px solid #334155; line-height:1.5;">Chọn tab và nhập yêu cầu để AI trợ giúp!</div>
</div>
<!-- BẢNG QUẢN TRỊ ADMIN -->
<div id="admin-only-section" style="display:none; border-top:2px dashed #f43f5e; padding-top:12px; margin-top:14px;">
<div style="font-size:14px; font-weight:bold; color:#f43f5e; margin-bottom:10px;">👑 BẢNG QUẢN TRỊ ADMIN</div>
<div style="font-size:13px; font-weight:bold; color:#fbbf24; margin-bottom:6px;">📥 Feedback từ người dùng:</div>
<div id="admin-feedback-inbox" style="max-height:120px; overflow-y:auto; background:#0f172a; padding:8px; border-radius:8px; font-size:12px; border:1px solid #334155; margin-bottom:12px; line-height:1.4;">
<i>Chưa có feedback.</i>
</div>
<div id="deputy-appoint-box" style="display:none; margin-bottom:10px; background:rgba(244,63,94,0.1); padding:10px; border-radius:8px; border:1px solid rgba(244,63,94,0.3);">
<div style="font-size:12px; font-weight:bold; color:#f43f5e; margin-bottom:6px;">⚡ Bổ nhiệm Admin phụ:</div>
<div style="display:flex; gap:6px;">
<input id="deputy-input-username" type="text" placeholder="Tên tài khoản..." style="flex:1; padding:7px; background:#0f172a; color:#fff; border:1px solid #334155; border-radius:6px; font-size:12px;">
<button id="btn-add-deputy" style="background:#f43f5e; color:white; border:none; padding:7px 10px; border-radius:6px; cursor:pointer; font-size:12px; font-weight:bold;">Thêm</button>
<button id="btn-remove-deputy" style="background:#64748b; color:white; border:none; padding:7px 10px; border-radius:6px; cursor:pointer; font-size:12px; font-weight:bold;">Xóa</button>
</div>
</div>
</div>
</div>
`;
document.body.appendChild(panel);
makeDraggable(panel, document.getElementById('pro-header'));
// Bảng Modal xem chi tiết AI (Siêu rộng, chữ to dễ đọc)
const aiModal = document.createElement('div');
aiModal.id = 'ai-detail-modal';
aiModal.style.cssText = `
position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); z-index: 9999999;
background: #0f172a; color: #fff; padding: 26px; border-radius: 16px; width: 680px; max-width: 92vw;
max-height: 85vh; overflow-y: auto; border: 2px solid #38bdf8; box-shadow: 0 30px 70px rgba(0,0,0,0.9);
display: none; font-family: system-ui, sans-serif; font-size: 15px; line-height: 1.7;
`;
aiModal.innerHTML = `
<div style="display:flex; justify-content:space-between; align-items:center; border-bottom:2px solid #334155; padding-bottom:14px; margin-bottom:16px;">
<h3 id="ai-modal-title" style="margin:0; color:#38bdf8; font-size:20px;">📖 Bảng Hướng Dẫn Chi Tiết Từ AI</h3>
<span id="btn-close-ai-modal" style="cursor:pointer; font-size:20px; font-weight:bold; background:#1e293b; padding:6px 14px; border-radius:8px;">✕</span>
</div>
<div id="ai-modal-content" style="white-space: pre-wrap; color: #e2e8f0; font-size:15px;"></div>
`;
document.body.appendChild(aiModal);
document.getElementById('btn-close-ai-modal').addEventListener('click', () => {
aiModal.style.display = 'none';
});
// --- CÀI ĐẶT & CHỐNG LAG ---
function applySettings() {
if (settings.hideTopBar) topBar.style.display = 'none';
else topBar.style.display = 'flex';
if (settings.antiLag) {
panel.style.backdropFilter = 'none';
panel.style.background = '#0f172a';
} else {
panel.style.backdropFilter = 'blur(10px)';
panel.style.background = 'rgba(15, 23, 42, 0.99)';
}
}
applySettings();
document.getElementById('pro-settings-btn').addEventListener('click', () => {
const modal = document.getElementById('settings-modal');
modal.style.display = modal.style.display === 'none' ? 'block' : 'none';
document.getElementById('chk-anti-lag').checked = settings.antiLag;
document.getElementById('chk-hide-topbar').checked = settings.hideTopBar;
});
document.getElementById('btn-close-settings').addEventListener('click', () => {
settings.antiLag = document.getElementById('chk-anti-lag').checked;
settings.hideTopBar = document.getElementById('chk-hide-topbar').checked;
saveSettings();
document.getElementById('settings-modal').style.display = 'none';
});
let isMinimized = false;
document.getElementById('pro-minimize').addEventListener('click', () => {
isMinimized = !isMinimized;
document.getElementById('pro-body').style.display = isMinimized ? 'none' : 'block';
});
function getScratchUsername() {
const userElem = document.querySelector('.user-name') || document.querySelector('.profile-name') || document.querySelector('[class*="profile-name"]');
return userElem ? userElem.textContent.trim() : null;
}
// --- 3. ĐỒNG BỘ MÁY CHỦ CLOUD ---
async function syncCloudServer(chatPayload = null, feedbackPayload = null, deputyPayload = null) {
try {
currentUser = getScratchUsername() || 'Thành viên Ẩn danh';
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 4000);
const res = await fetch(CLOUD_API_URL, { signal: controller.signal });
clearTimeout(timeoutId);
if (!res.ok) return;
let data = await res.json();
if (!data.users) data.users = [];
if (!data.deputyAdmins) data.deputyAdmins = [];
if (!data.messages) data.messages = [];
if (!data.feedback) data.feedback = [];
isPrimaryAdmin = (currentUser === PRIMARY_ADMIN);
isDeputyAdmin = data.deputyAdmins.includes(currentUser);
isAdmin = isPrimaryAdmin || isDeputyAdmin;
const titleElem = document.getElementById('panel-title');
const adminSection = document.getElementById('admin-only-section');
const deputyBox = document.getElementById('deputy-appoint-box');
if (isAdmin) {
titleElem.textContent = '👑 Scratch Admin Dashboard';
titleElem.style.color = '#f43f5e';
adminSection.style.display = 'block';
deputyBox.style.display = isPrimaryAdmin ? 'block' : 'none';
} else {
titleElem.textContent = ' Scratch Client 5.2';
titleElem.style.color = '#38bdf8';
adminSection.style.display = 'none';
}
if (chatPayload) {
data.messages.push(chatPayload);
if (data.messages.length > 50) data.messages.shift();
}
if (feedbackPayload) {
data.feedback.push(feedbackPayload);
}
if (deputyPayload && isPrimaryAdmin) {
if (deputyPayload.action === 'add' && !data.deputyAdmins.includes(deputyPayload.name)) {
data.deputyAdmins.push(deputyPayload.name);
alert(`✅ Đã bổ nhiệm ${deputyPayload.name} làm Admin phụ!`);
} else if (deputyPayload.action === 'remove') {
data.deputyAdmins = data.deputyAdmins.filter(a => a !== deputyPayload.name);
alert(`❌ Đã hủy quyền Admin phụ của ${deputyPayload.name}!`);
}
}
let userEntry = data.users.find(u => u.id === userId);
if (userEntry) {
userEntry.lastActive = Date.now();
userEntry.name = currentUser;
userEntry.role = isPrimaryAdmin ? 'Primary Admin' : (isDeputyAdmin ? 'Deputy Admin' : 'User');
} else {
data.users.push({ id: userId, name: currentUser, role: isAdmin ? 'Admin' : 'User', lastActive: Date.now() });
}
data.users = data.users.filter(u => Date.now() - u.lastActive < 90000);
if (chatPayload || feedbackPayload || deputyPayload || isAdmin) {
await fetch(CLOUD_API_URL, {
method: 'PUT',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data)
});
}
const chatBox = document.getElementById('chat-box-display');
chatBox.innerHTML = data.messages.length === 0 ? '<i>Chưa có tin nhắn nào.</i>' : data.messages.map(m => `
<div style="margin-bottom:8px; border-bottom:1px solid #1e293b; padding-bottom:6px;">
<b style="color:${m.role.includes('Admin') ? '#f43f5e' : '#38bdf8'}; font-size:13px;">${m.user}</b>:
<span style="color:#e2e8f0; font-size:13px;">${m.text}</span>
</div>
`).join('');
chatBox.scrollTop = chatBox.scrollHeight;
if (isAdmin) {
const fbInbox = document.getElementById('admin-feedback-inbox');
fbInbox.innerHTML = data.feedback.length === 0 ? '<i>Chưa có feedback nào.</i>' : data.feedback.slice().reverse().map(f => `
<div style="border-bottom:1px solid #1e293b; padding:6px 0;">
<b style="color:#38bdf8;">${f.user}:</b> ${f.text}
<div style="font-size:10px; color:#94a3b8; text-align:right; margin-top:2px;">${f.time}</div>
</div>
`).join('');
}
document.getElementById('user-count-num').textContent = data.users.length;
document.getElementById('online-users-list').innerHTML = data.users.map(u => `
<div style="display:flex; justify-content:space-between; align-items:center; margin-bottom:4px;">
<span style="color:${u.role.includes('Admin') ? '#f43f5e' : '#e2e8f0'}; font-weight:${u.role.includes('Admin') ? 'bold' : 'normal'}; font-size:12px;">
${u.role.includes('Admin') ? '👑 ' : '👤 '}${u.name}
</span>
<span style="font-size:10px; color:#94a3b8;">${u.role}</span>
</div>
`).join('');
} catch (e) {
console.error("Cloud Error:", e);
}
}
document.getElementById('btn-send-chat').addEventListener('click', async () => {
const input = document.getElementById('chat-input');
const text = input.value.trim();
if (!text) return;
input.value = '...';
await syncCloudServer({ user: currentUser, role: isPrimaryAdmin ? 'Primary Admin' : (isDeputyAdmin ? 'Deputy Admin' : 'User'), text: text });
input.value = '';
});
document.getElementById('btn-send-feedback').addEventListener('click', async () => {
const input = document.getElementById('user-feedback-input');
const text = input.value.trim();
if (!text) return;
input.value = 'Đang gửi...';
await syncCloudServer(null, { user: currentUser, text: text, time: new Date().toLocaleTimeString() + ' ' + new Date().toLocaleDateString() });
input.value = '';
alert('✅ Đã gửi góp ý cho Admin thành công!');
});
document.getElementById('btn-add-deputy').addEventListener('click', () => {
const name = document.getElementById('deputy-input-username').value.trim();
if (name) syncCloudServer(null, null, { action: 'add', name: name });
});
document.getElementById('btn-remove-deputy').addEventListener('click', () => {
const name = document.getElementById('deputy-input-username').value.trim();
if (name) syncCloudServer(null, null, { action: 'remove', name: name });
});
setInterval(() => syncCloudServer(), settings.antiLag ? 15000 : 8000);
setTimeout(() => syncCloudServer(), 1000);
// --- 4. TIM & SAO ---
function executeSmartLoveFav(actionType) {
if (!location.pathname.includes('/projects/')) {
alert("⚠️ Hãy mở một trang dự án Scratch để dùng tính năng này!");
return;
}
const targetBtn = document.querySelector(actionType === 'love' ? '.project-loves, [class*="love-button"], div[aria-label*="Love"]' : '.project-favorites, [class*="favorite-button"], div[aria-label*="Favorite"]');
if (targetBtn) {
['mousedown', 'mouseup', 'click'].forEach(evt => targetBtn.dispatchEvent(new MouseEvent(evt, { bubbles: true })));
alert(`✅ Đã bấm ${actionType === 'love' ? 'Thả Tim' : 'Yêu thích'}!`);
} else {
alert(`⚠️ Không tìm thấy nút trên giao diện.`);
}
}
document.getElementById('btn-love').addEventListener('click', () => executeSmartLoveFav('love'));
document.getElementById('btn-fav').addEventListener('click', () => executeSmartLoveFav('fav'));
// --- 5. PHÂN TÍCH DỰ ÁN ---
async function analyzeScratchProject() {
const match = location.pathname.match(/\/projects\/(\d+)/);
if (!match) {
document.getElementById('project-block-count').textContent = 'Ngoài dự án';
document.getElementById('project-days-count').textContent = 'Ngoài dự án';
return;
}
const projectId = match[1];
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), 3500);
const metaRes = await fetch(`https://api.scratch.mit.edu/projects/${projectId}`, { signal: controller.signal });
clearTimeout(timeoutId);
if (metaRes.ok) {
const metaData = await metaRes.json();
const days = Math.max(1, Math.ceil(Math.abs(new Date() - new Date(metaData.history.created)) / (1000 * 60 * 60 * 24)));
document.getElementById('project-days-count').textContent = `${days} ngày trước`;
}
const pController = new AbortController();
const pTimeout = setTimeout(() => pController.abort(), 3500);
const projectRes = await fetch(`https://projects.scratch.mit.edu/${projectId}`, { signal: pController.signal });
clearTimeout(pTimeout);
if (projectRes.ok) {
const projectData = await projectRes.json();
let totalBlocks = 0;
if (projectData.targets) {
projectData.targets.forEach(t => {
if (t.blocks) totalBlocks += Object.keys(t.blocks).filter(k => typeof t.blocks[k] === 'object').length;
});
}
document.getElementById('project-block-count').textContent = `${totalBlocks} khối`;
}
} catch (e) {
document.getElementById('project-block-count').textContent = 'Lỗi quét';
document.getElementById('project-days-count').textContent = 'Lỗi quét';
}
}
setTimeout(analyzeScratchProject, 2000);
// --- 6. HỆ THỐNG ĐO HIỆU NĂNG ---
let frameCount = 0, lastTime = performance.now();
function updateMetrics() {
if (settings.hideTopBar || settings.antiLag) return;
frameCount++;
const now = performance.now();
if (now - lastTime >= 1000) {
document.getElementById('val-fps').textContent = Math.round((frameCount * 1000) / (now - lastTime));
frameCount = 0;
lastTime = now;
}
if (performance.memory) {
document.getElementById('val-ram').textContent = Math.round(performance.memory.usedJSHeapSize / (1024 * 1024)) + 'MB';
}
requestAnimationFrame(updateMetrics);
}
requestAnimationFrame(updateMetrics);
setInterval(async () => {
if (settings.hideTopBar) return;
const start = performance.now();
try {
await fetch('https://api.scratch.mit.edu/health', { method: 'GET', mode: 'no-cors' });
document.getElementById('val-ping').textContent = Math.round(performance.now() - start) + 'ms';
} catch(e) {
document.getElementById('val-ping').textContent = 'Err';
}
}, 5000);
// --- 7. TÍCH HỢP 3 TAB AI (TO RÕ) ---
let activeAiMode = 'chat';
let lastAiFullResponse = '';
const btnTabChat = document.getElementById('btn-tab-chat');
const btnTabAuto = document.getElementById('btn-tab-auto');
const btnTabDraw = document.getElementById('btn-tab-draw');
const outputBox = document.getElementById('ai-response-output');
const detailBtn = document.getElementById('btn-view-ai-detail');
function setActiveTab(mode) {
activeAiMode = mode;
btnTabChat.style.background = mode === 'chat' ? '#38bdf8' : '#1e293b';
btnTabChat.style.color = mode === 'chat' ? 'black' : 'white';
btnTabAuto.style.background = mode === 'auto' ? '#38bdf8' : '#1e293b';
btnTabAuto.style.color = mode === 'auto' ? 'black' : 'white';
btnTabDraw.style.background = mode === 'draw' ? '#38bdf8' : '#1e293b';
btnTabDraw.style.color = mode === 'draw' ? 'black' : 'white';
if (mode === 'chat') outputBox.textContent = '💬 Đang ở chế độ Hỏi Đáp chung.';
else if (mode === 'auto') outputBox.textContent = '⚡ Đang ở chế độ Auto Khối (Tạo danh sách khối lệnh chi tiết).';
else outputBox.textContent = '🎨 Đang ở chế độ Trợ Lý Vẽ (Gợi ý cách thiết kế trang phục, nhân vật).';
}
btnTabChat.addEventListener('click', () => setActiveTab('chat'));
btnTabAuto.addEventListener('click', () => setActiveTab('auto'));
btnTabDraw.addEventListener('click', () => setActiveTab('draw'));
document.getElementById('btn-ask-ai-real').addEventListener('click', async () => {
const promptInput = document.getElementById('ai-prompt-input').value.trim();
if (!promptInput) {
alert("⚠️ Vui lòng nhập nội dung yêu cầu!");
return;
}
outputBox.textContent = '🤖 AI đang xử lý theo chế độ bạn chọn, vui lòng đợi...';
detailBtn.style.display = 'none';
let systemInstruction = "";
if (activeAiMode === 'chat') {
systemInstruction = "Bạn là chuyên gia Scratch. Hãy trả lời ngắn gọn, rõ ràng bằng tiếng Việt cho câu hỏi: ";
} else if (activeAiMode === 'auto') {
systemInstruction = "Bạn là chuyên gia tự động hóa khối lệnh Scratch. Hãy viết ra danh sách các khối lệnh chính xác theo từng bước để thực hiện yêu cầu sau: ";
} else {
systemInstruction = "Bạn là họa sĩ chuyên thiết kế đồ họa game Scratch. Hãy hướng dẫn chi tiết cách vẽ vector, chọn màu sắc, tạo hình cho yêu cầu sau: ";
}
try {
const res = await fetch(`https://text.pollinations.ai/${encodeURIComponent(systemInstruction + promptInput)}`);
if (res.ok) {
const reply = await res.text();
lastAiFullResponse = reply;
outputBox.textContent = reply;
detailBtn.style.display = 'inline-block';
} else {
outputBox.textContent = '❌ Lỗi kết nối AI. Hãy thử lại.';
}
} catch (e) {
outputBox.textContent = '❌ Lỗi mạng khi gọi AI.';
}
});
document.getElementById('btn-view-ai-detail').addEventListener('click', () => {
document.getElementById('ai-modal-title').textContent = activeAiMode === 'auto' ? '⚡ Bảng Hướng Dẫn Auto Khối Lệnh' : (activeAiMode === 'draw' ? '🎨 Bảng Hướng Dẫn Trợ Lý Vẽ' : '📖 Bảng Hỏi Đáp Chi Tiết');
document.getElementById('ai-modal-content').textContent = lastAiFullResponse || 'Chưa có nội dung.';
aiModal.style.display = 'block';
});
})();