feat: move artwork feature

-some bug fixes
-extracted artwork reprocessing
This commit is contained in:
MiraclePatrickLumowa authored and GitHub committed 2025-09-21 15:47:09 +08:00
1 parent fa59c90ddf
commit ad71cbd430
42 files changed
+31413

No files matched your search

+642
View File
@@ -0,0 +1,642 @@
// ==UserScript==
// @name WPlace AutoBOT Script Manager
// @namespace http://tampermonkey.net/
// @version 2025-09-08.1
// @description Script manager and launcher for WPlace AutoBOT
// @author TH3C0D3R
// @match https://wplace.live/*
// @grant none
// @icon
// ==/UserScript==l
; (async () => {
console.log('%c🚀 WPlace AutoBOT Script Manager Loading...', 'color: #00ff41; font-weight: bold; font-size: 16px;');
// Available scripts configuration
const AVAILABLE_SCRIPTS = [
{
name: 'Auto-Farm.js',
displayName: '🌾 Auto Farm',
description: 'Automated farming and pixel painting',
icon: '🌾',
category: 'automation'
},
{
name: 'Auto-Image.js',
displayName: '🖼️ Auto Image',
description: 'Automated image processing and placement',
icon: '🖼️',
category: 'automation'
},
{
name: 'Auto-Repair.js',
displayName: '🔧 Auto Repair',
description: 'Automated repair and maintenance tasks',
icon: '🔧',
category: 'utility'
},
{
name: 'Art-Extractor.js',
displayName: '🎨 Art Extractor',
description: 'Extract artwork areas to JSON for auto-repair',
icon: '🎨',
category: 'utility'
}
]; // Neon theme styling
const NEON_STYLES = `
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
.script-manager-container {
position: fixed !important;
top: 50% !important;
left: 50% !important;
transform: translate(-50%, -50%) !important;
background: #1a1a2e;
border: 3px solid #00ff41;
border-radius: 0;
box-shadow:
0 0 30px rgb(0 255 65 / 50%),
inset 0 0 30px rgb(0 255 65 / 10%),
0 0 0 1px #00ff41;
font-family: 'Press Start 2P', monospace, 'Courier New';
z-index: 10001 !important;
min-width: 600px;
max-width: 800px;
max-height: 80vh;
overflow: hidden;
color: #00ff41;
animation: neon-pulse 2s ease-in-out infinite alternate;
}
.script-manager-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #00ff41, transparent);
z-index: 1;
pointer-events: none;
animation: scanline 3s linear infinite;
opacity: 0.7;
}
.script-manager-header {
background: #16213e;
border-bottom: 2px solid #00ff41;
padding: 15px 20px;
position: relative;
display: flex;
justify-content: space-between;
align-items: center;
}
.header-content {
display: flex;
align-items: center;
gap: 15px;
}
.header-icon {
width: 32px;
height: 32px;
border-radius: 6px;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.4);
transition: all 0.3s ease;
animation: pixel-blink 3s infinite;
}
.header-icon:hover {
transform: scale(1.1);
box-shadow: 0 0 25px rgba(0, 255, 65, 0.6);
}
.script-manager-title {
color: #00ff41;
font-size: 14px;
text-shadow: 0 0 15px #00ff41;
margin: 0;
text-transform: uppercase;
letter-spacing: 2px;
animation: text-glow 2s ease-in-out infinite alternate;
}
.script-manager-close {
background: #16213e;
border: 2px solid #ff073a;
border-radius: 0;
color: #ff073a;
width: 30px;
height: 30px;
cursor: pointer;
font-family: 'Press Start 2P', monospace;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.3s ease;
text-shadow: 0 0 10px #ff073a;
}
.script-manager-close:hover {
background: #ff073a;
color: #1a1a2e;
box-shadow: 0 0 20px #ff073a;
animation: pixel-blink 0.5s infinite;
}
.script-manager-content {
padding: 20px;
max-height: 60vh;
overflow-y: auto;
background: linear-gradient(45deg, rgba(0,255,65,0.03) 0%, rgba(22,33,62,0.05) 100%);
}
.script-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 15px;
margin-bottom: 20px;
}
.script-card {
background: #16213e;
border: 2px solid #00ff41;
border-radius: 0;
padding: 15px;
cursor: pointer;
transition: all 0.3s ease;
position: relative;
overflow: hidden;
}
.script-card::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(0,255,65,0.1), transparent);
transform: rotate(45deg);
transition: all 0.5s ease;
opacity: 0;
}
.script-card:hover::before {
animation: neon-sweep 1s ease-in-out;
}
.script-card:hover {
background: rgba(0,255,65,0.1);
box-shadow:
0 0 25px rgb(0 255 65 / 60%),
inset 0 0 25px rgb(0 255 65 / 15%);
transform: translateY(-3px);
animation: card-glow 0.5s ease-in-out infinite alternate;
}
.script-card-header {
display: flex;
align-items: center;
margin-bottom: 10px;
}
.script-icon {
font-size: 24px;
margin-right: 10px;
filter: drop-shadow(0 0 10px #00ff41);
}
.script-title {
color: #00ff41;
font-size: 11px;
text-shadow: 0 0 10px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
margin: 0;
}
.script-description {
color: #00ff41dd;
font-size: 8px;
line-height: 1.4;
text-shadow: 0 0 5px #00ff41;
margin-bottom: 15px;
}
.script-category {
background: rgba(255, 107, 53, 0.2);
border: 1px solid #ff6b35;
color: #ff6b35;
padding: 3px 8px;
font-size: 7px;
text-transform: uppercase;
letter-spacing: 1px;
text-shadow: 0 0 5px #ff6b35;
display: inline-block;
}
.script-manager-footer {
background: #16213e;
border-top: 2px solid #00ff41;
padding: 15px 20px;
display: flex;
justify-content: space-between;
align-items: center;
}
.status-text {
color: #00ff41dd;
font-size: 8px;
text-shadow: 0 0 5px #00ff41;
}
.action-buttons {
display: flex;
gap: 10px;
}
.neon-btn {
background: #16213e;
border: 2px solid #00ff41;
border-radius: 0;
color: #00ff41;
padding: 8px 15px;
font-family: 'Press Start 2P', monospace;
font-size: 8px;
text-transform: uppercase;
cursor: pointer;
transition: all 0.3s ease;
text-shadow: 0 0 8px #00ff41;
letter-spacing: 1px;
}
.neon-btn:hover {
background: rgba(0,255,65,0.1);
box-shadow: 0 0 20px rgb(0 255 65 / 60%);
animation: pixel-blink 0.5s infinite;
}
.neon-btn.secondary {
border-color: #ff6b35;
color: #ff6b35;
text-shadow: 0 0 8px #ff6b35;
}
.neon-btn.secondary:hover {
background: rgba(255, 107, 53, 0.1);
box-shadow: 0 0 20px rgb(255 107 53 / 60%);
}
.script-manager-backdrop {
position: fixed !important;
top: 0 !important;
left: 0 !important;
width: 100% !important;
height: 100% !important;
background: rgba(0, 0, 0, 0.8);
z-index: 10000 !important;
backdrop-filter: blur(5px);
animation: backdrop-fade-in 0.3s ease-out;
}
/* Loading animation */
.loading-container {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
height: 200px;
}
.loading-spinner {
width: 40px;
height: 40px;
border: 3px solid #16213e;
border-top: 3px solid #00ff41;
border-radius: 0;
animation: neon-spin 1s linear infinite;
margin-bottom: 15px;
box-shadow: 0 0 20px rgb(0 255 65 / 50%);
}
.loading-text {
color: #00ff41;
font-size: 8px;
text-shadow: 0 0 10px #00ff41;
text-transform: uppercase;
letter-spacing: 2px;
animation: text-pulse 1.5s ease-in-out infinite;
}
/* Custom scrollbar */
.script-manager-content::-webkit-scrollbar {
width: 12px;
}
.script-manager-content::-webkit-scrollbar-track {
background: #16213e;
border: 1px solid #00ff41;
}
.script-manager-content::-webkit-scrollbar-thumb {
background: #00ff41;
border-radius: 0;
box-shadow: 0 0 10px #00ff41;
}
.script-manager-content::-webkit-scrollbar-thumb:hover {
background: #39ff14;
box-shadow: 0 0 15px #39ff14;
}
/* Animations */
@keyframes neon-pulse {
0% { box-shadow: 0 0 30px rgb(0 255 65 / 50%), inset 0 0 30px rgb(0 255 65 / 10%), 0 0 0 1px #00ff41; }
100% { box-shadow: 0 0 40px rgb(0 255 65 / 70%), inset 0 0 40px rgb(0 255 65 / 15%), 0 0 0 1px #00ff41; }
}
@keyframes text-glow {
0% { text-shadow: 0 0 15px #00ff41; }
100% { text-shadow: 0 0 25px #00ff41, 0 0 35px #00ff41; }
}
@keyframes pixel-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.7; }
}
@keyframes scanline {
0% { transform: translateY(-100%); }
100% { transform: translateY(400px); }
}
@keyframes neon-sweep {
0% { opacity: 0; transform: translateX(-100%) translateY(-100%) rotate(45deg); }
50% { opacity: 1; }
100% { opacity: 0; transform: translateX(100%) translateY(100%) rotate(45deg); }
}
@keyframes card-glow {
0% { box-shadow: 0 0 25px rgb(0 255 65 / 60%), inset 0 0 25px rgb(0 255 65 / 15%); }
100% { box-shadow: 0 0 35px rgb(0 255 65 / 80%), inset 0 0 35px rgb(0 255 65 / 20%); }
}
@keyframes neon-spin {
0% { transform: rotate(0deg); }
100% { transform: rotate(360deg); }
}
@keyframes text-pulse {
0%, 100% { opacity: 1; }
50% { opacity: 0.6; }
}
@keyframes backdrop-fade-in {
0% { opacity: 0; }
100% { opacity: 1; }
}
/* Responsive design */
@media (max-width: 768px) {
.script-manager-container {
min-width: 90vw;
max-width: 95vw;
}
.script-grid {
grid-template-columns: 1fr;
}
.script-manager-title {
font-size: 10px;
}
}
`;
// Execute script function - Fixed to work like extension popup
async function executeScript(scriptName) {
console.group(`%c🚀 Executing ${scriptName}`, 'color: #00ff41; font-weight: bold;');
try {
// Show loading in the UI
showLoading(`Launching ${scriptName}...`);
// The Script Manager runs in MAIN world context and doesn't have direct Chrome API access
// Instead, we need to communicate back to the content script which can use Chrome APIs
console.log('%c🔄 Script Manager context - delegating to content script', 'color: #ff6b35;');
// Create a custom event to communicate with the content script
const executeEvent = new CustomEvent('autobot-execute-script', {
detail: { scriptName: scriptName }
});
// Dispatch the event to the content script
window.dispatchEvent(executeEvent);
// Show success immediately since we're delegating
console.log(`%c✅ ${scriptName} execution delegated to content script`, 'color: #39ff14; font-weight: bold;');
showSuccess(`${scriptName} execution started!`);
// Auto-close after success
setTimeout(() => {
closeScriptManager();
}, 1500);
} catch (error) {
console.error(`%c❌ Failed to execute ${scriptName}:`, 'color: #ff073a; font-weight: bold;', error);
showError(`Failed to launch ${scriptName}: ${error.message}`);
} finally {
console.groupEnd();
}
}
// UI Management functions
function showLoading(message) {
const container = document.getElementById('script-manager-content');
if (!container) return;
container.innerHTML = `
<div class="loading-container">
<div class="loading-spinner"></div>
<div class="loading-text">${message}</div>
</div>
`;
}
function showSuccess(message) {
const statusText = document.querySelector('.status-text');
if (statusText) {
statusText.textContent = `✅ ${message}`;
statusText.style.color = '#39ff14';
statusText.style.textShadow = '0 0 10px #39ff14';
}
}
function showError(message) {
const statusText = document.querySelector('.status-text');
if (statusText) {
statusText.textContent = `❌ ${message}`;
statusText.style.color = '#ff073a';
statusText.style.textShadow = '0 0 10px #ff073a';
}
// Reset the content to show scripts again
setTimeout(() => {
renderScripts();
}, 3000);
}
function renderScripts() {
const container = document.getElementById('script-manager-content');
if (!container) return;
const scriptGrid = AVAILABLE_SCRIPTS.map(script => `
<div class="script-card" onclick="executeScript('${script.name}')">
<div class="script-card-header">
<div class="script-icon">${script.icon}</div>
<h3 class="script-title">${script.displayName}</h3>
</div>
<p class="script-description">${script.description}</p>
<span class="script-category">${script.category}</span>
</div>
`).join('');
container.innerHTML = `
<div class="script-grid">
${scriptGrid}
</div>
`;
}
// Close script manager
function closeScriptManager() {
const container = document.getElementById('script-manager-container');
const backdrop = document.getElementById('script-manager-backdrop');
if (container) {
container.style.animation = 'neon-fade-out 0.3s ease-in forwards';
setTimeout(() => {
container.remove();
}, 300);
}
if (backdrop) {
backdrop.style.animation = 'backdrop-fade-out 0.3s ease-in forwards';
setTimeout(() => {
backdrop.remove();
}, 300);
}
// Remove ESC key listener
document.removeEventListener('keydown', handleEscKey);
console.log('%c👋 Script Manager closed', 'color: #ff6b35;');
}
// ESC key handler
function handleEscKey(event) {
if (event.key === 'Escape') {
closeScriptManager();
}
}
// Main function to show script manager
function showScriptManager() {
// Remove any existing manager
const existing = document.getElementById('script-manager-container');
if (existing) existing.remove();
const existingBackdrop = document.getElementById('script-manager-backdrop');
if (existingBackdrop) existingBackdrop.remove();
console.log('%c🎮 Opening Script Manager with Neon Theme', 'color: #00ff41; font-weight: bold;');
// Get icon URL for display
let iconUrl = '';
try {
if (chrome && chrome.runtime && chrome.runtime.getURL) {
iconUrl = chrome.runtime.getURL('icons/icon32.png');
console.log('📷 Icon URL:', iconUrl);
}
} catch (e) {
console.log('Extension context not available for icon');
}
// Inject styles
if (!document.getElementById('script-manager-styles')) {
const styleElement = document.createElement('style');
styleElement.id = 'script-manager-styles';
styleElement.textContent = NEON_STYLES;
document.head.appendChild(styleElement);
}
// Create backdrop
const backdrop = document.createElement('div');
backdrop.id = 'script-manager-backdrop';
backdrop.className = 'script-manager-backdrop';
backdrop.addEventListener('click', closeScriptManager);
// Create container
const container = document.createElement('div');
container.id = 'script-manager-container';
container.className = 'script-manager-container';
container.innerHTML = `
<div class="script-manager-header">
<div class="header-content">
${iconUrl ? `<img src="${iconUrl}" alt="AutoBOT" class="header-icon" onerror="this.style.display='none'">` : ''}
<h2 class="script-manager-title">⚡ WPlace AutoBOT Script Manager ⚡</h2>
</div>
<button class="script-manager-close" onclick="closeScriptManager()">×</button>
</div>
<div id="script-manager-content" class="script-manager-content">
<!-- Scripts will be rendered here -->
</div>
<div class="script-manager-footer">
<div class="status-text">Ready to launch scripts</div>
<div class="action-buttons">
<button class="neon-btn secondary" onclick="closeScriptManager()">Cancel</button>
<button class="neon-btn" onclick="window.location.reload()">Refresh Page</button>
</div>
</div>
`;
// Add to page
document.body.appendChild(backdrop);
document.body.appendChild(container);
// Debug: Check positioning
console.log('%c🔍 Script Manager Positioning Debug:', 'color: #ff6b35; font-weight: bold;');
console.log(` - Container position: ${getComputedStyle(container).position}`);
console.log(` - Container top: ${getComputedStyle(container).top}`);
console.log(` - Container left: ${getComputedStyle(container).left}`);
console.log(` - Container transform: ${getComputedStyle(container).transform}`);
console.log(` - Container z-index: ${getComputedStyle(container).zIndex}`);
console.log(` - Backdrop z-index: ${getComputedStyle(backdrop).zIndex}`);
// Render scripts
renderScripts();
// Add ESC key listener
document.addEventListener('keydown', handleEscKey);
// Focus container for accessibility
container.focus();
console.log('%c✅ Script Manager opened successfully', 'color: #39ff14; font-weight: bold;');
}
// Make functions globally available
window.executeScript = executeScript;
window.closeScriptManager = closeScriptManager;
window.showScriptManager = showScriptManager;
// Auto-start the script manager
console.log('%c🎯 Auto-launching Script Manager...', 'color: #00ff41; font-weight: bold;');
showScriptManager();
console.log('%c🚀 WPlace AutoBOT Script Manager Ready!', 'color: #39ff14; font-weight: bold; font-size: 16px;');
})();
File diff suppressed because it is too large. Load diff
+921
View File
@@ -0,0 +1,921 @@
// Background service worker for WPlace AutoBOT Extension
// Test resource loading on extension starto
chrome.runtime.onStartup.addListener(async () => {
console.log('🚀 WPlace AutoBOT Extension started');
const resources = await loadExtensionResources();
console.log('📦 Initial resource test:', resources);
});
chrome.runtime.onInstalled.addListener(async () => {
console.log('🔧 WPlace AutoBOT Extension installed');
const resources = await loadExtensionResources();
console.log('📦 Installation resource test:', resources);
});
chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
if (request.action === 'executeScript') {
// Get tabId from sender or requests
const tabId = request.tabId || sender.tab?.id;
if (!tabId) {
sendResponse({ success: false, error: 'Could not determine target tab' });
return;
}
// Use IIFE for async handlings
(async () => {
try {
await executeLocalScript(request.scriptName, tabId);
sendResponse({ success: true });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
})();
return true; // Important: indicates async response
}
});
async function executeLocalScript(scriptName, tabId) {
try {
console.log(`Loading script: ${scriptName}`);
// Check if we need to inject dependencies first for Auto-Image.js or Art-Extractor.js
if (scriptName === 'Auto-Image.js' || scriptName === 'Art-Extractor.js') {
console.log(`🔑 ${scriptName} detected, ensuring dependencies are loaded first...`);
try {
// First inject token-manager.js
const tokenManagerUrl = chrome.runtime.getURL('scripts/token-manager.js');
const tokenManagerResponse = await fetch(tokenManagerUrl);
if (tokenManagerResponse.ok) {
const tokenManagerCode = await tokenManagerResponse.text();
console.log('🔑 Token manager loaded, injecting first...');
// Execute token manager first
await chrome.scripting.executeScript({
target: { tabId: tabId },
world: "MAIN",
func: (code) => {
console.log('%c🔑 Loading WPlace Token Manager...', 'color: #4ade80; font-weight: bold;');
const script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
script.remove();
console.log('%c✅ Token Manager loaded successfully', 'color: #4ade80; font-weight: bold;');
},
args: [tokenManagerCode]
});
console.log('✅ Token manager injected successfully');
} else {
console.warn('⚠️ Could not load token-manager.js, proceeding without it');
}
// Then inject image-processor.js
const imageProcessorUrl = chrome.runtime.getURL('scripts/image-processor.js');
const imageProcessorResponse = await fetch(imageProcessorUrl);
if (imageProcessorResponse.ok) {
const imageProcessorCode = await imageProcessorResponse.text();
console.log('🖼️ Image processor loaded, injecting second...');
// Execute image processor second
await chrome.scripting.executeScript({
target: { tabId: tabId },
world: "MAIN",
func: (code) => {
console.log('%c🖼️ Loading WPlace Image Processor...', 'color: #3b82f6; font-weight: bold;');
const script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
script.remove();
console.log('%c✅ Image Processor loaded successfully', 'color: #3b82f6; font-weight: bold;');
},
args: [imageProcessorCode]
});
console.log('✅ Image processor injected successfully');
} else {
console.warn('⚠️ Could not load image-processor.js, proceeding without it');
}
// Then inject overlay-manager.js
const overlayManagerUrl = chrome.runtime.getURL('scripts/overlay-manager.js');
const overlayManagerResponse = await fetch(overlayManagerUrl);
if (overlayManagerResponse.ok) {
const overlayManagerCode = await overlayManagerResponse.text();
console.log('🎨 Overlay manager loaded, injecting third...');
// Execute overlay manager third
await chrome.scripting.executeScript({
target: { tabId: tabId },
world: "MAIN",
func: (code) => {
console.log('%c🎨 Loading WPlace Overlay Manager...', 'color: #8b5cf6; font-weight: bold;');
const script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
script.remove();
console.log('%c✅ Overlay Manager loaded successfully', 'color: #8b5cf6; font-weight: bold;');
},
args: [overlayManagerCode]
});
console.log('✅ Overlay manager injected successfully');
} else {
console.warn('⚠️ Could not load overlay-manager.js, proceeding without it');
}
// Finally inject utils-manager.js
const utilsManagerUrl = chrome.runtime.getURL('scripts/utils-manager.js');
const utilsManagerResponse = await fetch(utilsManagerUrl);
if (utilsManagerResponse.ok) {
const utilsManagerCode = await utilsManagerResponse.text();
console.log('🛠️ Utils manager loaded, injecting fourth...');
// Execute utils manager fourth
await chrome.scripting.executeScript({
target: { tabId: tabId },
world: "MAIN",
func: (code) => {
console.log('%c🛠️ Loading WPlace Utils Manager...', 'color: #f59e0b; font-weight: bold;');
const script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
script.remove();
console.log('%c✅ Utils Manager loaded successfully', 'color: #f59e0b; font-weight: bold;');
},
args: [utilsManagerCode]
});
console.log('✅ Utils manager injected successfully');
} else {
console.warn('⚠️ Could not load utils-manager.js, proceeding without it');
}
// Small delay to ensure dependencies are fully initialized
await new Promise(resolve => setTimeout(resolve, 200));
} catch (error) {
console.warn('⚠️ Error loading dependencies:', error);
}
}
// Determine script path - Script-manager files are in root, others in scripts/
let scriptUrl;
if (scriptName === 'Script-manager.js' || scriptName === 'Script-manager-fixed.js') {
scriptUrl = chrome.runtime.getURL(scriptName);
} else {
scriptUrl = chrome.runtime.getURL(`scripts/${scriptName}`);
}
const response = await fetch(scriptUrl);
if (!response.ok) {
throw new Error(`Failed to load script: ${response.status} ${response.statusText}`);
}
const scriptCode = await response.text();
console.log(`Script loaded: ${scriptCode.length} characters`);
// Load theme and language resources
const resources = await loadExtensionResources();
// Execute in MAIN world context (bypasses CSP)
await chrome.scripting.executeScript({
target: { tabId: tabId },
world: "MAIN", // Key: executes in page context, not extension context
func: (code, name, themeCSS, languages) => {
console.log(`%c🚀 Executing ${name}...`, 'color: #4ade80; font-weight: bold; font-size: 14px;');
// Create detailed resource report
console.group(`%c📊 WPlace AutoBOT Resource Report for ${name}`, 'color: #3b82f6; font-weight: bold; font-size: 16px;');
// Debug: Log what resources we received
console.log(`%c📦 Raw Resources Received:`, 'color: #8b5cf6; font-weight: bold;');
console.log(` - Themes object:`, themeCSS);
console.log(` - Languages object:`, languages);
console.log(` - Theme count: ${Object.keys(themeCSS || {}).length}`);
console.log(` - Language count: ${Object.keys(languages || {}).length}`);
// Inject CSS themes if available
if (themeCSS && Object.keys(themeCSS).length > 0) {
console.group(`%c🎨 Theme Processing`, 'color: #8b5cf6; font-weight: bold;');
console.log(`%c📁 Loading ${Object.keys(themeCSS).length} theme files from extension local storage...`, 'color: #8b5cf6;');
// Create a global themes object
window.AUTOBOT_THEMES = themeCSS;
// Log detailed theme information
Object.entries(themeCSS).forEach(([filename, content]) => {
console.log(`%c📄 Theme File: ${filename}`, 'color: #8b5cf6; font-weight: bold;');
console.log(` 📏 Size: ${content.length.toLocaleString()} characters`);
console.log(` 📍 Source: Extension local file (chrome-extension://)`);
console.log(` 🔗 Full path: themes/${filename}`);
// Show first few lines as preview
const preview = content.substring(0, 200).split('\\n').slice(0, 3).join('\\n');
console.log(` 👀 Preview: ${preview}${content.length > 200 ? '...' : ''}`);
console.log(` ✅ Status: Loaded successfully`);
});
// Inject auto-image-styles.css if available
if (themeCSS['auto-image-styles.css']) {
const autoImageContent = themeCSS['auto-image-styles.css'];
const styleElement = document.createElement('style');
styleElement.id = 'autobot-auto-image-styles';
styleElement.textContent = autoImageContent;
document.head.appendChild(styleElement);
console.log(`%c✨ AUTO-INJECTED: auto-image-styles.css`, 'color: #10b981; font-weight: bold;');
console.log(` 📏 Injected size: ${autoImageContent.length.toLocaleString()} characters`);
console.log(` 📍 Source: Extension local file`);
console.log(` 🎯 Target: <head> as <style> element`);
console.log(` 🆔 Element ID: autobot-auto-image-styles`);
} else {
console.warn(`%c⚠️ auto-image-styles.css not found in loaded themes`, 'color: #f59e0b; font-weight: bold;');
}
console.groupEnd();
} else {
console.warn(`%c⚠️ No themes received from extension`, 'color: #f59e0b; font-weight: bold;');
console.log(` 📁 Expected source: Extension local files`);
console.log(` 📋 Expected files: auto-image-styles.css, acrylic.css, classic.css, etc.`);
window.AUTOBOT_THEMES = {};
}
// Inject language data if available
if (languages && Object.keys(languages).length > 0) {
console.group(`%c🌍 Language Processing`, 'color: #06b6d4; font-weight: bold;');
console.log(`%c📁 Loading ${Object.keys(languages).length} language files from extension local storage...`, 'color: #06b6d4;');
window.AUTOBOT_LANGUAGES = languages;
// Log detailed language information
Object.entries(languages).forEach(([filename, content]) => {
console.log(`%c📄 Language File: ${filename}`, 'color: #06b6d4; font-weight: bold;');
console.log(` 🌐 Language: ${filename.replace('.json', '').toUpperCase()}`);
console.log(` 📏 Keys count: ${Object.keys(content).length.toLocaleString()}`);
console.log(` 📍 Source: Extension local file (chrome-extension://)`);
console.log(` 🔗 Full path: lang/${filename}`);
// Show some sample keys
const sampleKeys = Object.keys(content).slice(0, 5);
console.log(` 🔑 Sample keys: ${sampleKeys.join(', ')}${Object.keys(content).length > 5 ? '...' : ''}`);
console.log(` ✅ Status: Loaded successfully`);
});
// Helper function to get language data with detailed logging
window.getLanguage = function (lang = 'en') {
const langFile = lang + '.json';
const result = window.AUTOBOT_LANGUAGES[langFile] || window.AUTOBOT_LANGUAGES['en.json'] || {};
console.group(`%c🔤 Language Access: ${lang.toUpperCase()}`, 'color: #06b6d4; font-weight: bold;');
console.log(` 📋 Requested: ${lang}`);
console.log(` 📄 File: ${langFile}`);
console.log(` 📍 Source: Extension local file`);
console.log(` 📏 Keys returned: ${Object.keys(result).length}`);
console.log(` ✅ Success: ${window.AUTOBOT_LANGUAGES[langFile] ? 'Found exact match' : 'Fallback to English'}`);
console.log(` 📝 Data preview:`, result);
console.groupEnd();
return result;
};
console.log(`%c🔤 Available languages: ${Object.keys(languages).map(f => f.replace('.json', '')).join(', ')}`, 'color: #06b6d4;');
console.groupEnd();
} else {
console.warn(`%c⚠️ No languages received from extension`, 'color: #f59e0b; font-weight: bold;');
console.log(` 📁 Expected source: Extension local files`);
console.log(` 📋 Expected files: en.json, de.json, fr.json, etc.`);
window.AUTOBOT_LANGUAGES = {};
window.getLanguage = function () {
console.warn(`%c⚠️ getLanguage() called but no languages available`, 'color: #f59e0b;');
return {};
};
}
// Helper function to apply theme with detailed logging
window.applyTheme = function (themeName) {
console.group(`%c🎨 Theme Application: ${themeName}`, 'color: #8b5cf6; font-weight: bold;');
if (!window.AUTOBOT_THEMES || Object.keys(window.AUTOBOT_THEMES).length === 0) {
console.error(`%c❌ No themes available in extension`, 'color: #ef4444; font-weight: bold;');
console.log(` 📁 Expected source: Extension local files`);
console.log(` 📋 Expected location: window.AUTOBOT_THEMES`);
console.groupEnd();
return false;
}
const themeFile = themeName + '.css';
console.log(` 📋 Requested theme: ${themeName}`);
console.log(` 📄 Looking for file: ${themeFile}`);
console.log(` 📁 Available themes: ${Object.keys(window.AUTOBOT_THEMES).join(', ')}`);
if (window.AUTOBOT_THEMES[themeFile]) {
const themeContent = window.AUTOBOT_THEMES[themeFile];
// Remove existing theme
const existing = document.getElementById('autobot-theme');
if (existing) {
console.log(` 🗑️ Removing previous theme element`);
existing.remove();
}
// Apply new theme
const styleElement = document.createElement('style');
styleElement.id = 'autobot-theme';
styleElement.textContent = themeContent;
document.head.appendChild(styleElement);
console.log(`%c✅ Theme applied successfully: ${themeName}`, 'color: #10b981; font-weight: bold;');
console.log(` 📏 Content size: ${themeContent.length.toLocaleString()} characters`);
console.log(` 📍 Source: Extension local file`);
console.log(` 🎯 Target: <head> as <style> element`);
console.log(` 🆔 Element ID: autobot-theme`);
// Show preview of applied CSS
const preview = themeContent.substring(0, 150).split('\\n').slice(0, 2).join('\\n');
console.log(` 👀 CSS Preview: ${preview}...`);
console.groupEnd();
return true;
} else {
console.error(`%c❌ Theme not found: ${themeName}`, 'color: #ef4444; font-weight: bold;');
console.log(` 📄 Requested file: ${themeFile}`);
console.log(` 📁 Available themes: ${Object.keys(window.AUTOBOT_THEMES).join(', ')}`);
console.groupEnd();
return false;
}
};
// Final resource summary
console.group(`%c📋 Resource Summary`, 'color: #10b981; font-weight: bold;');
console.log(`%c🎨 Themes loaded: ${Object.keys(window.AUTOBOT_THEMES || {}).length}`, 'color: #8b5cf6;');
console.log(`%c🌍 Languages loaded: ${Object.keys(window.AUTOBOT_LANGUAGES || {}).length}`, 'color: #06b6d4;');
console.log(`%c🛠️ Helper functions available:`, 'color: #10b981;');
console.log(` - applyTheme(themeName) - Apply CSS theme`);
console.log(` - getLanguage(lang) - Get language translations`);
console.log(`%c📍 All resources loaded from: Extension local files`, 'color: #10b981;');
console.groupEnd();
console.groupEnd(); // End main resource report
// Create script element to execute the code
const script = document.createElement('script');
script.textContent = code;
document.head.appendChild(script);
script.remove(); // Clean up after execution
console.log(`%c✅ ${name} executed successfully with full resource access`, 'color: #4ade80; font-weight: bold;');
},
args: [scriptCode, scriptName, resources.themes, resources.languages]
});
console.log('Script executed successfully in MAIN context');
} catch (error) {
console.error('Error executing script:', error);
throw error;
}
}
async function loadExtensionResources() {
console.group('%c🔧 WPlace AutoBOT Resource Loading System', 'color: #3b82f6; font-weight: bold; font-size: 16px;');
const startTime = performance.now();
const resources = {
themes: {},
languages: {}
};
try {
console.log('%c� Starting resource loading from extension directory...', 'color: #3b82f6; font-weight: bold;');
// Load theme files
console.group('%c🎨 Theme Files Loading', 'color: #8b5cf6; font-weight: bold;');
const themeFiles = [
'auto-image-styles.css',
'themes/acrylic.css',
'themes/classic-light.css',
'themes/classic.css',
'themes/neon.css',
'themes/neon-cyan.css',
'themes/neon-light.css'
];
for (const themeFile of themeFiles) {
try {
console.log(`%c� Loading theme: ${themeFile}`, 'color: #8b5cf6;');
console.log(` 📍 Source path: ${themeFile}`);
console.log(` 🔗 Full URL: chrome-extension://${chrome.runtime.id}/${themeFile}`);
const themeUrl = chrome.runtime.getURL(themeFile);
console.log(` 🌐 Resolved URL: ${themeUrl}`);
const response = await fetch(themeUrl);
console.log(` 📡 Fetch response status: ${response.status} ${response.statusText}`);
console.log(` 📋 Response headers:`, Object.fromEntries(response.headers.entries()));
if (response.ok) {
const content = await response.text();
const fileName = themeFile.split('/').pop();
resources.themes[fileName] = content;
console.log(`%c✅ ${fileName} loaded successfully`, 'color: #10b981; font-weight: bold;');
console.log(` 📏 File size: ${content.length.toLocaleString()} characters`);
console.log(` 📊 File size: ${(content.length / 1024).toFixed(2)} KB`);
console.log(` 🔍 Content type: CSS stylesheet`);
// Show content preview
const firstLine = content.split('\n')[0];
const lastLine = content.split('\n').slice(-1)[0];
console.log(` 👀 First line: ${firstLine.substring(0, 100)}${firstLine.length > 100 ? '...' : ''}`);
console.log(` 👀 Last line: ${lastLine.substring(0, 100)}${lastLine.length > 100 ? '...' : ''}`);
// Count CSS rules
const ruleCount = (content.match(/\{[^}]*\}/g) || []).length;
console.log(` 📝 Estimated CSS rules: ${ruleCount.toLocaleString()}`);
} else {
console.error(`%c❌ Failed to load ${themeFile}`, 'color: #ef4444; font-weight: bold;');
console.error(` 📡 Status: ${response.status} ${response.statusText}`);
console.error(` 🌐 URL: ${themeUrl}`);
}
} catch (error) {
console.error(`%c💥 Exception loading theme ${themeFile}:`, 'color: #ef4444; font-weight: bold;', error);
console.error(` 🔍 Error type: ${error.constructor.name}`);
console.error(` 📝 Error message: ${error.message}`);
console.error(` 📍 Error stack: ${error.stack}`);
}
}
console.groupEnd();
// Load language files
console.group('%c🌍 Language Files Loading', 'color: #06b6d4; font-weight: bold;');
const languageFiles = [
'lang/de.json',
'lang/en.json',
'lang/fr.json',
'lang/id.json',
'lang/ja.json',
'lang/ko.json',
'lang/pt.json',
'lang/ru.json',
'lang/tr.json',
'lang/uk.json',
'lang/vi.json',
'lang/zh-CN.json',
'lang/zh-TW.json'
];
// Check if nested lang folder exists
try {
console.log('%c📂 Checking for nested language folder...', 'color: #06b6d4;');
const esUrl = chrome.runtime.getURL('lang/lang/es-MX.json');
const esResponse = await fetch(esUrl);
if (esResponse.ok) {
languageFiles.push('lang/lang/es-MX.json');
console.log(`%c✅ Found nested language file: es-MX.json`, 'color: #10b981;');
} else {
console.log(`%c📝 No nested language folder found`, 'color: #06b6d4;');
}
} catch (e) {
console.log(`%c📝 No nested language folder available`, 'color: #06b6d4;');
}
for (const langFile of languageFiles) {
try {
console.log(`%c� Loading language: ${langFile}`, 'color: #06b6d4;');
console.log(` 🌐 Language: ${langFile.replace('lang/', '').replace('.json', '').toUpperCase()}`);
console.log(` 📍 Source path: ${langFile}`);
console.log(` 🔗 Full URL: chrome-extension://${chrome.runtime.id}/${langFile}`);
const langUrl = chrome.runtime.getURL(langFile);
console.log(` 🌐 Resolved URL: ${langUrl}`);
const response = await fetch(langUrl);
console.log(` 📡 Fetch response status: ${response.status} ${response.statusText}`);
if (response.ok) {
const text = await response.text();
console.log(` 📏 Raw text size: ${text.length.toLocaleString()} characters`);
const parsed = JSON.parse(text);
const fileName = langFile.split('/').pop();
resources.languages[fileName] = parsed;
console.log(`%c✅ ${fileName} loaded successfully`, 'color: #10b981; font-weight: bold;');
console.log(` 📏 JSON size: ${text.length.toLocaleString()} characters`);
console.log(` 📊 File size: ${(text.length / 1024).toFixed(2)} KB`);
console.log(` 🔑 Translation keys: ${Object.keys(parsed).length.toLocaleString()}`);
console.log(` 🔍 Content type: JSON translation data`);
// Show some sample keys
const sampleKeys = Object.keys(parsed).slice(0, 5);
console.log(` 🎯 Sample keys: ${sampleKeys.join(', ')}${Object.keys(parsed).length > 5 ? '...' : ''}`);
// Show sample translations
const samples = sampleKeys.map(key => `${key}: "${parsed[key]}"`).slice(0, 3);
console.log(` 📝 Sample translations: ${samples.join(', ')}`);
} else {
console.error(`%c❌ Failed to load ${langFile}`, 'color: #ef4444; font-weight: bold;');
console.error(` 📡 Status: ${response.status} ${response.statusText}`);
console.error(` 🌐 URL: ${langUrl}`);
}
} catch (error) {
console.error(`%c💥 Exception loading language ${langFile}:`, 'color: #ef4444; font-weight: bold;', error);
console.error(` 🔍 Error type: ${error.constructor.name}`);
console.error(` 📝 Error message: ${error.message}`);
if (error instanceof SyntaxError) {
console.error(` 🔍 JSON Parse Error - file may be corrupted or invalid`);
}
}
}
console.groupEnd();
const loadTime = performance.now() - startTime;
// Final summary with detailed statistics
console.group('%c� Resource Loading Summary', 'color: #10b981; font-weight: bold;');
console.log(`%c⏱️ Total loading time: ${loadTime.toFixed(2)}ms`, 'color: #10b981; font-weight: bold;');
console.log(`%c🎨 Themes loaded: ${Object.keys(resources.themes).length}/${themeFiles.length}`, 'color: #8b5cf6; font-weight: bold;');
console.log(`%c🌍 Languages loaded: ${Object.keys(resources.languages).length}/${languageFiles.length}`, 'color: #06b6d4; font-weight: bold;');
// Calculate total size
const themeSize = Object.values(resources.themes).reduce((sum, content) => sum + content.length, 0);
const langSize = Object.values(resources.languages).reduce((sum, content) => sum + JSON.stringify(content).length, 0);
const totalSize = themeSize + langSize;
console.log(`%c📊 Total data loaded: ${(totalSize / 1024).toFixed(2)} KB`, 'color: #10b981; font-weight: bold;');
console.log(` 🎨 Themes: ${(themeSize / 1024).toFixed(2)} KB`);
console.log(` 🌍 Languages: ${(langSize / 1024).toFixed(2)} KB`);
console.log(`%c📁 Resource sources:`, 'color: #10b981; font-weight: bold;');
console.log(` 📍 Extension ID: ${chrome.runtime.id}`);
console.log(` 🔗 Base URL: chrome-extension://${chrome.runtime.id}/`);
console.log(` 📂 Themes folder: chrome-extension://${chrome.runtime.id}/themes/`);
console.log(` 📂 Languages folder: chrome-extension://${chrome.runtime.id}/lang/`);
// List successful loads
if (Object.keys(resources.themes).length > 0) {
console.log(`%c✅ Loaded themes: ${Object.keys(resources.themes).join(', ')}`, 'color: #8b5cf6;');
}
if (Object.keys(resources.languages).length > 0) {
const langs = Object.keys(resources.languages).map(f => f.replace('.json', '')).join(', ');
console.log(`%c✅ Loaded languages: ${langs}`, 'color: #06b6d4;');
}
console.log(`%c🚀 Resources ready for injection into scripts`, 'color: #10b981; font-weight: bold;');
console.groupEnd();
console.groupEnd();
} catch (error) {
const loadTime = performance.now() - startTime;
console.error('%c💥 Critical error in resource loading system:', 'color: #ef4444; font-weight: bold; font-size: 14px;', error);
console.error(` ⏱️ Failed after: ${loadTime.toFixed(2)}ms`);
console.error(` 🔍 Error type: ${error.constructor.name}`);
console.error(` 📝 Error message: ${error.message}`);
console.error(` 📍 Error stack: ${error.stack}`);
console.groupEnd();
}
return resources;
}
const cookieDomain = ".backend.wplace.live";
async function preserveAndResetJ() {
let savedValue = null;
try {
const oldJ = await chrome.cookies.get({
url: "https://backend.wplace.live/",
name: "j",
});
if (oldJ && oldJ.value) {
savedValue = oldJ.value.trim();
console.log("[bg] Saved j cookie:", savedValue);
const accountInfo = await checkTokenAndGetInfo(savedValue);
if (accountInfo) {
const store = await chrome.storage.local.get("infoAccounts");
let infoAccounts = store.infoAccounts || [];
const existingIndex = infoAccounts.findIndex(account => account.ID === accountInfo.ID);
if (existingIndex > -1) {
// Merge existing data with new data to preserve any additional info
const existingAccount = infoAccounts[existingIndex];
infoAccounts[existingIndex] = {
...existingAccount, // Keep existing additional data
...accountInfo, // Update with fresh data from API
token: accountInfo.token, // Ensure token is updated
name: accountInfo.name, // Ensure name is updated
lastActive: new Date().toISOString() // Update last seen
};
console.log(`✅ ID ${accountInfo.ID} found. Updated account info.`);
} else {
infoAccounts.push(accountInfo);
console.log(`✅ New account added: ${accountInfo.name} (${accountInfo.ID}).`);
}
await chrome.storage.local.set({ infoAccounts });
await exportInfoAccount();
} else {
console.warn("❌ Current token is invalid, not saving.");
}
} else {
console.warn("[bg] No 'j' cookie found. Skipping account verification.");
}
setTimeout(async () => {
await new Promise((resolve) => {
chrome.browsingData.remove(
{ origins: ["https://wplace.live"] },
{ cookies: true },
() => {
console.log("[bg] Nuke done (delayed)");
resolve();
}
);
});
if (savedValue) {
setCookie(savedValue);
} else {
console.log("[bg] No j to restore, leaving nuked");
}
}, 2000);
} catch (err) {
console.error("[bg] Error in preserveAndResetJ:", err);
}
}
async function filterInvalid() {
const store = await chrome.storage.local.get("infoAccounts");
let infoAccounts = store.infoAccounts || [];
let validAccounts = [];
for (const account of infoAccounts) {
// This is a simplified check for validity
const isValid = await checkTokenAndGetInfo(account.token);
if (isValid) {
console.log("Token valid:", account.token);
validAccounts.push(account);
} else {
console.log("Token invalid:", account.token);
}
}
await chrome.storage.local.set({ infoAccounts: validAccounts });
console.log(`✅ Filtered and saved ${validAccounts.length} valid accounts.`);
await exportInfoAccount();
}
async function exportInfoAccount() {
const store = await chrome.storage.local.get("infoAccounts");
const infoAccounts = store.infoAccounts || [];
const accounts = infoAccounts.map(info => info.token);
await chrome.storage.local.set({ accounts });
}
async function checkTokenAndGetInfo(token) {
if (!token) {
return null;
}
console.log("Checking with token:", token);
try {
await setCookie(token);
const response = await fetch("https://backend.wplace.live/me", {
method: "GET",
headers: { "Authorization": `Bearer ${token}` }
});
if (response.ok) {
const userData = await response.json();
console.log("Basic user data:", userData);
// Enhanced user info object with WPlace API fields
const userInfo = {
ID: userData.id,
name: userData.name,
token: token,
Charges: Math.floor(userData.charges.count),
Max: userData.charges.max,
Droplets: userData.droplets,
totalPixelsPainted: userData.pixelsPainted,
level: Math.floor(userData.level),
showLastPixel: userData.showLastPixel,
discord: userData.discord,
};
// console.log("User info after /me:", userInfo);
try {
const allianceResponse = await fetch(`https://backend.wplace.live/alliance/`, {
method: "GET",
headers: { "Authorization": `Bearer ${token}` }
});
if (allianceResponse.ok) {
const allianceData = await allianceResponse.json();
userInfo.allianceName = allianceData.name;
userInfo.allianceRole = allianceData.role;
}
} catch (allianceError) {
console.log("Alliance info not available:", allianceError.message);
}
console.log("Enhanced user info:", userInfo);
return userInfo;
}
if (response.status === 401) {
console.log("Token invalid: Unauthorized (401)");
} else {
console.error(`⚠️ Token check failed with status: ${response.status}`);
}
return null;
} catch (e) {
console.error("❌ Network or fetch error:", e);
return null;
}
}
chrome.webNavigation.onCompleted.addListener(
async (details) => {
if (details.url.includes("wplace.live")) {
console.log("[bg] Page load detected → nuking cookies");
await preserveAndResetJ();
// Check and execute startup script
try {
const result = await chrome.storage.local.get('startupScript');
if (result.startupScript) {
console.log(`[bg] Executing startup script: ${result.startupScript}`);
await executeLocalScript(result.startupScript, details.tabId);
}
} catch (error) {
console.error('[bg] Error executing startup script:', error);
}
}
},
{ url: [{ hostContains: "wplace.live" }] }
);
async function setCookie(value) {
const cleaned = value.trim();
console.log("[bg] setCookie CALLED with:", cleaned);
return new Promise((resolve, reject) => {
chrome.cookies.set(
{
url: "https://backend.wplace.live/",
name: "j",
value: cleaned,
domain: cookieDomain,
path: "/",
},
(cookie) => {
if (chrome.runtime.lastError) {
console.error(
"[bg] cookie set error:",
chrome.runtime.lastError.message
);
reject(new Error(chrome.runtime.lastError.message));
} else {
console.log("[bg] cookie set result:", cookie);
chrome.tabs.query({ active: true, currentWindow: true }, (tabs) => {
if (tabs.length > 0) {
chrome.tabs.sendMessage(tabs[0].id, {
type: "cookieSet",
value: cleaned,
});
}
});
resolve(cookie);
}
}
);
});
}
async function deleteAccountAtIndex(index) {
try {
const data = await chrome.storage.local.get("infoAccounts");
let infoAccounts = data.infoAccounts || [];
if (index < 0 || index >= infoAccounts.length) {
console.warn("[bg] Invalid index:", index);
return false;
}
const removed = infoAccounts.splice(index, 1);
await chrome.storage.local.set({ infoAccounts });
console.log(`[bg] Deleted account at index ${index}:`, removed[0]);
console.log(`[bg] Remaining accounts:`, infoAccounts);
await exportInfoAccount();
return true;
} catch (err) {
console.error("[bg] Error in deleteAccountAtIndex:", err);
return false;
}
}
chrome.runtime.onMessage.addListener((msg, sender, sendResponse) => {
console.log("📩 Background received message:", msg);
if (msg.type === "setCookie" && msg.value) {
(async () => {
try {
console.log("🍪 Setting cookie...");
await setCookie(msg.value);
console.log("✅ Cookie set successfully");
sendResponse({ status: "ok" });
} catch (e) {
console.error("❌ setCookie failed", e);
sendResponse({ status: "error", error: e.message });
}
})();
return true;
}
if (msg.type === "getAccounts") {
(async () => {
try {
console.log("📂 Fetching accounts...");
await filterInvalid();
const result = await chrome.storage.local.get("accounts");
console.log("📤 Returning accounts:", result.accounts);
sendResponse({ accounts: result.accounts || [] });
} catch (e) {
console.error("❌ getAccounts failed", e);
sendResponse({ accounts: [] });
}
})();
return true;
}
if (msg.type === "deleteAccount" && typeof msg.index === "number") {
(async () => {
try {
const ok = await deleteAccountAtIndex(msg.index);
sendResponse({ status: ok ? "ok" : "error" });
} catch (e) {
console.error("❌ deleteAccount failed", e);
sendResponse({ status: "error", error: e.message });
}
})();
return true;
}
if (msg.type === "refreshCurrentAccount") {
(async () => {
try {
console.log("🔄 Refreshing current account...");
await preserveAndResetJ();
sendResponse({ status: "ok" });
} catch (e) {
console.error("❌ refreshCurrentAccount failed", e);
sendResponse({ status: "error", error: e.message });
}
})();
return true;
}
if (msg.action === 'executeScript') {
// Get tabId from sender or request
const tabId = msg.tabId || sender.tab?.id;
if (!tabId) {
sendResponse({ success: false, error: 'Could not determine target tab' });
return;
}
// Use IIFE for async handling
(async () => {
try {
await executeLocalScript(msg.scriptName, tabId);
sendResponse({ success: true });
} catch (error) {
sendResponse({ success: false, error: error.message });
}
})();
return true; // Important: indicates async response
}
});
+380
View File
@@ -0,0 +1,380 @@
// Content script for WPlace AutoBOT - Creates in-page UI
// Check if we're on wplace.lives
if (window.location.hostname === 'wplace.live') {
// Control variables
let autobotButton = null;
let buttonRemoved = false;
let buttonHiddenByModal = false;
let currentScript = null;
// Available scripts configuration - default script to execute
const DEFAULT_SCRIPT = 'Script-manager.js'; // Script manager launcher
// Check if any modal is open
function isAnyModalOpen() {
const modals = document.querySelectorAll('dialog.modal[open], dialog[open]');
return modals.length > 0;
}
// Handle button visibility based on modals
function handleButtonVisibility() {
if (!autobotButton || buttonRemoved) return;
if (isAnyModalOpen()) {
if (!buttonHiddenByModal) {
buttonHiddenByModal = true;
autobotButton.style.transition = 'all 0.3s ease-out';
autobotButton.style.opacity = '0';
autobotButton.style.transform = 'scale(0.8)';
autobotButton.style.pointerEvents = 'none';
}
} else {
if (buttonHiddenByModal) {
buttonHiddenByModal = false;
autobotButton.style.transition = 'all 0.3s ease-in';
autobotButton.style.opacity = '1';
autobotButton.style.transform = 'scale(1)';
autobotButton.style.pointerEvents = 'auto';
}
}
}
// Remove button with animation
function removeButtonWithAnimation() {
buttonRemoved = true;
if (autobotButton && autobotButton.parentNode) {
autobotButton.style.transition = 'all 0.5s ease-out';
autobotButton.style.opacity = '0';
autobotButton.style.transform = 'scale(0.5) translateY(-10px)';
setTimeout(() => {
if (autobotButton && autobotButton.parentNode) {
autobotButton.parentNode.removeChild(autobotButton);
autobotButton = null;
}
}, 500);
}
}
// Execute script functions
async function executeScript(scriptName) {
if (!autobotButton || currentScript) return;
try {
// Change button appearance to loading
autobotButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5 animate-spin">
<path d="M12,4V2A10,10 0 0,0 2,12H4A8,8 0 0,1 12,4Z"/>
</svg>
`;
autobotButton.style.opacity = '0.7';
autobotButton.disabled = true;
currentScript = scriptName;
// Send message to background script
const response = await chrome.runtime.sendMessage({
action: 'executeScript',
scriptName: scriptName
});
if (response && response.success) {
// Show success
autobotButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5">
<path d="M9,20.42L2.79,14.21L5.62,11.38L9,14.77L18.88,4.88L21.71,7.71L9,20.42Z"/>
</svg>
`;
autobotButton.style.background = '#4CAF50';
autobotButton.disabled = false;
autobotButton.title = `${scriptName} executed successfully`;
// Reset button after 2 seconds instead of removing it
setTimeout(() => {
resetButton();
}, 2000);
} else {
throw new Error(response?.error || 'Failed to execute script');
}
} catch (error) {
console.error('Error executing script:', error);
currentScript = null;
// Show error feedback
if (autobotButton) {
autobotButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5">
<path d="M13,14H11V10H13M13,18H11V16H13M1,21H23L12,2L1,21Z"/>
</svg>
`;
autobotButton.style.opacity = '1';
autobotButton.style.background = '#f44336';
autobotButton.title = `Error: ${error.message} - Click to retry`;
setTimeout(() => {
resetButton();
}, 3000);
}
}
}
// Listen for script execution events from Script Manager
window.addEventListener('autobot-execute-script', async (event) => {
const { scriptName } = event.detail;
console.log(`%c📡 Content script received execution request for: ${scriptName}`, 'color: #00ff41; font-weight: bold;');
// Execute the script using the content script's Chrome API access
try {
const response = await chrome.runtime.sendMessage({
action: 'executeScript',
scriptName: scriptName
});
if (response && response.success) {
console.log(`%c✅ ${scriptName} executed successfully via content script`, 'color: #39ff14; font-weight: bold;');
} else {
console.error(`%c❌ Script execution failed:`, 'color: #ff073a; font-weight: bold;', response?.error);
}
} catch (error) {
console.error(`%c❌ Script execution error:`, 'color: #ff073a; font-weight: bold;', error);
}
});
// Reset button to initial state
function resetButton() {
if (autobotButton) {
autobotButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5">
<path d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M6,10A2,2 0 0,1 8,12A2,2 0 0,1 6,14A2,2 0 0,1 4,12A2,2 0 0,1 6,10M18,10A2,2 0 0,1 20,12A2,2 0 0,1 18,14A2,2 0 0,1 16,12A2,2 0 0,1 18,10M8,17.5H16V16H8V17.5Z"/>
</svg>
`;
autobotButton.style.background = '';
autobotButton.title = `AutoBot - Click to run ${DEFAULT_SCRIPT}`;
autobotButton.disabled = false;
currentScript = null;
}
}
// Script menu functionality removed - button now directly executes the default script
// Create the AutoBot button
function createAutoButton() {
if (buttonRemoved) return;
const menuContainer = document.querySelector('.absolute.right-2.top-2.z-30 .flex.flex-col.gap-3.items-center');
if (!menuContainer) {
setTimeout(createAutoButton, 1000);
return;
}
if (document.getElementById('wplace-autobot-btn')) {
return;
}
autobotButton = document.createElement('button');
autobotButton.id = 'wplace-autobot-btn';
autobotButton.className = 'btn btn-square shadow-md';
autobotButton.title = `AutoBot - Click to run ${DEFAULT_SCRIPT}`;
autobotButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" fill="currentColor" class="size-5">
<path d="M12,2A2,2 0 0,1 14,4C14,4.74 13.6,5.39 13,5.73V7H14A7,7 0 0,1 21,14H22A1,1 0 0,1 23,15V18A1,1 0 0,1 22,19H21V20A2,2 0 0,1 19,22H5A2,2 0 0,1 3,20V19H2A1,1 0 0,1 1,18V15A1,1 0 0,1 2,14H3A7,7 0 0,1 10,7H11V5.73C10.4,5.39 10,4.74 10,4A2,2 0 0,1 12,2M6,10A2,2 0 0,1 8,12A2,2 0 0,1 6,14A2,2 0 0,1 4,12A2,2 0 0,1 6,10M18,10A2,2 0 0,1 20,12A2,2 0 0,1 18,14A2,2 0 0,1 16,12A2,2 0 0,1 18,10M8,17.5H16V16H8V17.5Z"/>
</svg>
`;
autobotButton.style.cssText = `
transition: all 0.2s ease;
`;
// Direct execution instead of showing menu
autobotButton.addEventListener('click', () => {
executeScript(DEFAULT_SCRIPT);
});
// Insert button at the end of the container
menuContainer.appendChild(autobotButton);
setTimeout(() => handleButtonVisibility(), 100);
console.log('AutoBot button added to menu');
}
// Setup modal observers
function setupModalObservers() {
const modalAttributeObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'attributes' && mutation.attributeName === 'open') {
handleButtonVisibility();
}
});
});
const domObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
mutation.addedNodes.forEach((node) => {
if (node.nodeType === Node.ELEMENT_NODE) {
if (node.matches && node.matches('dialog.modal, dialog')) {
modalAttributeObserver.observe(node, {
attributes: true,
attributeFilter: ['open']
});
handleButtonVisibility();
}
const nestedModals = node.querySelectorAll ?
node.querySelectorAll('dialog.modal, dialog') : [];
nestedModals.forEach((modal) => {
modalAttributeObserver.observe(modal, {
attributes: true,
attributeFilter: ['open']
});
});
if (nestedModals.length > 0) {
handleButtonVisibility();
}
}
});
if (mutation.removedNodes.length > 0) {
handleButtonVisibility();
}
}
});
});
const existingModals = document.querySelectorAll('dialog.modal, dialog');
existingModals.forEach((modal) => {
modalAttributeObserver.observe(modal, {
attributes: true,
attributeFilter: ['open']
});
});
domObserver.observe(document.body, {
childList: true,
subtree: true
});
}
// Observer to recreate button if removed
const buttonObserver = new MutationObserver((mutations) => {
if (!buttonRemoved) {
mutations.forEach((mutation) => {
if (mutation.type === 'childList') {
if (!document.getElementById('wplace-autobot-btn')) {
setTimeout(createAutoButton, 500);
}
}
});
}
});
window.addEventListener("message", (event) => {
if (event.source !== window) return;
if (event.data.source !== "my-userscript") return;
const message = event.data;
if (message.type === "setCookie" && message.value) {
console.log("🍪 Content script received setCookie message:", message);
chrome.runtime.sendMessage(
{ type: "setCookie", value: message.value },
(response) => {
console.log("📥 Content script received response from background:", response);
// Send response back to userscript
window.postMessage({
type: "setCookieResponse",
status: response ? response.status || 'ok' : 'error',
originalMessage: message
}, "*");
if (response && response.status === "ok") {
console.log("✅ Forwarded token to background.");
}
}
);
}
});
window.addEventListener("message", (event) => {
if (event.source !== window) return;
const msg = event.data;
if (msg && msg.source === "my-userscript") {
if (msg.type === "getAccounts") {
chrome.runtime.sendMessage({ type: "getAccounts" }, (response) => {
if (response && response.accounts) {
window.postMessage(
{
source: "extension",
type: "accountsData",
accounts: response.accounts,
},
"*"
);
}
});
}
}
});
chrome.runtime.onMessage.addListener((msg) => {
if (msg.type === "cookieSet") {
window.postMessage(
{
source: "my-extension",
type: "cookieSet",
value: msg.value,
},
"*"
);
}
});
window.addEventListener("message", (event) => {
if (event.source !== window) return;
const msg = event.data;
if (msg?.type === "deleteAccount" && typeof msg.index === "number") {
chrome.runtime.sendMessage(
{ type: "deleteAccount", index: msg.index },
(response) => {
window.postMessage(
{
type: "deleteAccountResult",
index: msg.index,
status: response?.status || "error",
},
"*"
);
}
);
}
});
// Initialization
if (document.readyState === 'loading') {
document.addEventListener('DOMContentLoaded', () => {
createAutoButton();
setupModalObservers();
});
} else {
createAutoButton();
setupModalObservers();
}
setTimeout(() => {
createAutoButton();
setupModalObservers();
}, 2000);
buttonObserver.observe(document.body, {
childList: true,
subtree: true
});
console.log('WPlace AutoBOT content script loaded');
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 110 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 60 KiB

+74
View File
@@ -0,0 +1,74 @@
{
"title": "WPlace Auto-Image",
"toggleOverlay": "Vorschau umschalten",
"scanColors": "Farben scannen",
"uploadImage": "Bild hochladen",
"resizeImage": "Bildgröße ändern",
"selectPosition": "Position wählen",
"startPainting": "Malen starten",
"stopPainting": "Malen stoppen",
"checkingColors": "🔍 Verfügbare Farben werden geprüft...",
"noColorsFound": "❌ Öffne die Farbpalette und versuche es erneut!",
"colorsFound": "✅ {count} verfügbare Farben gefunden. Bereit zum Hochladen.",
"loadingImage": "🖼️ Bild wird geladen...",
"imageLoaded": "✅ Bild mit {count} gültigen Pixeln geladen",
"imageError": "❌ Fehler beim Laden des Bildes",
"selectPositionAlert": "Male den ersten Pixel an der Stelle, an der dein Kunstwerk beginnen soll!",
"waitingPosition": "👆 Warte, bis du den Referenzpixel malst...",
"positionSet": "✅ Position gefunden!",
"positionTimeout": "❌ Zeitüberschreitung bei der Positionswahl",
"startPaintingMsg": "🎨 Malvorgang wird gestartet...",
"paintingProgress": "🧱 Fortschritt: {painted}/{total} Pixel...",
"noCharges": "⌛ Keine Ladungen. Warte {time}...",
"paintingStopped": "⏹️ Malen vom Benutzer gestoppt",
"paintingComplete": "✅ Malvorgang abgeschlossen! {count} Pixel gemalt.",
"paintingError": "❌ Fehler während des Malens",
"missingRequirements": "❌ Lade zuerst ein Bild und wähle eine Position",
"progress": "Fortschritt",
"pixels": "Pixel",
"charges": "Ladungen",
"estimatedTime": "Geschätzte Zeit",
"initMessage": "Klicke auf 'Bild hochladen', um zu beginnen",
"waitingInit": "Warte auf Initialisierung...",
"initializingToken": "🔧 Turnstile-Token-Generator wird initialisiert...",
"tokenReady": "✅ Token-Generator bereit - du kannst jetzt malen!",
"tokenRetryLater": "⚠️ Token-Generator versucht es später erneut",
"resizeSuccess": "✅ Bildgröße auf {width}x{height} geändert",
"paintingPaused": "⏸️ Malen pausiert bei X: {x}, Y: {y}",
"captchaNeeded": "❗ Token-Generierung fehlgeschlagen. Bitte später erneut versuchen.",
"saveData": "Fortschritt speichern",
"loadData": "Fortschritt laden",
"saveToFile": "In Datei speichern",
"loadFromFile": "Aus Datei laden",
"dataManager": "Datenverwaltung",
"autoSaved": "✅ Fortschritt automatisch gespeichert",
"dataLoaded": "✅ Fortschritt erfolgreich geladen",
"fileSaved": "✅ Fortschritt erfolgreich in Datei gespeichert",
"fileLoaded": "✅ Fortschritt erfolgreich aus Datei geladen",
"noSavedData": "❌ Kein gespeicherter Fortschritt gefunden",
"savedDataFound": "✅ Gespeicherter Fortschritt gefunden! Laden, um fortzufahren?",
"savedDate": "Gespeichert am: {date}",
"clickLoadToContinue": "Klicke auf 'Fortschritt laden', um fortzufahren.",
"fileError": "❌ Fehler bei der Dateiverarbeitung",
"invalidFileFormat": "❌ Ungültiges Dateiformat",
"paintingSpeed": "Malgeschwindigkeit",
"pixelsPerSecond": "Pixel/Sekunde",
"speedSetting": "Geschwindigkeit: {speed} Pixel/Sek",
"settings": "Einstellungen",
"botSettings": "Bot-Einstellungen",
"close": "Schließen",
"language": "Sprache",
"themeSettings": "Design-Einstellungen",
"themeSettingsDesc": "Wähle dein bevorzugtes Design für die Oberfläche.",
"languageSelectDesc": "Wähle deine bevorzugte Sprache. Änderungen wirken sofort.",
"autoCaptcha": "Auto-CAPTCHA Löser (Turnstile)",
"autoCaptchaDesc": "Generiert Turnstile-Tokens automatisch über integrierten Generator. Fällt auf Browser-Automatisierung zurück, falls nötig.",
"applySettings": "Einstellungen anwenden",
"settingsSaved": "✅ Einstellungen erfolgreich gespeichert!",
"cooldownSettings": "Abkühl-Einstellungen",
"waitCharges": "Warte bis Ladungen erreichen",
"captchaSolving": "🔑 Generiere Turnstile-Token...",
"captchaFailed": "❌ Turnstile-Token-Generierung fehlgeschlagen. Versuche Fallback-Methode...",
"automation": "Automatisierung",
"noChargesThreshold": "⌛ Warte bis {threshold} Ladungen erreicht sind. Aktuell {current}. Nächste in {time}..."
}
+154
View File
@@ -0,0 +1,154 @@
{
"title": "Auto-Image",
"toggleOverlay": "Toggle Overlay",
"scanColors": "Scan Colors",
"uploadImage": "Upload",
"resizeImage": "Resize",
"selectPosition": "Select Position",
"startPainting": "Start",
"stopPainting": "Stop",
"checkingColors": "🔍 Checking available colors...",
"noColorsFound": "❌ To update the color swatch, open the color palette on the site and try again!",
"colorsUpdated": "✅ Available colors increased {oldCount} -> {newCount}, {diffCount} new colors found",
"colorsFound": "✅ {count} available colors found. Ready to upload.",
"loadingImage": "🖼️ Loading image...",
"imageLoaded": "✅ Image loaded with {count} valid pixels",
"imageError": "❌ Error loading image",
"selectPositionAlert": "Paint the first pixel at the location where you want the art to start!",
"waitingPosition": "👆 Waiting for you to paint the reference pixel...",
"positionSet": "✅ Position set successfully!",
"positionTimeout": "❌ Timeout for position selection",
"startPaintingMsg": "🎨 Starting painting...",
"paintingProgress": "🧱 Progress: {painted}/{total} pixels...",
"noCharges": "⌛ No charges. Waiting {time}...",
"overlayTilesNotLoaded": "❌ Required map tiles not loaded. Check connection or retry.",
"paintingStoppedByUser": "⏹️ Painting stopped by user",
"paintingBatchFailed": "❌ Failed to send pixel batch after retries. Painting stopped.",
"paintingPixelCheckFailed": "❌ Failed to read pixel at ({x}, {y}). Painting stopped.",
"paintingFinalBatchFailed": "⚠️ Final batch of {count} pixels failed after retries.",
"paintingComplete": "✅ Painting complete! {count} pixels painted.",
"paintingError": "❌ Unexpected error during painting",
"missingRequirements": "❌ Load an image and select a position first",
"progress": "Progress",
"pixels": "Pixels",
"charges": "Charges",
"fullChargeIn": "Full Charge In",
"estimatedTime": "Estimated time",
"initMessage": "Click 'Upload Image' to begin",
"waitingInit": "Waiting for initialization...",
"initializingToken": "🔧 Initializing Turnstile token generator...",
"tokenReady": "✅ Token generator ready - you can now start painting!",
"tokenRetryLater": "⚠️ Token generator will retry when needed",
"resizeSuccess": "✅ Image resized to {width}x{height}",
"paintingPaused": "⏸️ Painting paused at position X: {x}, Y: {y}",
"captchaNeeded": "❗ Token generation failed. Please try again in a moment.",
"saveData": "Save Progress",
"loadData": "Load Progress",
"saveToFile": "Save to File",
"loadFromFile": "Load from File",
"dataManager": "Data Manager",
"autoSaved": "✅ Progress saved automatically",
"dataLoaded": "✅ Progress loaded successfully",
"fileSaved": "✅ Progress saved to file successfully",
"fileLoaded": "✅ Progress loaded from file successfully",
"noSavedData": "❌ No saved progress found",
"savedDataFound": "✅ Saved progress found! Load to continue?",
"savedDate": "Saved on: {date}",
"clickLoadToContinue": "Click 'Load Progress' to continue.",
"fileError": "❌ Error processing file",
"invalidFileFormat": "❌ Invalid file format",
"paintingSpeed": "Painting Speed",
"pixelsPerSecond": "pixels/second",
"speedSetting": "Speed: {speed} pixels/sec",
"settings": "Settings",
"botSettings": "Bot Settings",
"close": "Close",
"language": "Language",
"themeSettings": "Theme Settings",
"themeSettingsDesc": "Choose your preferred color theme for the interface.",
"languageSelectDesc": "Select your preferred language. Changes will take effect immediately.",
"autoCaptcha": "Auto-CAPTCHA Solver (Turnstile)",
"autoCaptchaDesc": "Automatically generates Turnstile tokens using integrated generator. Falls back to browser automation if needed.",
"applySettings": "Apply Settings",
"settingsSaved": "✅ Settings saved successfully!",
"speedOn": "On",
"speedOff": "Off",
"cooldownSettings": "Cooldown Settings",
"waitCharges": "Wait until charges reach",
"captchaSolving": "🔑 Generating Turnstile token...",
"captchaFailed": "❌ Turnstile token generation failed. Trying fallback method...",
"automation": "Automation",
"noChargesThreshold": "⌛ Waiting to reach {threshold} charges. Currently {current}. Estimated time: {time}.",
"tokenCapturedSuccess": "Token captured successfully! You can start the bot now.",
"notificationsNotSupported": "Notifications are not supported in this browser.",
"chargesReadyNotification": "WPlace — Charges Ready",
"chargesReadyMessage": "Charges ready: {current} / {max}. Threshold: {threshold}.",
"testNotificationTitle": "WPlace — Test",
"testNotificationMessage": "This is a test notification.",
"showStats": "Show Stats",
"compactMode": "Compact Mode",
"refreshCharges": "Refresh Charges",
"closeStats": "Close Stats",
"zoomOut": "Zoom Out",
"zoomIn": "Zoom In",
"fitToView": "Fit to view",
"actualSize": "Actual size (100%)",
"panMode": "Pan (drag to move view)",
"clearIgnoredPixels": "Clear all ignored pixels",
"invertMask": "Invert mask",
"waitingSetupComplete": "🔄 Waiting for initial setup to complete...",
"waitingTokenGenerator": "🔄 Waiting for token generator to initialize...",
"uploadImageFirst": "Upload an image first to capture available colors",
"pleaseWaitInitialSetup": "🔄 Please wait for the initial setup to complete before loading progress.",
"pleaseWaitFileSetup": "🔄 Please wait for the initial setup to complete before loading from file.",
"errorSavingProgress": "❌ Error saving progress",
"errorLoadingProgress": "❌ Error loading progress",
"fileOperationsAvailable": "📂 File operations (Load/Upload) are now available!",
"tokenGeneratorReady": "🔑 Token generator ready!",
"paintingStats": "Painting Stats",
"enablePaintingSpeedLimit": "Enable painting speed limit (batch size control)",
"enableNotifications": "Enable notifications",
"notifyOnChargesThreshold": "Notify when charges reach threshold",
"onlyWhenNotFocused": "Only when tab is not focused",
"repeatEvery": "Repeat every",
"minutesPl": "minute(s)",
"grantPermission": "Grant Permission",
"test": "Test",
"showAllColorsIncluding": "Show All Colors (including unavailable)",
"chromaWeight": "Chroma Weight",
"downloadPreview": "Download Preview",
"apply": "Apply",
"cancel": "Cancel",
"fit": "Fit",
"hundred": "100%",
"clear": "Clear",
"invert": "Invert",
"reprocessingOverlay": "Re-processing overlay...",
"overlayUpdated": "Overlay updated!",
"notificationsEnabled": "Notifications enabled.",
"notificationsPermissionDenied": "Notifications permission denied.",
"overlayEnabled": "Overlay enabled.",
"overlayDisabled": "Overlay disabled.",
"tokenSourceSet": "Token source set to: {source}",
"batchModeSet": "Batch mode set to: {mode}",
"randomRange": "Random Range",
"normalFixedSize": "Normal Fixed Size",
"advancedColorSettingsReset": "Advanced color settings reset.",
"shiftRowAltColumn": "Shift = Row • Alt = Column",
"hideTurnstileBtn": "Hide",
"turnstileInstructions": "Cloudflare Turnstile — please complete the check if shown",
"uploadImageFirstColors": "Please upload an image first to capture available colors",
"availableColors": "Available Colors ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Expand Mode",
"minimize": "Minimize",
"restore": "Restore",
"hideStats": "Hide Stats",
"paintOptions": "Paint Options",
"paintWhitePixels": "Paint White",
"paintWhitePixelsDescription": "If enabled, template white pixels will be painted.",
"paintTransparentPixels": "Paint Transparent",
"paintTransparentPixelsDescription": "If enabled, template transparent pixels will be painted",
"paintUnavailablePixels": "Paint Unavailable",
"paintUnavailablePixelsDescription": "If enabled, template colors that are unavailable will be painted using the closest available color"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Auto-Image",
"toggleOverlay": "Basculer l'overlay",
"scanColors": "Scanner les couleurs",
"uploadImage": "Télécharger l'image",
"resizeImage": "Redimensionner l'image",
"selectPosition": "Sélectionner la position",
"startPainting": "Commencer à peindre",
"stopPainting": "Arrêter de peindre",
"checkingColors": "🔍 Vérification des couleurs disponibles...",
"noColorsFound": "❌ Ouvrez la palette de couleurs sur le site et réessayez!",
"colorsFound": "✅ {count} couleurs trouvées. Prêt à télécharger.",
"loadingImage": "🖼️ Chargement de l'image...",
"imageLoaded": "✅ Image chargée avec {count} pixels valides",
"imageError": "❌ Erreur lors du chargement de l'image",
"selectPositionAlert": "Peignez le premier pixel à l'endroit où vous voulez que l'art commence!",
"waitingPosition": "👆 En attente que vous peigniez le pixel de référence...",
"positionSet": "✅ Position définie avec succès!",
"positionTimeout": "❌ Délai d'attente pour la sélection de position",
"startPaintingMsg": "🎨 Début de la peinture...",
"paintingProgress": "🧱 Progrès: {painted}/{total} pixels...",
"noCharges": "⌛ Aucune charge. En attente {time}...",
"paintingStopped": "⏹️ Peinture arrêtée par l'utilisateur",
"paintingComplete": "✅ Peinture terminée! {count} pixels peints.",
"paintingError": "❌ Erreur pendant la peinture",
"missingRequirements": "❌ Veuillez charger une image et sélectionner une position d'abord",
"progress": "Progrès",
"pixels": "Pixels",
"charges": "Charges",
"estimatedTime": "Temps estimé",
"initMessage": "Cliquez sur 'Télécharger l'image' pour commencer",
"waitingInit": "En attente d'initialisation...",
"initializingToken": "🔧 Initialisation du générateur de tokens Turnstile...",
"tokenReady": "✅ Générateur de tokens prêt - vous pouvez commencer à peindre!",
"tokenRetryLater": "⚠️ Le générateur de tokens réessaiera si nécessaire",
"resizeSuccess": "✅ Image redimensionnée en {width}x{height}",
"paintingPaused": "⏸️ Peinture en pause à la position X: {x}, Y: {y}",
"captchaNeeded": "❗ Échec de la génération de token. Veuillez réessayer dans un moment.",
"saveData": "Sauvegarder le progrès",
"loadData": "Charger le progrès",
"saveToFile": "Sauvegarder dans un fichier",
"loadFromFile": "Charger depuis un fichier",
"dataManager": "Données",
"autoSaved": "✅ Progrès sauvegardé automatiquement",
"dataLoaded": "✅ Progrès chargé avec succès",
"fileSaved": "✅ Sauvegardé dans un fichier avec succès",
"fileLoaded": "✅ Chargé depuis un fichier avec succès",
"noSavedData": "❌ Aucun progrès sauvegardé trouvé",
"savedDataFound": "✅ Progrès sauvegardé trouvé! Charger pour continuer?",
"savedDate": "Sauvegardé le: {date}",
"clickLoadToContinue": "Cliquez sur 'Charger le progrès' pour continuer.",
"fileError": "❌ Erreur lors du traitement du fichier",
"invalidFileFormat": "❌ Format de fichier invalide",
"paintingSpeed": "Vitesse de peinture",
"pixelsPerSecond": "pixels/seconde",
"speedSetting": "Vitesse: {speed} pixels/sec",
"settings": "Paramètres",
"botSettings": "Paramètres du Bot",
"close": "Fermer",
"language": "Langue",
"themeSettings": "Paramètres de Thème",
"themeSettingsDesc": "Choisissez votre thème de couleurs préféré pour l'interface.",
"languageSelectDesc": "Sélectionnez votre langue préférée. Les changements prendront effet immédiatement.",
"autoCaptcha": "Résolveur de CAPTCHA automatique (Turnstile)",
"autoCaptchaDesc": "Génère automatiquement des jetons Turnstile en utilisant le générateur intégré. Se replie sur l'automatisation du navigateur si nécessaire.",
"applySettings": "Appliquer les paramètres",
"settingsSaved": "✅ Paramètres enregistrés avec succès !",
"speedOn": "Activé",
"speedOff": "Désactivé",
"cooldownSettings": "Paramètres de recharge",
"waitCharges": "Attendre que les charges atteignent",
"captchaSolving": "🔑 Génération du jeton Turnstile...",
"captchaFailed": "❌ Échec de génération du jeton Turnstile. Tentative de méthode alternative...",
"automation": "Automatisation",
"noChargesThreshold": "⌛ En attente que les charges atteignent {threshold}. Actuel: {current}. Prochaine dans {time}...",
"tokenCapturedSuccess": "Jeton capturé avec succès ! Vous pouvez démarrer le bot maintenant.",
"notificationsNotSupported": "Les notifications ne sont pas supportées dans ce navigateur.",
"chargesReadyNotification": "WPlace — Charges Prêtes",
"chargesReadyMessage": "Charges prêtes : {current} / {max}. Seuil : {threshold}.",
"testNotificationTitle": "WPlace — Test",
"testNotificationMessage": "Ceci est une notification de test.",
"showStats": "Afficher les Stats",
"compactMode": "Mode Compact",
"refreshCharges": "Actualiser les Charges",
"closeStats": "Fermer les Stats",
"zoomOut": "Dézoomer",
"zoomIn": "Zoomer",
"fitToView": "Ajuster à la vue",
"actualSize": "Taille réelle (100%)",
"panMode": "Panoramique (glisser pour déplacer la vue)",
"clearIgnoredPixels": "Effacer tous les pixels ignorés",
"invertMask": "Inverser le masque",
"waitingSetupComplete": "🔄 En attente de la fin de l'installation initiale...",
"waitingTokenGenerator": "🔄 En attente de l'initialisation du générateur de jetons...",
"uploadImageFirst": "Téléchargez d'abord une image pour capturer les couleurs disponibles",
"pleaseWaitInitialSetup": "🔄 Veuillez attendre la fin de l'installation initiale avant de charger les progrès.",
"pleaseWaitFileSetup": "🔄 Veuillez attendre la fin de l'installation initiale avant de charger depuis un fichier.",
"errorSavingProgress": "❌ Erreur lors de la sauvegarde des progrès",
"errorLoadingProgress": "❌ Erreur lors du chargement des progrès",
"fileOperationsAvailable": "📂 Les opérations sur fichiers (Charger/Télécharger) sont maintenant disponibles !",
"tokenGeneratorReady": "🔑 Générateur de jetons prêt !",
"paintingStats": "Statistiques de Peinture",
"enablePaintingSpeedLimit": "Activer la limite de vitesse de peinture (contrôle de la taille de lot)",
"enableNotifications": "Activer les notifications",
"notifyOnChargesThreshold": "Notifier quand les charges atteignent le seuil",
"onlyWhenNotFocused": "Seulement quand l'onglet n'est pas au premier plan",
"repeatEvery": "Répéter toutes les",
"minutesPl": "minute(s)",
"grantPermission": "Accorder la Permission",
"test": "Test",
"showAllColorsIncluding": "Afficher toutes les couleurs (y compris indisponibles)",
"chromaWeight": "Poids de Chrominance",
"downloadPreview": "Télécharger l'Aperçu",
"apply": "Appliquer",
"cancel": "Annuler",
"fit": "Ajuster",
"hundred": "100%",
"clear": "Effacer",
"invert": "Inverser",
"reprocessingOverlay": "Retraitement de l'overlay...",
"overlayUpdated": "Overlay mis à jour !",
"notificationsEnabled": "Notifications activées.",
"notificationsPermissionDenied": "Permission de notifications refusée.",
"overlayEnabled": "Overlay activé.",
"overlayDisabled": "Overlay désactivé.",
"tokenSourceSet": "Source de jeton définie à : {source}",
"batchModeSet": "Mode lot défini à : {mode}",
"randomRange": "Plage Aléatoire",
"normalFixedSize": "Taille Fixe Normale",
"advancedColorSettingsReset": "Paramètres de couleur avancés réinitialisés.",
"shiftRowAltColumn": "Shift = Ligne • Alt = Colonne",
"hideTurnstileBtn": "Masquer",
"turnstileInstructions": "Cloudflare Turnstile — veuillez compléter la vérification si affichée",
"uploadImageFirstColors": "Veuillez d'abord télécharger une image pour capturer les couleurs disponibles",
"availableColors": "Couleurs Disponibles ({count})",
"colorTooltip": "ID : {id}\nRVB : {rgb}",
"expandMode": "Mode Étendu",
"minimize": "Réduire",
"restore": "Restaurer",
"hideStats": "Masquer les Stats",
"paintOptions": "Options de peinture",
"paintWhitePixels": "Peindre les pixels blancs",
"paintTransparentPixels": "Peindre les pixels transparents"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Auto-Image",
"toggleOverlay": "Toggle Overlay",
"scanColors": "Pindai Warna",
"uploadImage": "Unggah Gambar",
"resizeImage": "Ubah Ukuran Gambar",
"selectPosition": "Pilih Posisi",
"startPainting": "Mulai Melukis",
"stopPainting": "Berhenti Melukis",
"checkingColors": "🔍 Memeriksa warna yang tersedia...",
"noColorsFound": "❌ Buka palet warna di situs dan coba lagi!",
"colorsFound": "✅ {count} warna ditemukan. Siap untuk diunggah.",
"loadingImage": "🖼️ Memuat gambar...",
"imageLoaded": "✅ Gambar dimuat dengan {count} piksel valid",
"imageError": "❌ Kesalahan saat memuat gambar",
"selectPositionAlert": "Lukis piksel pertama di lokasi tempat karya seni akan dimulai!",
"waitingPosition": "👆 Menunggu Anda melukis piksel referensi...",
"positionSet": "✅ Posisi berhasil diatur!",
"positionTimeout": "❌ Waktu habis untuk memilih posisi",
"startPaintingMsg": "🎨 Mulai melukis...",
"paintingProgress": "🧱 Progres: {painted}/{total} piksel...",
"noCharges": "⌛ Tidak ada muatan. Menunggu {time}...",
"paintingStopped": "⏹️ Melukis dihentikan oleh pengguna",
"paintingComplete": "✅ Melukis selesai! {count} piksel telah dilukis.",
"paintingError": "❌ Kesalahan selama melukis",
"missingRequirements": "❌ Unggah gambar dan pilih posisi terlebih dahulu",
"progress": "Progres",
"pixels": "Piksel",
"charges": "Muatan",
"estimatedTime": "Perkiraan waktu",
"initMessage": "Klik 'Unggah Gambar' untuk memulai",
"waitingInit": "Menunggu inisialisasi...",
"initializingToken": "🔧 Menginisialisasi generator token Turnstile...",
"tokenReady": "✅ Generator token siap - Anda bisa mulai melukis!",
"tokenRetryLater": "⚠️ Generator token akan mencoba lagi saat diperlukan",
"resizeSuccess": "✅ Gambar berhasil diubah ukurannya menjadi {width}x{height}",
"paintingPaused": "⏸️ Melukis dijeda di posisi X: {x}, Y: {y}",
"captchaNeeded": "❗ Pembuatan token gagal. Silakan coba lagi sebentar lagi.",
"saveData": "Simpan Progres",
"loadData": "Muat Progres",
"saveToFile": "Simpan ke File",
"loadFromFile": "Muat dari File",
"dataManager": "Data",
"autoSaved": "✅ Progres disimpan secara otomatis",
"dataLoaded": "✅ Progres berhasil dimuat",
"fileSaved": "✅ Berhasil disimpan ke file",
"fileLoaded": "✅ Berhasil dimuat dari file",
"noSavedData": "❌ Tidak ditemukan progres yang disimpan",
"savedDataFound": "✅ Progres yang disimpan ditemukan! Muat untuk melanjutkan?",
"savedDate": "Disimpan pada: {date}",
"clickLoadToContinue": "Klik 'Muat Progres' untuk melanjutkan.",
"fileError": "❌ Kesalahan saat memproses file",
"invalidFileFormat": "❌ Format file tidak valid",
"paintingSpeed": "Kecepatan Melukis",
"pixelsPerSecond": "piksel/detik",
"speedSetting": "Kecepatan: {speed} piksel/detik",
"settings": "Pengaturan",
"botSettings": "Pengaturan Bot",
"close": "Tutup",
"language": "Bahasa",
"themeSettings": "Pengaturan Tema",
"themeSettingsDesc": "Pilih tema warna favorit Anda untuk antarmuka.",
"languageSelectDesc": "Pilih bahasa yang Anda inginkan. Perubahan akan berlaku segera.",
"autoCaptcha": "Penyelesai CAPTCHA Otomatis",
"autoCaptchaDesc": "Mencoba menyelesaikan CAPTCHA secara otomatis dengan mensimulasikan penempatan piksel manual saat token kedaluwarsa.",
"applySettings": "Terapkan Pengaturan",
"settingsSaved": "✅ Pengaturan berhasil disimpan!",
"speedOn": "Nyala",
"speedOff": "Mati",
"cooldownSettings": "Pengaturan Cooldown",
"waitCharges": "Tunggu hingga muatan mencapai",
"captchaSolving": "🤖 Mencoba menyelesaikan CAPTCHA...",
"captchaFailed": "❌ Gagal menyelesaikan CAPTCHA. Lukis satu piksel secara manual.",
"automation": "Automasi",
"noChargesThreshold": "⌛ Menunggu muatan mencapai {threshold}. Saat ini: {current}. Berikutnya dalam {time}...",
"tokenCapturedSuccess": "Token berhasil ditangkap! Anda bisa memulai bot sekarang.",
"notificationsNotSupported": "Notifikasi tidak didukung di browser ini.",
"chargesReadyNotification": "WPlace — Muatan Siap",
"chargesReadyMessage": "Muatan siap: {current} / {max}. Batas: {threshold}.",
"testNotificationTitle": "WPlace — Tes",
"testNotificationMessage": "Ini adalah notifikasi tes.",
"showStats": "Tampilkan Statistik",
"compactMode": "Mode Kompak",
"refreshCharges": "Segarkan Muatan",
"closeStats": "Tutup Statistik",
"zoomOut": "Perkecil",
"zoomIn": "Perbesar",
"fitToView": "Sesuaikan tampilan",
"actualSize": "Ukuran sebenarnya (100%)",
"panMode": "Geser (seret untuk memindahkan tampilan)",
"clearIgnoredPixels": "Bersihkan semua piksel yang diabaikan",
"invertMask": "Balik masker",
"waitingSetupComplete": "🔄 Menunggu pengaturan awal selesai...",
"waitingTokenGenerator": "🔄 Menunggu generator token diinisialisasi...",
"uploadImageFirst": "Unggah gambar terlebih dahulu untuk menangkap warna yang tersedia",
"pleaseWaitInitialSetup": "🔄 Harap tunggu pengaturan awal selesai sebelum memuat progres.",
"pleaseWaitFileSetup": "🔄 Harap tunggu pengaturan awal selesai sebelum memuat dari file.",
"errorSavingProgress": "❌ Kesalahan menyimpan progres",
"errorLoadingProgress": "❌ Kesalahan memuat progres",
"fileOperationsAvailable": "📂 Operasi file (Muat/Unggah) sekarang tersedia!",
"tokenGeneratorReady": "🔑 Generator token siap!",
"paintingStats": "Statistik Melukis",
"enablePaintingSpeedLimit": "Aktifkan batas kecepatan melukis (kontrol ukuran batch)",
"enableNotifications": "Aktifkan notifikasi",
"notifyOnChargesThreshold": "Beri tahu saat muatan mencapai batas",
"onlyWhenNotFocused": "Hanya saat tab tidak difokuskan",
"repeatEvery": "Ulangi setiap",
"minutesPl": "menit",
"grantPermission": "Berikan Izin",
"test": "Tes",
"showAllColorsIncluding": "Tampilkan Semua Warna (termasuk yang tidak tersedia)",
"chromaWeight": "Bobot Kroma",
"downloadPreview": "Unduh Pratinjau",
"apply": "Terapkan",
"cancel": "Batal",
"fit": "Sesuaikan",
"hundred": "100%",
"clear": "Bersihkan",
"invert": "Balik",
"reprocessingOverlay": "Memproses ulang overlay...",
"overlayUpdated": "Overlay diperbarui!",
"notificationsEnabled": "Notifikasi diaktifkan.",
"notificationsPermissionDenied": "Izin notifikasi ditolak.",
"overlayEnabled": "Overlay diaktifkan.",
"overlayDisabled": "Overlay dinonaktifkan.",
"tokenSourceSet": "Sumber token diatur ke: {source}",
"batchModeSet": "Mode batch diatur ke: {mode}",
"randomRange": "Rentang Acak",
"normalFixedSize": "Ukuran Tetap Normal",
"advancedColorSettingsReset": "Pengaturan warna lanjutan direset.",
"shiftRowAltColumn": "Shift = Baris • Alt = Kolom",
"hideTurnstileBtn": "Sembunyikan",
"turnstileInstructions": "Cloudflare Turnstile — harap selesaikan pemeriksaan jika ditampilkan",
"uploadImageFirstColors": "Harap unggah gambar terlebih dahulu untuk menangkap warna yang tersedia",
"availableColors": "Warna Tersedia ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Mode Perluas",
"minimize": "Minimalkan",
"restore": "Pulihkan",
"hideStats": "Sembunyikan Statistik",
"paintOptions": "Opsi Pewarnaan",
"paintWhitePixels": "Warnai piksel putih",
"paintTransparentPixels": "Warnai piksel transparan"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace 自動画像",
"toggleOverlay": "オーバーレイ切替",
"scanColors": "色をスキャン",
"uploadImage": "画像をアップロード",
"resizeImage": "画像サイズ変更",
"selectPosition": "位置を選択",
"startPainting": "描画開始",
"stopPainting": "描画停止",
"checkingColors": "🔍 利用可能な色を確認中...",
"noColorsFound": "❌ サイトでカラーパレットを開いて再試行してください!",
"colorsFound": "✅ 利用可能な色 {count} 件を検出。アップロード可能。",
"loadingImage": "🖼️ 画像を読み込み中...",
"imageLoaded": "✅ 画像を読み込みました。有効なピクセル {count}",
"imageError": "❌ 画像の読み込みエラー",
"selectPositionAlert": "作品を開始したい位置に最初のピクセルを置いてください!",
"waitingPosition": "👆 参照ピクセルの描画を待っています...",
"positionSet": "✅ 位置を設定しました!",
"positionTimeout": "❌ 位置選択のタイムアウト",
"startPaintingMsg": "🎨 描画を開始...",
"paintingProgress": "🧱 進捗: {painted}/{total} ピクセル...",
"noCharges": "⌛ チャージなし。{time} 待機...",
"paintingStopped": "⏹️ ユーザーにより停止されました",
"paintingComplete": "✅ 描画完了! {count} ピクセル描画。",
"paintingError": "❌ 描画中にエラー",
"missingRequirements": "❌ 先に画像を読み込み位置を選択してください",
"progress": "進捗",
"pixels": "ピクセル",
"charges": "チャージ",
"estimatedTime": "推定時間",
"initMessage": "「画像をアップロード」をクリックして開始",
"waitingInit": "初期化待機中...",
"initializingToken": "🔧 Turnstile トークン生成器を初期化中...",
"tokenReady": "✅ トークン生成器準備完了 - 描画できます!",
"tokenRetryLater": "⚠️ 必要に応じて再試行します",
"resizeSuccess": "✅ 画像を {width}x{height} にリサイズ",
"paintingPaused": "⏸️ X: {x}, Y: {y} で一時停止",
"captchaNeeded": "❗ トークン生成に失敗。少ししてから再試行してください。",
"saveData": "進捗を保存",
"loadData": "進捗を読み込み",
"saveToFile": "ファイルへ保存",
"loadFromFile": "ファイルから読み込み",
"dataManager": "データ管理",
"autoSaved": "✅ 自動保存しました",
"dataLoaded": "✅ 進捗を読み込みました",
"fileSaved": "✅ ファイルに保存しました",
"fileLoaded": "✅ ファイルから読み込みました",
"noSavedData": "❌ 保存された進捗がありません",
"savedDataFound": "✅ 保存された進捗が見つかりました。続行しますか?",
"savedDate": "保存日時: {date}",
"clickLoadToContinue": "「進捗を読み込み」をクリックして続行。",
"fileError": "❌ ファイル処理エラー",
"invalidFileFormat": "❌ 無効なファイル形式",
"paintingSpeed": "描画速度",
"pixelsPerSecond": "ピクセル/秒",
"speedSetting": "速度: {speed} ピクセル/秒",
"settings": "設定",
"botSettings": "ボット設定",
"close": "閉じる",
"language": "言語",
"themeSettings": "テーマ設定",
"themeSettingsDesc": "インターフェースの好きなカラーテーマを選択。",
"languageSelectDesc": "希望言語を選択。変更は即時反映されます。",
"autoCaptcha": "自動 CAPTCHA ソルバー",
"autoCaptchaDesc": "統合ジェネレーターで Turnstile トークンを自動生成し必要に応じてブラウザ自動化にフォールバック。",
"applySettings": "設定を適用",
"settingsSaved": "✅ 設定を保存しました!",
"speedOn": "オン",
"speedOff": "オフ",
"cooldownSettings": "クールダウン設定",
"waitCharges": "チャージ数が次に達するまで待機",
"captchaSolving": "🔑 Turnstile トークン生成中...",
"captchaFailed": "❌ トークン生成失敗。フォールバックを試行...",
"automation": "自動化",
"noChargesThreshold": "⌛ チャージ {threshold} を待機中。現在 {current}。次は {time} 後...",
"tokenCapturedSuccess": "トークンキャプチャ成功!ボットを開始できます。",
"notificationsNotSupported": "このブラウザでは通知がサポートされていません。",
"chargesReadyNotification": "WPlace — チャージ準備完了",
"chargesReadyMessage": "チャージ準備完了: {current} / {max}。しきい値: {threshold}。",
"testNotificationTitle": "WPlace — テスト",
"testNotificationMessage": "これはテスト通知です。",
"showStats": "統計表示",
"compactMode": "コンパクトモード",
"refreshCharges": "チャージ更新",
"closeStats": "統計を閉じる",
"zoomOut": "縮小",
"zoomIn": "拡大",
"fitToView": "画面に合わせる",
"actualSize": "実際のサイズ (100%)",
"panMode": "パン(ドラッグで移動)",
"clearIgnoredPixels": "無視されたピクセルをすべてクリア",
"invertMask": "マスクを反転",
"waitingSetupComplete": "🔄 初期セットアップの完了を待機中...",
"waitingTokenGenerator": "🔄 トークンジェネレータの初期化を待機中...",
"uploadImageFirst": "利用可能な色を取得するために最初に画像をアップロードしてください",
"pleaseWaitInitialSetup": "🔄 進捗を読み込む前に初期セットアップの完了をお待ちください。",
"pleaseWaitFileSetup": "🔄 ファイルから読み込む前に初期セットアップの完了をお待ちください。",
"errorSavingProgress": "❌ 進捗の保存エラー",
"errorLoadingProgress": "❌ 進捗の読み込みエラー",
"fileOperationsAvailable": "📂 ファイル操作(読み込み/アップロード)が利用可能になりました!",
"tokenGeneratorReady": "🔑 トークンジェネレータ準備完了!",
"paintingStats": "描画統計",
"enablePaintingSpeedLimit": "描画速度制限を有効化(バッチサイズ制御)",
"enableNotifications": "通知を有効化",
"notifyOnChargesThreshold": "チャージがしきい値に達したら通知",
"onlyWhenNotFocused": "タブが非アクティブの時のみ",
"repeatEvery": "繰り返し間隔",
"minutesPl": "分",
"grantPermission": "許可を与える",
"test": "テスト",
"showAllColorsIncluding": "すべての色を表示(利用不可含む)",
"chromaWeight": "彩度重み",
"downloadPreview": "プレビューダウンロード",
"apply": "適用",
"cancel": "キャンセル",
"fit": "フィット",
"hundred": "100%",
"clear": "クリア",
"invert": "反転",
"reprocessingOverlay": "オーバーレイ再処理中...",
"overlayUpdated": "オーバーレイ更新完了!",
"notificationsEnabled": "通知が有効になりました。",
"notificationsPermissionDenied": "通知の許可が拒否されました。",
"overlayEnabled": "オーバーレイが有効になりました。",
"overlayDisabled": "オーバーレイが無効になりました。",
"tokenSourceSet": "トークンソースを設定: {source}",
"batchModeSet": "バッチモードを設定: {mode}",
"randomRange": "ランダム範囲",
"normalFixedSize": "通常固定サイズ",
"advancedColorSettingsReset": "高度な色設定をリセットしました。",
"shiftRowAltColumn": "Shift = 行 • Alt = 列",
"hideTurnstileBtn": "隠す",
"turnstileInstructions": "Cloudflare Turnstile — 表示された場合は確認を完了してください",
"uploadImageFirstColors": "利用可能な色を取得するために最初に画像をアップロードしてください",
"availableColors": "利用可能な色 ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "展開モード",
"minimize": "最小化",
"restore": "復元",
"hideStats": "統計を非表示",
"paintOptions": "描画オプション",
"paintWhitePixels": "白いピクセルを描画",
"paintTransparentPixels": "透明ピクセルを描画"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace 자동 이미지",
"toggleOverlay": "오버레이 전환",
"scanColors": "색상 스캔",
"uploadImage": "이미지 업로드",
"resizeImage": "크기 조정",
"selectPosition": "위치 선택",
"startPainting": "그리기 시작",
"stopPainting": "그리기 중지",
"checkingColors": "🔍 사용 가능한 색상 확인 중...",
"noColorsFound": "❌ 사이트에서 색상 팔레트를 연 후 다시 시도하세요!",
"colorsFound": "✅ 사용 가능한 색상 {count}개 발견. 업로드 준비 완료.",
"loadingImage": "🖼️ 이미지 불러오는 중...",
"imageLoaded": "✅ 이미지 로드 완료. 유효 픽셀 {count}개",
"imageError": "❌ 이미지 로드 오류",
"selectPositionAlert": "작품을 시작할 위치에 첫 픽셀을 칠하세요!",
"waitingPosition": "👆 기준 픽셀을 칠할 때까지 대기 중...",
"positionSet": "✅ 위치 설정 완료!",
"positionTimeout": "❌ 위치 선택 시간 초과",
"startPaintingMsg": "🎨 그리기 시작...",
"paintingProgress": "🧱 진행: {painted}/{total} 픽셀...",
"noCharges": "⌛ 사용 가능 횟수 없음. {time} 대기...",
"paintingStopped": "⏹️ 사용자에 의해 중지됨",
"paintingComplete": "✅ 그리기 완료! {count} 픽셀 그렸습니다.",
"paintingError": "❌ 그리는 중 오류",
"missingRequirements": "❌ 먼저 이미지를 불러오고 위치를 선택하세요",
"progress": "진행",
"pixels": "픽셀",
"charges": "횟수",
"estimatedTime": "예상 시간",
"initMessage": "'이미지 업로드'를 클릭하여 시작",
"waitingInit": "초기화 대기 중...",
"initializingToken": "🔧 Turnstile 토큰 생성기 초기화 중...",
"tokenReady": "✅ 토큰 생성 준비 완료 - 그리기를 시작할 수 있습니다!",
"tokenRetryLater": "⚠️ 필요 시 다시 시도합니다",
"resizeSuccess": "✅ 이미지가 {width}x{height} 크기로 조정됨",
"paintingPaused": "⏸️ 위치 X: {x}, Y: {y} 에서 일시 중지",
"captchaNeeded": "❗ 토큰 생성 실패. 잠시 후 다시 시도하세요.",
"saveData": "진행 저장",
"loadData": "진행 불러오기",
"saveToFile": "파일로 저장",
"loadFromFile": "파일에서 불러오기",
"dataManager": "데이터",
"autoSaved": "✅ 진행 자동 저장됨",
"dataLoaded": "✅ 진행 불러오기 성공",
"fileSaved": "✅ 파일 저장 성공",
"fileLoaded": "✅ 파일 불러오기 성공",
"noSavedData": "❌ 저장된 진행 없음",
"savedDataFound": "✅ 저장된 진행 발견! 계속하려면 불러오시겠습니까?",
"savedDate": "저장 시각: {date}",
"clickLoadToContinue": "'진행 불러오기'를 클릭하여 계속.",
"fileError": "❌ 파일 처리 오류",
"invalidFileFormat": "❌ 잘못된 파일 형식",
"paintingSpeed": "그리기 속도",
"pixelsPerSecond": "픽셀/초",
"speedSetting": "속도: {speed} 픽셀/초",
"settings": "설정",
"botSettings": "봇 설정",
"close": "닫기",
"language": "언어",
"themeSettings": "테마 설정",
"themeSettingsDesc": "인터페이스용 선호 색상 테마를 선택하세요.",
"languageSelectDesc": "선호 언어를 선택하세요. 변경 사항은 즉시 적용됩니다.",
"autoCaptcha": "자동 CAPTCHA 해결",
"autoCaptchaDesc": "통합 생성기를 사용해 Turnstile 토큰을 자동 생성하고 필요 시 브라우저 자동화로 폴백.",
"applySettings": "설정 적용",
"settingsSaved": "✅ 설정 저장 완료!",
"speedOn": "켜짐",
"speedOff": "꺼짐",
"cooldownSettings": "쿨다운 설정",
"waitCharges": "횟수가 다음 값에 도달할 때까지 대기",
"captchaSolving": "🔑 Turnstile 토큰 생성 중...",
"captchaFailed": "❌ 토큰 생성 실패. 폴백 시도...",
"automation": "자동화",
"noChargesThreshold": "⌛ 횟수가 {threshold} 에 도달할 때까지 대기 중. 현재 {current}. 다음 {time} 후...",
"tokenCapturedSuccess": "토큰 생성 성공! 이제 봇을 시작할 수 있습니다.",
"notificationsNotSupported": "이 브라우저는 알림을 지원하지 않습니다.",
"chargesReadyNotification": "WPlace — 횟수 준비 완료",
"chargesReadyMessage": "횟수 준비 완료: {current} / {max}. 임계값: {threshold}.",
"testNotificationTitle": "WPlace — 테스트",
"testNotificationMessage": "이것은 테스트 알림입니다.",
"showStats": "통계 보기",
"compactMode": "컴팩트 모드",
"refreshCharges": "횟수 새로고침",
"closeStats": "통계 닫기",
"zoomOut": "축소",
"zoomIn": "확대",
"fitToView": "화면에 맞춤",
"actualSize": "실제 크기 (100%)",
"panMode": "이동 (끌어서 보기 이동)",
"clearIgnoredPixels": "무시된 모든 픽셀 지우기",
"invertMask": "마스크 반전",
"waitingSetupComplete": "🔄 초기 설정 완료 대기 중...",
"waitingTokenGenerator": "🔄 토큰 생성기 초기화 대기 중...",
"uploadImageFirst": "사용 가능한 색상을 얻기 위해 먼저 이미지를 업로드하세요",
"pleaseWaitInitialSetup": "🔄 진행도를 로드하기 전에 초기 설정 완료를 기다리세요.",
"pleaseWaitFileSetup": "🔄 파일에서 로드하기 전에 초기 설정 완료를 기다리세요.",
"errorSavingProgress": "❌ 진행 저장 오류",
"errorLoadingProgress": "❌ 진행 로드 오류",
"fileOperationsAvailable": "📂 파일 작업(로드/업로드)이 이제 사용 가능합니다!",
"tokenGeneratorReady": "🔑 토큰 생성기 준비 완료!",
"paintingStats": "그리기 통계",
"enablePaintingSpeedLimit": "그리기 속도 제한 활성화 (배치 크기 제어)",
"enableNotifications": "알림 활성화",
"notifyOnChargesThreshold": "횟수가 임계값에 도달하면 알림",
"onlyWhenNotFocused": "탭이 포커스되지 않았을 때만",
"repeatEvery": "반복 간격",
"minutesPl": "분",
"grantPermission": "권한 부여",
"test": "테스트",
"showAllColorsIncluding": "모든 색상 보기 (사용 불가 포함)",
"chromaWeight": "색도 가중치",
"downloadPreview": "미리보기 다운로드",
"apply": "적용",
"cancel": "취소",
"fit": "맞춤",
"hundred": "100%",
"clear": "지우기",
"invert": "반전",
"reprocessingOverlay": "오버레이 재처리 중...",
"overlayUpdated": "오버레이 업데이트 완료!",
"notificationsEnabled": "알림이 활성화되었습니다.",
"notificationsPermissionDenied": "알림 권한이 거부되었습니다.",
"overlayEnabled": "오버레이가 활성화되었습니다.",
"overlayDisabled": "오버레이가 비활성화되었습니다.",
"tokenSourceSet": "토큰 소스 설정: {source}",
"batchModeSet": "배치 모드 설정: {mode}",
"randomRange": "랜덤 범위",
"normalFixedSize": "일반 고정 크기",
"advancedColorSettingsReset": "고급 색상 설정이 초기화되었습니다.",
"shiftRowAltColumn": "Shift = 행 • Alt = 열",
"hideTurnstileBtn": "숨기기",
"turnstileInstructions": "Cloudflare Turnstile — 표시되면 검사를 완료하세요",
"uploadImageFirstColors": "사용 가능한 색상을 얻기 위해 먼저 이미지를 업로드하세요",
"availableColors": "사용 가능한 색상 ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "확장 모드",
"minimize": "최소화",
"restore": "복원",
"hideStats": "통계 숨김",
"paintOptions": "페인트 옵션",
"paintWhitePixels": "흰색 픽셀 칠하기",
"paintTransparentPixels": "투명 픽셀 칠하기"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Auto-Imagen",
"toggleOverlay": "Alternar superposición",
"scanColors": "Escanear colores",
"uploadImage": "Subir imagen",
"resizeImage": "Cambiar tamaño de imagen",
"selectPosition": "Seleccionar posición",
"startPainting": "Iniciar pintado",
"stopPainting": "Detener pintado",
"checkingColors": "🔍 Verificando colores disponibles...",
"noColorsFound": "❌ ¡Abre la paleta de colores en el sitio e inténtalo de nuevo!",
"colorsFound": "✅ Se encontraron {count} colores disponibles. Listo para subir.",
"loadingImage": "🖼️ Cargando imagen...",
"imageLoaded": "✅ Imagen cargada con {count} píxeles válidos",
"imageError": "❌ Error al cargar la imagen",
"selectPositionAlert": "¡Pinta el primer píxel en la ubicación donde quieres que comience el arte!",
"waitingPosition": "👆 Esperando que pintes el píxel de referencia...",
"positionSet": "✅ ¡Posición establecida con éxito!",
"positionTimeout": "❌ Tiempo de espera para seleccionar la posición agotado",
"startPaintingMsg": "🎨 Iniciando pintado...",
"paintingProgress": "🧱 Progreso: {painted}/{total} píxeles...",
"noCharges": "⌛ Sin cargas. Esperando {time}...",
"paintingStopped": "⏹️ Pintado detenido por el usuario",
"paintingComplete": "✅ ¡Pintado completo! Se pintaron {count} píxeles.",
"paintingError": "❌ Error durante el pintado",
"missingRequirements": "❌ Primero carga una imagen y selecciona una posición",
"progress": "Progreso",
"pixels": "Píxeles",
"charges": "Cargas",
"estimatedTime": "Tiempo estimado",
"initMessage": "Haz clic en 'Subir imagen' para comenzar",
"waitingInit": "Esperando inicialización...",
"initializingToken": "🔧 Inicializando generador de token Turnstile...",
"tokenReady": "✅ ¡Generador de token listo! Ya puedes empezar a pintar.",
"tokenRetryLater": "⚠️ El generador de token volverá a intentarlo cuando sea necesario",
"resizeSuccess": "✅ Imagen redimensionada a {width}x{height}",
"paintingPaused": "⏸️ Pintado pausado en la posición X: {x}, Y: {y}",
"captchaNeeded": "❗ Falló la generación de token. Por favor, inténtalo de nuevo en un momento.",
"saveData": "Guardar progreso",
"loadData": "Cargar progreso",
"saveToFile": "Guardar en archivo",
"loadFromFile": "Cargar desde archivo",
"dataManager": "Gestor de datos",
"autoSaved": "✅ Progreso guardado automáticamente",
"dataLoaded": "✅ Progreso cargado con éxito",
"fileSaved": "✅ Progreso guardado en archivo con éxito",
"fileLoaded": "✅ Progreso cargado desde archivo con éxito",
"noSavedData": "❌ No se encontró progreso guardado",
"savedDataFound": "✅ ¡Se encontró progreso guardado! ¿Cargar para continuar?",
"savedDate": "Guardado el: {date}",
"clickLoadToContinue": "Haz clic en 'Cargar progreso' para continuar.",
"fileError": "❌ Error al procesar el archivo",
"invalidFileFormat": "❌ Formato de archivo inválido",
"paintingSpeed": "Velocidad de pintado",
"pixelsPerSecond": "píxeles/segundo",
"speedSetting": "Velocidad: {speed} píxeles/seg",
"settings": "Ajustes",
"botSettings": "Ajustes del bot",
"close": "Cerrar",
"language": "Idioma",
"themeSettings": "Ajustes de tema",
"themeSettingsDesc": "Elige tu tema de color preferido para la interfaz.",
"languageSelectDesc": "Selecciona tu idioma preferido. Los cambios se aplicarán inmediatamente.",
"autoCaptcha": "Solucionador automático de CAPTCHA (Turnstile)",
"autoCaptchaDesc": "Genera automáticamente tokens de Turnstile usando el generador integrado. Utiliza la automatización del navegador como alternativa si es necesario.",
"applySettings": "Aplicar ajustes",
"settingsSaved": "✅ ¡Ajustes guardados con éxito!",
"speedOn": "Activado",
"speedOff": "Desactivado",
"cooldownSettings": "Ajustes de tiempo de espera",
"waitCharges": "Esperar hasta que las cargas lleguen a",
"captchaSolving": "🔑 Generando token de Turnstile...",
"captchaFailed": "❌ Falló la generación de token de Turnstile. Probando método alternativo...",
"automation": "Automatización",
"noChargesThreshold": "⌛ Esperando que las cargas lleguen a {threshold}. Actual: {current}. Siguiente en {time}...",
"tokenCapturedSuccess": "¡Token capturado con éxito! Ya puedes iniciar el bot.",
"notificationsNotSupported": "Las notificaciones no son compatibles con este navegador.",
"chargesReadyNotification": "WPlace — Cargas listas",
"chargesReadyMessage": "Cargas listas: {current} / {max}. Umbral: {threshold}.",
"testNotificationTitle": "WPlace — Prueba",
"testNotificationMessage": "Esta es una notificación de prueba.",
"showStats": "Mostrar estadísticas",
"compactMode": "Modo compacto",
"refreshCharges": "Actualizar cargas",
"closeStats": "Cerrar estadísticas",
"zoomOut": "Alejar",
"zoomIn": "Acercar",
"fitToView": "Ajustar a la vista",
"actualSize": "Tamaño real (100%)",
"panMode": "Desplazamiento (arrastra para mover la vista)",
"clearIgnoredPixels": "Limpiar todos los píxeles ignorados",
"invertMask": "Invertir máscara",
"waitingSetupComplete": "🔄 Esperando que se complete la configuración inicial...",
"waitingTokenGenerator": "🔄 Esperando que se inicialice el generador de tokens...",
"uploadImageFirst": "Sube una imagen primero para capturar los colores disponibles",
"pleaseWaitInitialSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar el progreso.",
"pleaseWaitFileSetup": "🔄 Por favor, espera a que se complete la configuración inicial antes de cargar desde un archivo.",
"errorSavingProgress": "❌ Error al guardar el progreso",
"errorLoadingProgress": "❌ Error al cargar el progreso",
"fileOperationsAvailable": "📂 ¡Las operaciones de archivo (Cargar/Subir) ya están disponibles!",
"tokenGeneratorReady": "🔑 ¡Generador de tokens listo!",
"paintingStats": "Estadísticas de pintado",
"enablePaintingSpeedLimit": "Activar límite de velocidad de pintado (control de lotes)",
"enableNotifications": "Activar notificaciones",
"notifyOnChargesThreshold": "Notificar cuando las cargas alcancen el umbral",
"onlyWhenNotFocused": "Solo cuando la pestaña no esté en foco",
"repeatEvery": "Repetir cada",
"minutesPl": "minuto(s)",
"grantPermission": "Otorgar permiso",
"test": "Probar",
"showAllColorsIncluding": "Mostrar todos los colores (incluidos los no disponibles)",
"chromaWeight": "Peso de croma",
"downloadPreview": "Descargar vista previa",
"apply": "Aplicar",
"cancel": "Cancelar",
"fit": "Ajustar",
"hundred": "100%",
"clear": "Limpiar",
"invert": "Invertir",
"reprocessingOverlay": "Reprocesando superposición...",
"overlayUpdated": "¡Superposición actualizada!",
"notificationsEnabled": "Notificaciones activadas.",
"notificationsPermissionDenied": "Permiso de notificaciones denegado.",
"overlayEnabled": "Superposición activada.",
"overlayDisabled": "Superposición desactivada.",
"tokenSourceSet": "Fuente de token establecida en: {source}",
"batchModeSet": "Modo por lotes establecido en: {mode}",
"randomRange": "Rango aleatorio",
"normalFixedSize": "Tamaño fijo normal",
"advancedColorSettingsReset": "Ajustes de color avanzados restablecidos.",
"shiftRowAltColumn": "Shift = Fila • Alt = Columna",
"hideTurnstileBtn": "Ocultar",
"turnstileInstructions": "Cloudflare Turnstile — por favor, completa la verificación si se muestra",
"uploadImageFirstColors": "Por favor, sube una imagen primero para capturar los colores disponibles",
"availableColors": "Colores disponibles ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Modo expandido",
"minimize": "Minimizar",
"restore": "Restaurar",
"hideStats": "Ocultar estadísticas",
"paintOptions": "Opciones de pintado",
"paintWhitePixels": "Pintar píxeles blancos",
"paintTransparentPixels": "Pintar píxeles transparentes"
}
+143
View File
@@ -0,0 +1,143 @@
{
"title": "WPlace Auto-Image",
"toggleOverlay": "Toggle Overlay",
"scanColors": "Escanear Cores",
"uploadImage": "Upload da Imagem",
"resizeImage": "Redimensionar Imagem",
"selectPosition": "Selecionar Posição",
"startPainting": "Iniciar Pintura",
"stopPainting": "Parar Pintura",
"checkingColors": "🔍 Verificando cores disponíveis...",
"noColorsFound": "❌ Abra a paleta de cores no site e tente novamente!",
"colorsFound": "✅ {count} cores encontradas. Pronto para upload.",
"loadingImage": "🖼️ Carregando imagem...",
"imageLoaded": "✅ Imagem carregada com {count} pixels válidos",
"imageError": "❌ Erro ao carregar imagem",
"selectPositionAlert": "Pinte o primeiro pixel на localização onde deseja que a arte comece!",
"waitingPosition": "👆 Aguardando você pintar o pixel de referência...",
"positionSet": "✅ Posição definida com sucesso!",
"positionTimeout": "❌ Tempo esgotado para selecionar posição",
"startPaintingMsg": "🎨 Iniciando pintura...",
"paintingProgress": "🧱 Progresso: {painted}/{total} pixels...",
"noCharges": "⌛ Sem cargas. Aguardando {time}...",
"paintingStopped": "⏹️ Pintura interromпида pelo usuário",
"paintingComplete": "✅ Pintura concluída! {count} pixels pintados.",
"paintingError": "❌ Erro durante a pintura",
"missingRequirements": "❌ Carregue uma imagem e selecione uma posição primeiro",
"progress": "Progresso",
"pixels": "Pixels",
"charges": "Cargas",
"estimatedTime": "Tempo estimado",
"initMessage": "Clique em 'Upload da Imagem' para começar",
"waitingInit": "Aguardando inicialização...",
"initializingToken": "🔧 Inicializando gerador de tokens Turnstile...",
"tokenReady": "✅ Gerador de tokens pronto - você pode começar a pintar!",
"tokenRetryLater": "⚠️ Gerador de tokens tentará novamente quando necessário",
"resizeSuccess": "✅ Imagem redimensionada для {width}x{height}",
"paintingPaused": "⏸️ Pintura pausada na posição X: {x}, Y: {y}",
"captchaNeeded": "❗ Falha na geração de token. Tente novamente em alguns instantes.",
"saveData": "Salvar Progresso",
"loadData": "Carregar Progresso",
"saveToFile": "Salvar em Arquivo",
"loadFromFile": "Carregar de Arquivo",
"dataManager": "Dados",
"autoSaved": "✅ Progresso salvo automaticamente",
"dataLoaded": "✅ Progresso carregado com sucesso",
"fileSaved": "✅ Salvo em arquivo com sucesso",
"fileLoaded": "✅ Carregado de arquivo com sucesso",
"noSavedData": "❌ Nenhum progresso salvo encontrado",
"savedDataFound": "✅ Progresso salvo encontrado! Carregar para continuar?",
"savedDate": "Salvo em: {date}",
"clickLoadToContinue": "Clique em 'Carregar Progresso' para continuar.",
"fileError": "❌ Erro ao processar arquivo",
"invalidFileFormat": "❌ Formato de arquivo inválido",
"paintingSpeed": "Velocidade de Pintura",
"pixelsPerSecond": "pixels/segundo",
"speedSetting": "Velocidade: {speed} pixels/seg",
"settings": "Configurações",
"botSettings": "Configurações do Bot",
"close": "Fechar",
"language": "Idioma",
"themeSettings": "Configurações de Tema",
"themeSettingsDesc": "Escolha seu tema de cores preferido para a interface.",
"languageSelectDesc": "Selecione seu idioma preferido. As alterações terão efeito imediatamente.",
"autoCaptcha": "Resolvedor de CAPTCHA Automático",
"autoCaptchaDesc": "Tenta resolver o CAPTCHA automaticamente simulando a colocação manual de um pixel quando o token expira.",
"applySettings": "Aplicar Configurações",
"settingsSaved": "✅ Configurações salvas com sucesso!",
"speedOn": "Ligado",
"speedOff": "Desligado",
"cooldownSettings": "Configurações de Cooldown",
"waitCharges": "Aguardar até as cargas atingirem",
"captchaSolving": "🤖 Tentando resolver o CAPTCHA...",
"captchaFailed": "❌ Falha ao resolver CAPTCHA. Pinte um pixel manualmente.",
"automation": "Automação", "noChargesThreshold": "⌛ Aguardando cargas atingirem {threshold}. Atual: {current}. Próxima em {time}...",
"tokenCapturedSuccess": "Token capturado com sucesso! Você pode iniciar o bot agora.",
"notificationsNotSupported": "Notificações não são suportadas neste navegador.",
"chargesReadyNotification": "WPlace — Cargas Prontas",
"chargesReadyMessage": "Cargas prontas: {current} / {max}. Limite: {threshold}.",
"testNotificationTitle": "WPlace — Teste",
"testNotificationMessage": "Esta é uma notificação de teste.",
"showStats": "Mostrar Estatísticas",
"compactMode": "Modo Compacto",
"refreshCharges": "Atualizar Cargas",
"closeStats": "Fechar Estatísticas",
"zoomOut": "Diminuir Zoom",
"zoomIn": "Aumentar Zoom",
"fitToView": "Ajustar à visualização",
"actualSize": "Tamanho real (100%)",
"panMode": "Arrastar (arrastar para mover visualização)",
"clearIgnoredPixels": "Limpar pixels ignorados",
"invertMask": "Inverter máscara",
"waitingSetupComplete": "🔄 Aguardando conclusão da configuração inicial...",
"waitingTokenGenerator": "🔄 Aguardando inicialização do gerador de tokens...",
"uploadImageFirst": "Faça upload de uma imagem primeiro para capturar cores disponíveis",
"pleaseWaitInitialSetup": "🔄 Aguarde a conclusão da configuração inicial antes de carregar o progresso.",
"pleaseWaitFileSetup": "🔄 Aguarde a conclusão da configuração inicial antes de carregar do arquivo.",
"errorSavingProgress": "❌ Erro ao salvar progresso",
"errorLoadingProgress": "❌ Erro ao carregar progresso",
"fileOperationsAvailable": "📂 Operações de arquivo (Carregar/Upload) agora disponíveis!",
"tokenGeneratorReady": "🔑 Gerador de tokens pronto!",
"paintingStats": "Estatísticas de Pintura",
"enablePaintingSpeedLimit": "Ativar limite de velocidade de pintura (controle de tamanho de lote)",
"enableNotifications": "Ativar notificações",
"notifyOnChargesThreshold": "Notificar quando as cargas atingirem o limite",
"onlyWhenNotFocused": "Apenas quando a aba não está em foco",
"repeatEvery": "Repetir a cada",
"minutesPl": "minuto(s)",
"grantPermission": "Conceder Permissão",
"test": "Teste",
"showAllColorsIncluding": "Mostrar Todas as Cores (incluindo indisponíveis)",
"chromaWeight": "Peso da Saturação",
"downloadPreview": "Baixar Pré-visualização",
"apply": "Aplicar",
"cancel": "Cancelar",
"fit": "Ajustar",
"hundred": "100%",
"clear": "Limpar",
"invert": "Inverter",
"reprocessingOverlay": "Reprocessando sobreposição...",
"overlayUpdated": "Sobreposição atualizada!",
"notificationsEnabled": "Notificações ativadas.",
"notificationsPermissionDenied": "Permissão de notificações negada.",
"overlayEnabled": "Sobreposição ativada.",
"overlayDisabled": "Sobreposição desativada.",
"tokenSourceSet": "Fonte de token definida para: {source}",
"batchModeSet": "Modo de lote definido para: {mode}",
"randomRange": "Intervalo Aleatório",
"normalFixedSize": "Tamanho Fixo Normal",
"advancedColorSettingsReset": "Configurações avançadas de cor redefinidas.",
"shiftRowAltColumn": "Shift = Linha • Alt = Coluna",
"hideTurnstileBtn": "Ocultar",
"turnstileInstructions": "Cloudflare Turnstile — complete a verificação se mostrada",
"uploadImageFirstColors": "Faça upload de uma imagem primeiro para capturar cores disponíveis",
"availableColors": "Cores Disponíveis ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Modo Expandir",
"minimize": "Minimizar",
"restore": "Restaurar",
"hideStats": "Ocultar Estatísticas",
"paintOptions": "Opções de pintura",
"paintWhitePixels": "Pintar pixels brancos",
"paintTransparentPixels": "Pintar pixels transparentes"
}
+143
View File
@@ -0,0 +1,143 @@
{
"title": "WPlace Авто-Изображение",
"toggleOverlay": "Toggle Overlay",
"scanColors": "Сканировать цвета",
"uploadImage": "Загрузить изображение",
"resizeImage": "Изменить размер изображения",
"selectPosition": "Выбрать позицию",
"startPainting": "Начать рисование",
"stopPainting": "Остановить рисование",
"checkingColors": "🔍 Проверка доступных цветов...",
"noColorsFound": "❌ Откройте палитру цветов на сайте и попробуйте снова!",
"colorsFound": "✅ Найдено доступных цветов: {count}. Готово к загрузке.",
"loadingImage": "🖼️ Загрузка изображения...",
"imageLoaded": "✅ Изображение загружено, валидных пикселей: {count}",
"imageError": "❌ Ошибка при загрузке изображения",
"selectPositionAlert": "Нарисуйте первый пиксель в месте, откуда начнётся рисунок!",
"waitingPosition": "👆 Ожидание, пока вы нарисуете опорный пиксель...",
"positionSet": "✅ Позиция успешно установлена!",
"positionTimeout": "❌ Время ожидания выбора позиции истекло",
"startPaintingMsg": "🎨 Начинаем рисование...",
"paintingProgress": "🧱 Прогресс: {painted}/{total} пикселей...",
"noCharges": "⌛ Нет зарядов. Ожидание {time}...",
"paintingStopped": "⏹️ Рисование остановлено пользователем",
"paintingComplete": "✅ Рисование завершено! Нарисовано пикселей: {count}.",
"paintingError": "❌ Ошибка во время рисования",
"missingRequirements": "❌ Сначала загрузите изображение и выберите позицию",
"progress": "Прогресс",
"pixels": "Пиксели",
"charges": "Заряды",
"estimatedTime": "Примерное время",
"initMessage": "Нажмите 'Загрузить изображение', чтобы начать",
"waitingInit": "Ожидание инициализации...",
"initializingToken": "🔧 Инициализация генератора Turnstile токенов...",
"tokenReady": "✅ Генератор токенов готов - можете начинать рисование!",
"tokenRetryLater": "⚠️ Генератор токенов повторит попытку при необходимости",
"resizeSuccess": "✅ Изображение изменено до {width}x{height}",
"paintingPaused": "⏸️ Рисование приостановлено на позиции X: {x}, Y: {y}",
"captchaNeeded": "❗ Генерация токена не удалась. Пожалуйста, попробуйте через некоторое время.",
"saveData": "Сохранить прогресс",
"loadData": "Загрузить прогресс",
"saveToFile": "Сохранить в файл",
"loadFromFile": "Загрузить из файла",
"dataManager": "Менеджер данных",
"autoSaved": "✅ Прогресс сохранён автоматически",
"dataLoaded": "✅ Прогресс успешно загружен",
"fileSaved": "✅ Прогресс успешно сохранён в файл",
"fileLoaded": "✅ Прогресс успешно загружен из файла",
"noSavedData": "❌ Сохранённый прогресс не найден",
"savedDataFound": "✅ Найден сохранённый прогресс! Загрузить, чтобы продолжить?",
"savedDate": "Сохранено: {date}",
"clickLoadToContinue": "Нажмите 'Загрузить прогресс', чтобы продолжить.",
"fileError": "❌ Ошибка при обработке файла",
"invalidFileFormat": "❌ Неверный формат файла",
"paintingSpeed": "Скорость рисования",
"pixelsPerSecond": "пикселей/сек",
"speedSetting": "Скорость: {speed} пикс./сек",
"settings": "Настройки",
"botSettings": "Настройки бота",
"close": "Закрыть",
"language": "Язык",
"themeSettings": "Настройки темы",
"themeSettingsDesc": "Выберите предпочтительную цветовую тему интерфейса.",
"languageSelectDesc": "Выберите предпочтительный язык. Изменения вступят в силу немедленно.",
"autoCaptcha": "Авто-решение CAPTCHA (Turnstile)",
"autoCaptchaDesc": "Автоматически генерирует Turnstile токены используя встроенный генератор. Возвращается к автоматизации браузера при необходимости.",
"applySettings": "Применить настройки",
"settingsSaved": "✅ Настройки успешно сохранены!",
"speedOn": "Вкл",
"speedOff": "Выкл",
"cooldownSettings": "Настройки перезарядки",
"waitCharges": "Ждать до накопления зарядов",
"captchaSolving": "🔑 Генерирую Turnstile токен...",
"captchaFailed": "❌ Не удалось сгенерировать Turnstile токен. Пробую резервный метод...",
"automation": "Автоматизация", "noChargesThreshold": "⌛ Ожидание зарядов до {threshold}. Сейчас {current}. Следующий через {time}...",
"tokenCapturedSuccess": "Токен успешно захвачен! Теперь можно запускать бота.",
"notificationsNotSupported": "Уведомления не поддерживаются в этом браузере.",
"chargesReadyNotification": "WPlace — Заряды готовы",
"chargesReadyMessage": "Заряды готовы: {current} / {max}. Порог: {threshold}.",
"testNotificationTitle": "WPlace — Тест",
"testNotificationMessage": "Это тестовое уведомление.",
"showStats": "Показать статистику",
"compactMode": "Компактный режим",
"refreshCharges": "Обновить заряды",
"closeStats": "Закрыть статистику",
"zoomOut": "Уменьшить",
"zoomIn": "Увеличить",
"fitToView": "По размеру экрана",
"actualSize": "Реальный размер (100%)",
"panMode": "Перемещение (перетаскивание для движения)",
"clearIgnoredPixels": "Очистить игнорируемые пиксели",
"invertMask": "Инвертировать маску",
"waitingSetupComplete": "🔄 Ждём завершения начальной настройки...",
"waitingTokenGenerator": "🔄 Ждём инициализации генератора токенов...",
"uploadImageFirst": "Сначала загрузите изображение для захвата доступных цветов",
"pleaseWaitInitialSetup": "🔄 Пожалуйста, подождите завершения начальной настройки перед загрузкой прогресса.",
"pleaseWaitFileSetup": "🔄 Пожалуйста, подождите завершения начальной настройки перед загрузкой из файла.",
"errorSavingProgress": "❌ Ошибка сохранения прогресса",
"errorLoadingProgress": "❌ Ошибка загрузки прогресса",
"fileOperationsAvailable": "📂 Файловые операции (Загрузка/Выгрузка) теперь доступны!",
"tokenGeneratorReady": "🔑 Генератор токенов готов!",
"paintingStats": "Статистика рисования",
"enablePaintingSpeedLimit": "Включить ограничение скорости рисования (контроль размера пакета)",
"enableNotifications": "Включить уведомления",
"notifyOnChargesThreshold": "Уведомлять при достижении порога зарядов",
"onlyWhenNotFocused": "Только когда вкладка не в фокусе",
"repeatEvery": "Повторять каждые",
"minutesPl": "минут(а/ы)",
"grantPermission": "Дать разрешение",
"test": "Тест",
"showAllColorsIncluding": "Показать все цвета (включая недоступные)",
"chromaWeight": "Вес цветности",
"downloadPreview": "Скачать превью",
"apply": "Применить",
"cancel": "Отменить",
"fit": "По размеру",
"hundred": "100%",
"clear": "Очистить",
"invert": "Инвертировать",
"reprocessingOverlay": "Переобработка оверлея...",
"overlayUpdated": "Оверлей обновлён!",
"notificationsEnabled": "Уведомления включены.",
"notificationsPermissionDenied": "В разрешении на уведомления отказано.",
"overlayEnabled": "Оверлей включён.",
"overlayDisabled": "Оверлей отключён.",
"tokenSourceSet": "Источник токенов установлен: {source}",
"batchModeSet": "Режим пакета установлен: {mode}",
"randomRange": "Случайный диапазон",
"normalFixedSize": "Нормальный фиксированный размер",
"advancedColorSettingsReset": "Продвинутые настройки цвета сброшены.",
"shiftRowAltColumn": "Shift = Строка • Alt = Столбец",
"hideTurnstileBtn": "Скрыть",
"turnstileInstructions": "Cloudflare Turnstile — пожалуйста, завершите проверку, если она показана",
"uploadImageFirstColors": "Пожалуйста, сначала загрузите изображение для захвата доступных цветов",
"availableColors": "Доступные цвета ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Режим расширения",
"minimize": "Свернуть",
"restore": "Восстановить",
"hideStats": "Скрыть статистику",
"paintOptions": "Параметры рисования",
"paintWhitePixels": "Рисовать белые пиксели",
"paintTransparentPixels": "Рисовать прозрачные пиксели"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Otomatik-Resim",
"toggleOverlay": "Katmanı Aç/Kapat",
"scanColors": "Renkleri Tara",
"uploadImage": "Resim Yükle",
"resizeImage": "Resmi Yeniden Boyutlandır",
"selectPosition": "Konum Seç",
"startPainting": "Boyamayı Başlat",
"stopPainting": "Boyamayı Durdur",
"checkingColors": "🔍 Uygun renkler kontrol ediliyor...",
"noColorsFound": "❌ Sitede renk paletini açın ve tekrar deneyin!",
"colorsFound": "✅ {count} uygun renk bulundu. Yüklemeye hazır.",
"loadingImage": "🖼️ Resim yükleniyor...",
"imageLoaded": "✅ Resim {count} geçerli piksel ile yüklendi",
"imageError": "❌ Resim yüklenirken hata oluştu",
"selectPositionAlert": "Sanatı başlatmak istediğiniz ilk pikseli boyayın!",
"waitingPosition": "👆 Referans pikseli boyamanız bekleniyor...",
"positionSet": "✅ Konum başarıyla ayarlandı!",
"positionTimeout": "❌ Konum seçme süresi doldu",
"startPaintingMsg": "🎨 Boyama başlatılıyor...",
"paintingProgress": "🧱 İlerleme: {painted}/{total} piksel...",
"noCharges": "⌛ Yeterli hak yok. Bekleniyor {time}...",
"paintingStopped": "⏹️ Boyama kullanıcı tarafından durduruldu",
"paintingComplete": "✅ Boyama tamamlandı! {count} piksel boyandı.",
"paintingError": "❌ Boyama sırasında hata oluştu",
"missingRequirements": "❌ Önce resim yükleyip konum seçmelisiniz",
"progress": "İlerleme",
"pixels": "Pikseller",
"charges": "Haklar",
"estimatedTime": "Tahmini süre",
"initMessage": "Başlamak için 'Resim Yükle'ye tıklayın",
"waitingInit": "Başlatma bekleniyor...",
"initializingToken": "🔧 Turnstile token üreticisi başlatılıyor...",
"tokenReady": "✅ Token üreteci hazır - artık boyamaya başlayabilirsiniz!",
"tokenRetryLater": "⚠️ Token üreteci gerektiğinde yeniden deneyecek",
"resizeSuccess": "✅ Resim {width}x{height} boyutuna yeniden boyutlandırıldı",
"paintingPaused": "⏸️ Boyama duraklatıldı, Konum X: {x}, Y: {y}",
"captchaNeeded": "❗ CAPTCHA gerekli. Devam etmek için bir pikseli manuel olarak boyayın.",
"saveData": "İlerlemeyi Kaydet",
"loadData": "İlerlemeyi Yükle",
"saveToFile": "Dosyaya Kaydet",
"loadFromFile": "Dosyadan Yükle",
"dataManager": "Veri Yöneticisi",
"autoSaved": "✅ İlerleme otomatik olarak kaydedildi",
"dataLoaded": "✅ İlerleme başarıyla yüklendi",
"fileSaved": "✅ İlerleme dosyaya başarıyla kaydedildi",
"fileLoaded": "✅ İlerleme dosyadan başarıyla yüklendi",
"noSavedData": "❌ Kayıtlı ilerleme bulunamadı",
"savedDataFound": "✅ Kayıtlı ilerleme bulundu! Devam etmek için yükleyin.",
"savedDate": "Kaydedilme tarihi: {date}",
"clickLoadToContinue": "Devam etmek için 'İlerlemeyi Yükle'ye tıklayın.",
"fileError": "❌ Dosya işlenirken hata oluştu",
"invalidFileFormat": "❌ Geçersiz dosya formatı",
"paintingSpeed": "Boyama Hızı",
"pixelsPerSecond": "piksel/saniye",
"speedSetting": "Hız: {speed} piksel/sn",
"settings": "Ayarlar",
"botSettings": "Bot Ayarları",
"close": "Kapat",
"language": "Dil",
"themeSettings": "Tema Ayarları",
"themeSettingsDesc": "Arayüz için tercih ettiğiniz renk temasını seçin.",
"languageSelectDesc": "Tercih ettiğiniz dili seçin. Değişiklikler hemen uygulanacaktır.",
"autoCaptcha": "Oto-CAPTCHA Çözücü",
"autoCaptchaDesc": "CAPTCHA süresi dolduğunda manuel piksel yerleştirmeyi taklit ederek otomatik çözmeyi dener.",
"applySettings": "Ayarları Uygula",
"settingsSaved": "✅ Ayarlar başarıyla kaydedildi!",
"speedOn": "Açık",
"speedOff": "Kapalı",
"cooldownSettings": "Bekleme Süresi Ayarları",
"waitCharges": "Haklar şu seviyeye ulaşana kadar bekle",
"captchaSolving": "🤖 CAPTCHA çözülmeye çalışılıyor...",
"captchaFailed": "❌ Oto-CAPTCHA başarısız oldu. Bir pikseli manuel boyayın.",
"automation": "Otomasyon",
"noChargesThreshold": "⌛ Hakların {threshold} seviyesine ulaşması bekleniyor. Şu anda {current}. Sonraki {time} içinde...",
"tokenCapturedSuccess": "Token başarıyla yakalandı! Şimdi botu başlatabilirsiniz.",
"notificationsNotSupported": "Bu tarayıcıda bildirimler desteklenmiyor.",
"chargesReadyNotification": "WPlace — Haklar Hazır",
"chargesReadyMessage": "Haklar hazır: {current} / {max}. Eşik: {threshold}.",
"testNotificationTitle": "WPlace — Test",
"testNotificationMessage": "Bu bir test bildirimidir.",
"showStats": "İstatistikleri Göster",
"compactMode": "Kompakt Mod",
"refreshCharges": "Hakları Yenile",
"closeStats": "İstatistikleri Kapat",
"zoomOut": "Uzaklaştır",
"zoomIn": "Yakınlaştır",
"fitToView": "Görünüme sığdır",
"actualSize": "Gerçek boyut (100%)",
"panMode": "Kaydır (görünümü hareket ettirmek için sürükle)",
"clearIgnoredPixels": "Görmezden gelinen tüm pikselleri temizle",
"invertMask": "Maskeyi ters çevir",
"waitingSetupComplete": "🔄 İlk kurulumun tamamlanması bekleniyor...",
"waitingTokenGenerator": "🔄 Token üretecinin başlatılması bekleniyor...",
"uploadImageFirst": "Uygun renkleri yakalamak için önce bir resim yükleyin",
"pleaseWaitInitialSetup": "🔄 İlerlemeyi yüklemeden önce lütfen ilk kurulumun tamamlanmasını bekleyin.",
"pleaseWaitFileSetup": "🔄 Dosyadan yüklemeden önce lütfen ilk kurulumun tamamlanmasını bekleyin.",
"errorSavingProgress": "❌ İlerlemeyi kaydetme hatası",
"errorLoadingProgress": "❌ İlerlemeyi yükleme hatası",
"fileOperationsAvailable": "📂 Dosya işlemleri (Yükle/Karşıya Yükle) artık mevcut!",
"tokenGeneratorReady": "🔑 Token üreteci hazır!",
"paintingStats": "Boyama İstatistikleri",
"enablePaintingSpeedLimit": "Boyama hız limitini etkinleştir (batch boyut kontrolü)",
"enableNotifications": "Bildirimleri etkinleştir",
"notifyOnChargesThreshold": "Haklar eşiğe ulaştığında bildir",
"onlyWhenNotFocused": "Sadece sekme odaklanmadığında",
"repeatEvery": "Her tekrarla",
"minutesPl": "dakika",
"grantPermission": "İzin Ver",
"test": "Test",
"showAllColorsIncluding": "Tüm Renkleri Göster (kullanılamayanlar dahil)",
"chromaWeight": "Renk Doygunluğu Ağırlığı",
"downloadPreview": "Önizlemeyi İndir",
"apply": "Uygula",
"cancel": "İptal",
"fit": "Sığdır",
"hundred": "100%",
"clear": "Temizle",
"invert": "Ters çevir",
"reprocessingOverlay": "Katman yeniden işleniyor...",
"overlayUpdated": "Katman güncellendi!",
"notificationsEnabled": "Bildirimler etkinleştirildi.",
"notificationsPermissionDenied": "Bildirim izni reddedildi.",
"overlayEnabled": "Katman etkinleştirildi.",
"overlayDisabled": "Katman devre dışı bırakıldı.",
"tokenSourceSet": "Token kaynağı şuna ayarlandı: {source}",
"batchModeSet": "Batch modu şuna ayarlandı: {mode}",
"randomRange": "Rastgele Aralık",
"normalFixedSize": "Normal Sabit Boyut",
"advancedColorSettingsReset": "Gelişmiş renk ayarları sıfırlandı.",
"shiftRowAltColumn": "Shift = Satır • Alt = Sütun",
"hideTurnstileBtn": "Gizle",
"turnstileInstructions": "Cloudflare Turnstile — gösterilirse lütfen kontrolü tamamlayın",
"uploadImageFirstColors": "Uygun renkleri yakalamak için lütfen önce bir resim yükleyin",
"availableColors": "Uygun Renkler ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Genişletme Modu",
"minimize": "Küçült",
"restore": "Geri Yükle",
"hideStats": "İstatistikleri Gizle",
"paintOptions": "Boya Seçenekleri",
"paintWhitePixels": "Beyaz pikselleri boya",
"paintTransparentPixels": "Şeffaf pikselleri boya"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Авто-Зображення",
"toggleOverlay": "Перемкнути оверлей",
"scanColors": "Сканувати кольори",
"uploadImage": "Завантажити зображення",
"resizeImage": "Змінити розмір зображення",
"selectPosition": "Вибрати позицію",
"startPainting": "Почати малювання",
"stopPainting": "Зупинити малювання",
"checkingColors": "🔍 Перевірка доступних кольорів...",
"noColorsFound": "❌ Відкрий палітру кольорів на сайті та спробуй ще раз!",
"colorsFound": "✅ Знайдено {count} доступних кольорів. Готово до завантаження.",
"loadingImage": "🖼️ Завантаження зображення...",
"imageLoaded": "✅ Зображення завантажено. Валідних пікселів: {count}",
"imageError": "❌ Помилка завантаження зображення",
"selectPositionAlert": "Намалюй перший піксель у місці, де має починатися арт!",
"waitingPosition": "👆 Очікування на малювання референсного пікселя...",
"positionSet": "✅ Позицію успішно встановлено!",
"positionTimeout": "❌ Час вибору позиції вичерпано",
"startPaintingMsg": "🎨 Початок малювання...",
"paintingProgress": "🧱 Прогрес: {painted}/{total} пікселів...",
"noCharges": "⌛ Немає зарядів. Очікування {time}...",
"paintingStopped": "⏹️ Малювання зупинено користувачем",
"paintingComplete": "✅ Малювання завершено! Намальовано {count} пікселів.",
"paintingError": "❌ Помилка під час малювання",
"missingRequirements": "❌ Спершу завантаж зображення та вибери позицію",
"progress": "Прогрес",
"pixels": "Пікселі",
"charges": "Заряди",
"estimatedTime": "Орієнтовний час",
"initMessage": "Натисни 'Завантажити зображення', щоб почати",
"waitingInit": "Очікування ініціалізації...",
"initializingToken": "🔧 Ініціалізація генератора токенів Turnstile...",
"tokenReady": "✅ Генератор токенів готовий – можна починати малювання!",
"tokenRetryLater": "⚠️ Генератор токенів повторить спробу за потреби",
"resizeSuccess": "✅ Зображення змінено до {width}x{height}",
"paintingPaused": "⏸️ Малювання призупинено на позиції X: {x}, Y: {y}",
"captchaNeeded": "❗ Не вдалося згенерувати токен. Спробуй трохи пізніше.",
"saveData": "Зберегти прогрес",
"loadData": "Завантажити прогрес",
"saveToFile": "Зберегти у файл",
"loadFromFile": "Завантажити з файлу",
"dataManager": "Менеджер даних",
"autoSaved": "✅ Прогрес збережено автоматично",
"dataLoaded": "✅ Прогрес успішно завантажено",
"fileSaved": "✅ Прогрес успішно збережено у файл",
"fileLoaded": "✅ Прогрес успішно завантажено з файлу",
"noSavedData": "❌ Не знайдено збереженого прогресу",
"savedDataFound": "✅ Знайдено збережений прогрес! Завантажити, щоб продовжити?",
"savedDate": "Збережено: {date}",
"clickLoadToContinue": "Натисни 'Завантажити прогрес', щоб продовжити.",
"fileError": "❌ Помилка обробки файлу",
"invalidFileFormat": "❌ Невірний формат файлу",
"paintingSpeed": "Швидкість малювання",
"pixelsPerSecond": "пікселів/секунда",
"speedSetting": "Швидкість: {speed} пікселів/сек",
"settings": "Налаштування",
"botSettings": "Налаштування бота",
"close": "Закрити",
"language": "Мова",
"themeSettings": "Налаштування теми",
"themeSettingsDesc": "Вибери бажану колірну тему для інтерфейсу.",
"languageSelectDesc": "Вибери бажану мову. Зміни набудуть чинності одразу.",
"autoCaptcha": "Авто-CAPTCHA (Turnstile)",
"autoCaptchaDesc": "Автоматично генерує токени Turnstile за допомогою вбудованого генератора. Використовує автоматизацію браузера у разі потреби.",
"applySettings": "Застосувати налаштування",
"settingsSaved": "✅ Налаштування успішно збережено!",
"speedOn": "Увімк",
"speedOff": "Вимк",
"cooldownSettings": "Налаштування відновлення",
"waitCharges": "Очікувати, доки кількість зарядів досягне",
"captchaSolving": "🔑 Генерація токена Turnstile...",
"captchaFailed": "❌ Не вдалося згенерувати токен Turnstile. Використовую запасний метод...",
"automation": "Автоматизація",
"noChargesThreshold": "⌛ Очікування, доки заряди досягнуть {threshold}. Зараз {current}. Наступне через {time}...",
"tokenCapturedSuccess": "Токен успішно захоплено! Можеш запускати бота.",
"notificationsNotSupported": "Сповіщення не підтримуються в цьому браузері.",
"chargesReadyNotification": "WPlace — Заряди готові",
"chargesReadyMessage": "Заряди готові: {current} / {max}. Поріг: {threshold}.",
"testNotificationTitle": "WPlace — Тест",
"testNotificationMessage": "Це тестове сповіщення.",
"showStats": "Показати статистику",
"compactMode": "Компактний режим",
"refreshCharges": "Оновити заряди",
"closeStats": "Закрити статистику",
"zoomOut": "Зменшити",
"zoomIn": "Збільшити",
"fitToView": "Підігнати під вікно",
"actualSize": "Реальний розмір (100%)",
"panMode": "Переміщення (перетягни для руху)",
"clearIgnoredPixels": "Очистити всі ігноровані пікселі",
"invertMask": "Інвертувати маску",
"waitingSetupComplete": "🔄 Очікування завершення початкового налаштування...",
"waitingTokenGenerator": "🔄 Очікування ініціалізації генератора токенів...",
"uploadImageFirst": "Спершу завантаж зображення для захоплення доступних кольорів",
"pleaseWaitInitialSetup": "🔄 Будь ласка, почекай завершення початкового налаштування перед завантаженням прогресу.",
"pleaseWaitFileSetup": "🔄 Будь ласка, почекай завершення початкового налаштування перед завантаженням з файлу.",
"errorSavingProgress": "❌ Помилка збереження прогресу",
"errorLoadingProgress": "❌ Помилка завантаження прогресу",
"fileOperationsAvailable": "📂 Файлові операції (Завантаження/Вивантаження) тепер доступні!",
"tokenGeneratorReady": "🔑 Генератор токенів готовий!",
"paintingStats": "Статистика малювання",
"enablePaintingSpeedLimit": "Увімкнути обмеження швидкості малювання (контроль розміру пакета)",
"enableNotifications": "Увімкнути уведомлення",
"notifyOnChargesThreshold": "Сповіщати при досягненні порогу зарядів",
"onlyWhenNotFocused": "Лише коли вкладка не в фокусі",
"repeatEvery": "Повторювати кожні",
"minutesPl": "хвилин",
"grantPermission": "Надати дозвіл",
"test": "Тест",
"showAllColorsIncluding": "Показати всі кольори (включаючи недоступні)",
"chromaWeight": "Вага насиченості",
"downloadPreview": "Завантажити попередній перегляд",
"apply": "Застосувати",
"cancel": "Скасувати",
"fit": "Підігнати",
"hundred": "100%",
"clear": "Очистити",
"invert": "Інвертувати",
"reprocessingOverlay": "Переобробка оверлея...",
"overlayUpdated": "Оверлей оновлено!",
"notificationsEnabled": "Уведомлення увімкнено.",
"notificationsPermissionDenied": "Дозвіл на уведомлення відхилено.",
"overlayEnabled": "Оверлей увімкнено.",
"overlayDisabled": "Оверлей вимкнено.",
"tokenSourceSet": "Джерело токенів встановлено: {source}",
"batchModeSet": "Пакетний режим встановлено: {mode}",
"randomRange": "Випадковий діапазон",
"normalFixedSize": "Звичайний фіксований розмір",
"advancedColorSettingsReset": "Просунуті налаштування кольорів скинуто.",
"shiftRowAltColumn": "Shift = Рядок • Alt = Стовпець",
"hideTurnstileBtn": "Сховати",
"turnstileInstructions": "Cloudflare Turnstile — будь ласка, заверши перевірку, якщо вона показана",
"uploadImageFirstColors": "Будь ласка, спершу завантаж зображення для захоплення доступних кольорів",
"availableColors": "Доступні кольори ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Режим розширення",
"minimize": "Мінімізувати",
"restore": "Відновити",
"hideStats": "Приховати статистику",
"paintOptions": "Параметри малювання",
"paintWhitePixels": "Малювати білі пікселі",
"paintTransparentPixels": "Малювати прозорі пікселі"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace Auto-Image",
"toggleOverlay": "Bật/tắt lớp phủ",
"scanColors": "Quét màu",
"uploadImage": "Tải lên hình ảnh",
"resizeImage": "Thay đổi kích thước",
"selectPosition": "Chọn vị trí",
"startPainting": "Bắt đầu vẽ",
"stopPainting": "Dừng vẽ",
"checkingColors": "🔍 Đang kiểm tra màu sắc có sẵn...",
"noColorsFound": "❌ Hãy mở bảng màu trên trang web và thử lại!",
"colorsFound": "✅ Tìm thấy {count} màu. Sẵn sàng để tải lên.",
"loadingImage": "🖼️ Đang tải hình ảnh...",
"imageLoaded": "✅ Đã tải hình ảnh với {count} pixel hợp lệ",
"imageError": "❌ Lỗi khi tải hình ảnh",
"selectPositionAlert": "Vẽ pixel đầu tiên tại vị trí bạn muốn tác phẩm nghệ thuật bắt đầu!",
"waitingPosition": "👆 Đang chờ bạn vẽ pixel tham chiếu...",
"positionSet": "✅ Đã đặt vị trí thành công!",
"positionTimeout": "❌ Hết thời gian chọn vị trí",
"startPaintingMsg": "🎨 Bắt đầu vẽ...",
"paintingProgress": "🧱 Tiến trình: {painted}/{total} pixel...",
"noCharges": "⌛ Không có điện tích. Đang chờ {time}...",
"paintingStopped": "⏹️ Người dùng đã dừng vẽ",
"paintingComplete": "✅ Hoàn thành vẽ! Đã vẽ {count} pixel.",
"paintingError": "❌ Lỗi trong quá trình vẽ",
"missingRequirements": "❌ Hãy tải lên hình ảnh và chọn vị trí trước",
"progress": "Tiến trình",
"pixels": "Pixel",
"charges": "Điện tích",
"estimatedTime": "Thời gian ước tính",
"initMessage": "Nhấp 'Tải lên hình ảnh' để bắt đầu",
"waitingInit": "Đang chờ khởi tạo...",
"initializingToken": "🔧 Đang khởi tạo bộ tạo token Turnstile...",
"tokenReady": "✅ Bộ tạo token đã sẵn sàng - bạn có thể bắt đầu vẽ!",
"tokenRetryLater": "⚠️ Bộ tạo token sẽ thử lại khi cần thiết",
"resizeSuccess": "✅ Đã thay đổi kích thước hình ảnh thành {width}x{height}",
"paintingPaused": "⏸️ Tạm dừng vẽ tại vị trí X: {x}, Y: {y}",
"captchaNeeded": "❗ Tạo token thất bại. Vui lòng thử lại sau.",
"saveData": "Lưu tiến trình",
"loadData": "Tải tiến trình",
"saveToFile": "Lưu vào tệp",
"loadFromFile": "Tải từ tệp",
"dataManager": "Dữ liệu",
"autoSaved": "✅ Đã tự động lưu tiến trình",
"dataLoaded": "✅ Đã tải tiến trình thành công",
"fileSaved": "✅ Đã lưu vào tệp thành công",
"fileLoaded": "✅ Đã tải từ tệp thành công",
"noSavedData": "❌ Không tìm thấy tiến trình đã lưu",
"savedDataFound": "✅ Tìm thấy tiến trình đã lưu! Tải để tiếp tục?",
"savedDate": "Đã lưu vào: {date}",
"clickLoadToContinue": "Nhấp 'Tải tiến trình' để tiếp tục.",
"fileError": "❌ Lỗi khi xử lý tệp",
"invalidFileFormat": "❌ Định dạng tệp không hợp lệ",
"paintingSpeed": "Tốc độ vẽ",
"pixelsPerSecond": "pixel/giây",
"speedSetting": "Tốc độ: {speed} pixel/giây",
"settings": "Cài đặt",
"botSettings": "Cài đặt Bot",
"close": "Đóng",
"language": "Ngôn ngữ",
"themeSettings": "Cài đặt Giao diện",
"themeSettingsDesc": "Chọn chủ đề màu sắc yêu thích cho giao diện.",
"languageSelectDesc": "Chọn ngôn ngữ ưa thích. Thay đổi sẽ có hiệu lực ngay lập tức.",
"autoCaptcha": "Tự động giải CAPTCHA",
"autoCaptchaDesc": "Tự động cố gắng giải CAPTCHA bằng cách mô phỏng việc đặt pixel thủ công khi token hết hạn.",
"applySettings": "Áp dụng cài đặt",
"settingsSaved": "✅ Đã lưu cài đặt thành công!",
"speedOn": "Bật",
"speedOff": "Tắt",
"cooldownSettings": "Cài đặt thời gian chờ",
"waitCharges": "Chờ cho đến khi số lần sạc đạt",
"captchaSolving": "🤖 Đang cố gắng giải CAPTCHA...",
"captchaFailed": "❌ Giải CAPTCHA tự động thất bại. Vui lòng vẽ một pixel thủ công.",
"automation": "Tự động hóa",
"noChargesThreshold": "⌛ Đang chờ số lần sạc đạt {threshold}. Hiện tại {current}. Lần tiếp theo trong {time}...",
"tokenCapturedSuccess": "Đã bắt token thành công! Bạn có thể khởi động bot ngay.",
"notificationsNotSupported": "Thông báo không được hỗ trợ trong trình duyệt này.",
"chargesReadyNotification": "WPlace — Điện tích sẵn sàng",
"chargesReadyMessage": "Điện tích sẵn sàng: {current} / {max}. Ngưỡng: {threshold}.",
"testNotificationTitle": "WPlace — Thử nghiệm",
"testNotificationMessage": "Đây là thông báo thử nghiệm.",
"showStats": "Hiển thị thống kê",
"compactMode": "Chế độ gọn",
"refreshCharges": "Làm mới điện tích",
"closeStats": "Đóng thống kê",
"zoomOut": "Thu nhỏ",
"zoomIn": "Phóng to",
"fitToView": "Vừa màn hình",
"actualSize": "Kích thước thực (100%)",
"panMode": "Di chuyển (kéo để di chuyển)",
"clearIgnoredPixels": "Xóa tất cả pixel bị bỏ qua",
"invertMask": "Đảo ngược mặt nạ",
"waitingSetupComplete": "🔄 Đang chờ thiết lập ban đầu hoàn tất...",
"waitingTokenGenerator": "🔄 Đang chờ bộ tạo token khởi tạo...",
"uploadImageFirst": "Tải lên hình ảnh trước để bắt màu có sẵn",
"pleaseWaitInitialSetup": "🔄 Vui lòng đợi thiết lập ban đầu hoàn tất trước khi tải tiến trình.",
"pleaseWaitFileSetup": "🔄 Vui lòng đợi thiết lập ban đầu hoàn tất trước khi tải từ tệp.",
"errorSavingProgress": "❌ Lỗi khi lưu tiến trình",
"errorLoadingProgress": "❌ Lỗi khi tải tiến trình",
"fileOperationsAvailable": "📂 Các thao tác tệp (Tải/Upload) đã có sẵn!",
"tokenGeneratorReady": "🔑 Bộ tạo token đã sẵn sàng!",
"paintingStats": "Thống kê vẽ",
"enablePaintingSpeedLimit": "Bật giới hạn tốc độ vẽ (kiểm soát kích thước lô)",
"enableNotifications": "Bật thông báo",
"notifyOnChargesThreshold": "Thông báo khi điện tích đạt ngưỡng",
"onlyWhenNotFocused": "Chỉ khi tab không được chọn",
"repeatEvery": "Lặp lại mỗi",
"minutesPl": "phút",
"grantPermission": "Cấp quyền",
"test": "Thử nghiệm",
"showAllColorsIncluding": "Hiển thị tất cả màu (bao gồm không có sẵn)",
"chromaWeight": "Trọng số độ bão hòa",
"downloadPreview": "Tải xuống xem trước",
"apply": "Áp dụng",
"cancel": "Hủy",
"fit": "Vừa khung",
"hundred": "100%",
"clear": "Xóa",
"invert": "Đảo ngược",
"reprocessingOverlay": "Đang xử lý lại lớp phủ...",
"overlayUpdated": "Lớp phủ đã cập nhật!",
"notificationsEnabled": "Đã bật thông báo.",
"notificationsPermissionDenied": "Quyền thông báo bị từ chối.",
"overlayEnabled": "Đã bật lớp phủ.",
"overlayDisabled": "Đã tắt lớp phủ.",
"tokenSourceSet": "Nguồn token được đặt thành: {source}",
"batchModeSet": "Chế độ lô được đặt thành: {mode}",
"randomRange": "Phạm vi ngẫu nhiên",
"normalFixedSize": "Kích thước cố định bình thường",
"advancedColorSettingsReset": "Đã đặt lại cài đặt màu nâng cao.",
"shiftRowAltColumn": "Shift = Hàng • Alt = Cột",
"hideTurnstileBtn": "Ẩn",
"turnstileInstructions": "Cloudflare Turnstile — vui lòng hoàn thành kiểm tra nếu được hiển thị",
"uploadImageFirstColors": "Vui lòng tải lên hình ảnh trước để bắt màu có sẵn",
"availableColors": "Màu có sẵn ({count})",
"colorTooltip": "ID: {id}\nRGB: {rgb}",
"expandMode": "Chế độ mở rộng",
"minimize": "Thu nhỏ",
"restore": "Khôi phục",
"hideStats": "Ẩn thống kê",
"paintOptions": "Tùy chọn vẽ",
"paintWhitePixels": "Vẽ điểm ảnh trắng",
"paintTransparentPixels": "Vẽ điểm ảnh trong suốt"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace 自动图像",
"toggleOverlay": "切换覆盖层",
"scanColors": "扫描颜色",
"uploadImage": "上传图像",
"resizeImage": "调整大小",
"selectPosition": "选择位置",
"startPainting": "开始绘制",
"stopPainting": "停止绘制",
"checkingColors": "🔍 正在检查可用颜色...",
"noColorsFound": "❌ 请在网站上打开调色板后再试!",
"colorsFound": "✅ 找到 {count} 个可用颜色,准备上传。",
"loadingImage": "🖼️ 正在加载图像...",
"imageLoaded": "✅ 图像已加载,包含 {count} 个有效像素",
"imageError": "❌ 加载图像时出错",
"selectPositionAlert": "请在你想让作品开始的位置绘制第一个像素!",
"waitingPosition": "👆 正在等待你绘制参考像素...",
"positionSet": "✅ 位置设置成功!",
"positionTimeout": "❌ 选择位置超时",
"startPaintingMsg": "🎨 开始绘制...",
"paintingProgress": "🧱 进度: {painted}/{total} 像素...",
"noCharges": "⌛ 无可用次数,等待 {time}...",
"paintingStopped": "⏹️ 已被用户停止",
"paintingComplete": "✅ 绘制完成!共绘制 {count} 个像素。",
"paintingError": "❌ 绘制过程中出错",
"missingRequirements": "❌ 请先加载图像并选择位置",
"progress": "进度",
"pixels": "像素",
"charges": "次数",
"estimatedTime": "预计时间",
"initMessage": "点击\"上传图像\"开始",
"waitingInit": "正在等待初始化...",
"initializingToken": "🔧 正在初始化 Turnstile 令牌生成器...",
"tokenReady": "✅ 令牌生成器已就绪 - 可以开始绘制!",
"tokenRetryLater": "⚠️ 令牌生成器稍后将重试",
"resizeSuccess": "✅ 图像已调整为 {width}x{height}",
"paintingPaused": "⏸️ 在位置 X: {x}, Y: {y} 暂停",
"captchaNeeded": "❗ 令牌生成失败,请稍后再试。",
"saveData": "保存进度",
"loadData": "加载进度",
"saveToFile": "保存到文件",
"loadFromFile": "从文件加载",
"dataManager": "数据管理",
"autoSaved": "✅ 进度已自动保存",
"dataLoaded": "✅ 进度加载成功",
"fileSaved": "✅ 已成功保存到文件",
"fileLoaded": "✅ 已成功从文件加载",
"noSavedData": "❌ 未找到已保存进度",
"savedDataFound": "✅ 找到已保存进度!是否加载继续?",
"savedDate": "保存时间: {date}",
"clickLoadToContinue": "点击\"加载进度\"继续。",
"fileError": "❌ 处理文件时出错",
"invalidFileFormat": "❌ 文件格式无效",
"paintingSpeed": "绘制速度",
"pixelsPerSecond": "像素/秒",
"speedSetting": "速度: {speed} 像素/秒",
"settings": "设置",
"botSettings": "机器人设置",
"close": "关闭",
"language": "语言",
"themeSettings": "主题设置",
"themeSettingsDesc": "为界面选择你喜欢的配色主题。",
"languageSelectDesc": "选择你偏好的语言,变更立即生效。",
"autoCaptcha": "自动 CAPTCHA 解决",
"autoCaptchaDesc": "使用集成的生成器自动生成 Turnstile 令牌,必要时回退到浏览器自动化。",
"applySettings": "应用设置",
"settingsSaved": "✅ 设置保存成功!",
"speedOn": "开启",
"speedOff": "关闭",
"cooldownSettings": "冷却设置",
"waitCharges": "等待次数达到",
"captchaSolving": "🔑 正在生成 Turnstile 令牌...",
"captchaFailed": "❌ 令牌生成失败。尝试回退方法...",
"automation": "自动化",
"noChargesThreshold": "⌛ 等待次数达到 {threshold}。当前 {current}。下次在 {time}...",
"tokenCapturedSuccess": "令牌捕获成功!现在可以启动机器人。",
"notificationsNotSupported": "此浏览器不支持通知。",
"chargesReadyNotification": "WPlace — 次数就绪",
"chargesReadyMessage": "次数就绪:{current} / {max}。阈值:{threshold}。",
"testNotificationTitle": "WPlace — 测试",
"testNotificationMessage": "这是一个测试通知。",
"showStats": "显示统计",
"compactMode": "紧凑模式",
"refreshCharges": "刷新次数",
"closeStats": "关闭统计",
"zoomOut": "缩小",
"zoomIn": "放大",
"fitToView": "适合窗口",
"actualSize": "实际大小 (100%)",
"panMode": "平移(拖动移动视图)",
"clearIgnoredPixels": "清除所有忽略的像素",
"invertMask": "反转蒙版",
"waitingSetupComplete": "🔄 等待初始设置完成...",
"waitingTokenGenerator": "🔄 等待令牌生成器初始化...",
"uploadImageFirst": "请先上传图像以获取可用颜色",
"pleaseWaitInitialSetup": "🔄 请等待初始设置完成后再加载进度。",
"pleaseWaitFileSetup": "🔄 请等待初始设置完成后再从文件加载。",
"errorSavingProgress": "❌ 保存进度时出错",
"errorLoadingProgress": "❌ 加载进度时出错",
"fileOperationsAvailable": "📂 文件操作(加载/上传)现已可用!",
"tokenGeneratorReady": "🔑 令牌生成器准备就绪!",
"paintingStats": "绘制统计",
"enablePaintingSpeedLimit": "启用绘制速度限制(批次大小控制)",
"enableNotifications": "启用通知",
"notifyOnChargesThreshold": "次数达到阈值时通知",
"onlyWhenNotFocused": "仅在标签页未聚焦时",
"repeatEvery": "重复间隔",
"minutesPl": "分钟",
"grantPermission": "授予权限",
"test": "测试",
"showAllColorsIncluding": "显示所有颜色(包括不可用)",
"chromaWeight": "色度权重",
"downloadPreview": "下载预览",
"apply": "应用",
"cancel": "取消",
"fit": "适合",
"hundred": "100%",
"clear": "清除",
"invert": "反转",
"reprocessingOverlay": "重新处理覆盖层...",
"overlayUpdated": "覆盖层已更新!",
"notificationsEnabled": "已启用通知。",
"notificationsPermissionDenied": "通知权限被拒绝。",
"overlayEnabled": "已启用覆盖层。",
"overlayDisabled": "已禁用覆盖层。",
"tokenSourceSet": "令牌源设置为:{source}",
"batchModeSet": "批次模式设置为:{mode}",
"randomRange": "随机范围",
"normalFixedSize": "正常固定大小",
"advancedColorSettingsReset": "已重置高级颜色设置。",
"shiftRowAltColumn": "Shift = 行 • Alt = 列",
"hideTurnstileBtn": "隐藏",
"turnstileInstructions": "Cloudflare Turnstile — 如有显示请完成验证",
"uploadImageFirstColors": "请先上传图像以获取可用颜色",
"availableColors": "可用颜色 ({count})",
"colorTooltip": "ID:{id}\nRGB:{rgb}",
"expandMode": "展开模式",
"minimize": "最小化",
"restore": "恢复",
"hideStats": "隐藏统计",
"paintOptions": "绘图选项",
"paintWhitePixels": "绘制白色像素",
"paintTransparentPixels": "绘制透明像素"
}
+144
View File
@@ -0,0 +1,144 @@
{
"title": "WPlace 自動圖像",
"toggleOverlay": "切換覆蓋層",
"scanColors": "掃描顏色",
"uploadImage": "上傳圖像",
"resizeImage": "調整大小",
"selectPosition": "選擇位置",
"startPainting": "開始繪製",
"stopPainting": "停止繪製",
"checkingColors": "🔍 正在檢查可用顏色...",
"noColorsFound": "❌ 請在網站上打開調色板後再試!",
"colorsFound": "✅ 找到 {count} 個可用顏色,準備上傳。",
"loadingImage": "🖼️ 正在載入圖像...",
"imageLoaded": "✅ 圖像已載入,包含 {count} 個有效像素",
"imageError": "❌ 載入圖像時出錯",
"selectPositionAlert": "請在你想讓作品開始的位置繪製第一個像素!",
"waitingPosition": "👆 正在等待你繪製參考像素...",
"positionSet": "✅ 位置設定成功!",
"positionTimeout": "❌ 選擇位置逾時",
"startPaintingMsg": "🎨 開始繪製...",
"paintingProgress": "🧱 進度: {painted}/{total} 像素...",
"noCharges": "⌛ 無可用次數,等待 {time}...",
"paintingStopped": "⏹️ 已被使用者停止",
"paintingComplete": "✅ 繪製完成!共繪製 {count} 個像素。",
"paintingError": "❌ 繪製過程中出錯",
"missingRequirements": "❌ 請先載入圖像並選擇位置",
"progress": "進度",
"pixels": "像素",
"charges": "次數",
"estimatedTime": "預計時間",
"initMessage": "點擊「上傳圖像」開始",
"waitingInit": "正在等待初始化...",
"initializingToken": "🔧 正在初始化 Turnstile 令牌產生器...",
"tokenReady": "✅ 令牌產生器已就緒 - 可以開始繪製!",
"tokenRetryLater": "⚠️ 令牌產生器稍後將重試",
"resizeSuccess": "✅ 圖像已調整為 {width}x{height}",
"paintingPaused": "⏸️ 在位置 X: {x}, Y: {y} 暫停",
"captchaNeeded": "❗ 令牌產生失敗,請稍後再試。",
"saveData": "儲存進度",
"loadData": "載入進度",
"saveToFile": "儲存至檔案",
"loadFromFile": "從檔案載入",
"dataManager": "資料管理",
"autoSaved": "✅ 進度已自動儲存",
"dataLoaded": "✅ 進度載入成功",
"fileSaved": "✅ 已成功儲存至檔案",
"fileLoaded": "✅ 已成功從檔案載入",
"noSavedData": "❌ 未找到已儲存進度",
"savedDataFound": "✅ 找到已儲存進度!是否載入以繼續?",
"savedDate": "儲存時間: {date}",
"clickLoadToContinue": "點擊「載入進度」繼續。",
"fileError": "❌ 處理檔案時出錯",
"invalidFileFormat": "❌ 檔案格式無效",
"paintingSpeed": "繪製速度",
"pixelsPerSecond": "像素/秒",
"speedSetting": "速度: {speed} 像素/秒",
"settings": "設定",
"botSettings": "機器人設定",
"close": "關閉",
"language": "語言",
"themeSettings": "主題設定",
"themeSettingsDesc": "為介面選擇你喜歡的配色主題。",
"languageSelectDesc": "選擇你偏好的語言,變更立即生效。",
"autoCaptcha": "自動 CAPTCHA 解決",
"autoCaptchaDesc": "使用整合的產生器自動產生 Turnstile 令牌,必要時回退到瀏覽器自動化。",
"applySettings": "套用設定",
"settingsSaved": "✅ 設定儲存成功!",
"speedOn": "開啟",
"speedOff": "關閉",
"cooldownSettings": "冷卻設定",
"waitCharges": "等待次數達到",
"captchaSolving": "🔑 正在產生 Turnstile 令牌...",
"captchaFailed": "❌ 令牌產生失敗。嘗試回退方法...",
"automation": "自動化",
"noChargesThreshold": "⌛ 等待次數達到 {threshold}。目前 {current}。下次在 {time}...",
"tokenCapturedSuccess": "令牌捕獲成功!現在可以啟動機器人。",
"notificationsNotSupported": "此瀏覽器不支持通知。",
"chargesReadyNotification": "WPlace — 次數就緒",
"chargesReadyMessage": "次數就緒:{current} / {max}。閾值:{threshold}。",
"testNotificationTitle": "WPlace — 測試",
"testNotificationMessage": "這是一個測試通知。",
"showStats": "顯示統計",
"compactMode": "緊凑模式",
"refreshCharges": "刷新次數",
"closeStats": "關閉統計",
"zoomOut": "縮小",
"zoomIn": "放大",
"fitToView": "適合視窗",
"actualSize": "實際大小 (100%)",
"panMode": "平移(拖拉移動視圖)",
"clearIgnoredPixels": "清除所有忽略的像素",
"invertMask": "反轉遮罩",
"waitingSetupComplete": "🔄 等待初始設定完成...",
"waitingTokenGenerator": "🔄 等待令牌產生器初始化...",
"uploadImageFirst": "請先上傳圖像以獲取可用顏色",
"pleaseWaitInitialSetup": "🔄 請等待初始設定完成後再載入進度。",
"pleaseWaitFileSetup": "🔄 請等待初始設定完成後再從檔案載入。",
"errorSavingProgress": "❌ 儲存進度時出錯",
"errorLoadingProgress": "❌ 載入進度時出錯",
"fileOperationsAvailable": "📂 檔案操作(載入/上傳)現已可用!",
"tokenGeneratorReady": "🔑 令牌產生器準備就緒!",
"paintingStats": "繪制統計",
"enablePaintingSpeedLimit": "啟用繪制速度限制(批次大小控制)",
"enableNotifications": "啟用通知",
"notifyOnChargesThreshold": "次數達到闾值時通知",
"onlyWhenNotFocused": "僅在標籤頁未聚焦時",
"repeatEvery": "重複間隔",
"minutesPl": "分鐘",
"grantPermission": "授予權限",
"test": "測試",
"showAllColorsIncluding": "顯示所有顏色(包括不可用)",
"chromaWeight": "色度權重",
"downloadPreview": "下載預覽",
"apply": "套用",
"cancel": "取消",
"fit": "適合",
"hundred": "100%",
"clear": "清除",
"invert": "反轉",
"reprocessingOverlay": "重新處理覆蓋層...",
"overlayUpdated": "覆蓋層已更新!",
"notificationsEnabled": "已啟用通知。",
"notificationsPermissionDenied": "通知權限被拒絕。",
"overlayEnabled": "已啟用覆蓋層。",
"overlayDisabled": "已禁用覆蓋層。",
"tokenSourceSet": "令牌來源設定為:{source}",
"batchModeSet": "批次模式設定為:{mode}",
"randomRange": "隨機範圍",
"normalFixedSize": "正常固定大小",
"advancedColorSettingsReset": "已重置進階顏色設定。",
"shiftRowAltColumn": "Shift = 列 • Alt = 行",
"hideTurnstileBtn": "隱藏",
"turnstileInstructions": "Cloudflare Turnstile — 如有顯示請完成驗證",
"uploadImageFirstColors": "請先上傳圖像以獲取可用顏色",
"availableColors": "可用顏色 ({count})",
"colorTooltip": "ID:{id}\nRGB:{rgb}",
"expandMode": "展開模式",
"minimize": "最小化",
"restore": "恢復",
"hideStats": "隱藏統計",
"paintOptions": "繪圖選項",
"paintWhitePixels": "繪製白色像素",
"paintTransparentPixels": "繪製透明像素"
}
+68
View File
@@ -0,0 +1,68 @@
{
"manifest_version": 3,
"name": "WPlace AutoBOT Script Launcher",
"version": "2.0.2",
"description": "Launch and manage automation scripts for WPlace",
"icons": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"64": "icons/icon64.png",
"128": "icons/icon128.png",
"256": "icons/icon256.png"
},
"permissions": [
"cookies",
"webRequest",
"scripting",
"tabs",
"storage",
"browsingData",
"webNavigation"
],
"host_permissions": [
"https://backend.wplace.live/*",
"https://wplace.live/*",
"https://*.wplace.live/*"
],
"action": {
"default_popup": "popup/popup.html",
"default_title": "WPlace AutoBOT",
"default_icon": {
"16": "icons/icon16.png",
"32": "icons/icon32.png",
"48": "icons/icon48.png",
"64": "icons/icon64.png",
"128": "icons/icon128.png",
"256": "icons/icon256.png"
}
},
"content_scripts": [
{
"matches": ["https://wplace.live/*"],
"js": ["content-script.js"],
"run_at": "document_end"
}
],
"background": {
"service_worker": "background.js"
},
"web_accessible_resources": [
{
"resources": [
"scripts/*.js",
"Script-manager.js",
"Script-manager-fixed.js",
"auto-image-styles.css",
"themes/*.css",
"lang/*.json",
"lang/lang/*.json",
"icons/*.png"
],
"matches": ["https://wplace.live/*"]
}
]
}
+917
View File
@@ -0,0 +1,917 @@
@import url('https://fonts.googleapis.com/css2?family=Press+Start+2P&display=swap');
body {
width: 350px;
min-height: 400px;
margin: 0;
padding: 0;
font-family: 'Press Start 2P', monospace, 'Courier New';
background: #1a1a2e;
color: #00ff41;
overflow: hidden;
}
.container {
display: flex;
flex-direction: column;
height: 100%;
border: 2px solid #00ff41;
box-shadow: 0 0 20px rgb(0 255 65 / 50%), inset 0 0 20px rgb(0 255 65 / 10%);
position: relative;
}
.container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, #00ff41, transparent);
z-index: 1;
pointer-events: none;
animation: scanline 3s linear infinite;
opacity: 0.7;
}
header {
background: #16213e;
border-bottom: 2px solid #00ff41;
padding: 15px;
position: relative;
}
.header-content {
display: flex;
align-items: center;
justify-content: center;
gap: 12px;
margin-bottom: 10px;
}
.header-icon {
width: 32px;
height: 32px;
border-radius: 6px;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.4);
transition: all 0.3s ease;
animation: pixel-blink 3s infinite;
}
.header-icon:hover {
transform: scale(1.1);
box-shadow: 0 0 25px rgba(0, 255, 65, 0.6);
}
header h1 {
margin: 0 0 10px 0;
font-size: 12px;
font-weight: 400;
color: #00ff41;
text-align: center;
text-shadow: 0 0 15px #00ff41;
text-transform: uppercase;
letter-spacing: 2px;
animation: text-glow 2s ease-in-out infinite alternate;
}
.status-indicator {
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
font-size: 8px;
color: #00ff41dd;
text-shadow: 0 0 8px #00ff41;
text-transform: uppercase;
}
.status-dot {
width: 8px;
height: 8px;
border-radius: 0;
background: #39ff14;
animation: pixel-blink 1s infinite;
box-shadow: 0 0 10px #39ff14;
}
.status-dot.running {
background: #ff6b35;
box-shadow: 0 0 10px #ff6b35;
}
.status-dot.error {
background: #ff073a;
box-shadow: 0 0 10px #ff073a;
}
/* Settings Dialog Styles */
.settings-section {
padding: 15px;
border-bottom: 1px solid #00ff41;
}
.settings-section h3 {
font-size: 10px;
color: #00ff41;
margin: 0 0 10px 0;
text-shadow: 0 0 8px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
}
.settings-select {
width: 100%;
padding: 8px;
background: #16213e;
border: 2px solid #00ff41;
color: #00ff41;
font-family: 'Press Start 2P', monospace;
font-size: 8px;
margin-bottom: 10px;
text-shadow: 0 0 5px #00ff41;
}
.settings-select:focus {
outline: none;
box-shadow: 0 0 15px rgb(0 255 65 / 50%);
}
.settings-select option {
background: #16213e;
color: #00ff41;
padding: 8px;
}
@keyframes pixel-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.7; }
}
@keyframes text-glow {
0% { text-shadow: 0 0 15px #00ff41; }
100% { text-shadow: 0 0 25px #00ff41, 0 0 35px #00ff41; }
}
@keyframes scanline {
0% { transform: translateY(-100%); }
100% { transform: translateY(400px); }
}
main {
flex: 1;
padding: 15px;
background: linear-gradient(45deg, rgba(0,255,65,0.03) 0%, rgba(22,33,62,0.05) 100%);
overflow-y: auto;
}
.script-section h3,
.control-section h4,
.logs-section h4 {
margin: 0 0 10px 0;
font-size: 10px;
font-weight: 400;
color: #00ff41;
text-shadow: 0 0 10px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
}
.scripts-list {
display: flex;
flex-direction: column;
gap: 8px;
margin-bottom: 20px;
}
.script-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px;
background: #16213e;
border: 2px solid #00ff41;
border-radius: 0;
transition: all 0.3s;
position: relative;
overflow: hidden;
}
.script-item::before {
content: '';
position: absolute;
top: -50%;
left: -50%;
width: 200%;
height: 200%;
background: linear-gradient(45deg, transparent, rgba(0,255,65,0.1), transparent);
transform: rotate(45deg);
transition: all 0.5s ease;
opacity: 0;
}
.script-item:hover::before {
animation: neon-sweep 1s ease-in-out;
}
.script-item:hover {
background: rgba(0,255,65,0.1);
box-shadow: 0 0 20px rgb(0 255 65 / 60%), inset 0 0 20px rgb(0 255 65 / 15%);
animation: card-glow 0.5s ease-in-out infinite alternate;
}
.script-info-left {
flex: 1;
}
.script-name {
font-weight: 400;
font-size: 8px;
color: #00ff41;
margin-bottom: 4px;
text-shadow: 0 0 8px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
}
.script-description {
font-size: 6px;
color: #00ff41bb;
text-shadow: 0 0 5px #00ff41;
line-height: 1.3;
}
.btn {
padding: 6px 12px;
border: 2px solid #00ff41;
border-radius: 0;
font-size: 7px;
font-weight: 400;
cursor: pointer;
transition: all 0.3s;
display: inline-flex;
align-items: center;
gap: 4px;
font-family: 'Press Start 2P', monospace;
text-transform: uppercase;
letter-spacing: 1px;
background: #16213e;
color: #00ff41;
text-shadow: 0 0 8px #00ff41;
}
.btn-primary {
border-color: #39ff14;
color: #39ff14;
text-shadow: 0 0 8px #39ff14;
}
.btn-primary:hover {
background: rgba(57, 255, 20, 0.1);
box-shadow: 0 0 15px rgb(57 255 20 / 60%);
animation: pixel-blink 0.5s infinite;
}
.btn-primary:disabled {
border-color: #00ff4180;
color: #00ff4180;
cursor: not-allowed;
animation: none;
}
.btn-danger {
border-color: #ff073a;
color: #ff073a;
text-shadow: 0 0 8px #ff073a;
}
.btn-danger:hover {
background: rgba(255, 7, 58, 0.1);
box-shadow: 0 0 15px rgb(255 7 58 / 60%);
animation: pixel-blink 0.5s infinite;
}
.icon {
font-size: 6px;
}
.current-script {
background: rgba(255, 107, 53, 0.1);
border: 2px solid #ff6b35;
border-radius: 0;
padding: 12px;
margin-bottom: 20px;
box-shadow: 0 0 15px rgb(255 107 53 / 30%);
}
.current-script h4 {
color: #ff6b35;
margin-bottom: 8px;
text-shadow: 0 0 10px #ff6b35;
font-size: 8px;
text-transform: uppercase;
}
.script-info {
display: flex;
justify-content: space-between;
align-items: center;
}
.script-info span {
font-weight: 400;
color: #ff6b35;
text-shadow: 0 0 8px #ff6b35;
font-size: 7px;
text-transform: uppercase;
}
/* Account Management Styles */
.account-section {
margin-top: 20px;
}
.section-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 12px;
}
.section-header h4 {
margin: 0;
font-size: 10px;
font-weight: 400;
color: #00ff41;
text-shadow: 0 0 10px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
}
.header-actions {
display: flex;
gap: 6px;
}
.btn-small {
padding: 4px 8px;
font-size: 6px;
}
.btn-secondary {
border-color: #888;
color: #888;
text-shadow: 0 0 8px #888;
}
.btn-secondary:hover {
background: rgba(136, 136, 136, 0.1);
box-shadow: 0 0 15px rgb(136 136 136 / 60%);
}
.accounts-container {
background: #16213e;
border: 2px solid #00ff41;
border-radius: 0;
padding: 8px;
max-height: 180px;
overflow-y: auto;
box-shadow: 0 0 10px rgb(0 255 65 / 30%);
}
.accounts-container::-webkit-scrollbar {
width: 8px;
}
.accounts-container::-webkit-scrollbar-track {
background: #16213e;
border: 1px solid #00ff41;
}
.accounts-container::-webkit-scrollbar-thumb {
background: #00ff41;
border-radius: 0;
box-shadow: 0 0 5px #00ff41;
}
.account-placeholder {
text-align: center;
padding: 20px;
color: #00ff41bb;
display: flex;
flex-direction: column;
align-items: center;
gap: 6px;
}
.placeholder-icon {
font-size: 24px;
opacity: 0.7;
}
.placeholder-text {
font-size: 8px;
text-transform: uppercase;
letter-spacing: 1px;
text-shadow: 0 0 5px #00ff41;
}
.placeholder-subtext {
font-size: 6px;
opacity: 0.8;
}
.account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 8px;
margin-bottom: 6px;
background: rgba(0, 255, 65, 0.05);
border: 1px solid rgba(0, 255, 65, 0.3);
transition: all 0.3s ease;
position: relative;
cursor: pointer;
}
.account-item:last-child {
margin-bottom: 0;
}
.account-item:hover {
background: rgba(0, 255, 65, 0.1);
border-color: #00ff41;
box-shadow: 0 0 10px rgb(0 255 65 / 40%);
}
.account-item.active {
background: rgba(57, 255, 20, 0.15);
border-color: #39ff14;
box-shadow: 0 0 15px rgb(57 255 20 / 50%);
}
.account-item.active::before {
content: '🟢';
position: absolute;
left: -8px;
top: 50%;
transform: translateY(-50%);
font-size: 8px;
animation: pixel-blink 2s infinite;
}
/* Account item info styling - simplified like old version */
.account-info {
flex: 1;
display: flex;
flex-direction: column;
gap: 4px;
}
.account-name {
font-size: 8px;
color: #00ff41;
text-shadow: 0 0 8px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
font-weight: 400;
margin-bottom: 2px;
}
.account-stats {
font-size: 11px;
color: #00ff41bb;
text-shadow: 0 0 5px #00ff41;
display: flex;
align-items: center;
gap: 8px;
}
.account-stats span {
color: #39ff14;
text-shadow: 0 0 5px #39ff14;
font-size: 11px;
display: flex;
align-items: center;
gap: 2px;
}
/* Legacy styles for backward compatibility */
.account-status {
display: flex;
flex-direction: column;
gap: 2px;
}
.pixel-status {
font-size: 6px;
display: flex;
align-items: center;
gap: 2px;
color: #00ff41bb;
}
.pixel-current {
color: #39ff14;
font-weight: bold;
text-shadow: 0 0 5px #39ff14;
}
.pixel-max {
color: #00ff41;
text-shadow: 0 0 5px #00ff41;
}
.pixel-percentage {
color: #ff6b35;
font-size: 5px;
text-shadow: 0 0 5px #ff6b35;
margin-left: 4px;
}
.alliance-status {
font-size: 6px;
display: flex;
align-items: center;
gap: 3px;
}
.alliance-status.has-alliance {
color: #39ff14;
text-shadow: 0 0 5px #39ff14;
}
.alliance-status.no-alliance {
color: #888;
text-shadow: 0 0 5px #888;
opacity: 0.7;
}
.total-pixels {
font-size: 6px;
color: #06b6d4;
text-shadow: 0 0 5px #06b6d4;
display: flex;
align-items: center;
gap: 3px;
}
.account-id {
font-size: 6px;
color: #00ff41bb;
text-shadow: 0 0 5px #00ff41;
}
.account-actions {
display: flex;
gap: 4px;
}
.btn-icon {
padding: 4px;
min-width: 20px;
font-size: 8px;
display: flex;
align-items: center;
justify-content: center;
}
/* Modal Styles */
.modal-overlay {
position: fixed;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0, 0, 0, 0.8);
display: flex;
align-items: center;
justify-content: center;
z-index: 1000;
backdrop-filter: blur(2px);
}
.modal-content {
background: #1a1a2e;
border: 2px solid #00ff41;
padding: 0;
min-width: 280px;
max-width: 320px;
box-shadow: 0 0 30px rgb(0 255 65 / 60%);
position: relative;
}
.modal-header {
background: #16213e;
border-bottom: 2px solid #00ff41;
padding: 12px 15px;
display: flex;
justify-content: space-between;
align-items: center;
}
.modal-header h4 {
margin: 0;
font-size: 9px;
color: #00ff41;
text-shadow: 0 0 10px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
}
.modal-close {
background: none;
border: none;
color: #ff073a;
font-size: 12px;
cursor: pointer;
padding: 0;
width: 20px;
height: 20px;
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 0 8px #ff073a;
transition: all 0.3s ease;
}
.modal-close:hover {
transform: scale(1.2);
text-shadow: 0 0 15px #ff073a;
}
.modal-body {
padding: 15px;
}
.form-group {
margin-bottom: 12px;
}
.form-group label {
display: block;
font-size: 7px;
color: #00ff41;
text-shadow: 0 0 8px #00ff41;
text-transform: uppercase;
letter-spacing: 1px;
margin-bottom: 4px;
}
.form-group input,
.form-group textarea {
width: 100%;
padding: 6px 8px;
background: #16213e;
border: 2px solid rgba(0, 255, 65, 0.5);
color: #00ff41;
font-family: 'Press Start 2P', monospace;
font-size: 6px;
resize: vertical;
box-sizing: border-box;
}
.form-group input:focus,
.form-group textarea:focus {
outline: none;
border-color: #00ff41;
box-shadow: 0 0 10px rgb(0 255 65 / 50%);
}
.form-group input::placeholder,
.form-group textarea::placeholder {
color: #00ff41bb;
opacity: 0.7;
}
.modal-footer {
background: #16213e;
border-top: 2px solid #00ff41;
padding: 12px 15px;
display: flex;
gap: 8px;
justify-content: flex-end;
}
/* Loading States */
.loading {
opacity: 0.6;
pointer-events: none;
}
.loading .btn {
animation: pixel-blink 1s infinite;
}
/* Notification system */
.notification {
position: fixed;
top: 10px;
right: 10px;
padding: 8px 12px;
background: #16213e;
border: 2px solid #00ff41;
color: #00ff41;
font-size: 7px;
z-index: 2000;
max-width: 200px;
animation: slideInRight 0.3s ease, fadeOut 0.3s ease 2.7s;
box-shadow: 0 0 15px rgb(0 255 65 / 50%);
}
.notification.error {
border-color: #ff073a;
color: #ff073a;
box-shadow: 0 0 15px rgb(255 7 58 / 50%);
}
.notification.success {
border-color: #39ff14;
color: #39ff14;
box-shadow: 0 0 15px rgb(57 255 20 / 50%);
}
.notification.warning {
border-color: #ff6b35;
color: #ff6b35;
box-shadow: 0 0 15px rgb(255 107 53 / 50%);
}
@keyframes slideInRight {
from { transform: translateX(100%); opacity: 0; }
to { transform: translateX(0); opacity: 1; }
}
@keyframes fadeOut {
from { opacity: 1; }
to { opacity: 0; }
}
/* Enhanced account item hover states */
.account-item:hover .account-actions {
opacity: 1;
}
.account-actions {
opacity: 0.7;
transition: opacity 0.3s ease;
}
/* Enhanced loading state */
.loading .account-item {
pointer-events: none;
opacity: 0.6;
}
.loading .btn {
opacity: 0.6;
cursor: not-allowed;
}
/* Better modal animations */
.modal-overlay {
animation: modalFadeIn 0.3s ease;
}
.modal-content {
animation: modalSlideIn 0.3s ease;
}
@keyframes modalFadeIn {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes modalSlideIn {
from { transform: translateY(-20px) scale(0.95); opacity: 0; }
to { transform: translateY(0) scale(1); opacity: 1; }
}
/* Better form validation states */
.form-group input:invalid,
.form-group textarea:invalid {
border-color: #ff073a;
box-shadow: 0 0 5px rgb(255 7 58 / 30%);
}
.form-group input:valid,
.form-group textarea:valid {
border-color: #39ff14;
box-shadow: 0 0 5px rgb(57 255 20 / 30%);
}
/* Improved active account indicator */
.account-item.active .account-name {
color: #39ff14;
text-shadow: 0 0 10px #39ff14;
}
.account-item.active .account-id {
color: #39ff14bb;
text-shadow: 0 0 8px #39ff14;
}
/* Better disabled state for buttons */
.btn:disabled {
opacity: 0.5;
cursor: not-allowed;
pointer-events: none;
}
.logs-section {
margin-top: 20px;
}
.logs {
background: #16213e;
border: 2px solid #00ff41;
border-radius: 0;
padding: 8px;
max-height: 120px;
overflow-y: auto;
font-size: 6px;
box-shadow: 0 0 10px rgb(0 255 65 / 30%);
}
.logs::-webkit-scrollbar {
width: 8px;
}
.logs::-webkit-scrollbar-track {
background: #16213e;
border: 1px solid #00ff41;
}
.logs::-webkit-scrollbar-thumb {
background: #00ff41;
border-radius: 0;
box-shadow: 0 0 5px #00ff41;
}
.log-entry {
padding: 4px 0;
color: #00ff41dd;
border-bottom: 1px solid rgba(0, 255, 65, 0.3);
text-shadow: 0 0 5px #00ff41;
line-height: 1.4;
}
.log-entry:last-child {
border-bottom: none;
}
.log-entry.success {
color: #39ff14;
text-shadow: 0 0 8px #39ff14;
}
.log-entry.error {
color: #ff073a;
text-shadow: 0 0 8px #ff073a;
}
.log-entry.warning {
color: #ff6b35;
text-shadow: 0 0 8px #ff6b35;
}
footer {
background: #16213e;
border-top: 2px solid #00ff41;
padding: 10px 15px;
}
.footer-info {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 6px;
color: #00ff41dd;
text-shadow: 0 0 5px #00ff41;
text-transform: uppercase;
}
.footer-info a {
color: #ff6b35;
text-decoration: none;
text-shadow: 0 0 8px #ff6b35;
transition: all 0.3s ease;
}
.footer-info a:hover {
color: #ff6b35;
text-shadow: 0 0 15px #ff6b35;
animation: pixel-blink 0.5s infinite;
}
/* Additional neon animations */
@keyframes neon-sweep {
0% { opacity: 0; transform: translateX(-100%) translateY(-100%) rotate(45deg); }
50% { opacity: 1; }
100% { opacity: 0; transform: translateX(100%) translateY(100%) rotate(45deg); }
}
@keyframes card-glow {
0% { box-shadow: 0 0 20px rgb(0 255 65 / 60%), inset 0 0 20px rgb(0 255 65 / 15%); }
100% { box-shadow: 0 0 30px rgb(0 255 65 / 80%), inset 0 0 30px rgb(0 255 65 / 20%); }
}
+116
View File
@@ -0,0 +1,116 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>WPlace AutoBOT</title>
<link rel="stylesheet" href="popup.css">
</head>
<body>
<div class="container">
<header>
<div class="header-content">
<img src="../icons/icon32.png" alt="AutoBOT" class="header-icon">
<h1>WPlace AutoBOT</h1>
</div>
<div class="status-indicator" id="statusIndicator">
<span class="status-dot" id="statusDot"></span>
<span id="statusText">Ready</span>
</div>
</header>
<main>
<div class="script-section">
<h3>Available Scripts</h3>
<div class="scripts-list" id="scriptsList">
<!-- Scripts will be populated here -->
</div>
</div>
<div class="account-section">
<div class="section-header">
<h4>Account Manager</h4>
<div class="header-actions">
<button class="btn btn-primary btn-small" id="refreshAccountBtn" title="Add Current Account">
<span class="icon">🔄</span>
Refresh
</button>
<button class="btn btn-primary btn-small" id="addAccountBtn" title="Add Account Manually">
<span class="icon">➕</span>
Add
</button>
</div>
</div>
<div class="accounts-container" id="accountsContainer">
<div class="account-placeholder" id="accountPlaceholder">
<span class="placeholder-icon">👤</span>
<span class="placeholder-text">No accounts saved</span>
<span class="placeholder-subtext">Click "Refresh" to add current account</span>
</div>
</div>
</div>
<!-- Add Account Modal -->
<div class="modal-overlay" id="addAccountModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h4>Add Account Manually</h4>
<button class="modal-close" id="closeModalBtn">✕</button>
</div>
<div class="modal-body">
<div class="form-group">
<label for="accountName">Account Name</label>
<input type="text" id="accountName" placeholder="Enter account name" maxlength="50">
</div>
<div class="form-group">
<label for="accountToken">Token (Cookie Value)</label>
<textarea id="accountToken" placeholder="Paste authentication token here" rows="3"></textarea>
</div>
</div>
<div class="modal-footer">
<button class="btn btn-primary" id="saveAccountBtn">
<span class="icon">💾</span>
Save Account
</button>
<button class="btn btn-secondary" id="cancelAccountBtn">
<span class="icon">✕</span>
Cancel
</button>
</div>
</div>
</div>
<!-- Settings Modal -->
<div class="modal-overlay" id="settingsModal" style="display: none;">
<div class="modal-content">
<div class="modal-header">
<h4>Settings</h4>
<button class="modal-close" id="closeSettingsBtn">✕</button>
</div>
<div class="settings-section">
<h3>Startup Script</h3>
<p style="font-size: 8px; color: #888; margin-bottom: 10px;">Select a script to run automatically when visiting wplace.live</p>
<select id="startupScriptSelect" class="settings-select">
<option value="">None</option>
<!-- Scripts will be populated here -->
</select>
</div>
<div class="modal-footer">
<button class="btn btn-secondary" id="closeSettingsModalBtn">Close</button>
</div>
</div>
</div>
</main>
<footer>
<div class="footer-info">
<span>v2.0.2</span>
<a href="#" id="settingsBtn">Settings</a>
</div>
</footer>
</div>
<script src="popup.js"></script>
</body>
</html>
+701
View File
@@ -0,0 +1,701 @@
// WPlace AutoBOT Popup Script with Account Management
// Available scripts configuration - Script Manager as primary launcher
const AVAILABLE_SCRIPTS = [
{
name: 'Script-manager-fixed.js',
displayName: '⚡ Script Manager (Fixed)',
description: 'Neon-themed script launcher with proper communication bridge',
filename: 'Script-manager-fixed.js',
isPrimary: true
},
{
name: 'Auto-Farm.js',
displayName: '🌾 Auto Farm',
description: 'Automated farming and pixel painting',
filename: 'Auto-Farm.js'
},
{
name: 'Auto-Image.js',
displayName: '🖼️ Auto Image',
description: 'Automated image processing and placement with account switching',
filename: 'Auto-Image.js'
},
{
name: 'Auto-Repair.js',
displayName: '🔧 Auto Repair',
description: 'Automated repair and maintenance tasks',
filename: 'Auto-Repair.js'
},
{
name: 'Art-Extractor.js',
displayName: '🎨 Art Extractor',
description: 'Extract artwork areas from canvas and save as JSON files',
filename: 'Art-Extractor.js'
}
];
// DOM elements
let statusDot, statusText, scriptsList, accountsContainer, accountPlaceholder;
let addAccountModal, accountNameInput, accountTokenInput;
let refreshAccountBtn, addAccountBtn, saveAccountBtn, cancelAccountBtn, closeModalBtn;
// Global state
let accounts = [];
let currentAccountIndex = -1;
let isLoading = false;
document.addEventListener('DOMContentLoaded', async () => {
initializeElements();
await loadScripts();
await loadAccounts();
setupEventListeners();
checkCurrentTab();
console.log('🚀 Account Manager initialized');
});
function initializeElements() {
// Existing elements
statusDot = document.getElementById('statusDot');
statusText = document.getElementById('statusText');
scriptsList = document.getElementById('scriptsList');
// Account management elements
accountsContainer = document.getElementById('accountsContainer');
accountPlaceholder = document.getElementById('accountPlaceholder');
// Modal elements
addAccountModal = document.getElementById('addAccountModal');
accountNameInput = document.getElementById('accountName');
accountTokenInput = document.getElementById('accountToken');
// Button elements
refreshAccountBtn = document.getElementById('refreshAccountBtn');
addAccountBtn = document.getElementById('addAccountBtn');
saveAccountBtn = document.getElementById('saveAccountBtn');
cancelAccountBtn = document.getElementById('cancelAccountBtn');
closeModalBtn = document.getElementById('closeModalBtn');
}
async function loadScripts() {
scriptsList.innerHTML = '';
AVAILABLE_SCRIPTS.forEach(script => {
const scriptElement = createScriptElement(script);
scriptsList.appendChild(scriptElement);
});
}
function createScriptElement(script) {
const div = document.createElement('div');
div.className = 'script-item';
// Add special styling for primary script
if (script.isPrimary) {
div.style.background = 'rgba(0,255,65,0.1)';
div.style.boxShadow = '0 0 15px rgb(0 255 65 / 30%)';
}
div.innerHTML = `
<div class="script-info-left">
<div class="script-name">${script.displayName}</div>
<div class="script-description">${script.description}</div>
</div>
<button class="btn ${script.isPrimary ? 'btn-primary' : 'btn-primary'} launch-btn" data-script="${script.filename}">
<span class="icon">${script.isPrimary ? '⚡' : '▶'}</span>
${script.isPrimary ? 'Open' : 'Launch'}
</button>
`;
return div;
}
function setupEventListeners() {
// Launch script buttons
scriptsList.addEventListener('click', async (e) => {
if (e.target.classList.contains('launch-btn') || e.target.closest('.launch-btn')) {
const btn = e.target.closest('.launch-btn') || e.target;
const scriptName = btn.dataset.script;
await launchScript(scriptName, btn);
}
});
// Account management buttons
refreshAccountBtn.addEventListener('click', handleRefreshAccount);
addAccountBtn.addEventListener('click', openAddAccountModal);
saveAccountBtn.addEventListener('click', handleSaveAccount);
cancelAccountBtn.addEventListener('click', closeAddAccountModal);
closeModalBtn.addEventListener('click', closeAddAccountModal);
// Modal close on overlay click
addAccountModal.addEventListener('click', (e) => {
if (e.target === addAccountModal) {
closeAddAccountModal();
}
});
// Account container click handling
accountsContainer.addEventListener('click', handleAccountClick);
// Settings button
document.getElementById('settingsBtn').addEventListener('click', (e) => {
e.preventDefault();
document.getElementById('settingsModal').style.display = 'flex';
loadStartupScript(); // Load current startup script selection
populateStartupScriptSelect(); // Populate script options
});
// Settings modal close buttons
document.getElementById('closeSettingsBtn').addEventListener('click', () => {
document.getElementById('settingsModal').style.display = 'none';
});
document.getElementById('closeSettingsModalBtn').addEventListener('click', () => {
document.getElementById('settingsModal').style.display = 'none';
});
// Settings modal close on overlay click
document.getElementById('settingsModal').addEventListener('click', (e) => {
if (e.target === document.getElementById('settingsModal')) {
document.getElementById('settingsModal').style.display = 'none';
}
});
// Startup script selection
document.getElementById('startupScriptSelect').addEventListener('change', async (e) => {
const scriptName = e.target.value;
await chrome.storage.local.set({ startupScript: scriptName });
showNotification(scriptName ?
`Startup script set to ${AVAILABLE_SCRIPTS.find(s => s.filename === scriptName).displayName}` :
'Startup script disabled',
'success'
);
});
}
async function loadAccounts() {
try {
setLoading(true);
const response = await chrome.runtime.sendMessage({ type: 'getAccounts' });
const storedAccounts = response?.accounts || [];
const result = await chrome.storage.local.get('infoAccounts');
const infoAccounts = result.infoAccounts || [];
accounts = storedAccounts.map((token, index) => {
const info = infoAccounts.find(acc => acc.token === token);
console.log(`Account ${index + 1} info:`, info);
return {
index,
token,
name: info?.name,
id: info?.ID,
Charges: info?.Charges,
Max: info?.Max,
Droplets: info?.Droplets,
maxPixels: info?.Max,
droplets: info?.Droplets,
allianceName: info?.allianceName,
allianceRole: info?.allianceRole,
totalPixelsPainted: info?.totalPixelsPainted,
level: info?.level,
};
});
renderAccounts();
console.log('📂 Loaded accounts with enhanced status:', accounts);
} catch (error) {
console.error('❌ Error loading accounts:', error);
showNotification('Failed to load accounts', 'error');
} finally {
setLoading(false);
}
}
function renderAccounts() {
if (accounts.length === 0) {
accountPlaceholder.style.display = 'flex';
accountsContainer.innerHTML = '';
accountsContainer.appendChild(accountPlaceholder);
return;
}
accountPlaceholder.style.display = 'none';
accountsContainer.innerHTML = '';
accounts.forEach((account, index) => {
const accountElement = createAccountElement(account, index);
accountsContainer.appendChild(accountElement);
});
}
function createAccountElement(account, index) {
const div = document.createElement('div');
div.className = `account-item ${account.isActive ? 'active' : ''}`;
div.dataset.index = index;
// Format like the old version: Name, Charges/Max, and Droplets
const charges = Math.floor(account.Charges);
const max = Math.floor(account.Max);
const droplets = Math.floor(account.Droplets);
div.innerHTML = `
<div class="account-info">
<div class="account-name">${escapeHtml(account.name)}</div>
<div class="account-stats">
<span>⚡ ${charges}/${max}</span>
<span>💧 ${droplets}</span>
</div>
</div>
<div class="account-actions">
<button class="btn btn-primary btn-icon switch-btn" title="Switch to this account" data-action="switch" data-index="${index}">
<span class="icon">🔄</span>
</button>
<button class="btn btn-danger btn-icon delete-btn" title="Delete account" data-action="delete" data-index="${index}">
<span class="icon">🗑️</span>
</button>
</div>
`;
return div;
}
async function handleAccountClick(e) {
e.preventDefault();
e.stopPropagation();
const action = e.target.dataset.action || e.target.closest('[data-action]')?.dataset.action;
const index = parseInt(e.target.dataset.index || e.target.closest('[data-index]')?.dataset.index);
if (isNaN(index) || index < 0 || index >= accounts.length) return;
switch (action) {
case 'switch':
await switchToAccount(index);
break;
case 'delete':
await deleteAccount(index);
break;
default:
// Click on account item itself - switch account
await switchToAccount(index);
break;
}
}
async function switchToAccount(index) {
try {
if (isLoading) return;
setLoading(true);
showNotification('Switching account...', 'info');
const account = accounts[index];
console.log('🔄 Switching to account:', account);
// Send switch message to background
const response = await chrome.runtime.sendMessage({
type: 'setCookie',
value: account.token
});
if (response?.status === 'ok') {
// Update active state
accounts.forEach((acc, i) => acc.isActive = i === index);
currentAccountIndex = index;
renderAccounts();
showNotification(`Switched to ${account.name}`, 'success');
console.log('✅ Account switched successfully');
} else {
throw new Error(response?.error || 'Failed to switch account');
}
} catch (error) {
console.error('❌ Error switching account:', error);
showNotification('Failed to switch account', 'error');
} finally {
setLoading(false);
}
}
async function deleteAccount(index) {
try {
if (isLoading) return;
const account = accounts[index];
// Confirm deletion
if (!confirm(`Delete account "${account.name}"?\n\nThis action cannot be undone.`)) {
return;
}
setLoading(true);
showNotification('Deleting account...', 'info');
console.log('🗑️ Deleting account:', account);
// Send delete message to background
const response = await chrome.runtime.sendMessage({
type: 'deleteAccount',
index: account.index
});
if (response?.status === 'ok') {
// Remove from local array
accounts.splice(index, 1);
// Update indices
accounts.forEach((acc, i) => acc.index = i);
// Clear active state if deleted account was active
if (currentAccountIndex === index) {
currentAccountIndex = -1;
} else if (currentAccountIndex > index) {
currentAccountIndex--;
}
renderAccounts();
showNotification(`Deleted ${account.name}`, 'success');
console.log('✅ Account deleted successfully');
} else {
throw new Error('Failed to delete account');
}
} catch (error) {
console.error('❌ Error deleting account:', error);
showNotification('Failed to delete account', 'error');
} finally {
setLoading(false);
}
}
async function handleRefreshAccount() {
try {
if (isLoading) return;
setLoading(true);
showNotification('Fetching current account...', 'info');
console.log('🔄 Refreshing account from current session');
// Get current tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab.url.includes('wplace.live')) {
throw new Error('Please navigate to wplace.live first');
}
// Trigger account preservation which will add current account
await chrome.runtime.sendMessage({ type: 'refreshCurrentAccount' });
// Wait a bit for the account to be processed
setTimeout(async () => {
await loadAccounts();
showNotification('Account refreshed successfully', 'success');
}, 2000);
} catch (error) {
console.error('❌ Error refreshing account:', error);
showNotification(error.message || 'Failed to refresh account', 'error');
setLoading(false);
}
}
function openAddAccountModal() {
addAccountModal.style.display = 'flex';
accountNameInput.value = '';
accountTokenInput.value = '';
accountNameInput.focus();
}
function closeAddAccountModal() {
addAccountModal.style.display = 'none';
accountNameInput.value = '';
accountTokenInput.value = '';
}
async function handleSaveAccount() {
try {
const name = accountNameInput.value.trim();
const token = accountTokenInput.value.trim();
if (!name) {
showNotification('Please enter an account name', 'error');
accountNameInput.focus();
return;
}
if (!token) {
showNotification('Please enter a token', 'error');
accountTokenInput.focus();
return;
}
// Basic token validation
if (token.length < 10) {
showNotification('Token appears to be too short', 'error');
accountTokenInput.focus();
return;
}
setLoading(true);
showNotification('Validating and saving account...', 'info');
console.log('💾 Saving manual account:', { name, token: token.substring(0, 20) + '...' });
// Validate token format (basic check)
const validation = await validateAccountToken(token);
if (!validation.valid) {
showNotification(validation.error || 'Invalid token format', 'error');
return;
}
const result = await chrome.storage.local.get('infoAccounts');
let infoAccounts = result.infoAccounts || [];
// Check if account already exists by token
const existingIndex = infoAccounts.findIndex(acc => acc.token === token);
const accountInfo = {
ID: 'manual_' + Date.now(), // Placeholder ID for manual accounts
name: name,
token: token,
Charges: 0,
Max: 100,
Droplets: 0,
currentPixels: 0,
maxPixels: 100,
droplets: 0,
alliance: 'Unknown',
allianceName: 'Unknown',
allianceRole: 'Member',
totalPixelsPainted: 0,
level: 1,
};
if (existingIndex > -1) {
// Update existing account
infoAccounts[existingIndex] = accountInfo;
showNotification('Account updated successfully', 'success');
} else {
// Add new account
infoAccounts.push(accountInfo);
showNotification('Account added successfully', 'success');
}
// Save to storage
await chrome.storage.local.set({ infoAccounts });
// Update accounts list in background
const accounts = infoAccounts.map(info => info.token);
await chrome.storage.local.set({ accounts });
closeAddAccountModal();
await loadAccounts();
console.log('✅ Manual account saved successfully');
} catch (error) {
console.error('❌ Error saving account:', error);
showNotification('Failed to save account', 'error');
} finally {
setLoading(false);
}
}
async function launchScript(scriptName, button) {
try {
showNotification('Launching ' + scriptName + '...', 'info');
// Update button state
button.disabled = true;
const originalContent = button.innerHTML;
button.innerHTML = '<span class="icon">⏳</span> Launching...';
// Get active tab
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (!tab.url.includes('wplace.live')) {
throw new Error('Please navigate to wplace.live first');
}
// Send message to background script
const response = await chrome.runtime.sendMessage({
action: 'executeScript',
scriptName: scriptName,
tabId: tab.id
});
if (response && response.success) {
button.innerHTML = '<span class="icon">✅</span> Launched';
button.style.background = '#4CAF50';
showNotification(scriptName + ' executed successfully', 'success');
// Close popup after success
setTimeout(() => {
window.close();
}, 1500);
} else {
throw new Error(response?.error || 'Failed to execute script');
}
} catch (error) {
console.error('Error launching script:', error);
showNotification('Failed to launch ' + scriptName + ': ' + error.message, 'error');
// Reset button
button.disabled = false;
button.innerHTML = originalContent;
button.style.background = '';
}
}
async function checkCurrentTab() {
try {
const [tab] = await chrome.tabs.query({ active: true, currentWindow: true });
if (tab.url.includes('wplace.live')) {
statusDot.className = 'status-dot';
statusText.textContent = 'Ready';
showNotification('Ready to launch scripts on wplace.live', 'success');
} else {
statusDot.className = 'status-dot error';
statusText.textContent = 'Not on WPlace';
showNotification('Please navigate to wplace.live to use scripts', 'warning');
// Disable all launch buttons
document.querySelectorAll('.launch-btn').forEach(btn => {
btn.disabled = true;
});
}
} catch (error) {
statusDot.className = 'status-dot error';
statusText.textContent = 'Error';
showNotification('Error checking current tab: ' + error.message, 'error');
}
}
function setLoading(loading) {
isLoading = loading;
document.body.classList.toggle('loading', loading);
// Disable buttons during loading
refreshAccountBtn.disabled = loading;
addAccountBtn.disabled = loading;
saveAccountBtn.disabled = loading;
}
function showNotification(message, type = 'info') {
// Remove existing notifications
const existingNotifications = document.querySelectorAll('.notification');
existingNotifications.forEach(n => n.remove());
// Create notification element
const notification = document.createElement('div');
notification.className = `notification ${type}`;
notification.textContent = message;
// Add to body
document.body.appendChild(notification);
// Auto remove after 3 seconds
setTimeout(() => {
if (notification.parentNode) {
notification.remove();
}
}, 3000);
// Update status indicator
if (type === 'error') {
statusDot.className = 'status-dot error';
statusText.textContent = 'Error';
} else if (type === 'success') {
statusDot.className = 'status-dot';
statusText.textContent = 'Success';
} else if (type === 'warning') {
statusDot.className = 'status-dot running';
statusText.textContent = 'Warning';
} else {
statusDot.className = 'status-dot';
statusText.textContent = 'Info';
}
// Only reset status if it's not the initial "Ready to launch" message
if (message !== 'Ready to launch scripts on wplace.live') {
setTimeout(() => {
checkCurrentTab();
}, 3000);
}
}
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
// Populate startup script select options
function populateStartupScriptSelect() {
const select = document.getElementById('startupScriptSelect');
select.innerHTML = '<option value="">None</option>';
AVAILABLE_SCRIPTS.forEach(script => {
const option = document.createElement('option');
option.value = script.filename;
option.textContent = script.displayName;
select.appendChild(option);
});
}
// Load startup script selection from storage
async function loadStartupScript() {
const result = await chrome.storage.local.get('startupScript');
const select = document.getElementById('startupScriptSelect');
if (select) {
select.value = result.startupScript || '';
}
}
// Enhanced account validation
async function validateAccountToken(token) {
try {
// This would typically make a request to validate the token
// For now, we'll do basic validation
if (!token || typeof token !== 'string' || token.length < 10) {
return { valid: false, error: 'Token appears to be invalid' };
}
// You could add actual API validation here
return { valid: true };
} catch (error) {
return { valid: false, error: error.message };
}
}
// Keyboard shortcuts
document.addEventListener('keydown', (e) => {
// Escape key closes modal
if (e.key === 'Escape' && addAccountModal.style.display === 'flex') {
closeAddAccountModal();
}
// Ctrl+R or F5 refreshes accounts
if ((e.ctrlKey && e.key === 'r') || e.key === 'F5') {
e.preventDefault();
handleRefreshAccount();
}
// Ctrl+N adds new account
if (e.ctrlKey && e.key === 'n') {
e.preventDefault();
openAddAccountModal();
}
});
File diff suppressed because it is too large. Load diff
+650
View File
@@ -0,0 +1,650 @@
// eslint-disable-next-line prettier/prettier
; (async () => {
const CONFIG = {
START_X: 742,
START_Y: 1148,
PIXELS_PER_LINE: 100,
DELAY: 1000,
THEME: {
primary: '#000000',
secondary: '#111111',
accent: '#222222',
text: '#ffffff',
highlight: '#775ce3',
success: '#00ff00',
error: '#ff0000',
},
};
const state = {
running: false,
paintedCount: 0,
charges: { count: 0, max: 80, cooldownMs: 30000 },
userInfo: null,
lastPixel: null,
minimized: false,
menuOpen: false,
language: 'en',
autoRefresh: true,
pausedForManual: false,
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const waitForSelector = async (selector, interval = 200, timeout = 5000) => {
const start = Date.now();
while (Date.now() - start < timeout) {
const el = document.querySelector(selector);
if (el) return el;
await sleep(interval);
}
return null;
};
const originalFetch = window.fetch;
let capturedCaptchaToken = null;
window.fetch = async (url, options = {}) => {
if (typeof url === 'string' && url.includes('https://backend.wplace.live/s0/pixel/')) {
try {
const payload = JSON.parse(options.body || '{}');
if (payload.t) {
console.log('✅ CAPTCHA Token Captured:', payload.t);
capturedCaptchaToken = payload.t;
if (state.pausedForManual) {
state.pausedForManual = false;
state.running = true;
updateUI(
state.language === 'pt' ? '🚀 Pintura reiniciada!' : '🚀 Farm resumed!',
'success'
);
paintLoop();
}
}
} catch (e) {}
}
return originalFetch(url, options);
};
const fetchAPI = async (url, options = {}) => {
try {
const res = await fetch(url, {
credentials: 'include',
...options,
});
return await res.json();
} catch (e) {
return null;
}
};
const getRandomPosition = () => ({
x: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE),
y: Math.floor(Math.random() * CONFIG.PIXELS_PER_LINE),
});
const paintPixel = async (x, y) => {
const randomColor = Math.floor(Math.random() * 31) + 1;
const url = `https://backend.wplace.live/s0/pixel/${CONFIG.START_X}/${CONFIG.START_Y}`;
const payload = JSON.stringify({
coords: [x, y],
colors: [randomColor],
t: capturedCaptchaToken,
});
try {
const res = await originalFetch(url, {
method: 'POST',
headers: { 'Content-Type': 'text/plain;charset=UTF-8' },
credentials: 'include',
body: payload,
});
if (res.status === 403) {
console.error('❌ 403 Forbidden. CAPTCHA token might be invalid or expired.');
capturedCaptchaToken = null;
stoppedForToken = true;
return 'token_error';
}
const data = await res.json();
return data;
} catch (e) {
return null;
}
};
const getCharge = async () => {
const data = await fetchAPI('https://backend.wplace.live/me');
if (data) {
state.userInfo = data;
state.charges = {
count: Math.floor(data.charges.count),
max: Math.floor(data.charges.max),
cooldownMs: data.charges.cooldownMs,
};
if (state.userInfo.level) {
state.userInfo.level = Math.floor(state.userInfo.level);
}
}
return state.charges;
};
const detectUserLocation = async () => {
try {
const response = await fetch('https://ipapi.co/json/');
const data = await response.json();
if (data.country === 'BR') {
state.language = 'pt';
} else if (data.country === 'US') {
state.language = 'en';
} else {
state.language = 'en';
}
} catch {
state.language = 'en';
}
};
const paintLoop = async () => {
while (state.running) {
const { count, cooldownMs } = state.charges;
if (count < 1) {
updateUI(
state.language === 'pt'
? `⌛ Sem cargas. Esperando ${Math.ceil(cooldownMs / 1000)}s...`
: `⌛ No charges. Waiting ${Math.ceil(cooldownMs / 1000)}s...`,
'status'
);
await sleep(cooldownMs);
await getCharge();
continue;
}
const randomPos = getRandomPosition();
const paintResult = await paintPixel(randomPos.x, randomPos.y);
if (paintResult === 'token_error') {
if (state.autoRefresh) {
await getCharge();
if (state.charges.count < 2) {
if (!state.pausedForManual) {
updateUI(
state.language === 'pt'
? '⚡ Aguardando pelo menos 2 cargas para auto-refresh...'
: 'Waiting for at least 2 charges for auto-refresh...',
'status'
);
state.pausedForManual = true;
}
while (state.charges.count < 2) {
await sleep(60000);
await getCharge();
updateStats();
}
state.pausedForManual = false;
}
updateUI(
state.language === 'pt'
? '❌ Token expirado. Aguardando elemento Paint...'
: '❌ CAPTCHA token expired. Waiting for Paint button...',
'error'
);
const mainPaintBtn = await waitForSelector(
'button.btn.btn-primary.btn-lg, button.btn-primary.sm\\:btn-xl'
);
if (mainPaintBtn) mainPaintBtn.click();
await sleep(500);
updateUI(
state.language === 'pt' ? 'Selecionando transparente...' : 'Selecting transparent...',
'status'
);
const transBtn = await waitForSelector('button#color-0');
if (transBtn) transBtn.click();
await sleep(500);
const canvas = await waitForSelector('canvas');
if (canvas) {
canvas.setAttribute('tabindex', '0');
canvas.focus();
const rect = canvas.getBoundingClientRect();
const centerX = Math.round(rect.left + rect.width / 2);
const centerY = Math.round(rect.top + rect.height / 2);
const moveEvt = new MouseEvent('mousemove', {
clientX: centerX,
clientY: centerY,
bubbles: true,
});
canvas.dispatchEvent(moveEvt);
const keyDown = new KeyboardEvent('keydown', {
key: ' ',
code: 'Space',
bubbles: true,
});
const keyUp = new KeyboardEvent('keyup', { key: ' ', code: 'Space', bubbles: true });
canvas.dispatchEvent(keyDown);
canvas.dispatchEvent(keyUp);
}
await sleep(500);
updateUI(
state.language === 'pt' ? 'Confirmando pintura...' : 'Confirming paint...',
'status'
);
let confirmBtn = await waitForSelector(
'button.btn.btn-primary.btn-lg, button.btn.btn-primary.sm\\:btn-xl'
);
if (!confirmBtn) {
const allPrimary = Array.from(document.querySelectorAll('button.btn-primary'));
confirmBtn = allPrimary.length ? allPrimary[allPrimary.length - 1] : null;
}
confirmBtn?.click();
} else {
// insufficient charges or auto-refresh disabled
if (state.autoRefresh && state.charges.count < 2) {
updateUI(
state.language === 'pt'
? '⚡ Cargas insuficientes para auto-refresh. Por favor, clique manualmente.'
: 'Insufficient charges for auto-refresh. Please click manually.',
'error'
);
}
if (!state.pausedForManual) {
updateUI(
state.language === 'pt'
? 'Auto-refresh desativado. Por favor, clique no botão pintura manualmente.'
: 'Auto-refresh disabled. Please click the Paint button manually.',
'status'
);
state.pausedForManual = true;
}
state.running = false;
return;
}
await sleep(1000);
continue;
}
if (paintResult?.painted === 1) {
state.paintedCount++;
state.lastPixel = {
x: CONFIG.START_X + randomPos.x,
y: CONFIG.START_Y + randomPos.y,
time: new Date(),
};
state.charges.count--;
document.getElementById('paintEffect').style.animation = 'pulse 0.5s';
setTimeout(() => {
document.getElementById('paintEffect').style.animation = '';
}, 500);
updateUI(state.language === 'pt' ? '✅ Pixel pintado!' : '✅ Pixel painted!', 'success');
} else {
updateUI(state.language === 'pt' ? '❌ Falha ao pintar' : '❌ Failed to paint', 'error');
}
await sleep(CONFIG.DELAY);
updateStats();
}
};
const createUI = () => {
if (state.menuOpen) return;
state.menuOpen = true;
const fontAwesome = document.createElement('link');
fontAwesome.rel = 'stylesheet';
fontAwesome.href = 'https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css';
document.head.appendChild(fontAwesome);
const style = document.createElement('style');
style.textContent = `
@keyframes pulse {
0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); }n
70% { box-shadow: 0 0 0 10px rgba(0, 255, 0, 0); }
100% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0); }
}
@keyframes slide-in {
from { transform: translateY(20px); opacity: 0; }
to { transform: translateY(0); opacity: 1; }
}
.wplace-bot-panel {
position: fixed;
top: 20px;
right: 20px;
width: 250px;
background: ${CONFIG.THEME.primary};
border: 1px solid ${CONFIG.THEME.accent};
border-radius: 8px;
padding: 0;
box-shadow: 0 5px 15px rgba(0,0,0,0.5);
z-index: 9999;
font-family: 'Segoe UI', Roboto, sans-serif;
color: ${CONFIG.THEME.text};
animation: slide-in 0.4s ease-out;
overflow: hidden;
}
.wplace-header {
padding: 12px 15px;
background: ${CONFIG.THEME.secondary};
color: ${CONFIG.THEME.highlight};
font-size: 16px;
font-weight: 600;
display: flex;
justify-content: space-between;
align-items: center;
cursor: move;
user-select: none;
}
.wplace-header-title {
display: flex;
align-items: center;
gap: 8px;
}
.wplace-header-controls {
display: flex;
gap: 10px;
}
.wplace-header-btn {
background: none;
border: none;
color: ${CONFIG.THEME.text};
cursor: pointer;
opacity: 0.7;
transition: opacity 0.2s;
}
.wplace-header-btn:hover {
opacity: 1;
}
.wplace-content {
padding: 15px;
display: ${state.minimized ? 'none' : 'block'};
}
.wplace-controls {
display: flex;
gap: 10px;
margin-bottom: 15px;
}
.wplace-btn {
flex: 1;
padding: 10px;
border: none;
border-radius: 6px;
font-weight: 600;
cursor: pointer;
display: flex;
align-items: center;
justify-content: center;
gap: 8px;
transition: all 0.2s;
}
.wplace-btn:hover {
transform: translateY(-2px);
}
.wplace-btn-primary {
background: ${CONFIG.THEME.accent};
color: white;
}
.wplace-btn-stop {
background: ${CONFIG.THEME.error};
color: white;
}
.wplace-stats {
background: ${CONFIG.THEME.secondary};
padding: 12px;
border-radius: 6px;
margin-bottom: 15px;
}
.wplace-stat-item {
display: flex;
justify-content: space-between;
padding: 6px 0;
font-size: 14px;
}
.wplace-stat-label {
display: flex;
align-items: center;
gap: 6px;
opacity: 0.8;
}
.wplace-status {
padding: 8px;
border-radius: 4px;
text-align: center;
font-size: 13px;
}
.status-default {
background: rgba(255,255,255,0.1);
}
.status-success {
background: rgba(0, 255, 0, 0.1);
color: ${CONFIG.THEME.success};
}
.status-error {
background: rgba(255, 0, 0, 0.1);
color: ${CONFIG.THEME.error};
}
#paintEffect {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
border-radius: 8px;
}
`;
document.head.appendChild(style);
const translations = {
pt: {
title: 'WPlace Auto-Farm',
start: 'Iniciar',
stop: 'Parar',
ready: 'Pronto para começar',
user: 'Usuário',
pixels: 'Pixels',
charges: 'Cargas',
level: 'Level',
},
en: {
title: 'WPlace Auto-Farm',
start: 'Start',
stop: 'Stop',
ready: 'Ready to start',
user: 'User',
pixels: 'Pixels',
charges: 'Charges',
level: 'Level',
},
};
const t = translations[state.language] || translations.en;
const panel = document.createElement('div');
panel.className = 'wplace-bot-panel';
panel.innerHTML = `
<div id="paintEffect"></div>
<div class="wplace-header">
<div class="wplace-header-title">
<i class="fas fa-paint-brush"></i>
<span>${t.title}</span>
</div>
<div class="wplace-header-controls">
<button id="minimizeBtn" class="wplace-header-btn" title="${state.language === 'pt' ? 'Minimizar' : 'Minimize'}">
<i class="fas fa-${state.minimized ? 'expand' : 'minus'}"></i>
</button>
</div>
</div>
<div class="wplace-content">
<div class="wplace-controls">
<button id="toggleBtn" class="wplace-btn wplace-btn-primary">
<i class="fas fa-play"></i>
<span>${t.start}</span>
</button>
<label style="display:flex; align-items:center; margin-left:10px;">
<input type="checkbox" id="autoRefreshCheckbox" ${state.autoRefresh ? 'checked' : ''}/>
<span style="margin-left:4px; font-size:14px;">Auto Refresh</span>
</label>
</div>
<div class="wplace-stats">
<div id="statsArea">
<div class="wplace-stat-item">
<div class="wplace-stat-label"><i class="fas fa-paint-brush"></i> ${state.language === 'pt' ? 'Carregando...' : 'Loading...'}</div>
</div>
</div>
</div>
<div id="statusText" class="wplace-status status-default">
${t.ready}
</div>
</div>
`;
document.body.appendChild(panel);
const header = panel.querySelector('.wplace-header');
let pos1 = 0,
pos2 = 0,
pos3 = 0,
pos4 = 0;
header.onmousedown = dragMouseDown;
function dragMouseDown(e) {
if (e.target.closest('.wplace-header-btn')) return;
e = e || window.event;
e.preventDefault();
pos3 = e.clientX;
pos4 = e.clientY;
document.onmouseup = closeDragElement;
document.onmousemove = elementDrag;
}
function elementDrag(e) {
e = e || window.event;
e.preventDefault();
pos1 = pos3 - e.clientX;
pos2 = pos4 - e.clientY;
pos3 = e.clientX;
pos4 = e.clientY;
panel.style.top = panel.offsetTop - pos2 + 'px';
panel.style.left = panel.offsetLeft - pos1 + 'px';
}
function closeDragElement() {
document.onmouseup = null;
document.onmousemove = null;
}
const toggleBtn = panel.querySelector('#toggleBtn');
const minimizeBtn = panel.querySelector('#minimizeBtn');
const statusText = panel.querySelector('#statusText');
const content = panel.querySelector('.wplace-content');
const statsArea = panel.querySelector('#statsArea');
toggleBtn.addEventListener('click', () => {
state.running = !state.running;
if (state.running && !capturedCaptchaToken) {
updateUI(
state.language === 'pt'
? '❌ Token não capturado. Clique em qualquer pixel primeiro.'
: '❌ CAPTCHA token not captured. Please click any pixel manually first.',
'error'
);
state.running = false;
return;
}
if (state.running) {
toggleBtn.innerHTML = `<i class="fas fa-stop"></i> <span>${t.stop}</span>`;
toggleBtn.classList.remove('wplace-btn-primary');
toggleBtn.classList.add('wplace-btn-stop');
updateUI(
state.language === 'pt' ? '🚀 Pintura iniciada!' : '🚀 Painting started!',
'success'
);
paintLoop();
} else {
toggleBtn.innerHTML = `<i class="fas fa-play"></i> <span>${t.start}</span>`;
toggleBtn.classList.add('wplace-btn-primary');
toggleBtn.classList.remove('wplace-btn-stop');
statsArea.innerHTML = '';
updateUI(state.language === 'pt' ? '⏹️ Parado' : '⏹️ Stopped', 'default');
}
});
minimizeBtn.addEventListener('click', () => {
state.minimized = !state.minimized;
content.style.display = state.minimized ? 'none' : 'block';
minimizeBtn.innerHTML = `<i class="fas fa-${state.minimized ? 'expand' : 'minus'}"></i>`;
});
const autoRefreshCheckbox = panel.querySelector('#autoRefreshCheckbox');
autoRefreshCheckbox.addEventListener('change', () => {
state.autoRefresh = autoRefreshCheckbox.checked;
});
window.addEventListener('beforeunload', () => {
state.menuOpen = false;
});
};
window.updateUI = (message, type = 'default') => {
const statusText = document.querySelector('#statusText');
if (statusText) {
statusText.textContent = message;
statusText.className = `wplace-status status-${type}`;
statusText.style.animation = 'none';
void statusText.offsetWidth;
statusText.style.animation = 'slide-in 0.3s ease-out';
}
};
window.updateStats = async () => {
await getCharge();
const statsArea = document.querySelector('#statsArea');
if (statsArea) {
const t = {
pt: {
user: 'Usuário',
pixels: 'Pixels',
charges: 'Cargas',
level: 'Level',
},
en: {
user: 'User',
pixels: 'Pixels',
charges: 'Charges',
level: 'Level',
},
}[state.language] || {
user: 'User',
pixels: 'Pixels',
charges: 'Charges',
level: 'Level',
};
statsArea.innerHTML = `
<div class="wplace-stat-item">
<div class="wplace-stat-label"><i class="fas fa-user"></i> ${t.user}</div>
<div>${state.userInfo.name}</div>
</div>
<div class="wplace-stat-item">
<div class="wplace-stat-label"><i class="fas fa-paint-brush"></i> ${t.pixels}</div>
<div>${state.paintedCount}</div>
</div>
<div class="wplace-stat-item">
<div class="wplace-stat-label"><i class="fas fa-bolt"></i> ${t.charges}</div>
<div>${Math.floor(state.charges.count)}/${Math.floor(state.charges.max)}</div>
</div>
<div class="wplace-stat-item">
<div class="wplace-stat-label"><i class="fas fa-star"></i> ${t.level}</div>
<div>${state.userInfo?.level || '0'}</div>
</div>
`;
}
};
await detectUserLocation();
createUI();
await getCharge();
updateStats();
})();
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
File diff suppressed because it is too large. Load diff
+837
View File
@@ -0,0 +1,837 @@
// ==UserScript==
// @name WPlace Overlay Manager
// @namespace http://tampermonkey.net/
// @version 2025-09-16.1
// @description Overlay management for WPlace AutoBot - handles tile processing, chunking, and rendering
// @author Wbot
// @match https://wplace.live/*
// @grant none
// ==/UserScript==
/**
* OverlayManager - Handles overlay processing, tile chunking, and canvas operations for WPlace AutoBot
* Extracted from Auto-Image.js for better modularity and reusabilityc
*/
class OverlayManager {
constructor() {
this.isEnabled = false;
this.startCoords = null; // { region: {x, y}, pixel: {x, y} }
this.imageBitmap = null;
this.chunkedTiles = new Map(); // Map<"tileX,tileY", ImageBitmap>
this.originalTiles = new Map(); // Map<"tileX,ttileY", ImageBitmap> store latest original tile bitmaps
this.originalTilesData = new Map(); // Map<"tileX,tileY", {w,h,data:Uint8ClampedArray}> cache full ImageData for fast pixel reads
this.tileSize = 1000;
this.processPromise = null; // Track ongoing processing
this.lastProcessedHash = null; // Cache invalidation
this.workerPool = null; // Web worker pool for heavy processing
this.neededTilesForSave = null; // Set of tiles needed for save file restoration
}
/**
* Toggle overlay enabled state
* @returns {boolean} New enabled state
*/
toggle() {
this.isEnabled = !this.isEnabled;
console.log(`Overlay ${this.isEnabled ? 'enabled' : 'disabled'}.`);
return this.isEnabled;
}
/**
* Enable overlay
*/
enable() {
this.isEnabled = true;
// Force canvas refresh if script was loaded manually (not on startup)
// This ensures overlay works even when tiles are already cached
this._triggerCanvasRefreshIfNeeded();
// Also try direct canvas overlay approach as backup
this._attemptDirectCanvasOverlay();
}
/**
* Attempt to directly overlay on the existing canvas
* @private
*/
_attemptDirectCanvasOverlay() {
// Only try this if we have overlay data and it's been more than 5 seconds since page load
const timeSinceLoad = Date.now() - (window.performance?.timing?.navigationStart || 0);
if (timeSinceLoad > 5000 && this.chunkedTiles.size > 0) {
setTimeout(() => {
this._drawDirectOverlay();
}, 500);
}
}
/**
* Draw overlay directly on the main canvas
* @private
*/
_drawDirectOverlay() {
try {
const canvas = document.querySelector('canvas');
if (!canvas) {
return;
}
const ctx = canvas.getContext('2d');
if (!ctx) {
return;
}
// Store original composite operation
const originalComposite = ctx.globalCompositeOperation;
const originalAlpha = ctx.globalAlpha;
// Set overlay blending
ctx.globalCompositeOperation = 'source-over';
ctx.globalAlpha = window.state?.overlayOpacity || 0.6;
// Draw overlay chunks directly on canvas
this.chunkedTiles.forEach((bitmap, tileKey) => {
const [tileX, tileY] = tileKey.split(',').map(Number);
// Convert tile coordinates to canvas coordinates
// This assumes WPlace uses 1000px tiles
const canvasX = tileX * 1000;
const canvasY = tileY * 1000;
try {
ctx.drawImage(bitmap, canvasX, canvasY);
} catch (error) {
// Ignore individual tile draw errors
}
});
// Restore original settings
ctx.globalCompositeOperation = originalComposite;
ctx.globalAlpha = originalAlpha;
} catch (error) {
// Ignore direct overlay errors silently
}
}
/**
* Trigger canvas refresh for manual script execution
* @private
*/
_triggerCanvasRefreshIfNeeded() {
// Only trigger refresh if we have overlay data and it's been more than 5 seconds since page load
// (indicates manual execution rather than startup execution)
const timeSinceLoad = Date.now() - (window.performance?.timing?.navigationStart || 0);
if (timeSinceLoad > 5000 && this.chunkedTiles.size > 0) {
// Try multiple methods to refresh the canvas tiles
setTimeout(() => {
this._attemptCanvasRefresh();
}, 100);
}
}
/**
* Attempt to refresh canvas tiles using various methods
* @private
*/
_attemptCanvasRefresh() {
try {
// Method 1: Clear browser cache for tile URLs
if ('caches' in window) {
caches.keys().then(cacheNames => {
cacheNames.forEach(cacheName => {
caches.delete(cacheName);
});
});
}
// Method 2: Force browser to bypass cache by adding cache-busting parameter
// We need to intercept and modify tile requests to add cache-busting
const originalFetch = window.fetch;
let fetchOverrideActive = true;
window.fetch = async function(...args) {
const url = args[0] instanceof Request ? args[0].url : args[0];
if (fetchOverrideActive && typeof url === 'string' && url.match(/\/\d+\/\d+\.png/)) {
// Add cache-busting parameter
const separator = url.includes('?') ? '&' : '?';
const cacheBustUrl = `${url}${separator}_cb=${Date.now()}`;
if (args[0] instanceof Request) {
args[0] = new Request(cacheBustUrl, args[0]);
} else {
args[0] = cacheBustUrl;
}
}
return originalFetch.apply(this, args);
};
// Disable cache-busting after 10 seconds
setTimeout(() => {
fetchOverrideActive = false;
}, 10000);
// Method 3: Try to trigger a slight zoom change
const canvas = document.querySelector('canvas');
if (canvas) {
// Dispatch a wheel event to trigger tile refresh
const wheelEvent = new WheelEvent('wheel', {
deltaY: 1,
deltaMode: 0,
bubbles: true,
cancelable: true,
view: window
});
canvas.dispatchEvent(wheelEvent);
// Immediately counter it
setTimeout(() => {
const counterEvent = new WheelEvent('wheel', {
deltaY: -1,
deltaMode: 0,
bubbles: true,
cancelable: true,
view: window
});
canvas.dispatchEvent(counterEvent);
}, 50);
}
// Method 4: Try to trigger resize event
window.dispatchEvent(new Event('resize'));
} catch (error) {
console.warn('⚠️ Could not trigger canvas refresh:', error);
}
}
/**
* Disable overlay
*/
disable() {
this.isEnabled = false;
}
/**
* Clear all overlay data and disable
*/
clear() {
this.disable();
this.imageBitmap = null;
this.chunkedTiles.clear();
this.originalTiles.clear();
this.originalTilesData.clear();
this.lastProcessedHash = null;
if (this.processPromise) {
this.processPromise = null;
}
}
/**
* Set the image bitmap for overlay processing
* @param {ImageBitmap} imageBitmap - The image to use for overlay
*/
async setImage(imageBitmap) {
this.imageBitmap = imageBitmap;
this.lastProcessedHash = null; // Invalidate cache
if (this.imageBitmap && this.startCoords) {
await this.processImageIntoChunks();
}
}
/**
* Set the position for overlay rendering
* @param {Object} startPosition - Starting pixel position {x, y}
* @param {Object} region - Region coordinates {x, y}
*/
async setPosition(startPosition, region) {
if (!startPosition || !region) {
this.startCoords = null;
this.chunkedTiles.clear();
this.lastProcessedHash = null;
return;
}
this.startCoords = { region, pixel: startPosition };
this.lastProcessedHash = null; // Invalidate cache
if (this.imageBitmap) {
await this.processImageIntoChunks();
}
}
/**
* Generate hash for cache invalidation
* @returns {string|null} Hash string or null if no data
* @private
*/
_generateProcessHash() {
if (!this.imageBitmap || !this.startCoords) return null;
const { width, height } = this.imageBitmap;
const { x: px, y: py } = this.startCoords.pixel;
const { x: rx, y: ry } = this.startCoords.region;
// Access global state for overlay settings
const blueMarbleEnabled = window.state?.blueMarbleEnabled || false;
const overlayOpacity = window.state?.overlayOpacity || 0.6;
return `${width}x${height}_${px},${py}_${rx},${ry}_${blueMarbleEnabled}_${overlayOpacity}`;
}
/**
* Process image into chunks with caching and batch processing
*/
async processImageIntoChunks() {
if (!this.imageBitmap || !this.startCoords) {
console.warn('🚫 Cannot process chunks - missing imageBitmap or startCoords');
return;
}
console.log('🔄 Processing image into chunks...', {
imageSize: `${this.imageBitmap.width}x${this.imageBitmap.height}`,
startCoords: this.startCoords
});
// Check if we're already processing to avoid duplicate work
if (this.processPromise) {
console.log('⏳ Already processing chunks - waiting...');
return this.processPromise;
}
// Check cache validity
const currentHash = this._generateProcessHash();
if (this.lastProcessedHash === currentHash && this.chunkedTiles.size > 0) {
// Using cached overlay chunks
return;
}
// Start processing
console.log('🚀 Starting new chunk processing...');
this.processPromise = this._doProcessImageIntoChunks();
try {
await this.processPromise;
this.lastProcessedHash = currentHash;
console.log(`✅ Chunk processing complete - ${this.chunkedTiles.size} tiles created`);
} finally {
this.processPromise = null;
}
}
/**
* Internal method to process image into chunks
* @private
*/
async _doProcessImageIntoChunks() {
const startTime = performance.now();
this.chunkedTiles.clear();
const { width: imageWidth, height: imageHeight } = this.imageBitmap;
const { x: startPixelX, y: startPixelY } = this.startCoords.pixel;
const { x: startRegionX, y: startRegionY } = this.startCoords.region;
const { startTileX, startTileY, endTileX, endTileY } = this.calculateTileRange(
startRegionX,
startRegionY,
startPixelX,
startPixelY,
imageWidth,
imageHeight,
this.tileSize
);
const totalTiles = (endTileX - startTileX + 1) * (endTileY - startTileY + 1);
console.log(`🔄 Processing ${totalTiles} overlay tiles...`);
// Process tiles in batches to avoid blocking the main thread
const batchSize = 4; // Process 4 tiles at a time
const tilesToProcess = [];
for (let ty = startTileY; ty <= endTileY; ty++) {
for (let tx = startTileX; tx <= endTileX; tx++) {
tilesToProcess.push({ tx, ty });
}
}
// Process tiles in batches with yielding
for (let i = 0; i < tilesToProcess.length; i += batchSize) {
const batch = tilesToProcess.slice(i, i + batchSize);
await Promise.all(
batch.map(async ({ tx, ty }) => {
const tileKey = `${tx},${ty}`;
const chunkBitmap = await this._processTile(
tx,
ty,
imageWidth,
imageHeight,
startPixelX,
startPixelY,
startRegionX,
startRegionY
);
if (chunkBitmap) {
this.chunkedTiles.set(tileKey, chunkBitmap);
}
})
);
// Yield control to prevent blocking
if (i + batchSize < tilesToProcess.length) {
await new Promise((resolve) => setTimeout(resolve, 0));
}
}
const processingTime = performance.now() - startTime;
console.log(
`✅ Overlay processed ${this.chunkedTiles.size} tiles in ${Math.round(processingTime)}ms`
);
}
/**
* Process a single tile
* @param {number} tx - Tile X coordinate
* @param {number} ty - Tile Y coordinate
* @param {number} imageWidth - Image width
* @param {number} imageHeight - Image height
* @param {number} startPixelX - Starting pixel X
* @param {number} startPixelY - Starting pixel Y
* @param {number} startRegionX - Starting region X
* @param {number} startRegionY - Starting region Y
* @returns {Promise<ImageBitmap|null>} Processed tile bitmap
* @private
*/
async _processTile(
tx,
ty,
imageWidth,
imageHeight,
startPixelX,
startPixelY,
startRegionX,
startRegionY
) {
const tileKey = `${tx},${ty}`;
// Calculate the portion of the image that overlaps with this tile
const imgStartX = (tx - startRegionX) * this.tileSize - startPixelX;
const imgStartY = (ty - startRegionY) * this.tileSize - startPixelY;
// Crop coordinates within the source image
const sX = Math.max(0, imgStartX);
const sY = Math.max(0, imgStartY);
const sW = Math.min(imageWidth - sX, this.tileSize - (sX - imgStartX));
const sH = Math.min(imageHeight - sY, this.tileSize - (sY - imgStartY));
if (sW <= 0 || sH <= 0) return null;
// Destination coordinates on the new chunk canvas
const dX = Math.max(0, -imgStartX);
const dY = Math.max(0, -imgStartY);
const chunkCanvas = new OffscreenCanvas(this.tileSize, this.tileSize);
const chunkCtx = chunkCanvas.getContext('2d');
chunkCtx.imageSmoothingEnabled = false;
chunkCtx.drawImage(this.imageBitmap, sX, sY, sW, sH, dX, dY, sW, sH);
// Blue marble effect with faster pixel manipulation
const blueMarbleEnabled = window.state?.blueMarbleEnabled || false;
if (blueMarbleEnabled) {
const imageData = chunkCtx.getImageData(dX, dY, sW, sH);
const data = imageData.data;
// Faster pixel manipulation using typed arrays
for (let i = 0; i < data.length; i += 4) {
const pixelIndex = i / 4;
const pixelY = Math.floor(pixelIndex / sW);
const pixelX = pixelIndex % sW;
if ((pixelX + pixelY) % 2 === 0 && data[i + 3] > 0) {
data[i + 3] = 0; // Set alpha to 0
}
}
chunkCtx.putImageData(imageData, dX, dY);
}
return await chunkCanvas.transferToImageBitmap();
}
/**
* Process and respond to tile requests with overlay compositing
* @param {Object} eventData - Event data containing endpoint, blobID, and blobData
*/
async processAndRespondToTileRequest(eventData) {
const { endpoint, blobID, blobData } = eventData;
let finalBlob = blobData;
if (this.isEnabled && this.chunkedTiles.size > 0) {
const tileMatch = endpoint.match(/(\d+)\/(\d+)\.png/);
if (tileMatch) {
const tileX = parseInt(tileMatch[1], 10);
const tileY = parseInt(tileMatch[2], 10);
const tileKey = `${tileX},${tileY}`;
const chunkBitmap = this.chunkedTiles.get(tileKey);
// Also store the original tile bitmap for later pixel color checks
try {
const originalBitmap = await createImageBitmap(blobData);
this.originalTiles.set(tileKey, originalBitmap);
// Cache full ImageData for fast pixel access (avoid repeated drawImage/getImageData)
try {
let canvas, ctx;
if (typeof OffscreenCanvas !== 'undefined') {
canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height);
ctx = canvas.getContext('2d');
} else {
canvas = document.createElement('canvas');
canvas.width = originalBitmap.width;
canvas.height = originalBitmap.height;
ctx = canvas.getContext('2d');
}
ctx.imageSmoothingEnabled = false;
ctx.drawImage(originalBitmap, 0, 0);
const imgData = ctx.getImageData(0, 0, originalBitmap.width, originalBitmap.height);
// Store typed array copy to avoid retaining large canvas
this.originalTilesData.set(tileKey, {
w: originalBitmap.width,
h: originalBitmap.height,
data: new Uint8ClampedArray(imgData.data),
});
} catch (e) {
// If ImageData extraction fails, still keep the bitmap as fallback
console.warn('OverlayManager: could not cache ImageData for', tileKey, e);
}
} catch (e) {
console.warn('OverlayManager: could not create original bitmap for', tileKey, e);
}
if (chunkBitmap) {
try {
// Use faster compositing for better performance
finalBlob = await this._compositeTileOptimized(blobData, chunkBitmap);
} catch (e) {
console.error('Error compositing overlay:', e);
// Fallback to original tile on error
finalBlob = blobData;
}
}
}
}
// Send the (possibly modified) blob back to the injected script
window.postMessage(
{
source: 'auto-image-overlay',
blobID: blobID,
blobData: finalBlob,
},
'*'
);
}
/**
* Get pixel color from a tile
* @param {number} tileX - Tile X coordinate
* @param {number} tileY - Tile Y coordinate
* @param {number} pixelX - Pixel X within tile
* @param {number} pixelY - Pixel Y within tile
* @returns {Promise<Array|null>} [r,g,b,a] or null
*/
async getTilePixelColor(tileX, tileY, pixelX, pixelY) {
const tileKey = `${tileX},${tileY}`;
// Get transparency threshold from global state or config
const alphaThresh = window.state?.customTransparencyThreshold ||
window.CONFIG?.TRANSPARENCY_THRESHOLD || 10;
// 1. Prefer cached ImageData if available
const cached = this.originalTilesData.get(tileKey);
if (cached && cached.data && cached.w > 0 && cached.h > 0) {
const x = Math.max(0, Math.min(cached.w - 1, pixelX));
const y = Math.max(0, Math.min(cached.h - 1, pixelY));
const idx = (y * cached.w + x) * 4;
const d = cached.data;
const a = d[idx + 3];
const paintTransparentPixels = window.state?.paintTransparentPixels || false;
if (!paintTransparentPixels && a < alphaThresh) {
// Treat as transparent / unavailable
return null;
}
return [d[idx], d[idx + 1], d[idx + 2], a];
}
// 2. Fallback: use bitmap, with retry
const maxRetries = 3;
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const bitmap = this.originalTiles.get(tileKey);
if (!bitmap) {
if (attempt === maxRetries) {
console.warn('OverlayManager: no bitmap for', tileKey, 'after', maxRetries, 'attempts');
} else {
await this._sleep(50 * attempt); // exponential delay
}
continue;
}
try {
let canvas, ctx;
if (typeof OffscreenCanvas !== 'undefined') {
canvas = new OffscreenCanvas(bitmap.width, bitmap.height);
ctx = canvas.getContext('2d');
} else {
canvas = document.createElement('canvas');
canvas.width = bitmap.width;
canvas.height = bitmap.height;
ctx = canvas.getContext('2d');
}
ctx.imageSmoothingEnabled = false;
ctx.drawImage(bitmap, 0, 0);
const x = Math.max(0, Math.min(bitmap.width - 1, pixelX));
const y = Math.max(0, Math.min(bitmap.height - 1, pixelY));
const data = ctx.getImageData(x, y, 1, 1).data;
const a = data[3];
const paintTransparentPixels = window.state?.paintTransparentPixels || false;
if (!paintTransparentPixels && a < alphaThresh) {
return null;
}
return [data[0], data[1], data[2], a];
} catch (e) {
console.warn('OverlayManager: failed to read pixel (attempt', attempt, ')', tileKey, e);
if (attempt < maxRetries) {
await this._sleep(50 * attempt);
} else {
console.error(
'OverlayManager: failed to read pixel after',
maxRetries,
'attempts',
tileKey
);
}
}
}
// 3. If everything fails, return null
return null;
}
/**
* Composite tile with overlay using optimized rendering
* @param {Blob} originalBlob - Original tile blob
* @param {ImageBitmap} overlayBitmap - Overlay bitmap
* @returns {Promise<Blob>} Composited tile blob
* @private
*/
async _compositeTileOptimized(originalBlob, overlayBitmap) {
const originalBitmap = await createImageBitmap(originalBlob);
const canvas = new OffscreenCanvas(originalBitmap.width, originalBitmap.height);
const ctx = canvas.getContext('2d');
// Disable antialiasing for pixel-perfect rendering
ctx.imageSmoothingEnabled = false;
// Draw original tile first
ctx.drawImage(originalBitmap, 0, 0);
// Set opacity and draw overlay with optimized blend mode
const overlayOpacity = window.state?.overlayOpacity || 0.6;
ctx.globalAlpha = overlayOpacity;
ctx.globalCompositeOperation = 'source-over';
ctx.drawImage(overlayBitmap, 0, 0);
// Use faster blob conversion with compression settings
return await canvas.convertToBlob({
type: 'image/png',
quality: 0.95, // Slight compression for faster processing
});
}
/**
* Wait until all required tiles are loaded and cached
* @param {number} startRegionX - Starting region X
* @param {number} startRegionY - Starting region Y
* @param {number} pixelWidth - Image width in pixels
* @param {number} pixelHeight - Image height in pixels
* @param {number} startPixelX - Starting pixel X offset
* @param {number} startPixelY - Starting pixel Y offset
* @param {number} timeoutMs - Timeout in milliseconds
* @returns {Promise<boolean>} True if tiles are ready
*/
async waitForTiles(
startRegionX,
startRegionY,
pixelWidth,
pixelHeight,
startPixelX = 0,
startPixelY = 0,
timeoutMs = 10000
) {
const { startTileX, startTileY, endTileX, endTileY } = this.calculateTileRange(
startRegionX,
startRegionY,
startPixelX,
startPixelY,
pixelWidth,
pixelHeight,
this.tileSize
);
const requiredTiles = [];
for (let ty = startTileY; ty <= endTileY; ty++) {
for (let tx = startTileX; tx <= endTileX; tx++) {
requiredTiles.push(`${tx},${ty}`);
}
}
if (requiredTiles.length === 0) return true;
const startTime = Date.now();
while (Date.now() - startTime < timeoutMs) {
const stopFlag = window.state?.stopFlag || false;
if (stopFlag) {
console.log('waitForTiles: stopped by user');
return false;
}
const missing = requiredTiles.filter((key) => !this.originalTiles.has(key));
if (missing.length === 0) {
console.log(`✅ All ${requiredTiles.length} required tiles are loaded`);
return true;
}
await this._sleep(100);
}
console.warn(`❌ Timeout waiting for tiles: ${requiredTiles.length} required,
${requiredTiles.filter((k) => this.originalTiles.has(k)).length} loaded`);
return false;
}
/**
* Calculate tile range for given parameters
* @param {number} startRegionX - Starting region X
* @param {number} startRegionY - Starting region Y
* @param {number} startPixelX - Starting pixel X
* @param {number} startPixelY - Starting pixel Y
* @param {number} width - Width in pixels
* @param {number} height - Height in pixels
* @param {number} tileSize - Size of each tile (default 1000)
* @returns {{startTileX: number, startTileY: number, endTileX: number, endTileY: number}}
*/
calculateTileRange(
startRegionX,
startRegionY,
startPixelX,
startPixelY,
width,
height,
tileSize = 1000
) {
const endPixelX = startPixelX + width;
const endPixelY = startPixelY + height;
return {
startTileX: startRegionX + Math.floor(startPixelX / tileSize),
startTileY: startRegionY + Math.floor(startPixelY / tileSize),
endTileX: startRegionX + Math.floor((endPixelX - 1) / tileSize),
endTileY: startRegionY + Math.floor((endPixelY - 1) / tileSize),
};
}
/**
* Preload tiles for save file coordinates to populate originalTiles cache
* This fixes the "no bitmap" error when loading save files
* @param {Object} startPosition - The start position from save file
* @param {Object} region - The region from save file
* @param {number} width - Image width
* @param {number} height - Image height
*/
/**
* Preload tiles for save file coordinates to populate originalTiles cache
* This fixes the "no bitmap" error when loading save files
* For WPlace, we can't manually fetch tiles like r/place - we need to trigger natural tile loading
* @param {Object} startPosition - The start position from save file
* @param {Object} region - The region from save file
* @param {number} width - Image width
* @param {number} height - Image height
*/
async preloadTilesForSaveFile(startPosition, region, width, height) {
if (!startPosition || !region || !width || !height) {
console.warn('OverlayManager: Missing parameters for tile preload');
return;
}
try {
// Calculate tiles that need to be loaded based on image area
const tileSize = 1000; // WPlace uses 1000px tiles, not 256px like r/place
const tiles = this.calculateTileRange(
region.x, region.y,
startPosition.x, startPosition.y,
width, height,
tileSize
);
console.log(`🔄 WPlace: Need to preload ${(tiles.endTileX - tiles.startTileX + 1) * (tiles.endTileY - tiles.startTileY + 1)} tiles for save file...`);
// For WPlace, instead of manually fetching tiles, we need to wait for natural tile loading
// The browser will request tiles as the user navigates, and we intercept them
// For now, just log the tiles we need and let the overlay work with what's available
const neededTiles = [];
for (let tileX = tiles.startTileX; tileX <= tiles.endTileX; tileX++) {
for (let tileY = tiles.startTileY; tileY <= tiles.endTileY; tileY++) {
neededTiles.push(`${tileX},${tileY}`);
}
}
console.log(`📍 WPlace: Will use tiles when available: ${neededTiles.slice(0, 5).join(', ')}${neededTiles.length > 5 ? ` and ${neededTiles.length - 5} more...` : ''}`);
// Store the needed tiles list for reference
this.neededTilesForSave = new Set(neededTiles);
console.log('✅ WPlace: Tile preload setup complete - tiles will populate as user navigates');
} catch (error) {
console.error('❌ Failed to setup tile preload for save file:', error);
}
}
/**
* Sleep utility
* @param {number} ms - Milliseconds to sleep
* @returns {Promise<void>}
* @private
*/
_sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Clean up resources
*/
cleanup() {
this.clear();
if (this.workerPool) {
// Clean up worker pool if implemented
this.workerPool = null;
}
}
}
// Create global instance
window.WPlaceOverlayManager = OverlayManager;
// Create global instance for Auto-Image.js compatibility
window.globalOverlayManager = new OverlayManager();
// Legacy compatibility - expose key methods globally for backward compatibility
window.OverlayManager = OverlayManager;
console.log('✅ WPlace Overlay Manager loaded and ready');
+846
View File
@@ -0,0 +1,846 @@
// ==UserScript==
// @name WPlace Token Manager
// @namespace http://tampermonkey.net/
// @version 2025-09-16.1
// @description Turnstile token management for WPlace AutoBot
// @author Wbot
// @match https://wplace.live/*
// @grant nones
// ==/UserScript==
/**
* TokenManager - Handles Turnstile token generation, caching, and validation for WPlace AutoBot
* Extracted from Auto-Image.js for better modularity and reusabilitys
*/
class TokenManager {
constructor() {
// Token state
this.turnstileToken = null;
this.tokenExpiryTime = 0;
this.tokenGenerationInProgress = false;
this._resolveToken = null;
this.tokenPromise = new Promise((resolve) => {
this._resolveToken = resolve;
});
// Configuration constants
this.TOKEN_LIFETIME = 240000; // 4 minutes (tokens typically last 5 min, use 4 for safety)
this.MAX_RETRIES = 10;
this.MAX_BATCH_RETRIES = 10; // Maximum attempts for batch sending
// Turnstile widget state
this.turnstileLoaded = false;
this._turnstileContainer = null;
this._turnstileOverlay = null;
this._turnstileWidgetId = null;
this._lastSitekey = null;
this._cachedSitekey = null;
// Retry counter
this.retryCount = 0;
// Initialize message listener for token capture
this._initializeTokenCapture();
}
/**
* Initialize token capture from injected script
*/
_initializeTokenCapture() {
window.addEventListener('message', (event) => {
const { source, token } = event.data;
if (source === 'turnstile-capture' && token) {
this.setTurnstileToken(token);
// Show success message if UI is expecting it
if (document.querySelector('#statusText')?.textContent.includes('CAPTCHA')) {
this._showAlert('Token captured successfully', 'success');
}
}
});
}
/**
* Set a new Turnstile token with expiry
* @param {string} token - The Turnstile token
*/
setTurnstileToken(token) {
if (this._resolveToken) {
this._resolveToken(token);
this._resolveToken = null;
}
this.turnstileToken = token;
this.tokenExpiryTime = Date.now() + this.TOKEN_LIFETIME;
console.log('✅ Turnstile token set successfully');
}
/**
* Check if current token is valid
* @returns {boolean} True if token exists and not expired
*/
isTokenValid() {
return this.turnstileToken && Date.now() < this.tokenExpiryTime;
}
/**
* Invalidate current token
*/
invalidateToken() {
this.turnstileToken = null;
this.tokenExpiryTime = 0;
console.log('🗑️ Token invalidated, will force fresh generation');
}
/**
* Ensure a valid token is available, generating if necessary
* @param {boolean} forceRefresh - Force generation of new token
* @returns {Promise<string|null>} The token or null if failed
*/
async ensureToken(forceRefresh = false) {
// Return cached token if still valid and not forcing refresh
if (this.isTokenValid() && !forceRefresh) {
return this.turnstileToken;
}
// Invalidate token if forcing refresh
if (forceRefresh) this.invalidateToken();
// Avoid multiple simultaneous token generations
if (this.tokenGenerationInProgress) {
console.log('🔄 Token generation already in progress, waiting...');
await this._sleep(2000);
return this.isTokenValid() ? this.turnstileToken : null;
}
this.tokenGenerationInProgress = true;
try {
console.log('🔄 Token expired or missing, generating new one...');
const token = await this.handleCaptchaWithRetry();
if (token && token.length > 20) {
this.setTurnstileToken(token);
console.log('✅ Token captured and cached successfully');
return token;
}
console.log('⚠️ Invisible Turnstile failed, forcing browser automation...');
const fallbackToken = await this.handleCaptchaFallback();
if (fallbackToken && fallbackToken.length > 20) {
this.setTurnstileToken(fallbackToken);
console.log('✅ Fallback token captured successfully');
return fallbackToken;
}
console.log('❌ All token generation methods failed');
return null;
} finally {
this.tokenGenerationInProgress = false;
}
}
/**
* Handle captcha generation with retry logic
* @returns {Promise<string|null>} The token or null if failed
*/
async handleCaptchaWithRetry() {
const startTime = performance.now();
try {
const { sitekey, token: preGeneratedToken } = await this.obtainSitekeyAndToken();
if (!sitekey) {
throw new Error('No valid sitekey found');
}
console.log('🔑 Using sitekey:', sitekey);
if (typeof window !== 'undefined' && window.navigator) {
console.log(
'🧭 UA:',
window.navigator.userAgent.substring(0, 50) + '...',
'Platform:',
window.navigator.platform
);
}
let token = null;
if (
preGeneratedToken &&
typeof preGeneratedToken === 'string' &&
preGeneratedToken.length > 20
) {
console.log('♻️ Reusing pre-generated Turnstile token');
token = preGeneratedToken;
} else {
if (this.isTokenValid()) {
console.log('♻️ Using existing cached token (from previous session)');
token = this.turnstileToken;
} else {
console.log('🔐 Generating new token with executeTurnstile...');
token = await this.executeTurnstile(sitekey, 'paint');
if (token) this.setTurnstileToken(token);
}
}
if (token && typeof token === 'string' && token.length > 20) {
const elapsed = Math.round(performance.now() - startTime);
console.log(`✅ Turnstile token generated successfully in ${elapsed}ms`);
return token;
} else {
throw new Error(`Invalid or empty token received - Length: ${token?.length || 0}`);
}
} catch (error) {
const elapsed = Math.round(performance.now() - startTime);
console.error(`❌ Turnstile token generation failed after ${elapsed}ms:`, error);
throw error;
}
}
/**
* Fallback token generation method using pixel placement automation
* @returns {Promise<string|null>} The token or null if failed
*/
async handleCaptchaFallback() {
return new Promise(async (resolve, reject) => {
try {
// Ensure we have a fresh promise to await for a new token capture
if (!this._resolveToken) {
this.tokenPromise = new Promise((res) => {
this._resolveToken = res;
});
}
const timeoutPromise = this._sleep(20000).then(() =>
reject(new Error('Auto-CAPTCHA timed out.'))
);
const solvePromise = (async () => {
// Wait for main paint button
const mainPaintBtn = await this._waitForSelector(
'button.btn.btn-primary.btn-lg, button.btn.btn-primary.sm\\:btn-xl',
200,
10000
);
if (!mainPaintBtn) throw new Error('Could not find the main paint button.');
mainPaintBtn.click();
await this._sleep(500);
// Click transparent color button
const transBtn = await this._waitForSelector('button#color-0', 200, 5000);
if (!transBtn) throw new Error('Could not find the transparent color button.');
transBtn.click();
await this._sleep(500);
// Find canvas and simulate painting
const canvas = await this._waitForSelector('canvas', 200, 5000);
if (!canvas) throw new Error('Could not find the canvas element.');
canvas.setAttribute('tabindex', '0');
canvas.focus();
const rect = canvas.getBoundingClientRect();
const centerX = Math.round(rect.left + rect.width / 2);
const centerY = Math.round(rect.top + rect.height / 2);
// Simulate mouse movement and spacebar press
canvas.dispatchEvent(
new MouseEvent('mousemove', {
clientX: centerX,
clientY: centerY,
bubbles: true,
})
);
canvas.dispatchEvent(
new KeyboardEvent('keydown', {
key: ' ',
code: 'Space',
bubbles: true,
})
);
await this._sleep(50);
canvas.dispatchEvent(
new KeyboardEvent('keyup', {
key: ' ',
code: 'Space',
bubbles: true,
})
);
await this._sleep(500);
// Delay before sending confirmation
await this._sleep(800);
// Keep confirming until token is captured
const confirmLoop = async () => {
while (!this.turnstileToken) {
let confirmBtn = await this._waitForSelector(
'button.btn.btn-primary.btn-lg, button.btn.btn-primary.sm\\:btn-xl'
);
if (!confirmBtn) {
const allPrimary = Array.from(document.querySelectorAll('button.btn-primary'));
confirmBtn = allPrimary.length ? allPrimary[allPrimary.length - 1] : null;
}
if (confirmBtn) {
confirmBtn.click();
}
await this._sleep(500); // 500ms delay between confirmation attempts
}
};
// Start confirmation loop and wait for token
confirmLoop();
const token = await this.tokenPromise;
await this._sleep(300); // small delay after token is captured
resolve(token);
})();
await Promise.race([solvePromise, timeoutPromise]);
} catch (error) {
console.error('Auto-CAPTCHA process failed:', error);
reject(error);
}
});
}
/**
* Load Turnstile script if not already loaded
* @returns {Promise<void>}
*/
async loadTurnstile() {
// If Turnstile is already present, just resolve.
if (window.turnstile) {
this.turnstileLoaded = true;
return Promise.resolve();
}
return new Promise((resolve, reject) => {
// Avoid adding the script twice
if (
document.querySelector(
'script[src^="https://challenges.cloudflare.com/turnstile/v0/api.js"]'
)
) {
const checkReady = () => {
if (window.turnstile) {
this.turnstileLoaded = true;
resolve();
} else {
setTimeout(checkReady, 100);
}
};
return checkReady();
}
const script = document.createElement('script');
script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit';
script.async = true;
script.defer = true;
script.onload = () => {
this.turnstileLoaded = true;
console.log('✅ Turnstile script loaded successfully');
resolve();
};
script.onerror = () => {
console.error('❌ Failed to load Turnstile script');
reject(new Error('Failed to load Turnstile'));
};
document.head.appendChild(script);
});
}
/**
* Create or reuse the turnstile container - completely hidden for token generation
* @returns {HTMLElement} The container element
*/
ensureTurnstileContainer() {
if (!this._turnstileContainer || !document.body.contains(this._turnstileContainer)) {
// Clean up old container if it exists
if (this._turnstileContainer) {
this._turnstileContainer.remove();
}
this._turnstileContainer = document.createElement('div');
this._turnstileContainer.className = 'wplace-turnstile-hidden';
this._turnstileContainer.setAttribute('aria-hidden', 'true');
this._turnstileContainer.id = 'turnstile-widget-container';
document.body.appendChild(this._turnstileContainer);
}
return this._turnstileContainer;
}
/**
* Interactive overlay container for visible widgets when needed
* @returns {HTMLElement} The overlay element
*/
ensureTurnstileOverlayContainer() {
if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) {
return this._turnstileOverlay;
}
const overlay = document.createElement('div');
overlay.id = 'turnstile-overlay-container';
overlay.className = 'wplace-turnstile-overlay wplace-overlay-hidden';
const title = document.createElement('div');
title.textContent = this._getText('turnstileInstructions', 'Complete the verification');
title.className = 'wplace-turnstile-title';
const host = document.createElement('div');
host.id = 'turnstile-overlay-host';
host.className = 'wplace-turnstile-host';
const hideBtn = document.createElement('button');
hideBtn.textContent = this._getText('hideTurnstileBtn', 'Hide');
hideBtn.className = 'wplace-turnstile-hide-btn';
hideBtn.addEventListener('click', () => overlay.remove());
overlay.appendChild(title);
overlay.appendChild(host);
overlay.appendChild(hideBtn);
document.body.appendChild(overlay);
this._turnstileOverlay = overlay;
return overlay;
}
/**
* Execute Turnstile widget to generate token
* @param {string} sitekey - The site key
* @param {string} action - The action name (default: 'paint')
* @returns {Promise<string|null>} The token or null if failed
*/
async executeTurnstile(sitekey, action = 'paint') {
await this.loadTurnstile();
// Try reusing existing widget first if sitekey matches
if (this._turnstileWidgetId && this._lastSitekey === sitekey && window.turnstile?.execute) {
try {
console.log('🔄 Reusing existing Turnstile widget...');
const token = await Promise.race([
window.turnstile.execute(this._turnstileWidgetId, { action }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Execute timeout')), 15000)
),
]);
if (token && token.length > 20) {
console.log('✅ Token generated via widget reuse');
return token;
}
} catch (error) {
console.log('⚠️ Widget reuse failed, will create a fresh widget:', error.message);
}
}
// Try invisible widget first
const invisibleToken = await this.createTurnstileWidget(sitekey, action);
if (invisibleToken && invisibleToken.length > 20) {
return invisibleToken;
}
console.log('⚠️ Falling back to interactive Turnstile (visible).');
return await this.createTurnstileWidgetInteractive(sitekey, action);
}
/**
* Create invisible Turnstile widget
* @param {string} sitekey - The site key
* @param {string} action - The action name
* @returns {Promise<string|null>} The token or null if failed
*/
async createTurnstileWidget(sitekey, action) {
return new Promise((resolve) => {
try {
// Force cleanup of any existing widget
if (this._turnstileWidgetId && window.turnstile?.remove) {
try {
window.turnstile.remove(this._turnstileWidgetId);
console.log('🧹 Cleaned up existing Turnstile widget');
} catch (e) {
console.warn('⚠️ Widget cleanup warning:', e.message);
}
}
const container = this.ensureTurnstileContainer();
container.innerHTML = '';
// Verify Turnstile is available
if (!window.turnstile?.render) {
console.error('❌ Turnstile not available for rendering');
resolve(null);
return;
}
console.log('🔧 Creating invisible Turnstile widget...');
const widgetId = window.turnstile.render(container, {
sitekey,
action,
size: 'invisible',
retry: 'auto',
'retry-interval': 8000,
callback: (token) => {
console.log('✅ Invisible Turnstile callback');
resolve(token);
},
'error-callback': () => resolve(null),
'timeout-callback': () => resolve(null),
});
this._turnstileWidgetId = widgetId;
this._lastSitekey = sitekey;
if (!widgetId) {
return resolve(null);
}
// Execute the widget and race with timeout
Promise.race([
window.turnstile.execute(widgetId, { action }),
new Promise((_, reject) =>
setTimeout(() => reject(new Error('Invisible execute timeout')), 12000)
),
])
.then(resolve)
.catch(() => resolve(null));
} catch (e) {
console.error('❌ Invisible Turnstile creation failed:', e);
resolve(null);
}
});
}
/**
* Create interactive Turnstile widget (visible)
* @param {string} sitekey - The site key
* @param {string} action - The action name
* @returns {Promise<string|null>} The token or null if failed
*/
async createTurnstileWidgetInteractive(sitekey, action) {
// Create a visible widget that users can interact with if needed
console.log('🔄 Creating interactive Turnstile widget (visible)');
return new Promise((resolve) => {
try {
// Force cleanup of any existing widget
if (this._turnstileWidgetId && window.turnstile?.remove) {
try {
window.turnstile.remove(this._turnstileWidgetId);
} catch (e) {
console.warn('⚠️ Widget cleanup warning:', e.message);
}
}
const overlay = this.ensureTurnstileOverlayContainer();
overlay.classList.remove('wplace-overlay-hidden');
overlay.style.display = 'block';
const host = overlay.querySelector('#turnstile-overlay-host');
host.innerHTML = '';
// Set a timeout for interactive mode
const timeout = setTimeout(() => {
console.warn('⏰ Interactive Turnstile widget timeout');
overlay.classList.add('wplace-overlay-hidden');
overlay.style.display = 'none';
resolve(null);
}, 60000); // 60 seconds for user interaction
const widgetId = window.turnstile.render(host, {
sitekey,
action,
size: 'normal',
theme: 'light',
callback: (token) => {
clearTimeout(timeout);
overlay.classList.add('wplace-overlay-hidden');
overlay.style.display = 'none';
console.log('✅ Interactive Turnstile completed successfully');
if (typeof token === 'string' && token.length > 20) {
resolve(token);
} else {
console.warn('❌ Invalid token from interactive widget');
resolve(null);
}
},
'error-callback': (error) => {
clearTimeout(timeout);
overlay.classList.add('wplace-overlay-hidden');
overlay.style.display = 'none';
console.warn('❌ Interactive Turnstile error:', error);
resolve(null);
},
});
this._turnstileWidgetId = widgetId;
this._lastSitekey = sitekey;
if (!widgetId) {
clearTimeout(timeout);
overlay.classList.add('wplace-overlay-hidden');
overlay.style.display = 'none';
console.warn('❌ Failed to create interactive Turnstile widget');
resolve(null);
} else {
console.log('✅ Interactive Turnstile widget created, waiting for user interaction...');
}
} catch (e) {
console.error('❌ Interactive Turnstile creation failed:', e);
resolve(null);
}
});
}
/**
* Cleanup method for when the script is disabled/reloaded
*/
cleanupTurnstile() {
if (this._turnstileWidgetId && window.turnstile?.remove) {
try {
window.turnstile.remove(this._turnstileWidgetId);
} catch (e) {
console.warn('Failed to cleanup Turnstile widget:', e);
}
}
if (this._turnstileContainer && document.body.contains(this._turnstileContainer)) {
this._turnstileContainer.remove();
}
if (this._turnstileOverlay && document.body.contains(this._turnstileOverlay)) {
this._turnstileOverlay.remove();
}
this._turnstileWidgetId = null;
this._turnstileContainer = null;
this._turnstileOverlay = null;
this._lastSitekey = null;
}
/**
* Obtain sitekey and token
* @param {string} fallback - Fallback sitekey
* @returns {Promise<{sitekey: string, token: string|null}>}
*/
async obtainSitekeyAndToken(fallback = '0x4AAAAAABpqJe8FO0N84q0F') {
// Cache sitekey to avoid repeated DOM queries
if (this._cachedSitekey) {
console.log('🔍 Using cached sitekey:', this._cachedSitekey);
return this.isTokenValid()
? {
sitekey: this._cachedSitekey,
token: this.turnstileToken,
}
: { sitekey: this._cachedSitekey, token: null };
}
// List of potential sitekeys to try
const potentialSitekeys = [
'0x4AAAAAABpqJe8FO0N84q0F', // WPlace common sitekey
'0x4AAAAAAAJ7xjKAp6Mt_7zw', // Alternative WPlace sitekey
'0x4AAAAAADm5QWx6Ov2LNF2g', // Another common sitekey
];
const trySitekey = async (sitekey, source) => {
if (!sitekey || sitekey.length < 10) return null;
console.log(`🔍 Testing sitekey from ${source}:`, sitekey);
const token = await this.executeTurnstile(sitekey);
if (token && token.length >= 20) {
console.log(`✅ Valid token generated from ${source} sitekey`);
this.setTurnstileToken(token);
this._cachedSitekey = sitekey;
return { sitekey, token };
} else {
console.log(`❌ Failed to get token from ${source} sitekey`);
return null;
}
};
try {
// 1️⃣ data-sitekey attribute
const sitekeySel = document.querySelector('[data-sitekey]');
if (sitekeySel) {
const sitekey = sitekeySel.getAttribute('data-sitekey');
const result = await trySitekey(sitekey, 'data attribute');
if (result) {
return result;
}
}
// 2️⃣ Turnstile element
const turnstileEl = document.querySelector('.cf-turnstile');
if (turnstileEl?.dataset?.sitekey) {
const sitekey = turnstileEl.dataset.sitekey;
const result = await trySitekey(sitekey, 'turnstile element');
if (result) {
return result;
}
}
// 3️⃣ Meta tags
const metaTags = document.querySelectorAll(
'meta[name*="turnstile"], meta[property*="turnstile"]'
);
for (const meta of metaTags) {
const content = meta.getAttribute('content');
const result = await trySitekey(content, 'meta tag');
if (result) {
return result;
}
}
// 4️⃣ Global variable
if (window.__TURNSTILE_SITEKEY) {
const result = await trySitekey(window.__TURNSTILE_SITEKEY, 'global variable');
if (result) {
return result;
}
}
// 5️⃣ Script tags
const scripts = document.querySelectorAll('script');
for (const script of scripts) {
const content = script.textContent || script.innerHTML;
const match = content.match(
/(?:sitekey|data-sitekey)['"\s\[\]:\=\(]*['"]?([0-9a-zA-Z_-]{20,})['"]?/i
);
if (match && match[1]) {
const extracted = match[1].replace(/['"]/g, '');
const result = await trySitekey(extracted, 'script content');
if (result) {
return result;
}
}
}
// 6️⃣ Known potential sitekeys
console.log('🔍 Testing known potential sitekeys...');
for (const testSitekey of potentialSitekeys) {
const result = await trySitekey(testSitekey, 'known list');
if (result) {
return result;
}
}
} catch (error) {
console.warn('⚠️ Error during sitekey detection:', error);
}
// 7️⃣ Fallback
console.log('🔧 Trying fallback sitekey:', fallback);
const fallbackResult = await trySitekey(fallback, 'fallback');
if (fallbackResult) {
return fallbackResult;
}
// 8️⃣ Ultimate fallback with no token
console.warn('❌ All sitekey detection methods failed, using fallback without token validation');
this._cachedSitekey = fallback;
return { sitekey: fallback, token: null };
}
// =============================================
// UTILITY METHODS
// =============================================
/**
* Sleep utility
* @param {number} ms - Milliseconds to sleep
* @returns {Promise<void>}
*/
_sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Wait for DOM element to appear
* @param {string} selector - CSS selector
* @param {number} pollInterval - Polling interval in ms
* @param {number} timeout - Timeout in ms
* @returns {Promise<Element|null>} The element or null if timeout
* @private
*/
_waitForSelector(selector, pollInterval = 200, timeout = 10000) {
return new Promise((resolve) => {
const endTime = Date.now() + timeout;
const poll = () => {
const element = document.querySelector(selector);
if (element) {
resolve(element);
} else if (Date.now() < endTime) {
setTimeout(poll, pollInterval);
} else {
resolve(null);
}
};
poll();
});
}
/**
* Simple translation helper (fallback to English)
* @param {string} key - Translation key
* @param {string} fallback - Fallback text
* @returns {string} Translated text or fallback
*/
_getText(key, fallback = '') {
// Try to use external translation function if available
if (typeof window.Utils?.t === 'function') {
return window.Utils.t(key);
}
// Fallback translations
const translations = {
turnstileInstructions: 'Complete the verification',
hideTurnstileBtn: 'Hide',
tokenCapturedSuccess: 'Token captured successfully'
};
return translations[key] || fallback || key;
}
/**
* Show alert helper (uses external alert if available)
* @param {string} message - Message to show
* @param {string} type - Alert type
*/
_showAlert(message, type = 'info') {
// Try to use external alert function if available
if (typeof window.Utils?.showAlert === 'function') {
window.Utils.showAlert(message, type);
} else {
console.log(`${type.toUpperCase()}: ${message}`);
}
}
/**
* Generate random string utility
* @param {number} len - Length of string
* @param {string} chars - Character set
* @returns {string} Random string
*/
_randStr(len, chars = 'abcdefghijklmnopqrstuvwxyz0123456789') {
return [...Array(len)]
.map(() =>
chars[
(crypto?.getRandomValues?.(new Uint32Array(1))[0] % chars.length) ||
Math.floor(Math.random() * chars.length)
]
)
.join('');
}
}
// Create global instance
window.WPlaceTokenManager = new TokenManager();
// Create global instance alias for Auto-Image.js compatibility
window.globalTokenManager = window.WPlaceTokenManager;
// Legacy compatibility - expose key methods globally for backward compatibility
window.setTurnstileToken = (token) => window.WPlaceTokenManager.setTurnstileToken(token);
window.ensureToken = (forceRefresh) => window.WPlaceTokenManager.ensureToken(forceRefresh);
window.isTokenValid = () => window.WPlaceTokenManager.isTokenValid();
window.invalidateToken = () => window.WPlaceTokenManager.invalidateToken();
console.log('✅ WPlace Token Manager loaded and ready');
File diff suppressed because it is too large. Load diff
+883
View File
@@ -0,0 +1,883 @@
/* WPlace Auto-Image Bot - Acrylic Theme */
/* Beautiful theme with translucent blurred panels */
@import url('https://fonts.googleapis.com/css2?family=Inter:ital,opsz,wght@0,14..32,100..900;1,14..32,100..900&display=swap');
@font-face {
font-family: 'Apple Color Emoji';
src: url('https://raw.githubusercontent.com/GORAlexComp/AppleColorEmojiFont/refs/heads/main/fonts/AppleColorEmoji.ttf');
}
/* Multi-account panel styling */
.wplace-theme-acrylic #wplace-accounts-container {
background: var(--wplace-primary);
color: var(--wplace-text);
border-radius: var(--wplace-radius);
box-shadow: none;
backdrop-filter: var(--wplace-backdrop);
border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
min-width: 250px;
}
.wplace-theme-acrylic #wplace-accounts-container * {
font-size: 15px;
line-height: normal;
}
/* Active account styles */
.wplace-theme-acrylic .wplace-accounts-container .active-account {
--active-bg: color-mix(in srgb, white 15%, transparent);
--active-color: var(--wplace-success);
--inactive-color: var(--wplace-text);
--progress-bg: rgb(255 255 255 / 10%);
--progress-fill: white;
}
/* Main Container */
.wplace-theme-acrylic #wplace-image-bot-container {
background: var(--wplace-primary);
color: var(--wplace-text);
border-radius: var(--wplace-radius);
box-shadow: none;
backdrop-filter: var(--wplace-backdrop);
border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
min-width: 300px;
}
.wplace-theme-acrylic #wplace-image-bot-container * {
font-size: 15px;
line-height: normal;
}
.wplace-theme-acrylic .wplace-content {
display: flex;
flex-direction: column;
/* gap: 10px; */
padding: 10px;
}
#wplace-image-bot-container.wplace-dragging {
transition: none !important;
box-shadow: none !important;
transform: none !important;
}
/* Stats */
.wplace-theme-acrylic #wplace-stats-container {
background: var(--wplace-primary);
color: var(--wplace-text);
border-radius: var(--wplace-radius);
box-shadow: none;
backdrop-filter: var(--wplace-backdrop);
border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
}
.wplace-theme-acrylic .wplace-stats {
margin-bottom: 0;
}
.wplace-theme-acrylic .wplace-colors-section {
border-top: none;
margin: 0;
}
/* Settings Container */
.wplace-theme-acrylic #wplace-settings-container {
background: var(--wplace-primary) !important;
color: var(--wplace-text) !important;
border-radius: var(--wplace-radius) !important;
box-shadow: none !important;
backdrop-filter: var(--wplace-backdrop) !important;
border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color) !important;
max-width: 400px !important;
}
.wplace-theme-acrylic .wplace-settings-header {
background: transparent;
padding: 0;
}
.wplace-theme-acrylic .wplace-settings-header:hover {
background: transparent !important;
}
.wplace-theme-acrylic .wplace-settings-icon {
animation: none;
font-size: 15px;
}
.wplace-theme-acrylic .wplace-settings-content {
padding: 10px;
display: flex;
flex-direction: column;
gap: 10px;
}
.wplace-theme-acrylic .wplace-settings-footer {
border-top: none;
padding: 10px;
}
.wplace-theme-acrylic .wplace-settings-section {
margin: 0;
}
.wplace-theme-acrylic .wplace-settings-section-wrapper,
.wplace-theme-acrylic .wplace-overlay-wrapper,
.wplace-theme-acrylic .wplace-batch-controls {
padding: 0 !important;
background: transparent !important;
border: none !important;
}
/* Headers */
.wplace-theme-acrylic .wplace-header,
.wplace-theme-acrylic .wplace-settings-title-wrapper {
height: 40px;
padding: 7px 7px 7px 10px;
font-weight: 600;
background: var(--wplace-accent);
color: var(--wplace-highlight);
}
.wplace-theme-acrylic .wplace-header span,
.wplace-theme-acrylic .wplace-settings-title {
max-width: 150px;
font-size: 15px;
font-weight: 600;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.wplace-theme-acrylic .wplace-header-controls #compactBtn {
display: none;
}
.wplace-theme-acrylic .wplace-header-btn,
.wplace-theme-acrylic .wplace-settings-close-btn {
background: #2e2e2e;
color: var(--wplace-highlight);
border: none;
font-size: 12px;
border-radius: 4px;
width: 24px;
height: 24px;
}
.wplace-theme-acrylic .wplace-header-btn i {
font-size: 12px !important;
}
.wplace-theme-acrylic .wplace-header-btn:hover,
.wplace-theme-acrylic .wplace-settings-close-btn:hover {
background: transparent;
transform: none;
}
/* Sections */
.wplace-theme-acrylic .wplace-section {
display: flex;
flex-direction: column;
gap: 8px;
background: transparent;
padding: 0;
border: none;
margin: 0;
}
.wplace-theme-acrylic .wplace-section-title {
color: var(--wplace-highlight);
text-transform: capitalize;
font-size: 13px;
font-weight: 600;
pointer-events: none;
letter-spacing: unset;
}
.wplace-theme-acrylic .wplace-section-title i {
display: none;
}
.wplace-theme-acrylic .wplace-status-section {
background: transparent;
padding: 0;
margin: 0;
border: none;
display: flex;
flex-direction: column;
gap: 8px;
}
/* Status Styling */
.wplace-theme-acrylic .wplace-stats {
background: transparent;
padding: 0;
border: none;
}
.wplace-theme-acrylic .wplace-stat-value {
color: var(--wplace-highlight);
}
.wplace-theme-acrylic .wplace-status {
border-radius: var(--wplace-radius);
border: none;
box-shadow: none;
padding: 10px;
font-weight: 500;
}
.wplace-theme-acrylic .status-default {
background: color-mix(in srgb, white 15%, transparent) !important;
}
.wplace-theme-acrylic .status-success {
background: color-mix(in srgb, var(--wplace-success) 15%, transparent);
color: var(--wplace-success);
}
.wplace-theme-acrylic .status-error {
background: color-mix(in srgb, var(--wplace-error) 15%, transparent);
color: var(--wplace-error);
}
.wplace-theme-acrylic .status-warning {
background: color-mix(in srgb, var(--wplace-warning) 15%, transparent);
color: var(--wplace-warning);
}
/* Buttons */
.wplace-theme-acrylic .wplace-btn {
border-radius: 8px;
border: none;
height: max-content;
padding: 10px;
gap: 5px;
flex-direction: column;
align-items: flex-start;
line-height: normal;
text-align: left;
font-size: 15px !important;
box-shadow: none;
}
.wplace-theme-acrylic .wplace-btn::before {
display: none;
}
.wplace-theme-acrylic .wplace-btn:disabled {
box-shadow: none !important;
}
.wplace-theme-acrylic .wplace-btn:hover:not(:disabled) {
transform: none;
animation: none;
box-shadow: none !important;
filter: brightness(0.9);
}
.wplace-theme-acrylic .wplace-btn-primary,
.wplace-theme-acrylic .wplace-btn-upload {
background: white !important;
color: black !important;
border: none !important;
}
.wplace-theme-acrylic .wplace-btn-upload:hover:not(:disabled) {
background: revert !important;
}
.wplace-theme-acrylic .wplace-btn-secondary,
.wplace-theme-acrylic .wplace-btn-select,
.wplace-theme-acrylic .wplace-btn-file,
.wplace-theme-acrylic .wplace-btn-overlay {
background: var(--wplace-secondary);
}
.wplace-theme-acrylic .wplace-btn-start {
background: var(--wplace-success);
color: white;
}
.wplace-theme-acrylic .wplace-btn-stop {
background: var(--wplace-error);
color: white;
}
.wplace-theme-acrylic .wplace-btn.active {
background: white;
color: black;
box-shadow: none !important;
}
.wplace-theme-acrylic .wplace-btn.active i {
filter: none;
}
.wplace-theme-acrylic .wplace-settings-apply-btn {
background: white;
color: black;
border-radius: 8px;
}
/* Compact Input Buttons */
.wplace-theme-acrylic .wplace-input-btn-small,
.wplace-theme-acrylic .wplace-input-btn-small:hover,
.wplace-theme-acrylic .wplace-input-btn-small:active {
background: white;
color: black;
border: none;
transform: none;
border-radius: 4px;
box-shadow: none;
font-weight: 500;
width: 25px;
height: 25px;
}
/* Alerts */
.wplace-theme-acrylic .wplace-alert-base {
border-radius: var(--wplace-radius);
backdrop-filter: var(--wplace-backdrop);
border: none;
box-shadow: none;
}
.wplace-theme-acrylic .wplace-alert-info {
background: color-mix(in srgb, #0000e5 50%, transparent);
}
.wplace-theme-acrylic .wplace-alert-success {
background: color-mix(in srgb, var(--wplace-success) 50%, transparent);
}
.wplace-theme-acrylic .wplace-alert-error {
background: color-mix(in srgb, var(--wplace-error) 50%, transparent);
}
.wplace-theme-acrylic .wplace-alert-warning {
background: color-mix(in srgb, var(--wplace-warning) 50%, transparent);
}
/* Progress Bar */
.wplace-theme-acrylic .wplace-progress {
background: var(--wplace-secondary);
border-radius: 4px;
margin: 0;
border: none;
}
.wplace-theme-acrylic .wplace-progress-bar {
background: white;
}
.wplace-theme-acrylic .wplace-progress-bar::after {
display: none;
}
/* Form Controls */
.wplace-theme-acrylic .wplace-settings-checkbox,
.wplace-theme-acrylic .wplace-notification-checkbox,
.wplace-theme-acrylic .wplace-speed-checkbox {
cursor: pointer;
width: 20px;
height: 20px;
flex-shrink: 0;
accent-color: var(--wplace-highlight);
}
.wplace-theme-acrylic .wplace-settings-select,
.wplace-theme-acrylic .wplace-settings-number-input,
.wplace-theme-acrylic .wplace-cooldown-input,
.wplace-theme-acrylic .wplace-notification-interval-input {
background: rgb(255 255 255 / 10%);
color: white;
border: 1px solid rgb(255 255 255 / 25%);
border-radius: 8px;
box-shadow: none;
transition-duration: 0.3s;
}
.wplace-theme-acrylic .wplace-settings-select:hover,
.wplace-theme-acrylic .wplace-settings-number-input:hover,
.wplace-theme-acrylic .wplace-cooldown-input:hover,
.wplace-theme-acrylic .wplace-notification-interval-input:hover {
border-color: rgb(255 255 255 / 50%);
}
.wplace-theme-acrylic .wplace-settings-select:focus,
.wplace-theme-acrylic .wplace-settings-number-input:focus,
.wplace-theme-acrylic .wplace-cooldown-input:focus,
.wplace-theme-acrylic .wplace-notification-interval-input:focus {
border-color: white;
}
.wplace-theme-acrylic .wplace-settings-option {
background: #2d3748;
color: white;
}
.wplace-theme-acrylic .wplace-settings-select option {
background: #2d3748;
color: white;
}
.wplace-theme-acrylic .wplace-cooldown-input {
border-radius: 4px !important;
}
.wplace-theme-acrylic .wplace-cooldown-input:focus {
background: rgb(255 255 255 / 10%);
box-shadow: none;
}
.wplace-theme-acrylic .wplace-overlay-opacity-value {
background-color: var(--wplace-secondary) !important;
border: none !important;
}
/* Sliders */
.wplace-theme-acrylic .wplace-slider,
.wplace-theme-acrylic .wplace-speed-slider,
.wplace-theme-acrylic .wplace-overlay-opacity-slider {
background: var(--wplace-secondary) !important;
border-radius: 3px !important;
height: 6px !important;
}
.wplace-theme-acrylic .wplace-slider::-webkit-slider-thumb,
.wplace-theme-acrylic .resize-slider::-webkit-slider-thumb,
.wplace-theme-acrylic .wplace-speed-slider::-webkit-slider-thumb,
.wplace-theme-acrylic .wplace-overlay-opacity-slider::-webkit-slider-thumb {
width: 16px !important;
height: 16px !important;
border-radius: 4px !important;
background: white !important;
box-shadow: none !important;
transform: none !important;
}
.wplace-theme-acrylic .wplace-speed-value {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
border-radius: 8px;
color: white;
box-shadow: 0 3px 10px rgb(79 172 254 / 30%);
border: 1px solid rgb(255 255 255 / 20%);
}
.wplace-theme-acrylic .resize-slider {
-webkit-appearance: revert;
}
/* Layout Controls */
.wplace-theme-acrylic .wplace-slider-container {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin: 0;
}
.wplace-theme-acrylic .wplace-input-label-compact {
color: rgba(255, 255, 255, 0.75);
margin-left: 4px;
text-transform: lowercase;
white-space: nowrap;
}
.wplace-theme-acrylic .wplace-cooldown-control {
margin: 0;
display: flex;
flex-direction: column;
gap: 8px;
}
.wplace-theme-acrylic .wplace-cooldown-control label {
font-weight: 400;
margin-bottom: 0;
display: block;
}
.wplace-theme-acrylic .wplace-cooldown-input-group {
margin: 0;
background: transparent;
}
/* Color Elements */
.wplace-theme-acrylic .wplace-color-item-name {
color: #ccc;
}
.wplace-theme-acrylic .wplace-color-swatch {
border: 1px solid rgb(255 255 255 / 20%);
border-radius: 4px;
}
.wplace-theme-acrylic .wplace-color-swatch:not(.active) {
filter: grayscale(80%);
}
.wplace-theme-acrylic .wplace-color-swatch.unavailable {
border-color: #666;
}
.wplace-theme-acrylic .wplace-color-swatch.unavailable:not(.active) {
filter: grayscale(90%);
}
.wplace-theme-acrylic .wplace-color-swatch.active::after {
text-shadow: 0 0 3px black;
}
.wplace-theme-acrylic .wplace-stat-color-swatch {
border-radius: 3px;
border: 1px solid rgb(255 255 255 / 10%);
box-shadow: inset 0 0 2px rgb(0 0 0 / 50%);
}
.wplace-theme-acrylic .wplace-stat-colors-grid {
background: var(--wplace-secondary);
border-radius: 8px;
}
.wplace-theme-acrylic .wplace-color-divider {
background: rgb(255 255 255 / 10%);
}
/* Icons */
.wplace-theme-acrylic .wplace-icon-key,
.wplace-theme-acrylic .wplace-icon-robot,
.wplace-theme-acrylic .wplace-icon-speed,
.wplace-theme-acrylic .wplace-icon-bell,
.wplace-theme-acrylic .wplace-icon-palette,
.wplace-theme-acrylic .wplace-icon-globe,
.wplace-theme-acrylic .wplace-icon-paint,
.wplace-theme-acrylic .wplace-icon-eye,
.wplace-theme-acrylic .wplace-icon-compass,
.wplace-theme-acrylic .wplace-icon-route,
.wplace-theme-acrylic .wplace-icon-table,
.wplace-theme-acrylic .wplace-icon-dice {
color: white;
}
/* Typography */
.wplace-theme-acrylic .wplace-cooldown-unit {
display: none;
}
.wplace-theme-acrylic .wplace-settings-toggle-title,
.wplace-theme-acrylic .wplace-overlay-opacity-label {
font-size: 15px !important;
}
.wplace-theme-acrylic .wplace-settings-description,
.wplace-theme-acrylic .wplace-random-batch-description,
.wplace-theme-acrylic .wplace-settings-toggle-description {
font-size: 12px;
color: rgb(255 255 255 / 75%) !important;
}
/* Turnstile Overlay */
.wplace-theme-acrylic .wplace-turnstile-overlay {
background: var(--wplace-primary);
border-radius: var(--wplace-radius);
box-shadow: none;
backdrop-filter: var(--wplace-backdrop);
border: none;
padding: 16px !important;
color: var(--wplace-text) !important;
}
.wplace-theme-acrylic .wplace-turnstile-hide-btn {
border-radius: 8px !important;
backdrop-filter: var(--wplace-backdrop) !important;
background: var(--wplace-primary) !important;
top: -25px !important;
right: 0 !important;
transition: all 0.3s ease !important;
}
.wplace-theme-acrylic .wplace-turnstile-hide-btn:hover {
background: white !important;
color: black !important;
}
/* Resize Tools */
.wplace-theme-acrylic .resize-tools button {
border-radius: 6px;
border: 1px solid rgb(255 255 255 / 20%);
background: rgb(255 255 255 / 6%);
color: #fff;
}
.wplace-theme-acrylic .resize-overlay {
background: rgb(0 0 0 / 50%);
}
/* TODO: More resize menu styles */
.wplace-theme-acrylic .resize-container {
background: var(--wplace-secondary);
backdrop-filter: var(--wplace-backdrop);
border: none;
border-radius: var(--wplace-radius);
color: var(--wplace-text);
}
/* Account switching UI styles */
.wplace-theme-acrylic .wplace-section {
margin: 5px;
border-radius: var(--wplace-radius);
background: rgba(255, 255, 255, 0.08);
padding: 5px;
border: 1px solid rgba(255, 255, 255, 0.15);
backdrop-filter: var(--wplace-backdrop);
}
.wplace-theme-acrylic .wplace-section-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--wplace-text);
margin: 2px;
font-size: 14px;
}
/* Auto-swap toggle switch */
.wplace-theme-acrylic .wplace-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.wplace-theme-acrylic .wplace-switch input {
opacity: 0;
width: 0;
height: 0;
}
.wplace-theme-acrylic .wplace-slider-round {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: rgba(255, 255, 255, 0.2);
transition: 0.4s;
border-radius: 24px;
backdrop-filter: blur(10px);
}
.wplace-theme-acrylic .wplace-slider-round:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.3);
}
.wplace-theme-acrylic .wplace-switch input:checked+.wplace-slider-round {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.wplace-theme-acrylic .wplace-switch input:checked+.wplace-slider-round:before {
transform: translateX(26px);
}
/* Account list container */
.wplace-theme-acrylic .accounts-list-container {
max-height: 500px;
overflow-y: auto;
/* margin-top: 10px; */
padding: 0 6px;
}
/* Individual account item */
.wplace-theme-acrylic .wplace-account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
margin: 4px 0;
background: rgba(255, 255, 255, 0.05);
border-radius: 8px;
border: 1px solid rgba(255, 255, 255, 0.15);
transition: all 0.3s ease;
backdrop-filter: blur(10px);
position: relative;
}
/* Account ordering number */
.wplace-theme-acrylic .wplace-account-number {
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
font-weight: bold;
z-index: 1;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
}
.wplace-theme-acrylic .wplace-account-item:hover {
background: rgba(255, 255, 255, 0.12);
border-color: rgba(255, 255, 255, 0.3);
transform: translateY(-1px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
}
.wplace-theme-acrylic .wplace-account-item.is-current {
background: linear-gradient(135deg, rgba(102, 126, 234, 0.3) 0%, rgba(118, 75, 162, 0.3) 100%);
border-color: #667eea;
box-shadow: 0 0 20px rgba(102, 126, 234, 0.4);
}
/* Account details */
.wplace-theme-acrylic .wplace-account-details {
flex: 1;
margin-left: 25px;
}
.wplace-theme-acrylic .wplace-account-name {
font-weight: 600;
color: var(--wplace-text);
font-size: 13px;
margin-bottom: 2px;
}
/* Account statistics */
.wplace-theme-acrylic .wplace-account-stats {
display: flex;
gap: 12px;
align-items: center;
font-size: 12px;
color: rgba(255, 255, 255, 0.75);
}
.wplace-theme-acrylic .wplace-account-stats i {
margin-right: 4px;
}
.wplace-theme-acrylic .wplace-account-stats .fas.fa-bolt {
color: #ffd700;
}
.wplace-theme-acrylic .wplace-account-stats .fas.fa-tint {
color: #00bfff;
}
/* Move Artwork Panel Styles for Acrylic Theme */
.wplace-theme-acrylic .wplace-move-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--wplace-primary);
border: var(--wplace-border-width) var(--wplace-border-style) var(--wplace-border-color);
border-radius: var(--wplace-radius);
padding: 24px;
z-index: 10000;
backdrop-filter: var(--wplace-backdrop);
min-width: 240px;
text-align: center;
font-family: var(--wplace-font);
box-shadow: 0 20px 50px rgba(0, 0, 0, 0.15);
}
.wplace-theme-acrylic .wplace-move-panel-title {
color: var(--wplace-text);
margin: 0 0 20px 0;
font-size: 18px;
font-weight: 600;
letter-spacing: 0.5px;
}
.wplace-theme-acrylic .wplace-move-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 12px;
margin-bottom: 20px;
}
.wplace-theme-acrylic .wplace-move-btn {
padding: 12px;
background: rgba(255, 255, 255, 0.2);
color: var(--wplace-text);
border: 1px solid rgba(255, 255, 255, 0.3);
border-radius: 12px;
cursor: pointer;
font-family: var(--wplace-font);
font-size: 16px;
font-weight: 600;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.wplace-theme-acrylic .wplace-move-btn:hover {
background: rgba(255, 255, 255, 0.3);
border-color: rgba(255, 255, 255, 0.5);
transform: translateY(-2px);
box-shadow: 0 8px 20px rgba(0, 0, 0, 0.1);
}
.wplace-theme-acrylic .wplace-move-btn:active {
transform: translateY(0);
background: rgba(255, 255, 255, 0.4);
}
.wplace-theme-acrylic .wplace-move-center {
color: var(--wplace-text);
padding: 12px;
font-size: 14px;
font-weight: 600;
display: flex;
align-items: center;
justify-content: center;
opacity: 0.8;
}
.wplace-theme-acrylic .wplace-move-close-btn {
background: rgba(255, 59, 48, 0.8);
color: white;
border: 1px solid rgba(255, 59, 48, 0.3);
padding: 12px 20px;
border-radius: 12px;
cursor: pointer;
font-family: var(--wplace-font);
font-size: 15px;
font-weight: 600;
transition: all 0.3s ease;
backdrop-filter: blur(10px);
}
.wplace-theme-acrylic .wplace-move-close-btn:hover {
background: rgba(255, 59, 48, 1);
transform: translateY(-1px);
box-shadow: 0 8px 20px rgba(255, 59, 48, 0.3);
}
.wplace-theme-acrylic .wplace-move-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.4);
z-index: 9999;
backdrop-filter: blur(8px);
}
+877
View File
@@ -0,0 +1,877 @@
/* WPlace Auto-Image Bot - Classic Light Theme */
/* Clean, bright theme based on classic design with light backgrounds */
.wplace-theme-classic-light {
/* === CORE COLORS === */
--wplace-primary: #E0E0E1; /* Quite white */
--wplace-secondary: #FBFBFB; /* Light white */
--wplace-accent: #F3F3F3; /* Darker white */
--wplace-text: #203C5D; /* Dark blue text */
--wplace-highlight: #203C5D; /* Dark blue highlight */
--wplace-success: #28a745; /* Bootstrap green */
--wplace-error: #dc3545; /* Bootstrap red */
--wplace-warning: #ffc107; /* Bootstrap yellow */
/* === UI PROPERTIES === */
--wplace-radius: 8px;
--wplace-btn-radius: 12px;
--wplace-border-style: solid;
--wplace-border-width: 1px;
--wplace-border-color: #415a77;
--wplace-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%);
--wplace-backdrop: blur(10px);
--wplace-font: 'Segoe UI', roboto, sans-serif;
/* === EXTENDED COLOR PALETTE === */
/* Icon colors */
--wplace-icon-primary: #4facfe; /* Light blue */
--wplace-icon-secondary: #00f2fe; /* Cyan */
--wplace-icon-palette: #003BFF; /* Pink */
/* Additional UI colors */
--wplace-danger: #dc3545; /* Bootstrap red */
--wplace-danger-dark: #c82333; /* Darker red */
--wplace-muted-text: rgb(33 37 41 / 70%);
--wplace-highlight-secondary: #203C5D;
/* Text variants */
--wplace-text-secondary: rgb(33 37 41 / 90%);
--wplace-text-muted: rgb(33 37 41 / 70%);
--wplace-text-dim: rgb(33 37 41 / 60%);
--wplace-text-faded: rgb(33 37 41 / 80%);
/* Background variants */
--wplace-bg-input: #f3f3f3; /* neutral-100 */
--wplace-bg-subtle: #f3f3f3; /* neutral-100 — hover */
--wplace-bg-faint: #e0e0e0; /* neutral-200 */
--wplace-bg-ghost: #d1d1d1; /* neutral-300 */
--wplace-bg-whisper: #e0e0e0; /* can be used as whisper on hover */
/* Border variants */
--wplace-border-subtle: #d1d1d1; /* neutral-300 */
--wplace-border-faint: #e0e0e0; /* neutral-200 */
--wplace-border-ghost: #e0e0e0; /* neutral-200 */
--wplace-border-ultra-faint: #e0e0e0; /* neutral-200 — very faint */
/* Shadow variants */
--wplace-shadow-drag: 0 12px 40px rgb(0 0 0 / 20%), 0 0 0 2px rgb(32 60 93 / 30%);
--wplace-shadow-notification: 0 4px 12px rgb(0 0 0 / 15%);
--wplace-shadow-slider-thumb: 0 3px 6px rgb(0 0 0 / 20%), 0 0 0 2px var(--wplace-icon-primary);
--wplace-shadow-slider-hover: 0 4px 8px rgb(0 0 0 / 25%), 0 0 0 3px var(--wplace-icon-primary);
/* Animation colors */
--wplace-pulse-start: rgb(40 167 69 / 70%);
--wplace-pulse-mid: rgb(40 167 69 / 0%);
--wplace-pulse-end: rgb(40 167 69 / 0%);
/* Slider colors */
--wplace-slider-thumb-bg: #203C5D;
--wplace-slider-track-bg: linear-gradient(to right, #4facfe 0%, #00f2fe 100%);
}
/* === COMPONENT STYLING === */
/* Multi-account panel styling */
.wplace-theme-classic-light #wplace-accounts-container {
background: linear-gradient(135deg, #fff 0%, #f8f9fa 100%) !important;
color: var(--wplace-text) !important;
border: 1px solid rgb(0 0 0 / 15%) !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
backdrop-filter: blur(10px) !important;
}
/* Active account styles */
.wplace-theme-classic-light .wplace-accounts-container .active-account {
--active-bg: var(--wplace-secondary) !important;
--active-color: var(--wplace-success) !important;
--inactive-color: var(--wplace-text) !important;
--progress-bg: rgb(0 0 0 / 10%) !important;
--progress-fill: var(--wplace-highlight) !important;
}
/* Main container with clean light styling */
.wplace-theme-classic-light #wplace-image-bot-container {
background: linear-gradient(135deg, #E0E0E1 0%, #FBFBFB 100%) !important;
color: var(--wplace-text) !important;
border: 1px solid rgb(0 0 0 / 15%) !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgb(0 0 0 / 15%), 0 0 0 1px rgb(0 0 0 / 8%) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
backdrop-filter: blur(10px) !important;
}
/* Stats container with proper contrast */
.wplace-theme-classic-light #wplace-stats-container {
background: linear-gradient(135deg, #FBFBFB 0%, #F3F3F3 100%) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgb(0 0 0 / 15%) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
color: var(--wplace-text) !important;
position: fixed !important;
overflow: hidden !important;
z-index: 9998 !important;
}
/* Headers with light gradient */
.wplace-theme-classic-light .wplace-header {
background: linear-gradient(135deg, #FBFBFB 0%, #F3F3F3 100%) !important;
color: var(--wplace-highlight) !important;
border-bottom: 1px solid rgb(0 0 0 / 15%) !important;
text-shadow: none !important;
}
/* Stats header styling */
.wplace-theme-classic-light #wplace-stats-container .wplace-header {
background: linear-gradient(135deg, #F3F3F3 0%, #E0E0E1 100%) !important;
color: var(--wplace-text) !important;
border-bottom: 1px solid rgb(0 0 0 / 20%) !important;
text-shadow: none !important;
font-weight: 600 !important;
}
/* Stats title specific styling */
.wplace-theme-classic-light #wplace-stats-container .wplace-header .wplace-stats-title {
color: var(--wplace-text) !important;
text-shadow: none !important;
}
/* Comprehensive text and element styling for light theme - scoped to bot containers only */
.wplace-theme-classic-light .wplace-status,
.wplace-theme-classic-light .wplace-stats,
.wplace-theme-classic-light .wplace-section,
.wplace-theme-classic-light .wplace-controls,
.wplace-theme-classic-light .wplace-data-management,
.wplace-theme-classic-light .wplace-cooldown-settings {
color: var(--wplace-text) !important;
}
/* Buttons with light styling */
.wplace-theme-classic-light .wplace-btn {
background: linear-gradient(135deg, #FBFBFB 0%, #F3F3F3 100%) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 12px !important;
color: var(--wplace-text) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
font-weight: 500 !important;
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1) !important;
}
/* Data management section buttons */
.wplace-theme-classic-light .wplace-data-management .wplace-btn {
background: linear-gradient(135deg, #FBFBFB 0%, #F3F3F3 100%) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
color: #203C5D !important;
font-weight: 500 !important;
}
.wplace-theme-classic-light .wplace-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #F3F3F3 0%, #E0E0E1 100%) !important;
box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important;
transform: translateY(-1px) !important;
}
.wplace-theme-classic-light .wplace-data-management .wplace-btn:hover:not(:disabled) {
background: linear-gradient(135deg, #F3F3F3 0%, #E0E0E1 100%) !important;
box-shadow: 0 4px 12px rgb(0 0 0 / 15%) !important;
}
/* Settings dialog */
.wplace-theme-classic-light #wplace-settings-container {
background: linear-gradient(135deg, #E0E0E1 0%, #FBFBFB 100%) !important;
border: 1px solid rgb(0 0 0 / 15%) !important;
border-radius: 12px !important;
box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
backdrop-filter: blur(10px) !important;
color: var(--wplace-text) !important;
}
/* Settings dialog text elements */
.wplace-theme-classic-light .wplace-stat-colors-grid * {
color: var(--wplace-text) !important;
font-weight: 500 !important;
}
.wplace-theme-classic-light #wplace-settings-container * {
color: var(--wplace-text) !important;
}
.wplace-theme-classic-light #wplace-settings-container .wplace-settings-section-wrapper * {
color: var(--wplace-text) !important;
}
/* Color palette text labels */
.wplace-theme-classic-light .wplace-color-label {
color: var(--wplace-text) !important;
font-weight: 500 !important;
text-shadow: none !important;
}
.wplace-theme-classic-light .wplace-color-name {
color: var(--wplace-text) !important;
font-weight: 500 !important;
}
.wplace-theme-classic-light .wplace-color-item-name {
color: #000 !important;
font-weight: 600 !important;
text-shadow: none !important;
}
/* Resize dialog */
.wplace-theme-classic-light .resize-container {
background: linear-gradient(135deg, #E0E0E1 0%, #FBFBFB 100%) !important;
border: 1px solid rgb(0 0 0 / 15%) !important;
border-radius: 12px !important;
box-shadow: 0 16px 48px rgb(0 0 0 / 20%), 0 0 0 1px rgb(0 0 0 / 8%) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
backdrop-filter: blur(10px) !important;
}
/* Settings header */
.wplace-theme-classic-light .wplace-settings-header {
background: linear-gradient(135deg, #FBFBFB 0%, #F3F3F3 100%) !important;
border-bottom: 1px solid rgb(0 0 0 / 15%) !important;
}
.wplace-theme-classic-light .wplace-settings-title {
color: var(--wplace-text) !important;
text-shadow: none !important;
}
.wplace-theme-classic-light .wplace-settings-close-btn {
background: rgb(0 0 0 / 5%) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 50% !important;
color: var(--wplace-text) !important;
transition: all 0.3s ease !important;
}
.wplace-theme-classic-light .wplace-settings-close-btn:hover {
background: rgb(220 53 69 / 10%) !important;
border-color: var(--wplace-error) !important;
box-shadow: 0 0 12px rgb(220 53 69 / 30%) !important;
}
/* Section titles */
.wplace-theme-classic-light .wplace-section-title {
color: var(--wplace-highlight) !important;
text-shadow: none !important;
font-weight: 600 !important;
}
/* Button variants with classic light colors */
.wplace-theme-classic-light .wplace-btn-start {
background: linear-gradient(135deg, #28a745 0%, #20c997 100%) !important;
color: white !important;
font-weight: 600 !important;
}
.wplace-theme-classic-light .wplace-btn-start:hover:not(:disabled) {
box-shadow: 0 4px 12px rgb(40 167 69 / 40%) !important;
}
.wplace-theme-classic-light .wplace-btn-stop {
background: linear-gradient(135deg, #dc3545 0%, #c82333 100%) !important;
color: white !important;
font-weight: 600 !important;
}
.wplace-theme-classic-light .wplace-btn-stop:hover:not(:disabled) {
box-shadow: 0 4px 12px rgb(220 53 69 / 40%) !important;
}
.wplace-theme-classic-light .wplace-btn-upload {
background: rgb(32 60 93 / 10%) !important;
border: 2px dashed var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
}
.wplace-theme-classic-light .wplace-btn-upload:hover:not(:disabled) {
background: rgb(32 60 93 / 15%) !important;
box-shadow: 0 4px 12px rgb(32 60 93 / 20%) !important;
}
/* Progress bars with clean light styling */
.wplace-theme-classic-light .wplace-progress {
background: rgb(0 0 0 / 10%) !important;
border: 1px solid rgb(0 0 0 / 15%) !important;
border-radius: 12px !important;
}
.wplace-theme-classic-light .wplace-progress-bar {
background: linear-gradient(135deg, #203C5D 0%, #203C5D 100%) !important;
box-shadow: none !important;
}
.wplace-theme-classic-light .wplace-progress-bar::after {
background: linear-gradient(90deg, transparent, rgb(255 255 255 / 30%), transparent) !important;
}
/* Status indicators with light colors */
.wplace-theme-classic-light .status-success {
background: rgb(40 167 69 / 10%) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
box-shadow: 0 0 15px rgb(40 167 69 / 20%) !important;
text-shadow: none !important;
}
.wplace-theme-classic-light .status-error {
background: rgb(220 53 69 / 10%) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
box-shadow: 0 0 15px rgb(220 53 69 / 20%) !important;
text-shadow: none !important;
}
.wplace-theme-classic-light .status-default {
background: rgb(32 60 93 / 10%) !important;
border-color: var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: none !important;
}
.wplace-theme-classic-light .wplace-stat-label {
color: var(--wplace-text) !important;
text-shadow: none !important;
font-weight: 500 !important;
}
.wplace-theme-classic-light .wplace-stat-value {
color: var(--wplace-highlight) !important;
text-shadow: none !important;
font-weight: 600 !important;
}
/* Sections with light styling */
.wplace-theme-classic-light .wplace-section {
background: rgb(0 0 0 / 3%) !important;
border: 1px solid rgb(0 0 0 / 10%) !important;
border-radius: 12px !important;
}
.wplace-theme-classic-light .wplace-status-section {
background: rgb(0 0 0 / 3%) !important;
border: 1px solid rgb(0 0 0 / 10%) !important;
border-radius: 12px !important;
}
.wplace-theme-classic-light .wplace-settings-section-wrapper {
background: rgb(0 0 0 / 3%) !important;
border: 1px solid rgb(0 0 0 / 10%) !important;
border-radius: 12px !important;
}
/* Form controls with light styling */
.wplace-theme-classic-light .wplace-settings-select {
background: #E0E0E1 !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 8px !important;
color: var(--wplace-text) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important;
}
.wplace-theme-classic-light .wplace-settings-select:focus {
border-color: var(--wplace-highlight) !important;
box-shadow: 0 0 0 2px rgb(32 60 93 / 30%) !important;
}
/* Dropdown menu options */
.wplace-theme-classic-light .wplace-settings-select option {
background: #E0E0E1 !important;
color: var(--wplace-text) !important;
}
.wplace-theme-classic-light .wplace-settings-option {
background: #E0E0E1 !important;
color: var(--wplace-text) !important;
}
/* Sliders with classic gradient */
.wplace-theme-classic-light .wplace-speed-slider {
background: linear-gradient(to right, #4facfe 0%, #32C8FE 100%) !important;
border-radius: 4px !important;
}
.wplace-theme-classic-light .wplace-overlay-opacity-slider {
background: linear-gradient(to right, #4facfe 0%, #32C8FE 100%) !important;
border-radius: 4px !important;
}
.wplace-theme-classic-light .wplace-slider {
background: linear-gradient(to right, #4facfe 0%, #32C8FE 100%) !important;
border-radius: 4px !important;
}
.wplace-theme-classic-light .wplace-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 50%;
background: var(--wplace-slider-thumb-bg);
border: 1px solid rgb(0 0 0 / 20%);
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgb(0 0 0 / 20%);
}
.wplace-theme-classic-light .wplace-speed-value {
background: linear-gradient(135deg, #4facfe 0%, #32C8FE 100%) !important;
border-radius: 8px !important;
color: white !important;
font-weight: 600 !important;
box-shadow: 0 3px 10px rgb(79 172 254 / 30%) !important;
border: 1px solid rgb(0 0 0 / 10%) !important;
}
/* Settings labels */
.wplace-theme-classic-light .wplace-settings-section-label {
color: var(--wplace-highlight) !important;
text-shadow: none !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
font-weight: 600 !important;
}
/* Icon colors for classic light theme */
.wplace-theme-classic-light .wplace-icon-key { color: #4facfe; }
.wplace-theme-classic-light .wplace-icon-robot { color: #4facfe; }
.wplace-theme-classic-light .wplace-icon-speed { color: #4facfe; }
.wplace-theme-classic-light .wplace-icon-bell { color: #ffc107; }
.wplace-theme-classic-light .wplace-icon-palette { color: #003BFF; }
.wplace-theme-classic-light .wplace-icon-globe { color: #ffeaa7; }
.wplace-theme-classic-light .wplace-icon-paint { color: #4facfe; }
.wplace-theme-classic-light .wplace-icon-eye { color: #203C5D; }
/* Clean light theme animations */
@keyframes light-shimmer {
0% {
transform: translateX(-100%);
}
100% {
transform: translateX(200%);
}
}
/* Turnstile/CF checkbox overlay - CRITICAL FIX */
.wplace-theme-classic-light .wplace-turnstile-overlay {
background: rgb(255 255 255 / 98%) !important;
border-radius: 12px !important;
box-shadow: 0 8px 32px rgb(0 0 0 / 30%) !important;
backdrop-filter: blur(10px) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
color: var(--wplace-text) !important;
font-family: 'Segoe UI', Roboto, sans-serif !important;
}
.wplace-theme-classic-light .wplace-turnstile-title {
color: var(--wplace-text) !important;
}
.wplace-theme-classic-light .wplace-turnstile-hide-btn {
color: var(--wplace-text) !important;
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 6px !important;
background: rgb(0 0 0 / 5%) !important;
}
.wplace-theme-classic-light .wplace-turnstile-hide-btn:hover {
background: rgb(0 0 0 / 10%) !important;
}
/* Dual control layout styling for classic light theme */
.wplace-theme-classic-light .wplace-dual-control-compact {
display: flex;
flex-direction: row;
align-items: center;
gap: 12px;
margin: 8px 0;
flex-wrap: wrap;
}
.wplace-theme-classic-light .wplace-slider-container-compact {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 160px;
}
.wplace-theme-classic-light .wplace-speed-slider-container-compact {
display: flex;
align-items: center;
gap: 8px;
flex: 1;
min-width: 160px;
}
.wplace-theme-classic-light .wplace-input-group-compact {
display: flex;
align-items: center;
gap: 4px;
flex-shrink: 0;
}
.wplace-theme-classic-light .wplace-input-btn-compact {
background: linear-gradient(135deg, #4facfe 0%, #32C8FE 100%);
color: white;
border: none;
border-radius: 4px;
width: 22px;
height: 22px;
cursor: pointer;
font-weight: bold;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.wplace-theme-classic-light .wplace-input-btn-compact:hover {
transform: translateY(-1px);
box-shadow: 0 4px 8px rgba(0,0,0,0.3);
background: linear-gradient(135deg, #32C8FE 0%, #4facfe 100%);
}
.wplace-theme-classic-light .wplace-input-btn-compact:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}
.wplace-theme-classic-light .wplace-number-input-compact {
background: #E0E0E1;
border: 1px solid rgb(0 0 0 / 20%);
border-radius: 4px;
color: var(--wplace-text);
padding: 4px 8px;
font-size: 12px;
width: 65px;
text-align: center;
transition: all 0.2s ease;
height: 22px;
}
.wplace-theme-classic-light .wplace-number-input-compact:focus {
outline: none;
border-color: #4facfe;
box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3);
background: #FBFBFB;
}
.wplace-theme-classic-light .wplace-input-label-compact {
color: var(--wplace-text);
font-size: 11px;
margin-left: 4px;
white-space: nowrap;
}
.wplace-theme-classic-light .wplace-batch-size-header {
margin-bottom: 8px;
}
.wplace-theme-classic-light .wplace-batch-size-label {
color: var(--wplace-highlight);
font-weight: 500;
font-size: 13px;
}
/* Cooldown control styling for classic light theme */
.wplace-theme-classic-light .wplace-cooldown-control {
margin: 12px 0;
}
.wplace-theme-classic-light .wplace-cooldown-control label {
color: var(--wplace-highlight);
font-weight: 500;
font-size: 13px;
margin-bottom: 8px;
display: block;
}
/* Color swatches with light styling */
.wplace-theme-classic-light .wplace-color-swatch {
border: 1px solid rgb(0 0 0 / 20%) !important;
border-radius: 4px !important;
box-shadow: 0 2px 8px rgb(0 0 0 / 10%) !important;
}
.wplace-theme-classic-light .wplace-color-swatch:hover {
box-shadow: 0 4px 16px rgb(32 60 93 / 30%) !important;
transform: translateY(-1px);
}
.wplace-theme-classic-light .wplace-stat-colors-grid {
background: rgb(0 0 0 / 5%) !important;
border-radius: 8px !important;
border: 1px solid rgb(0 0 0 / 10%) !important;
}
/* Account switching UI styles */
.wplace-theme-classic-light .wplace-section {
margin: 15px 0;
border-radius: var(--wplace-radius);
background: rgba(0, 0, 0, 0.03);
padding: 15px;
border: 1px solid rgba(0, 0, 0, 0.1);
}
.wplace-theme-classic-light .wplace-section-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--wplace-text);
margin-bottom: 10px;
font-size: 14px;
}
/* Auto-swap toggle switch */
.wplace-theme-classic-light .wplace-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.wplace-theme-classic-light .wplace-switch input {
opacity: 0;
width: 0;
height: 0;
}
.wplace-theme-classic-light .wplace-slider-round {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #ccc;
transition: 0.4s;
border-radius: 24px;
}
.wplace-theme-classic-light .wplace-slider-round:before {
position: absolute;
content: "";
height: 18px;
width: 18px;
left: 3px;
bottom: 3px;
background-color: white;
transition: 0.4s;
border-radius: 50%;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.wplace-theme-classic-light .wplace-switch input:checked + .wplace-slider-round {
background-color: #4facfe;
}
.wplace-theme-classic-light .wplace-switch input:checked + .wplace-slider-round:before {
transform: translateX(26px);
}
/* Account list container */
.wplace-theme-classic-light .accounts-list-container {
max-height: 200px;
overflow-y: auto;
margin-top: 10px;
padding: 0 6px;
}
/* Individual account item */
.wplace-theme-classic-light .wplace-account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
margin: 4px 0;
background: rgba(0, 0, 0, 0.02);
border-radius: 8px;
border: 1px solid rgba(0, 0, 0, 0.08);
transition: all 0.3s ease;
position: relative;
}
/* Account ordering number */
.wplace-theme-classic-light .wplace-account-number {
position: absolute;
top: 3px;
left: 3px;
width: 16px;
height: 16px;
background: #4facfe;
color: white;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
font-size: 9px;
font-weight: bold;
z-index: 1;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}
.wplace-theme-classic-light .wplace-account-item:hover {
background: rgba(0, 0, 0, 0.05);
border-color: rgba(0, 0, 0, 0.15);
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
}
.wplace-theme-classic-light .wplace-account-item.is-current {
background: rgba(79, 172, 254, 0.1);
border-color: #4facfe;
box-shadow: 0 0 10px rgba(79, 172, 254, 0.2);
}
/* Account details */
.wplace-theme-classic-light .wplace-account-details {
flex: 1;
margin-left: 20px;
min-width: 0;
}
.wplace-theme-classic-light .wplace-account-name {
font-weight: 600;
color: var(--wplace-text);
font-size: 12px;
margin-bottom: 1px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Account statistics */
.wplace-theme-classic-light .wplace-account-stats {
display: flex;
gap: 8px;
align-items: center;
font-size: 11px;
color: rgba(0, 0, 0, 0.6);
flex-shrink: 0;
}
.wplace-theme-classic-light .wplace-account-stats i {
margin-right: 4px;
}
.wplace-theme-classic-light .wplace-account-stats .fas.fa-bolt {
color: #ff9800;
}
.wplace-theme-classic-light .wplace-account-stats .fas.fa-tint {
color: #2196f3;
}
/* Move Artwork Panel Styles for Classic Light Theme */
.wplace-theme-classic-light .wplace-move-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--wplace-primary);
border: 2px solid var(--wplace-border-color);
border-radius: var(--wplace-radius);
padding: 20px;
z-index: 10000;
box-shadow: var(--wplace-shadow);
min-width: 200px;
text-align: center;
backdrop-filter: var(--wplace-backdrop);
font-family: var(--wplace-font);
}
.wplace-theme-classic-light .wplace-move-panel-title {
color: var(--wplace-text);
margin: 0 0 15px 0;
font-size: 16px;
font-weight: 600;
}
.wplace-theme-classic-light .wplace-move-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 8px;
margin-bottom: 15px;
}
.wplace-theme-classic-light .wplace-move-btn {
padding: 10px;
background: linear-gradient(135deg, #4CAF50 0%, #45a049 100%);
color: white;
border: 1px solid var(--wplace-border-color);
border-radius: var(--wplace-btn-radius);
cursor: pointer;
font-family: var(--wplace-font);
font-size: 14px;
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
}
.wplace-theme-classic-light .wplace-move-btn:hover {
background: linear-gradient(135deg, #45a049 0%, #4CAF50 100%);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgb(0 0 0 / 15%);
}
.wplace-theme-classic-light .wplace-move-btn:active {
transform: translateY(0);
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
}
.wplace-theme-classic-light .wplace-move-center {
color: var(--wplace-highlight);
padding: 10px;
font-size: 12px;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
}
.wplace-theme-classic-light .wplace-move-close-btn {
background: linear-gradient(135deg, #f44336 0%, #d32f2f 100%);
color: white;
border: 1px solid var(--wplace-border-color);
padding: 10px 16px;
border-radius: var(--wplace-btn-radius);
cursor: pointer;
font-family: var(--wplace-font);
transition: all 0.2s ease;
box-shadow: 0 2px 4px rgb(0 0 0 / 10%);
}
.wplace-theme-classic-light .wplace-move-close-btn:hover {
background: linear-gradient(135deg, #d32f2f 0%, #f44336 100%);
transform: translateY(-1px);
box-shadow: 0 4px 8px rgb(0 0 0 / 15%);
}
.wplace-theme-classic-light .wplace-move-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
z-index: 9999;
backdrop-filter: blur(4px);
}
File diff suppressed because it is too large. Load diff
+815
View File
@@ -0,0 +1,815 @@
/* WPlace Auto-Image Bot - Neon Cyan Theme */
/* Retro cyberpunk aesthetic with cyan neon glow effects */
.wplace-theme-neon-cyan {
/* Neon theme colors - matching upstream main neon theme */
--wplace-primary: #1959A1;
--wplace-secondary: #3C74AF;
--wplace-accent: #538CC0;
--wplace-text: #81DCF7;
--wplace-highlight: #EA9C00;
--wplace-success: #39ff14;
--wplace-error: #ff073a;
--wplace-warning: #ff0;
/* Neon UI properties */
--wplace-radius: 0;
--wplace-border-style: solid;
--wplace-border-width: 2px;
--wplace-border-color: #81DCF7;
--wplace-shadow: 0 0 20px rgb(234 156 0 / 40%), inset 0 0 20px rgb(234 156 0 / 10%);
--wplace-backdrop: none;
--wplace-font: 'Press Start 2P', monospace, 'Courier New';
/* Z-index layers */
--wplace-z-overlay: 10000;
--wplace-z-alert: 10002;
--wplace-z-settings: 10002;
/* Feature toggles */
--wplace-scanline: 1;
--wplace-pixel-blink: 1;
/* Icon colors - neon variants */
--wplace-icon-primary: #81DCF7;
--wplace-icon-secondary: #39ff14;
--wplace-icon-palette: #EA9C00;
/* Additional UI colors - neon variants */
--wplace-danger: #ff073a;
--wplace-danger-dark: #cc0531;
--wplace-muted-text: #00ff4180;
--wplace-highlight-secondary: #fa0;
/* Text variants - neon style */
--wplace-text-secondary: #3C74AF;
--wplace-text-muted: #00ff41;
--wplace-text-dim: #00ff41;
--wplace-text-faded: #00ff41cc;
/* Background variants - neon style */
--wplace-bg-input: rgb(234 156 0 / 15%);
--wplace-bg-subtle: rgb(234 156 0 / 8%);
--wplace-bg-faint: rgb(234 156 0 / 5%);
--wplace-bg-ghost: rgb(234 156 0 / 3%);
--wplace-bg-whisper: rgb(234 156 0 / 2%);
/* Border variants - neon style */
--wplace-border-subtle: rgb(234 156 0 / 40%);
--wplace-border-faint: rgb(234 156 0 / 25%);
--wplace-border-ghost: rgb(234 156 0 / 15%);
--wplace-border-ultra-faint: rgb(234 156 0 / 8%);
/* Shadow variants - neon style */
--wplace-shadow-drag: 0 12px 40px rgb(234 156 0 / 60%), 0 0 0 2px rgb(234 156 0 / 80%), 0 0 20px rgb(234 156 0 / 30%);
--wplace-shadow-notification: 0 4px 12px rgb(234 156 0 / 40%), 0 0 15px rgb(234 156 0 / 20%);
--wplace-shadow-slider-thumb: 0 3px 6px rgb(234 156 0 / 50%), 0 0 0 2px var(--wplace-icon-primary), 0 0 10px rgb(234 156 0 / 30%);
--wplace-shadow-slider-hover: 0 4px 8px rgb(234 156 0 / 60%), 0 0 0 3px var(--wplace-icon-primary), 0 0 15px rgb(234 156 0 / 40%);
/* Animation colors - neon style */
--wplace-pulse-start: rgb(234 156 0 / 80%);
--wplace-pulse-mid: rgb(234 156 0 / 0%);
--wplace-pulse-end: rgb(234 156 0 / 0%);
/* Slider colors - neon style */
--wplace-slider-thumb-bg: #81DCF7;
}
/* Neon-specific styling overrides */
.wplace-theme-neon-cyan #wplace-image-bot-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-cyan #wplace-stats-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
position: fixed !important;
overflow: hidden !important;
z-index: 9998 !important;
}
.wplace-theme-neon-cyan .wplace-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-cyan .wplace-section {
background: rgb(22 33 62 / 50%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-cyan .wplace-status-section {
background: rgb(22 33 62 / 50%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-cyan .wplace-section-title {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
text-transform: uppercase !important;
font-family: var(--wplace-font) !important;
font-size: 10px !important;
}
.wplace-theme-neon-cyan .wplace-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 9px !important;
text-transform: uppercase !important;
}
/* Pixel blinking and neon glow animation for buttons */
.wplace-theme-neon-cyan .wplace-btn:hover:not(:disabled) {
box-shadow: 0 0 15px var(--wplace-text), inset 0 0 15px rgb(234 156 0 / 10%) !important;
animation: pixel-blink 0.5s infinite, neon-glow 1s ease-in-out infinite alternate !important;
}
.wplace-theme-neon-cyan .wplace-btn-start {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon-cyan .wplace-btn-stop {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon-cyan .wplace-btn-upload {
background: var(--wplace-secondary) !important;
border: 1px dashed var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon-cyan .wplace-btn-select {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon-cyan .wplace-btn-file {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-warning) !important;
color: var(--wplace-warning) !important;
text-shadow: 0 0 8px var(--wplace-warning) !important;
}
.wplace-theme-neon-cyan .wplace-progress {
background: rgb(0 0 0 / 80%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-cyan .wplace-progress-bar {
background: linear-gradient(90deg, var(--wplace-success) 0%, var(--wplace-text) 100%) !important;
box-shadow: 0 0 10px var(--wplace-success) !important;
}
.wplace-theme-neon-cyan .wplace-stat-value {
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon-cyan .status-success {
background: rgb(57 255 20 / 10%) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
box-shadow: 0 0 15px var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon-cyan .status-error {
background: rgb(255 7 58 / 10%) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon-cyan .status-default {
background: rgb(234 156 0 / 10%) !important;
border-color: var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Settings dialog neon styling */
.wplace-theme-neon-cyan #wplace-settings-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 30px var(--wplace-text), inset 0 0 30px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-cyan .wplace-settings-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
}
.wplace-theme-neon-cyan .wplace-settings-header h3 {
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 16px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon-cyan .wplace-settings-close-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
}
.wplace-theme-neon-cyan .wplace-settings-close-btn:hover {
background: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
}
.wplace-theme-neon-cyan .wplace-settings-section-wrapper {
background: rgb(22 33 62 / 30%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-cyan .wplace-settings-select {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 11px !important;
}
.wplace-theme-neon-cyan .wplace-settings-section-label {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 12px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon-cyan .wplace-speed-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
box-shadow: 0 0 10px rgb(234 156 0 / 30%) !important;
}
.wplace-theme-neon-cyan .wplace-overlay-opacity-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Neon slider styling */
.wplace-theme-neon-cyan .wplace-slider,
.wplace-theme-neon-cyan .wplace-speed-slider,
.wplace-theme-neon-cyan .wplace-overlay-opacity-slider {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 10px rgb(234 156 0 / 30%) !important;
}
.wplace-theme-neon-cyan .wplace-slider::-webkit-slider-thumb,
.wplace-theme-neon-cyan .wplace-speed-slider::-webkit-slider-thumb,
.wplace-theme-neon-cyan .wplace-overlay-opacity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 0 !important;
background: var(--wplace-slider-thumb-bg) !important;
border: 2px solid var(--wplace-text) !important;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 0 8px rgb(234 156 0 / 50%) !important;
}
.wplace-theme-neon-cyan .wplace-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon-cyan .wplace-speed-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon-cyan .wplace-overlay-opacity-slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 0 15px var(--wplace-text) !important;
}
/* Scanline animation for neon theme */
.wplace-theme-neon-cyan #wplace-image-bot-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline 3s linear infinite;
opacity: 0.7;
}
.wplace-theme-neon-cyan #wplace-stats-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline-stats 4s linear infinite;
opacity: 0.7;
}
/* Text glow animations */
@keyframes neon-glow {
0%, 100% {
text-shadow: 0 0 5px currentcolor, 0 0 10px currentcolor, 0 0 15px currentcolor;
}
50% {
text-shadow: 0 0 2px currentcolor, 0 0 5px currentcolor, 0 0 8px currentcolor;
}
}
@keyframes pixel-blink {
0%, 50% {
opacity: 1;
}
51%, 100% {
opacity: 0.7;
}
}
@keyframes scanline {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(400px);
}
}
@keyframes scanline-stats {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(300px);
}
}
/* Dual control layout styling for neon theme */
.wplace-theme-neon-cyan .wplace-dual-control-compact {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin: 6px 0;
flex-wrap: wrap;
}
.wplace-theme-neon-cyan .wplace-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon-cyan .wplace-speed-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon-cyan .wplace-input-group-compact {
display: flex;
align-items: center;
gap: 3px;
flex-shrink: 0;
}
.wplace-theme-neon-cyan .wplace-input-btn-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
width: 18px;
height: 18px;
cursor: pointer;
font-weight: bold;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-cyan .wplace-input-btn-compact:hover {
box-shadow: 0 0 10px var(--wplace-text) !important;
animation: pixel-blink 0.5s infinite !important;
}
.wplace-theme-neon-cyan .wplace-number-input-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
padding: 2px 4px;
font-size: 10px;
width: 65px;
text-align: center;
transition: all 0.2s ease;
height: 18px;
font-family: var(--wplace-font) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon-cyan .wplace-number-input-compact:focus {
outline: none;
box-shadow: 0 0 10px var(--wplace-text) !important;
background: rgb(234 156 0 / 15%) !important;
}
.wplace-theme-neon-cyan .wplace-input-label-compact {
color: var(--wplace-text) !important;
font-size: 9px;
margin-left: 3px;
white-space: nowrap;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
.wplace-theme-neon-cyan .wplace-batch-size-header {
margin-bottom: 6px;
}
.wplace-theme-neon-cyan .wplace-batch-size-label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
/* Cooldown control styling for neon theme */
.wplace-theme-neon-cyan .wplace-cooldown-control {
margin: 8px 0;
}
.wplace-theme-neon-cyan .wplace-cooldown-control label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
margin-bottom: 6px;
display: block;
}
/* Resize dialog styling for neon theme */
.wplace-theme-neon-cyan .resize-container {
background-color: #1959A1 !important; /* Solid neon blue */
opacity: 1 !important; /* Fully opaque */
backdrop-filter: none !important; /* Remove blur effects */
box-shadow: none !important; /* Optional: remove glows */
font-family: 'Press Start 2P', monospace !important;
}
/* Account switching UI styles */
.wplace-theme-neon-cyan .wplace-section {
margin: 15px 0;
border-radius: 0;
background: rgba(0, 255, 255, 0.05);
padding: 15px;
border: 2px solid #00ffff;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.3);
font-family: var(--wplace-font);
}
.wplace-theme-neon-cyan .wplace-section-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--wplace-text);
margin-bottom: 10px;
font-size: 12px;
text-transform: uppercase;
text-shadow: 0 0 8px var(--wplace-text);
font-family: var(--wplace-font);
}
/* Auto-swap toggle switch */
.wplace-theme-neon-cyan .wplace-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.wplace-theme-neon-cyan .wplace-switch input {
opacity: 0;
width: 0;
height: 0;
}
.wplace-theme-neon-cyan .wplace-slider-round {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #1a1a2e;
transition: 0.4s;
border-radius: 0;
border: 2px solid #333;
box-shadow: 0 0 10px rgba(0, 255, 255, 0.2);
}
.wplace-theme-neon-cyan .wplace-slider-round:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: #fff;
transition: 0.4s;
border-radius: 0;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
}
.wplace-theme-neon-cyan .wplace-switch input:checked + .wplace-slider-round {
background-color: #00ffff;
box-shadow: 0 0 20px rgba(0, 255, 255, 0.6);
}
.wplace-theme-neon-cyan .wplace-switch input:checked + .wplace-slider-round:before {
transform: translateX(26px);
box-shadow: 0 0 12px rgba(0, 255, 255, 0.8);
}
/* Account list container */
.wplace-theme-neon-cyan .accounts-list-container {
max-height: 200px;
overflow-y: auto;
margin-top: 10px;
padding: 0 6px;
}
/* Individual account item */
.wplace-theme-neon-cyan .wplace-account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
margin: 4px 0;
background: rgba(0, 255, 255, 0.03);
border-radius: 0;
border: 1px solid #00ffff;
transition: all 0.3s ease;
box-shadow: 0 0 5px rgba(0, 255, 255, 0.2);
position: relative;
}
/* Account ordering number */
.wplace-theme-neon-cyan .wplace-account-number {
position: absolute;
top: 3px;
left: 3px;
width: 8x;
height: 8px;
background: #00ffff;
color: #1a1a2e;
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: bold;
z-index: 1;
border: 1px solid #00ffff;
box-shadow: 0 0 8px rgba(0, 255, 255, 0.6);
font-family: var(--wplace-font);
}
.wplace-theme-neon-cyan .wplace-account-item:hover {
background: rgba(0, 255, 255, 0.08);
border-color: #00ffff;
box-shadow: 0 0 15px rgba(0, 255, 255, 0.4);
transform: translateX(2px);
}
.wplace-theme-neon-cyan .wplace-account-item.is-current {
background: rgba(0, 255, 255, 0.15);
border-color: #00ffff;
box-shadow: 0 0 25px rgba(0, 255, 255, 0.6);
animation: neon-cyan-pulse 2s infinite;
}
@keyframes neon-cyan-pulse {
0%, 100% { box-shadow: 0 0 25px rgba(0, 255, 255, 0.6); }
50% { box-shadow: 0 0 35px rgba(0, 255, 255, 0.8); }
}
/* Account details */
.wplace-theme-neon-cyan .wplace-account-details {
flex: 1;
margin-left: 20px;
min-width: 0;
}
.wplace-theme-neon-cyan .wplace-account-name {
font-weight: 600;
color: var(--wplace-text);
font-size: 8px;
margin-bottom: 1px;
text-transform: uppercase;
text-shadow: 0 0 5px var(--wplace-text);
font-family: var(--wplace-font);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Account statistics */
.wplace-theme-neon-cyan .wplace-account-stats {
display: flex;
gap: 8px;
align-items: center;
font-size: 8px;
color: rgba(0, 255, 255, 0.8);
text-transform: uppercase;
font-family: var(--wplace-font);
flex-shrink: 0;
}
.wplace-theme-neon-cyan .wplace-account-stats i {
margin-right: 4px;
text-shadow: 0 0 5px currentColor;
}
.wplace-theme-neon-cyan .wplace-account-stats .fas.fa-bolt {
color: #ffff00;
text-shadow: 0 0 8px #ffff00;
}
.wplace-theme-neon-cyan .wplace-account-stats .fas.fa-tint {
color: #00ffff;
text-shadow: 0 0 8px #00ffff;
}
/* Move Artwork Panel Styles for Neon Cyan Theme */
.wplace-theme-neon-cyan .wplace-move-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
padding: 20px;
z-index: 10000;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgba(129, 220, 247, 0.1) !important;
min-width: 200px;
text-align: center;
font-family: var(--wplace-font) !important;
color: var(--wplace-text) !important;
}
.wplace-theme-neon-cyan .wplace-move-panel-title {
color: var(--wplace-text) !important;
margin: 0 0 15px 0;
font-size: 12px !important;
text-transform: uppercase;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
animation: cyan-pulse 2s infinite;
}
.wplace-theme-neon-cyan .wplace-move-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 4px;
margin-bottom: 15px;
}
.wplace-theme-neon-cyan .wplace-move-btn {
padding: 8px !important;
background: var(--wplace-secondary) !important;
color: var(--wplace-text) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
cursor: pointer;
font-family: var(--wplace-font) !important;
font-size: 10px !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
transition: all 0.2s ease;
text-transform: uppercase;
}
.wplace-theme-neon-cyan .wplace-move-btn:hover {
background: var(--wplace-accent) !important;
box-shadow: 0 0 15px var(--wplace-text) !important;
animation: cyan-flicker 0.5s infinite;
}
.wplace-theme-neon-cyan .wplace-move-center {
color: var(--wplace-highlight) !important;
padding: 8px;
font-size: 9px !important;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 0 5px var(--wplace-highlight) !important;
text-transform: uppercase;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-cyan .wplace-move-close-btn {
background: var(--wplace-error) !important;
color: white !important;
border: 1px solid var(--wplace-error) !important;
padding: 8px 12px;
border-radius: 0 !important;
cursor: pointer;
font-family: var(--wplace-font) !important;
font-size: 9px !important;
text-transform: uppercase;
text-shadow: 0 0 5px white !important;
transition: all 0.2s ease;
}
.wplace-theme-neon-cyan .wplace-move-close-btn:hover {
box-shadow: 0 0 15px var(--wplace-error) !important;
animation: error-pulse 0.5s infinite;
}
.wplace-theme-neon-cyan .wplace-move-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(25, 89, 161, 0.9) !important;
z-index: 9999;
}
@keyframes cyan-pulse {
0%, 100% { text-shadow: 0 0 5px var(--wplace-text), 0 0 10px var(--wplace-text); }
50% { text-shadow: 0 0 10px var(--wplace-text), 0 0 20px var(--wplace-text); }
}
@keyframes cyan-flicker {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.8; }
}
@keyframes error-pulse {
0%, 100% { box-shadow: 0 0 5px var(--wplace-error); }
50% { box-shadow: 0 0 15px var(--wplace-error); }
}
+881
View File
@@ -0,0 +1,881 @@
/* WPlace Auto-Image Bot - Neon Light Theme */
/* Retro cyberpunk aesthetic with light neon glow effects */
.wplace-theme-neon-light {
/* Neon theme colors - matching upstream main neon theme */
--wplace-primary: #E0E0E1;
--wplace-secondary: #FBFBFB;
--wplace-accent: #203C5D;
--wplace-text: #203C5D;
--wplace-highlight: #203C5D;
--wplace-success: #39ff14;
--wplace-error: #ff073a;
--wplace-warning: #ff0;
/* Neon UI properties */
--wplace-radius: 0;
--wplace-border-style: solid;
--wplace-border-width: 2px;
--wplace-border-color: #203C5D;
--wplace-shadow: 0 0 20px rgb(234 156 0 / 40%), inset 0 0 20px rgb(234 156 0 / 10%);
--wplace-backdrop: none;
--wplace-font: 'Press Start 2P', monospace, 'Courier New';
/* Z-index layers */
--wplace-z-overlay: 10000;
--wplace-z-alert: 10002;
--wplace-z-settings: 10002;
/* Feature toggles */
--wplace-scanline: 1;
--wplace-pixel-blink: 1;
/* Icon colors - neon variants */
--wplace-icon-primary: #203C5D;
--wplace-icon-secondary: #39ff14;
--wplace-icon-palette: #203C5D;
/* Additional UI colors - neon variants */
--wplace-danger: #ff073a;
--wplace-danger-dark: #cc0531;
--wplace-muted-text: #00ff4180;
--wplace-highlight-secondary: #fa0;
/* Text variants - neon style */
--wplace-text-secondary: #3C74AF;
--wplace-text-muted: #00ff41;
--wplace-text-dim: #00ff41;
--wplace-text-faded: #00ff41cc;
/* Background variants - neon style */
--wplace-bg-input: rgb(234 156 0 / 15%);
--wplace-bg-subtle: rgb(234 156 0 / 8%);
--wplace-bg-faint: rgb(234 156 0 / 5%);
--wplace-bg-ghost: rgb(234 156 0 / 3%);
--wplace-bg-whisper: rgb(234 156 0 / 2%);
/* Border variants - neon style */
--wplace-border-subtle: rgb(234 156 0 / 40%);
--wplace-border-faint: rgb(234 156 0 / 25%);
--wplace-border-ghost: rgb(234 156 0 / 15%);
--wplace-border-ultra-faint: rgb(234 156 0 / 8%);
/* Shadow variants - neon style */
--wplace-shadow-drag: 0 12px 40px rgb(234 156 0 / 60%), 0 0 0 2px rgb(234 156 0 / 80%), 0 0 20px rgb(234 156 0 / 30%);
--wplace-shadow-notification: 0 4px 12px rgb(234 156 0 / 40%), 0 0 15px rgb(234 156 0 / 20%);
--wplace-shadow-slider-thumb: 0 3px 6px rgb(234 156 0 / 50%), 0 0 0 2px var(--wplace-icon-primary), 0 0 10px rgb(234 156 0 / 30%);
--wplace-shadow-slider-hover: 0 4px 8px rgb(234 156 0 / 60%), 0 0 0 3px var(--wplace-icon-primary), 0 0 15px rgb(234 156 0 / 40%);
/* Animation colors - neon style */
--wplace-pulse-start: rgb(234 156 0 / 80%);
--wplace-pulse-mid: rgb(234 156 0 / 0%);
--wplace-pulse-end: rgb(234 156 0 / 0%);
/* Slider colors - neon style */
--wplace-slider-thumb-bg: #203C5D;
}
/* Neon-specific styling overrides */
.wplace-theme-neon-light #wplace-image-bot-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-light #wplace-stats-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
position: fixed !important;
overflow: hidden !important;
z-index: 9998 !important;
}
.wplace-theme-neon-light .wplace-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-light .wplace-section {
background: rgb(22 33 62 / 30%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-light .wplace-status-section {
background: rgb(22 33 62 / 30%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-light .wplace-section-title {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
text-transform: uppercase !important;
font-family: var(--wplace-font) !important;
font-size: 10px !important;
}
.wplace-theme-neon-light .wplace-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 9px !important;
text-transform: uppercase !important;
}
/* Pixel blinking and neon glow animation for buttons */
.wplace-theme-neon-light .wplace-btn:hover:not(:disabled) {
box-shadow: 0 0 15px var(--wplace-text), inset 0 0 15px rgb(234 156 0 / 10%) !important;
animation: pixel-blink 0.5s infinite, neon-glow 1s ease-in-out infinite alternate !important;
}
.wplace-theme-neon-light .wplace-btn-start {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon-light .wplace-btn-stop {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon-light .wplace-btn-upload {
background: var(--wplace-secondary) !important;
border: 1px dashed var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon-light .wplace-btn-select {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon-light .wplace-btn-file {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-warning) !important;
color: var(--wplace-warning) !important;
text-shadow: 0 0 8px var(--wplace-warning) !important;
}
.wplace-theme-neon-light .wplace-progress {
background: rgb(0 0 0 / 80%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-light .wplace-progress-bar {
background: linear-gradient(90deg, var(--wplace-success) 0%, var(--wplace-text) 100%) !important;
box-shadow: 0 0 10px var(--wplace-success) !important;
}
.wplace-theme-neon-light .wplace-stat-value {
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon-light .status-success {
background: rgb(57 255 20 / 10%) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
box-shadow: 0 0 15px var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon-light .status-error {
background: rgb(255 7 58 / 10%) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon-light .status-default {
background: rgb(234 156 0 / 10%) !important;
border-color: var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Settings dialog neon styling */
.wplace-theme-neon-light #wplace-settings-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 30px var(--wplace-text), inset 0 0 30px rgb(234 156 0 / 10%) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-light .wplace-settings-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
}
.wplace-theme-neon-light .wplace-settings-header h3 {
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 16px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon-light .wplace-settings-close-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
}
.wplace-theme-neon-light .wplace-settings-close-btn:hover {
background: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
}
.wplace-theme-neon-light .wplace-settings-section-wrapper {
background: rgb(22 33 62 / 10%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon-light .wplace-settings-select {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 11px !important;
}
.wplace-theme-neon-light .wplace-settings-section-label {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 12px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon-light .wplace-speed-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
box-shadow: 0 0 10px rgb(234 156 0 / 30%) !important;
}
.wplace-theme-neon-light .wplace-overlay-opacity-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Neon slider styling */
.wplace-theme-neon-light .wplace-slider,
.wplace-theme-neon-light .wplace-speed-slider,
.wplace-theme-neon-light .wplace-overlay-opacity-slider {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 10px rgb(234 156 0 / 30%) !important;
}
.wplace-theme-neon-light .wplace-slider::-webkit-slider-thumb,
.wplace-theme-neon-light .wplace-speed-slider::-webkit-slider-thumb,
.wplace-theme-neon-light .wplace-overlay-opacity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 0 !important;
background: var(--wplace-slider-thumb-bg) !important;
border: 2px solid var(--wplace-text) !important;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 0 8px rgb(234 156 0 / 50%) !important;
}
.wplace-theme-neon-light .wplace-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon-light .wplace-speed-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon-light .wplace-overlay-opacity-slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 0 15px var(--wplace-text) !important;
}
/* Scanline animation for neon theme */
.wplace-theme-neon-light #wplace-image-bot-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline 3s linear infinite;
opacity: 0.7;
}
.wplace-theme-neon-light #wplace-stats-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline-stats 4s linear infinite;
opacity: 0.7;
}
/* Text glow animations */
@keyframes neon-glow {
0%, 100% {
text-shadow: 0 0 5px currentcolor, 0 0 10px currentcolor, 0 0 15px currentcolor;
}
50% {
text-shadow: 0 0 2px currentcolor, 0 0 5px currentcolor, 0 0 8px currentcolor;
}
}
@keyframes pixel-blink {
0%, 50% {
opacity: 1;
}
51%, 100% {
opacity: 0.7;
}
}
@keyframes scanline {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(400px);
}
}
@keyframes scanline-stats {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(300px);
}
}
/* Dual control layout styling for neon theme */
.wplace-theme-neon-light .wplace-dual-control-compact {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin: 6px 0;
flex-wrap: wrap;
}
.wplace-theme-neon-light .wplace-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon-light .wplace-speed-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon-light .wplace-input-group-compact {
display: flex;
align-items: center;
gap: 3px;
flex-shrink: 0;
}
.wplace-theme-neon-light .wplace-input-btn-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
width: 18px;
height: 18px;
cursor: pointer;
font-weight: bold;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon-light .wplace-input-btn-compact:hover {
box-shadow: 0 0 10px var(--wplace-text) !important;
animation: pixel-blink 0.5s infinite !important;
}
.wplace-theme-neon-light .wplace-number-input-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
padding: 2px 4px;
font-size: 10px;
width: 65px;
text-align: center;
transition: all 0.2s ease;
height: 18px;
font-family: var(--wplace-font) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon-light .wplace-number-input-compact:focus {
outline: none;
box-shadow: 0 0 10px var(--wplace-text) !important;
background: rgb(234 156 0 / 15%) !important;
}
.wplace-theme-neon-light .wplace-input-label-compact {
color: var(--wplace-text) !important;
font-size: 9px;
margin-left: 3px;
white-space: nowrap;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
.wplace-theme-neon-light .wplace-batch-size-header {
margin-bottom: 6px;
}
.wplace-theme-neon-light .wplace-batch-size-label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
/* Cooldown control styling for neon theme */
.wplace-theme-neon-light .wplace-cooldown-control {
margin: 8px 0;
}
.wplace-theme-neon-light .wplace-cooldown-control label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
margin-bottom: 6px;
display: block;
}
/* Resize dialog styling for neon theme */
.wplace-theme-neon-light .resize-container {
background-color: #E0E0E1 !important; /* Solid neon white */
border: 3px solid #203C5D !important;
border-radius: 0 !important;
backdrop-filter: none !important; /* Remove blur effects */
box-shadow: none !important; /* Optional: remove glows */
font-family: 'Press Start 2P', monospace !important;
}
/* Account switching UI styles */
.wplace-theme-neon-light .wplace-section {
margin: 15px 0;
border-radius: 0;
background: rgba(32, 60, 93, 0.05);
padding: 15px;
border: 2px solid #203C5D;
box-shadow: 0 0 20px rgba(32, 60, 93, 0.2);
font-family: var(--wplace-font);
}
.wplace-theme-neon-light .wplace-section-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--wplace-text);
margin-bottom: 10px;
font-size: 12px;
text-transform: uppercase;
text-shadow: 0 0 5px var(--wplace-text);
font-family: var(--wplace-font);
}
/* Auto-swap toggle switch */
.wplace-theme-neon-light .wplace-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.wplace-theme-neon-light .wplace-switch input {
opacity: 0;
width: 0;
height: 0;
}
.wplace-theme-neon-light .wplace-slider-round {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #f0f0f0;
transition: 0.4s;
border-radius: 0;
border: 2px solid #ccc;
box-shadow: 0 0 8px rgba(32, 60, 93, 0.15);
}
.wplace-theme-neon-light .wplace-slider-round:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: #203C5D;
transition: 0.4s;
border-radius: 0;
box-shadow: 0 0 8px rgba(32, 60, 93, 0.3);
}
.wplace-theme-neon-light .wplace-switch input:checked + .wplace-slider-round {
background-color: #203C5D;
box-shadow: 0 0 15px rgba(32, 60, 93, 0.4);
}
.wplace-theme-neon-light .wplace-switch input:checked + .wplace-slider-round:before {
transform: translateX(26px);
background-color: #fff;
box-shadow: 0 0 10px rgba(32, 60, 93, 0.5);
}
/* Account list container */
.wplace-theme-neon-light .accounts-list-container {
max-height: 200px;
overflow-y: auto;
margin-top: 10px;
padding: 0 6px;
}
/* Individual account item */
.wplace-theme-neon-light .wplace-account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
margin: 4px 0;
background: rgba(32, 60, 93, 0.03);
border-radius: 0;
border: 1px solid #203C5D;
transition: all 0.3s ease;
box-shadow: 0 0 5px rgba(32, 60, 93, 0.1);
position: relative;
}
/* Account ordering number */
.wplace-theme-neon-light .wplace-account-number {
position: absolute;
top: 3px;
left: 3px;
width: 8px;
height: 8px;
background: #203C5D;
color: white;
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: bold;
z-index: 1;
border: 1px solid #203C5D;
box-shadow: 0 0 6px rgba(32, 60, 93, 0.4);
font-family: var(--wplace-font);
}
.wplace-theme-neon-light .wplace-account-item:hover {
background: rgba(32, 60, 93, 0.08);
border-color: #203C5D;
box-shadow: 0 0 12px rgba(32, 60, 93, 0.2);
transform: translateX(2px);
}
.wplace-theme-neon-light .wplace-account-item.is-current {
background: rgba(32, 60, 93, 0.15);
border-color: #203C5D;
box-shadow: 0 0 20px rgba(32, 60, 93, 0.3);
animation: neon-light-pulse 2s infinite;
}
@keyframes neon-light-pulse {
0%, 100% { box-shadow: 0 0 20px rgba(32, 60, 93, 0.3); }
50% { box-shadow: 0 0 25px rgba(32, 60, 93, 0.4); }
}
/* Account details */
.wplace-theme-neon-light .wplace-account-details {
flex: 1;
margin-left: 20px;
min-width: 0;
}
.wplace-theme-neon-light .wplace-account-name {
font-weight: 600;
color: var(--wplace-text);
font-size: 8px;
margin-bottom: 1px;
text-transform: uppercase;
text-shadow: 0 0 3px var(--wplace-text);
font-family: var(--wplace-font);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Account statistics */
.wplace-theme-neon-light .wplace-account-stats {
display: flex;
gap: 8px;
align-items: center;
font-size: 8px;
color: rgba(32, 60, 93, 0.8);
text-transform: uppercase;
font-family: var(--wplace-font);
flex-shrink: 0;
}
.wplace-theme-neon-light .wplace-account-stats i {
margin-right: 4px;
text-shadow: 0 0 3px currentColor;
}
.wplace-theme-neon-light .wplace-account-stats .fas.fa-bolt {
color: #ff6b35;
text-shadow: 0 0 5px #ff6b35;
}
.wplace-theme-neon-light .wplace-account-stats .fas.fa-tint {
color: #203C5D;
text-shadow: 0 0 5px #203C5D;
}
/* Move Artwork Panel */
.wplace-theme-neon-light .wplace-move-overlay {
position: fixed;
top: 0;
left: 0;
width: 100vw;
height: 100vh;
background: rgba(32, 60, 93, 0.3);
backdrop-filter: blur(4px);
z-index: 10000;
display: flex;
align-items: center;
justify-content: center;
animation: light-fade-in 0.3s ease-out;
}
.wplace-theme-neon-light .wplace-move-panel {
background: var(--wplace-secondary);
border: 2px solid var(--wplace-accent);
border-radius: var(--wplace-radius);
padding: 20px;
box-shadow: var(--wplace-shadow),
0 0 25px rgba(234, 156, 0, 0.6),
inset 0 0 15px rgba(234, 156, 0, 0.1);
color: var(--wplace-text);
font-family: var(--wplace-font);
text-align: center;
min-width: 200px;
animation: light-panel-pulse 2s ease-in-out infinite alternate;
}
.wplace-theme-neon-light .wplace-move-panel h3 {
margin: 0 0 15px 0;
color: var(--wplace-accent);
text-shadow: 0 0 8px var(--wplace-accent);
font-size: 14px;
font-weight: bold;
letter-spacing: 1px;
text-transform: uppercase;
}
.wplace-theme-neon-light .wplace-move-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
grid-template-rows: 1fr 1fr 1fr;
gap: 8px;
width: 150px;
height: 150px;
margin: 0 auto 15px;
}
.wplace-theme-neon-light .wplace-move-btn {
background: linear-gradient(135deg, var(--wplace-primary) 0%, var(--wplace-secondary) 100%);
border: 2px solid var(--wplace-accent);
border-radius: var(--wplace-radius);
color: var(--wplace-accent);
font-size: 16px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 0 5px currentColor;
box-shadow: 0 0 10px rgba(234, 156, 0, 0.3);
font-family: var(--wplace-font);
}
.wplace-theme-neon-light .wplace-move-btn:hover {
background: linear-gradient(135deg, var(--wplace-accent) 0%, var(--wplace-highlight) 100%);
color: var(--wplace-secondary);
text-shadow: 0 0 10px currentColor;
box-shadow: 0 0 20px rgba(234, 156, 0, 0.7),
inset 0 0 15px rgba(234, 156, 0, 0.2);
transform: scale(1.05);
animation: light-glow 0.5s ease-in-out;
}
.wplace-theme-neon-light .wplace-move-btn:active {
transform: scale(0.95);
box-shadow: 0 0 15px rgba(234, 156, 0, 0.9),
inset 0 0 20px rgba(234, 156, 0, 0.3);
}
.wplace-theme-neon-light .wplace-move-btn[data-direction="up"] {
grid-column: 2;
grid-row: 1;
}
.wplace-theme-neon-light .wplace-move-btn[data-direction="left"] {
grid-column: 1;
grid-row: 2;
}
.wplace-theme-neon-light .wplace-move-btn[data-direction="right"] {
grid-column: 3;
grid-row: 2;
}
.wplace-theme-neon-light .wplace-move-btn[data-direction="down"] {
grid-column: 2;
grid-row: 3;
}
.wplace-theme-neon-light .wplace-move-close {
background: linear-gradient(135deg, var(--wplace-error) 0%, #ff6b6b 100%);
border: 2px solid var(--wplace-error);
border-radius: var(--wplace-radius);
color: var(--wplace-secondary);
padding: 8px 16px;
font-size: 12px;
font-weight: bold;
cursor: pointer;
transition: all 0.2s ease;
text-shadow: 0 0 5px currentColor;
box-shadow: 0 0 10px rgba(255, 7, 58, 0.3);
font-family: var(--wplace-font);
text-transform: uppercase;
letter-spacing: 0.5px;
}
.wplace-theme-neon-light .wplace-move-close:hover {
background: linear-gradient(135deg, #ff6b6b 0%, var(--wplace-error) 100%);
box-shadow: 0 0 20px rgba(255, 7, 58, 0.7),
inset 0 0 10px rgba(255, 7, 58, 0.2);
transform: scale(1.05);
animation: light-error-pulse 0.5s ease-in-out;
}
/* Neon Light Theme Animations */
@keyframes light-fade-in {
from {
opacity: 0;
backdrop-filter: blur(0px);
}
to {
opacity: 1;
backdrop-filter: blur(4px);
}
}
@keyframes light-panel-pulse {
0% {
box-shadow: var(--wplace-shadow),
0 0 25px rgba(234, 156, 0, 0.6),
inset 0 0 15px rgba(234, 156, 0, 0.1);
}
100% {
box-shadow: var(--wplace-shadow),
0 0 35px rgba(234, 156, 0, 0.8),
inset 0 0 20px rgba(234, 156, 0, 0.15);
}
}
@keyframes light-glow {
0%, 100% {
box-shadow: 0 0 20px rgba(234, 156, 0, 0.7),
inset 0 0 15px rgba(234, 156, 0, 0.2);
}
50% {
box-shadow: 0 0 30px rgba(234, 156, 0, 0.9),
inset 0 0 25px rgba(234, 156, 0, 0.3);
}
}
@keyframes light-error-pulse {
0%, 100% {
box-shadow: 0 0 20px rgba(255, 7, 58, 0.7),
inset 0 0 10px rgba(255, 7, 58, 0.2);
}
50% {
box-shadow: 0 0 30px rgba(255, 7, 58, 0.9),
inset 0 0 15px rgba(255, 7, 58, 0.3);
}
}
+929
View File
@@ -0,0 +1,929 @@
/* WPlace Auto-Image Bot - Neon Theme */
/* Retro cyberpunk aesthetic with green neon glow effects */
.wplace-theme-neon {
/* Neon theme colors - matching upstream main neon theme */
--wplace-primary: #1a1a2e;
--wplace-secondary: #16213e;
--wplace-accent: #0f3460;
--wplace-text: #00ff41;
--wplace-highlight: #ff6b35;
--wplace-success: #39ff14;
--wplace-error: #ff073a;
--wplace-warning: #ff0;
/* Neon UI properties */
--wplace-radius: 0;
--wplace-border-style: solid;
--wplace-border-width: 2px;
--wplace-border-color: #00ff41;
--wplace-shadow: 0 0 20px rgb(0 255 65 / 30%), inset 0 0 20px rgb(0 255 65 / 10%);
--wplace-backdrop: none;
--wplace-font: 'Press Start 2P', monospace, 'Courier New';
/* Z-index layers */
--wplace-z-overlay: 10000;
--wplace-z-alert: 10002;
--wplace-z-settings: 10002;
/* Feature toggles */
--wplace-scanline: 1;
--wplace-pixel-blink: 1;
/* Icon colors - neon variants */
--wplace-icon-primary: #00ff41;
--wplace-icon-secondary: #39ff14;
--wplace-icon-palette: #ff6b35;
/* Additional UI colors - neon variants */
--wplace-danger: #ff073a;
--wplace-danger-dark: #cc0531;
--wplace-muted-text: #00ff4180;
--wplace-highlight-secondary: #fa0;
/* Text variants - neon style */
--wplace-text-secondary: #00ff41dd;
--wplace-text-muted: #00ff41bb;
--wplace-text-dim: #00ff4199;
--wplace-text-faded: #00ff41cc;
/* Background variants - neon style */
--wplace-bg-input: rgb(0 255 65 / 15%);
--wplace-bg-subtle: rgb(0 255 65 / 8%);
--wplace-bg-faint: rgb(0 255 65 / 5%);
--wplace-bg-ghost: rgb(0 255 65 / 3%);
--wplace-bg-whisper: rgb(0 255 65 / 2%);
/* Border variants - neon style */
--wplace-border-subtle: rgb(0 255 65 / 40%);
--wplace-border-faint: rgb(0 255 65 / 25%);
--wplace-border-ghost: rgb(0 255 65 / 15%);
--wplace-border-ultra-faint: rgb(0 255 65 / 8%);
/* Shadow variants - neon style */
--wplace-shadow-drag: 0 12px 40px rgb(0 255 65 / 60%), 0 0 0 2px rgb(0 255 65 / 80%), 0 0 20px rgb(0 255 65 / 30%);
--wplace-shadow-notification: 0 4px 12px rgb(0 255 65 / 40%), 0 0 15px rgb(0 255 65 / 20%);
--wplace-shadow-slider-thumb: 0 3px 6px rgb(0 255 65 / 50%), 0 0 0 2px var(--wplace-icon-primary), 0 0 10px rgb(0 255 65 / 30%);
--wplace-shadow-slider-hover: 0 4px 8px rgb(0 255 65 / 60%), 0 0 0 3px var(--wplace-icon-primary), 0 0 15px rgb(0 255 65 / 40%);
/* Animation colors - neon style */
--wplace-pulse-start: rgb(0 255 65 / 80%);
--wplace-pulse-mid: rgb(0 255 65 / 0%);
--wplace-pulse-end: rgb(0 255 65 / 0%);
/* Slider colors - neon style */
--wplace-slider-thumb-bg: #00ff41;
}
/* Neon-specific styling overrides */
.wplace-theme-neon #wplace-image-bot-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important;
font-family: var(--wplace-font) !important;
}
/* Multi-Account Panel Styles */
.wplace-theme-neon #wplace-multi-account-container {
position: fixed !important;
top: 180px !important;
right: 20px !important;
width: 320px !important;
background: rgb(22 33 62 / 50%) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important;
font-family: var(--wplace-font) !important;
z-index: 9997 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon #wplace-multi-account-container .wplace-panel-header {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 10px !important;
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
text-transform: uppercase !important;
font-size: 9px !important;
}
.wplace-theme-neon #wplace-multi-account-container .wplace-header-btn {
background: transparent !important;
border: 1px solid var(--wplace-text) !important;
color: var(--wplace-text) !important;
font-size: 8px !important;
padding: 4px 8px !important;
cursor: pointer !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase !important;
}
.wplace-theme-neon #wplace-multi-account-container .wplace-header-btn:hover {
box-shadow: 0 0 15px var(--wplace-text) !important;
animation: pixel-blink 0.5s infinite !important;
}
.wplace-theme-neon .wplace-multi-account-content {
padding: 10px !important;
}
.wplace-theme-neon .wplace-multi-account-info {
font-size: 9px !important;
text-transform: uppercase !important;
margin-bottom: 10px !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-multi-account-current,
.wplace-theme-neon .wplace-multi-account-stats {
margin-bottom: 8px !important;
font-size: 8px !important;
}
.wplace-theme-neon .wplace-multi-account-toggle {
margin-bottom: 12px !important;
display: flex !important;
align-items: center !important;
}
.wplace-theme-neon .wplace-multi-account-switch {
margin-right: 8px !important;
}
.wplace-theme-neon .wplace-multi-account-list {
max-height: 200px !important;
overflow-y: auto !important;
}
.wplace-theme-neon .wplace-multi-account-item {
display: flex !important;
justify-content: space-between !important;
align-items: center !important;
padding: 8px !important;
margin-bottom: 4px !important;
background: rgb(22 33 62 / 50%) !important;
border: 1px solid var(--wplace-text) !important;
font-size: 8px !important;
text-transform: uppercase !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-multi-account-item.active {
background: rgb(0 255 65 / 10%) !important;
border-color: var(--wplace-success) !important;
box-shadow: 0 0 15px var(--wplace-success) !important;
}
.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar {
width: 6px !important;
}
.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-track {
background: rgb(22 33 62 / 50%) !important;
}
.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-thumb {
background: var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 10px var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-multi-account-list::-webkit-scrollbar-thumb:hover {
background: var(--wplace-success) !important;
box-shadow: 0 0 15px var(--wplace-success) !important;
}
/* Move Artwork Panel Styles for Neon Theme */
.wplace-theme-neon .wplace-move-panel {
position: fixed;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
padding: 20px;
z-index: 10000;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important;
min-width: 200px;
text-align: center;
font-family: var(--wplace-font) !important;
color: var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-move-panel-title {
color: var(--wplace-text) !important;
margin: 0 0 15px 0;
font-size: 12px !important;
text-transform: uppercase;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
animation: pixel-blink 2s infinite;
}
.wplace-theme-neon .wplace-move-controls {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 4px;
margin-bottom: 15px;
}
.wplace-theme-neon .wplace-move-btn {
padding: 8px !important;
background: var(--wplace-secondary) !important;
color: var(--wplace-text) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
cursor: pointer;
font-family: var(--wplace-font) !important;
font-size: 10px !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
transition: all 0.2s ease;
text-transform: uppercase;
}
.wplace-theme-neon .wplace-move-btn:hover {
background: var(--wplace-accent) !important;
box-shadow: 0 0 15px var(--wplace-text) !important;
animation: pixel-blink 0.5s infinite;
}
.wplace-theme-neon .wplace-move-btn:active {
background: var(--wplace-text) !important;
color: var(--wplace-primary) !important;
box-shadow: 0 0 20px var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-move-center {
color: var(--wplace-highlight) !important;
padding: 8px;
font-size: 9px !important;
font-weight: bold;
display: flex;
align-items: center;
justify-content: center;
text-shadow: 0 0 5px var(--wplace-highlight) !important;
text-transform: uppercase;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-move-close-btn {
background: var(--wplace-error) !important;
color: white !important;
border: 1px solid var(--wplace-error) !important;
padding: 8px 12px;
border-radius: 0 !important;
cursor: pointer;
font-family: var(--wplace-font) !important;
font-size: 9px !important;
text-transform: uppercase;
text-shadow: 0 0 5px white !important;
transition: all 0.2s ease;
}
.wplace-theme-neon .wplace-move-close-btn:hover {
box-shadow: 0 0 15px var(--wplace-error) !important;
animation: pixel-blink 0.5s infinite;
}
.wplace-theme-neon .wplace-move-overlay {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(26, 26, 46, 0.9) !important;
z-index: 9999;
}
/* Neon theme animations */
@keyframes pixel-blink {
0%, 50% { opacity: 1; }
51%, 100% { opacity: 0.7; }
}
.wplace-theme-neon #wplace-stats-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 20px var(--wplace-text), inset 0 0 20px rgb(0 255 65 / 10%) !important;
font-family: var(--wplace-font) !important;
position: fixed !important;
overflow: hidden !important;
z-index: 9998 !important;
}
.wplace-theme-neon .wplace-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-section {
background: rgb(22 33 62 / 50%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon .wplace-status-section {
background: rgb(22 33 62 / 50%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon .wplace-section-title {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
text-transform: uppercase !important;
font-family: var(--wplace-font) !important;
font-size: 10px !important;
}
.wplace-theme-neon .wplace-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 9px !important;
text-transform: uppercase !important;
}
/* Pixel blinking and neon glow animation for buttons */
.wplace-theme-neon .wplace-btn:hover:not(:disabled) {
box-shadow: 0 0 15px var(--wplace-text), inset 0 0 15px rgb(0 255 65 / 10%) !important;
animation: pixel-blink 0.5s infinite, neon-glow 1s ease-in-out infinite alternate !important;
}
.wplace-theme-neon .wplace-btn-start {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon .wplace-btn-stop {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon .wplace-btn-upload {
background: var(--wplace-secondary) !important;
border: 1px dashed var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon .wplace-btn-select {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-highlight) !important;
color: var(--wplace-highlight) !important;
text-shadow: 0 0 8px var(--wplace-highlight) !important;
}
.wplace-theme-neon .wplace-btn-file {
background: var(--wplace-secondary) !important;
border-color: var(--wplace-warning) !important;
color: var(--wplace-warning) !important;
text-shadow: 0 0 8px var(--wplace-warning) !important;
}
.wplace-theme-neon .wplace-progress {
background: rgb(0 0 0 / 80%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon .wplace-progress-bar {
background: linear-gradient(90deg, var(--wplace-success) 0%, var(--wplace-text) 100%) !important;
box-shadow: 0 0 10px var(--wplace-success) !important;
}
.wplace-theme-neon .wplace-stat-value {
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon .status-success {
background: rgb(57 255 20 / 10%) !important;
border-color: var(--wplace-success) !important;
color: var(--wplace-success) !important;
box-shadow: 0 0 15px var(--wplace-success) !important;
text-shadow: 0 0 8px var(--wplace-success) !important;
}
.wplace-theme-neon .status-error {
background: rgb(255 7 58 / 10%) !important;
border-color: var(--wplace-error) !important;
color: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
text-shadow: 0 0 8px var(--wplace-error) !important;
}
.wplace-theme-neon .status-default {
background: rgb(0 255 65 / 10%) !important;
border-color: var(--wplace-text) !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Settings dialog neon styling */
.wplace-theme-neon #wplace-settings-container {
background: var(--wplace-primary) !important;
border: 2px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 30px var(--wplace-text), inset 0 0 30px rgb(0 255 65 / 10%) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-settings-header {
background: var(--wplace-secondary) !important;
border-bottom: 1px solid var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-settings-header h3 {
color: var(--wplace-text) !important;
text-shadow: 0 0 10px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 16px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon .wplace-settings-close-btn {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-settings-close-btn:hover {
background: var(--wplace-error) !important;
box-shadow: 0 0 15px var(--wplace-error) !important;
}
.wplace-theme-neon .wplace-settings-section-wrapper {
background: rgb(22 33 62 / 30%) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
}
.wplace-theme-neon .wplace-settings-select {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 11px !important;
}
.wplace-theme-neon .wplace-settings-section-label {
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
font-size: 12px !important;
text-transform: uppercase !important;
}
.wplace-theme-neon .wplace-speed-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important;
}
.wplace-theme-neon .wplace-overlay-opacity-value {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
/* Neon slider styling */
.wplace-theme-neon .wplace-slider,
.wplace-theme-neon .wplace-speed-slider,
.wplace-theme-neon .wplace-overlay-opacity-slider {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
box-shadow: 0 0 10px rgb(0 255 65 / 30%) !important;
}
.wplace-theme-neon .wplace-slider::-webkit-slider-thumb,
.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb,
.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 16px;
height: 16px;
border-radius: 0 !important;
background: var(--wplace-slider-thumb-bg) !important;
border: 2px solid var(--wplace-text) !important;
cursor: pointer;
transition: all 0.2s ease;
box-shadow: 0 0 8px rgb(0 255 65 / 50%) !important;
}
.wplace-theme-neon .wplace-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon .wplace-speed-slider::-webkit-slider-thumb:hover,
.wplace-theme-neon .wplace-overlay-opacity-slider::-webkit-slider-thumb:hover {
transform: scale(1.1);
box-shadow: 0 0 15px var(--wplace-text) !important;
}
/* Scanline animation for neon theme */
.wplace-theme-neon #wplace-image-bot-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline 3s linear infinite;
opacity: 0.7;
}
.wplace-theme-neon #wplace-stats-container::before {
content: '';
position: absolute;
top: 0;
left: 0;
right: 0;
height: 2px;
background: linear-gradient(90deg, transparent, var(--wplace-text), transparent);
z-index: 1;
pointer-events: none;
animation: scanline-stats 4s linear infinite;
opacity: 0.7;
}
/* Text glow animations */
@keyframes neon-glow {
0%, 100% {
text-shadow: 0 0 5px currentcolor, 0 0 10px currentcolor, 0 0 15px currentcolor;
}
50% {
text-shadow: 0 0 2px currentcolor, 0 0 5px currentcolor, 0 0 8px currentcolor;
}
}
@keyframes pixel-blink {
0%, 50% {
opacity: 1;
}
51%, 100% {
opacity: 0.7;
}
}
@keyframes scanline {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(400px);
}
}
@keyframes scanline-stats {
0% {
transform: translateY(-100%);
}
100% {
transform: translateY(300px);
}
}
/* Dual control layout styling for neon theme */
.wplace-theme-neon .wplace-dual-control-compact {
display: flex;
flex-direction: row;
align-items: center;
gap: 8px;
margin: 6px 0;
flex-wrap: wrap;
}
.wplace-theme-neon .wplace-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon .wplace-speed-slider-container-compact {
display: flex;
align-items: center;
gap: 6px;
flex: 1;
min-width: 160px;
}
.wplace-theme-neon .wplace-input-group-compact {
display: flex;
align-items: center;
gap: 3px;
flex-shrink: 0;
}
.wplace-theme-neon .wplace-input-btn-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
width: 18px;
height: 18px;
cursor: pointer;
font-weight: bold;
font-size: 10px;
display: flex;
align-items: center;
justify-content: center;
transition: all 0.2s ease;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
}
.wplace-theme-neon .wplace-input-btn-compact:hover {
box-shadow: 0 0 10px var(--wplace-text) !important;
animation: pixel-blink 0.5s infinite !important;
}
.wplace-theme-neon .wplace-number-input-compact {
background: var(--wplace-secondary) !important;
border: 1px solid var(--wplace-text) !important;
border-radius: 0 !important;
color: var(--wplace-text) !important;
padding: 2px 4px;
font-size: 10px;
width: 65px;
text-align: center;
transition: all 0.2s ease;
height: 18px;
font-family: var(--wplace-font) !important;
text-shadow: 0 0 5px var(--wplace-text) !important;
}
.wplace-theme-neon .wplace-number-input-compact:focus {
outline: none;
box-shadow: 0 0 10px var(--wplace-text) !important;
background: rgb(0 255 65 / 15%) !important;
}
.wplace-theme-neon .wplace-input-label-compact {
color: var(--wplace-text) !important;
font-size: 9px;
margin-left: 3px;
white-space: nowrap;
text-shadow: 0 0 5px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
.wplace-theme-neon .wplace-batch-size-header {
margin-bottom: 6px;
}
.wplace-theme-neon .wplace-batch-size-label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
}
/* Cooldown control styling for neon theme */
.wplace-theme-neon .wplace-cooldown-control {
margin: 8px 0;
}
.wplace-theme-neon .wplace-cooldown-control label {
color: var(--wplace-text) !important;
font-weight: 500;
font-size: 11px;
text-shadow: 0 0 8px var(--wplace-text) !important;
font-family: var(--wplace-font) !important;
text-transform: uppercase;
margin-bottom: 6px;
display: block;
}
/* Resize dialog styling for neon theme */
.wplace-theme-neon .resize-container {
background: #1a1a2e !important;
border: 3px solid #00ff41 !important;
border-radius: 0 !important;
box-shadow: 0 0 30px rgb(0 255 65 / 50%) !important;
font-family: 'Press Start 2P', monospace !important;
}
/* Account switching UI styles */
.wplace-theme-neon .wplace-section {
margin: 15px 0;
border-radius: 0;
background: rgba(0, 255, 65, 0.05);
padding: 15px;
border: 2px solid #00ff41;
box-shadow: 0 0 20px rgba(0, 255, 65, 0.3);
font-family: var(--wplace-font);
}
.wplace-theme-neon .wplace-section-title {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
color: var(--wplace-text);
margin-bottom: 10px;
font-size: 12px;
text-transform: uppercase;
text-shadow: 0 0 8px var(--wplace-text);
font-family: var(--wplace-font);
}
/* Auto-swap toggle switch */
.wplace-theme-neon .wplace-switch {
position: relative;
display: inline-block;
width: 50px;
height: 24px;
}
.wplace-theme-neon .wplace-switch input {
opacity: 0;
width: 0;
height: 0;
}
.wplace-theme-neon .wplace-slider-round {
position: absolute;
cursor: pointer;
top: 0;
left: 0;
right: 0;
bottom: 0;
background-color: #1a1a2e;
transition: 0.4s;
border-radius: 0;
border: 2px solid #333;
box-shadow: 0 0 10px rgba(0, 255, 65, 0.2);
}
.wplace-theme-neon .wplace-slider-round:before {
position: absolute;
content: "";
height: 14px;
width: 14px;
left: 3px;
bottom: 3px;
background-color: #fff;
transition: 0.4s;
border-radius: 0;
box-shadow: 0 0 8px rgba(255, 255, 255, 0.5);
}
.wplace-theme-neon .wplace-switch input:checked + .wplace-slider-round {
background-color: #00ff41;
box-shadow: 0 0 20px rgba(0, 255, 65, 0.6);
}
.wplace-theme-neon .wplace-switch input:checked + .wplace-slider-round:before {
transform: translateX(26px);
box-shadow: 0 0 12px rgba(0, 255, 65, 0.8);
}
/* Account list container */
.wplace-theme-neon .accounts-list-container {
max-height: 200px;
overflow-y: auto;
margin-top: 10px;
padding: 0 6px;
}
/* Individual account item */
.wplace-theme-neon .wplace-account-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 6px 8px;
margin: 4px 0;
background: rgba(0, 255, 65, 0.03);
border-radius: 0;
border: 1px solid #00ff41;
transition: all 0.3s ease;
box-shadow: 0 0 5px rgba(0, 255, 65, 0.2);
position: relative;
}
/* Account ordering number */
.wplace-theme-neon .wplace-account-number {
position: absolute;
top: 3px;
left: 3px;
width: 8px;
height: 8px;
background: #00ff41;
color: #1a1a2e;
border-radius: 0;
display: flex;
align-items: center;
justify-content: center;
font-size: 8px;
font-weight: bold;
z-index: 1;
border: 1px solid #00ff41;
box-shadow: 0 0 8px rgba(0, 255, 65, 0.6);
font-family: var(--wplace-font);
}
.wplace-theme-neon .wplace-account-item:hover {
background: rgba(0, 255, 65, 0.08);
border-color: #00ff41;
box-shadow: 0 0 15px rgba(0, 255, 65, 0.4);
transform: translateX(2px);
}
.wplace-theme-neon .wplace-account-item.is-current {
background: rgba(0, 255, 65, 0.15);
border-color: #00ff41;
box-shadow: 0 0 25px rgba(0, 255, 65, 0.6);
animation: neon-pulse 2s infinite;
}
@keyframes neon-pulse {
0%, 100% { box-shadow: 0 0 25px rgba(0, 255, 65, 0.6); }
50% { box-shadow: 0 0 35px rgba(0, 255, 65, 0.8); }
}
/* Account details */
.wplace-theme-neon .wplace-account-details {
flex: 1;
margin-left: 20px;
min-width: 0;
}
.wplace-theme-neon .wplace-account-name {
font-weight: 600;
color: var(--wplace-text);
font-size: 8px;
margin-bottom: 1px;
text-transform: uppercase;
text-shadow: 0 0 5px var(--wplace-text);
font-family: var(--wplace-font);
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
/* Account statistics */
.wplace-theme-neon .wplace-account-stats {
display: flex;
gap: 8px;
align-items: center;
font-size: 8px;
color: rgba(0, 255, 65, 0.8);
text-transform: uppercase;
font-family: var(--wplace-font);
flex-shrink: 0;
}
.wplace-theme-neon .wplace-account-stats i {
margin-right: 4px;
text-shadow: 0 0 5px currentColor;
}
.wplace-theme-neon .wplace-account-stats .fas.fa-bolt {
color: #ffff00;
text-shadow: 0 0 8px #ffff00;
}
.wplace-theme-neon .wplace-account-stats .fas.fa-tint {
color: #00bfff;
text-shadow: 0 0 8px #00bfff;
}