From 498e0176797265f8e598aa3679a7bf5942fd97a8 Mon Sep 17 00:00:00 2001 From: Staninna <26458805+Staninna@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:11:39 +0200 Subject: [PATCH 1/7] idk if this gonna fuck me over later but i started `decoupled-css` on main and this is currently upstream main --- Auto-Image.js | 2027 ++++++++++++++++++++++++++++++++++--------------- 1 file changed, 1396 insertions(+), 631 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index 49a8b95..6fae8ba 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -21,10 +21,10 @@ COOLDOWN_CHARGE_THRESHOLD: 1, // Default wait threshold // Desktop Notifications (defaults) NOTIFICATIONS: { - ENABLED: true, - ON_CHARGES_REACHED: true, - ONLY_WHEN_UNFOCUSED: true, - REPEAT_MINUTES: 5, // repeat reminder while threshold condition holds + ENABLED: true, + ON_CHARGES_REACHED: true, + ONLY_WHEN_UNFOCUSED: true, + REPEAT_MINUTES: 5, // repeat reminder while threshold condition holds }, OVERLAY: { OPACITY_DEFAULT: 0.6, @@ -1072,28 +1072,28 @@ initialSetupComplete: false, // Track if initial startup setup is complete (only happens once) overlayOpacity: CONFIG.OVERLAY.OPACITY_DEFAULT, blueMarbleEnabled: CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT, - ditheringEnabled: true, - // Advanced color matching settings - colorMatchingAlgorithm: 'lab', - enableChromaPenalty: true, - chromaPenaltyWeight: 0.15, - customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD, - customWhiteThreshold: CONFIG.WHITE_THRESHOLD, - resizeSettings: null, - originalImage: null, - resizeIgnoreMask: null, - // Notification prefs and runtime bookkeeping - notificationsEnabled: CONFIG.NOTIFICATIONS.ENABLED, - notifyOnChargesReached: CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED, - notifyOnlyWhenUnfocused: CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED, - notificationIntervalMinutes: CONFIG.NOTIFICATIONS.REPEAT_MINUTES, - _lastChargesNotifyAt: 0, - _lastChargesBelow: true, - // Smart save tracking - _lastSavePixelCount: 0, - _lastSaveTime: 0, - _saveInProgress: false, - paintedMap: null, + ditheringEnabled: true, + // Advanced color matching settings + colorMatchingAlgorithm: 'lab', + enableChromaPenalty: true, + chromaPenaltyWeight: 0.15, + customTransparencyThreshold: CONFIG.TRANSPARENCY_THRESHOLD, + customWhiteThreshold: CONFIG.WHITE_THRESHOLD, + resizeSettings: null, + originalImage: null, + resizeIgnoreMask: null, + // Notification prefs and runtime bookkeeping + notificationsEnabled: CONFIG.NOTIFICATIONS.ENABLED, + notifyOnChargesReached: CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED, + notifyOnlyWhenUnfocused: CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED, + notificationIntervalMinutes: CONFIG.NOTIFICATIONS.REPEAT_MINUTES, + _lastChargesNotifyAt: 0, + _lastChargesBelow: true, + // Smart save tracking + _lastSavePixelCount: 0, + _lastSaveTime: 0, + _saveInProgress: false, + paintedMap: null, } let _updateResizePreview = () => { }; @@ -1126,8 +1126,8 @@ this.disable(); this.imageBitmap = null; this.chunkedTiles.clear(); - this.originalTiles.clear(); - this.originalTilesData.clear(); + this.originalTiles.clear(); + this.originalTilesData.clear(); this.lastProcessedHash = null; if (this.processPromise) { this.processPromise = null; @@ -1194,7 +1194,7 @@ 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; @@ -1223,7 +1223,7 @@ // 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); @@ -1271,18 +1271,18 @@ if (state.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); } @@ -1411,7 +1411,7 @@ 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; @@ -1424,7 +1424,7 @@ ctx.drawImage(overlayBitmap, 0, 0); // Use faster blob conversion with compression settings - return await canvas.convertToBlob({ + return await canvas.convertToBlob({ type: 'image/png', quality: 0.95 // Slight compression for faster processing }); @@ -1481,7 +1481,7 @@ } tokenGenerationInProgress = true; - + try { console.log("๐Ÿ”„ Token expired or missing, generating new one..."); const token = await handleCaptchaWithRetry(); @@ -1659,7 +1659,7 @@ 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"]')) { @@ -1673,7 +1673,7 @@ }; return checkReady(); } - + const script = document.createElement('script'); script.src = 'https://challenges.cloudflare.com/turnstile/v0/api.js?render=explicit'; script.async = true; @@ -1698,7 +1698,7 @@ if (this._turnstileContainer) { this._turnstileContainer.remove(); } - + this._turnstileContainer = document.createElement('div'); this._turnstileContainer.style.cssText = ` position: fixed !important; @@ -1802,24 +1802,24 @@ try { // Force cleanup of any existing widget if (this._turnstileWidgetId && window.turnstile?.remove) { - try { - window.turnstile.remove(this._turnstileWidgetId); + 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, @@ -1834,10 +1834,10 @@ 'error-callback': () => resolve(null), 'timeout-callback': () => resolve(null) }); - + this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; - + if (!widgetId) { return resolve(null); } @@ -1857,31 +1857,31 @@ 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); + try { + window.turnstile.remove(this._turnstileWidgetId); } catch (e) { console.warn('โš ๏ธ Widget cleanup warning:', e.message); } } - + const overlay = this.ensureTurnstileOverlayContainer(); 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.style.display = 'none'; resolve(null); }, 60000); // 60 seconds for user interaction - + const widgetId = window.turnstile.render(host, { sitekey, action, @@ -1891,7 +1891,7 @@ clearTimeout(timeout); overlay.style.display = 'none'; console.log('โœ… Interactive Turnstile completed successfully'); - + if (typeof token === 'string' && token.length > 20) { resolve(token); } else { @@ -1906,10 +1906,10 @@ resolve(null); }, }); - + this._turnstileWidgetId = widgetId; this._lastSitekey = sitekey; - + if (!widgetId) { clearTimeout(timeout); overlay.style.display = 'none'; @@ -1938,15 +1938,15 @@ 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; @@ -2024,11 +2024,11 @@ this._cachedSitekey = testSitekey; return testSitekey; } - + } catch (error) { console.warn('Error detecting sitekey:', error); } - + console.log("๐Ÿ” Using fallback sitekey:", fallback); this._cachedSitekey = fallback; return fallback; @@ -2168,7 +2168,7 @@ // Use provided palette or derive from COLOR_MAP if (!palette || palette.length === 0) { palette = Object.values(CONFIG.COLOR_MAP) - .filter(c => c.rgb) + .filter(c => c.rgb) .map(c => [c.rgb.r, c.rgb.g, c.rgb.b]); } if (state.colorMatchingAlgorithm === 'legacy') { @@ -2222,17 +2222,17 @@ }, createImageUploader: () => - new Promise((resolve) => { - const input = document.createElement("input") - input.type = "file" - input.accept = "image/png,image/jpeg" - input.onchange = () => { - const fr = new FileReader() - fr.onload = () => resolve(fr.result) - fr.readAsDataURL(input.files[0]) - } - input.click() - }), + new Promise((resolve) => { + const input = document.createElement("input") + input.type = "file" + input.accept = "image/png,image/jpeg" + input.onchange = () => { + const fr = new FileReader() + fr.onload = () => resolve(fr.result) + fr.readAsDataURL(input.files[0]) + } + input.click() + }), createFileDownloader: (data, filename) => { const blob = new Blob([data], { type: "application/json" }) @@ -2247,30 +2247,30 @@ }, createFileUploader: () => - new Promise((resolve, reject) => { - const input = document.createElement("input") - input.type = "file" - input.accept = ".json" - input.onchange = (e) => { - const file = e.target.files[0] - if (file) { - const reader = new FileReader() - reader.onload = () => { - try { - const data = JSON.parse(reader.result) - resolve(data) - } catch (error) { - reject(new Error("Invalid JSON file")) + new Promise((resolve, reject) => { + const input = document.createElement("input") + input.type = "file" + input.accept = ".json" + input.onchange = (e) => { + const file = e.target.files[0] + if (file) { + const reader = new FileReader() + reader.onload = () => { + try { + const data = JSON.parse(reader.result) + resolve(data) + } catch (error) { + reject(new Error("Invalid JSON file")) + } } + reader.onerror = () => reject(new Error("File reading error")) + reader.readAsText(file) + } else { + reject(new Error("No file selected")) } - reader.onerror = () => reject(new Error("File reading error")) - reader.readAsText(file) - } else { - reject(new Error("No file selected")) } - } - input.click() - }), + input.click() + }), extractAvailableColors: () => { const colorElements = document.querySelectorAll('[id^="color-"]') @@ -2363,8 +2363,8 @@ markPixelPainted: (x, y, regionX = 0, regionY = 0) => { const actualX = x + regionX; const actualY = y + regionY; - - if (state.paintedMap && state.paintedMap[actualY] && + + if (state.paintedMap && state.paintedMap[actualY] && actualX >= 0 && actualX < state.paintedMap[actualY].length) { state.paintedMap[actualY][actualX] = true; } @@ -2373,8 +2373,8 @@ isPixelPainted: (x, y, regionX = 0, regionY = 0) => { const actualX = x + regionX; const actualY = y + regionY; - - if (state.paintedMap && state.paintedMap[actualY] && + + if (state.paintedMap && state.paintedMap[actualY] && actualX >= 0 && actualX < state.paintedMap[actualY].length) { return state.paintedMap[actualY][actualX]; } @@ -2386,28 +2386,28 @@ const now = Date.now(); const pixelsSinceLastSave = state.paintedPixels - state._lastSavePixelCount; const timeSinceLastSave = now - state._lastSaveTime; - + // Save conditions: // 1. Every 25 pixels (reduced from 50 for more frequent saves) // 2. At least 30 seconds since last save (prevent spam) // 3. Not already saving - return !state._saveInProgress && - pixelsSinceLastSave >= 25 && - timeSinceLastSave >= 30000; + return !state._saveInProgress && + pixelsSinceLastSave >= 25 && + timeSinceLastSave >= 30000; }, performSmartSave: () => { if (!Utils.shouldAutoSave()) return false; - + state._saveInProgress = true; const success = Utils.saveProgress(); - + if (success) { state._lastSavePixelCount = state.paintedPixels; state._lastSaveTime = Date.now(); console.log(`๐Ÿ’พ Auto-saved at ${state.paintedPixels} pixels`); } - + state._saveInProgress = false; return success; }, @@ -2461,7 +2461,7 @@ migrateProgressToV2: (saved) => { if (!saved) return saved; const isV1 = !saved.version || saved.version === '1' || saved.version === '1.0' || saved.version === '1.1'; - if (!isV1) return saved; + if (!isV1) return saved; try { const migrated = { ...saved }; @@ -2506,7 +2506,7 @@ } }, - saveProgress: () => { + saveProgress: () => { try { // Pack painted map if available let paintedMapPacked = null; @@ -2535,13 +2535,13 @@ availableColors: state.availableColors, }, imageData: state.imageData - ? { - width: state.imageData.width, - height: state.imageData.height, - pixels: Array.from(state.imageData.pixels), - totalPixels: state.imageData.totalPixels, - } - : null, + ? { + width: state.imageData.width, + height: state.imageData.height, + pixels: Array.from(state.imageData.pixels), + totalPixels: state.imageData.totalPixels, + } + : null, paintedMapPacked: paintedMapPacked, } @@ -2611,7 +2611,7 @@ const imageData = new ImageData(state.imageData.pixels, state.imageData.width, state.imageData.height); ctx.putImageData(imageData, 0, 0); const proc = new ImageProcessor(''); - proc.img = canvas; + proc.img = canvas; proc.canvas = canvas; proc.ctx = ctx; state.imageData.processor = proc; @@ -2635,7 +2635,7 @@ } }, - saveProgressToFile: () => { + saveProgressToFile: () => { try { // Pack painted map if available let paintedMapPacked = null; @@ -2664,13 +2664,13 @@ availableColors: state.availableColors, }, imageData: state.imageData - ? { - width: state.imageData.width, - height: state.imageData.height, - pixels: Array.from(state.imageData.pixels), - totalPixels: state.imageData.totalPixels, - } - : null, + ? { + width: state.imageData.width, + height: state.imageData.height, + pixels: Array.from(state.imageData.pixels), + totalPixels: state.imageData.totalPixels, + } + : null, paintedMapPacked: paintedMapPacked, } @@ -2683,7 +2683,7 @@ } }, - loadProgressFromFile: async () => { + loadProgressFromFile: async () => { try { const data = await Utils.createFileUploader() if (!data || !data.state) { @@ -2697,7 +2697,7 @@ } else { migrated = Utils.migrateProgressToV21(data) || data; } - const success = Utils.restoreProgress(migrated) + const success = Utils.restoreProgress(migrated) return success } catch (error) { console.error("Error loading from file:", error) @@ -2714,9 +2714,9 @@ try { // Recreate ImageBitmap from loaded pixel data const imageData = new ImageData( - state.imageData.pixels, - state.imageData.width, - state.imageData.height + state.imageData.pixels, + state.imageData.width, + state.imageData.height ); const canvas = new OffscreenCanvas(state.imageData.width, state.imageData.height); @@ -2865,96 +2865,96 @@ // Desktop Notification Manager const NotificationManager = { - pollTimer: null, - pollIntervalMs: 60_000, - icon() { - const link = document.querySelector("link[rel~='icon']"); - return link?.href || (location.origin + "/favicon.ico"); - }, - async requestPermission() { - if (!("Notification" in window)) { - Utils.showAlert("Notifications are not supported in this browser.", "warning"); - return "denied"; - } - if (Notification.permission === "granted") return "granted"; - try { - const perm = await Notification.requestPermission(); - return perm; - } catch { - return Notification.permission; - } - }, - canNotify() { - return state.notificationsEnabled && - typeof Notification !== "undefined" && - Notification.permission === "granted"; - }, - notify(title, body, tag = "wplace-charges", force = false) { - if (!this.canNotify()) return false; - if (!force && state.notifyOnlyWhenUnfocused && document.hasFocus()) return false; - try { - new Notification(title, { - body, - tag, - renotify: true, - icon: this.icon(), - badge: this.icon(), - silent: false, - }); - return true; - } catch { - // Graceful fallback - Utils.showAlert(body, "info"); - return false; - } - }, - resetEdgeTracking() { - state._lastChargesBelow = state.currentCharges < state.cooldownChargeThreshold; - state._lastChargesNotifyAt = 0; - }, - maybeNotifyChargesReached(force = false) { - if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; - const reached = state.currentCharges >= state.cooldownChargeThreshold; - const now = Date.now(); - const repeatMs = Math.max(1, Number(state.notificationIntervalMinutes || 5)) * 60_000; - if (reached) { - const shouldEdge = state._lastChargesBelow || force; - const shouldRepeat = now - (state._lastChargesNotifyAt || 0) >= repeatMs; - if (shouldEdge || shouldRepeat) { - const msg = `Charges ready: ${Math.floor(state.currentCharges)} / ${state.maxCharges}. Threshold: ${state.cooldownChargeThreshold}.`; - this.notify("WPlace โ€” Charges Ready", msg, "wplace-notify-charges"); - state._lastChargesNotifyAt = now; - } - state._lastChargesBelow = false; - } else { - state._lastChargesBelow = true; - } - }, - startPolling() { - this.stopPolling(); - if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; - // lightweight background polling - this.pollTimer = setInterval(async () => { - try { - const { charges, cooldown, max } = await WPlaceService.getCharges(); - state.currentCharges = Math.floor(charges); - state.cooldown = cooldown; - state.maxCharges = Math.max(1, Math.floor(max)); - this.maybeNotifyChargesReached(); - } catch { /* ignore */ } - }, this.pollIntervalMs); - }, - stopPolling() { - if (this.pollTimer) { - clearInterval(this.pollTimer); - this.pollTimer = null; - } - }, - syncFromState() { - this.resetEdgeTracking(); - if (state.notificationsEnabled && state.notifyOnChargesReached) this.startPolling(); - else this.stopPolling(); - }, + pollTimer: null, + pollIntervalMs: 60_000, + icon() { + const link = document.querySelector("link[rel~='icon']"); + return link?.href || (location.origin + "/favicon.ico"); + }, + async requestPermission() { + if (!("Notification" in window)) { + Utils.showAlert("Notifications are not supported in this browser.", "warning"); + return "denied"; + } + if (Notification.permission === "granted") return "granted"; + try { + const perm = await Notification.requestPermission(); + return perm; + } catch { + return Notification.permission; + } + }, + canNotify() { + return state.notificationsEnabled && + typeof Notification !== "undefined" && + Notification.permission === "granted"; + }, + notify(title, body, tag = "wplace-charges", force = false) { + if (!this.canNotify()) return false; + if (!force && state.notifyOnlyWhenUnfocused && document.hasFocus()) return false; + try { + new Notification(title, { + body, + tag, + renotify: true, + icon: this.icon(), + badge: this.icon(), + silent: false, + }); + return true; + } catch { + // Graceful fallback + Utils.showAlert(body, "info"); + return false; + } + }, + resetEdgeTracking() { + state._lastChargesBelow = state.currentCharges < state.cooldownChargeThreshold; + state._lastChargesNotifyAt = 0; + }, + maybeNotifyChargesReached(force = false) { + if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; + const reached = state.currentCharges >= state.cooldownChargeThreshold; + const now = Date.now(); + const repeatMs = Math.max(1, Number(state.notificationIntervalMinutes || 5)) * 60_000; + if (reached) { + const shouldEdge = state._lastChargesBelow || force; + const shouldRepeat = now - (state._lastChargesNotifyAt || 0) >= repeatMs; + if (shouldEdge || shouldRepeat) { + const msg = `Charges ready: ${Math.floor(state.currentCharges)} / ${state.maxCharges}. Threshold: ${state.cooldownChargeThreshold}.`; + this.notify("WPlace โ€” Charges Ready", msg, "wplace-notify-charges"); + state._lastChargesNotifyAt = now; + } + state._lastChargesBelow = false; + } else { + state._lastChargesBelow = true; + } + }, + startPolling() { + this.stopPolling(); + if (!state.notificationsEnabled || !state.notifyOnChargesReached) return; + // lightweight background polling + this.pollTimer = setInterval(async () => { + try { + const { charges, cooldown, max } = await WPlaceService.getCharges(); + state.currentCharges = Math.floor(charges); + state.cooldown = cooldown; + state.maxCharges = Math.max(1, Math.floor(max)); + this.maybeNotifyChargesReached(); + } catch { /* ignore */ } + }, this.pollIntervalMs); + }, + stopPolling() { + if (this.pollTimer) { + clearInterval(this.pollTimer); + this.pollTimer = null; + } + }, + syncFromState() { + this.resetEdgeTracking(); + if (state.notificationsEnabled && state.notifyOnChargesReached) this.startPolling(); + else this.stopPolling(); + }, }; // COLOR MATCHING FUNCTION - Optimized with caching @@ -3078,7 +3078,7 @@ // Check if this color is available in the captured colors const isAvailable = state.availableColors.some(c => - c.rgb[0] === rgb.r && c.rgb[1] === rgb.g && c.rgb[2] === rgb.b + c.rgb[0] === rgb.r && c.rgb[1] === rgb.g && c.rgb[2] === rgb.b ); // If not showing all colors and this color is not available, skip it @@ -3143,29 +3143,29 @@ } async function handleCaptcha() { const startTime = performance.now(); - + // Check user's token source preference if (state.tokenSource === "manual") { console.log("๐ŸŽฏ Manual token source selected - using pixel placement automation"); return await handleCaptchaFallback(); } - + // Generator mode (pure) or Hybrid mode - try generator first try { // Use optimized token generation with automatic sitekey detection const sitekey = Utils.detectSitekey(); console.log("๐Ÿ”‘ Generating Turnstile token for sitekey:", sitekey); console.log('๐Ÿงญ UA:', navigator.userAgent.substring(0, 50) + '...', 'Platform:', navigator.platform); - + // Add additional checks before token generation if (!window.turnstile) { await Utils.loadTurnstile(); } - + const token = await Utils.generatePaintToken(sitekey); - + console.log(`๐Ÿ” Token received - Type: ${typeof token}, Value: ${token ? (typeof token === 'string' ? (token.length > 50 ? token.substring(0, 50) + '...' : token) : JSON.stringify(token)) : 'null/undefined'}, Length: ${token?.length || 0}`); - + if (typeof token === 'string' && token.length > 20) { const duration = Math.round(performance.now() - startTime); console.log(`โœ… Turnstile token generated successfully in ${duration}ms`); @@ -3176,7 +3176,7 @@ } catch (error) { const duration = Math.round(performance.now() - startTime); console.error(`โŒ Turnstile token generation failed after ${duration}ms:`, error); - + // Fallback to manual pixel placement for hybrid mode if (state.tokenSource === "hybrid") { console.log("๐Ÿ”„ Hybrid mode: Generator failed, automatically switching to manual pixel placement..."); @@ -3292,222 +3292,406 @@ document.head.appendChild(googleFonts) } - // Link external CSS file - const cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'https://staninna.github.io/WPlace-AutoBOT/auto-image-styles.css'; - cssLink.setAttribute('data-wplace-theme', 'true'); - document.head.appendChild(cssLink); - - // Apply theme-specific dynamic styles const style = document.createElement("style") - style.setAttribute("data-wplace-theme-dynamic", "true") + style.setAttribute("data-wplace-theme", "true") style.textContent = ` - /* Dynamic theme-specific styles */ - ${theme.animations.glow ? ` - .wplace-header { animation: neonGlow 2s ease-in-out infinite alternate; } - .fas, .fa { filter: drop-shadow(0 0 3px currentColor); } - ` : ""} - - ${theme.animations.pixelBlink ? ` - .wplace-btn:hover:not(:disabled) { animation: pixelBlink 0.5s infinite; } - .status-error { animation: pixelBlink 0.5s infinite; } - .wplace-progress-bar::after { animation: pixelBlink 1s infinite; } - ` : ""} - - ${theme.animations.scanline ? ` - #wplace-image-bot-container::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - height: 2px; - background: linear-gradient(90deg, transparent, ${theme.neon}, transparent); - animation: scanline 3s linear infinite; - z-index: 1; - pointer-events: none; + ${theme.animations.glow + ? ` + @keyframes neonGlow { + 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; + } + }` + : "" + } + + ${theme.animations.pixelBlink + ? ` + @keyframes pixelBlink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.7; } + }` + : "" + } + + ${theme.animations.scanline + ? ` + @keyframes scanline { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(400px); } + }` + : "" + } + + @keyframes pulse { + 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); } + 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 slideIn { + from { transform: translateY(-10px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + @keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } + } - /* Theme-specific container styles */ #wplace-image-bot-container { + position: fixed; + top: 20px; + left: 20px; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; + max-height: calc(100vh - 40px); background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` - : theme.primary - }; + ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` + : theme.primary + }; border: ${theme.borderWidth} ${theme.borderStyle} ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; border-radius: ${theme.borderRadius}; + padding: 0; box-shadow: ${theme.boxShadow}; + z-index: 9998; font-family: ${theme.fontFamily}; color: ${theme.text}; + animation: slideIn 0.4s ease-out; + overflow-y: auto; /* Allow scrolling for main panel */ + overflow-x: hidden; ${theme.backdropFilter ? `backdrop-filter: ${theme.backdropFilter};` : ""} + transition: all 0.3s ease; + user-select: none; ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} } - ${CONFIG.currentTheme === "Neon Retro" ? ` - #wplace-image-bot-container::after { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - repeating-linear-gradient( - 0deg, - transparent, - transparent 2px, - rgba(0, 255, 65, 0.03) 2px, - rgba(0, 255, 65, 0.03) 4px - ); - pointer-events: none; - z-index: 1; - } - ` : ""} + ${theme.animations.scanline + ? ` + #wplace-image-bot-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, ${theme.neon}, transparent); + animation: scanline 3s linear infinite; + z-index: 1; + pointer-events: none; + }` + : "" + } + ${CONFIG.currentTheme === "Neon Retro" + ? ` + #wplace-image-bot-container::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.03) 2px, + rgba(0, 255, 65, 0.03) 4px + ); + pointer-events: none; + z-index: 1; + }` + : "" + } + + #wplace-image-bot-container.wplace-dragging { + transition: none; + box-shadow: 0 12px 40px rgba(0,0,0,0.8), 0 0 0 2px rgba(255,255,255,0.2); + transform: scale(1.02); + z-index: 9999; + } + #wplace-image-bot-container.wplace-minimized { + width: 200px; + height: auto; + overflow: hidden; + } + #wplace-image-bot-container.wplace-compact { + width: 240px; + } + + /* Stats Container */ #wplace-stats-container { + position: fixed; + top: 20px; + left: 330px; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; + max-height: calc(100vh - 40px); background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` - : theme.primary - }; + ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` + : theme.primary + }; border: ${theme.borderWidth} ${theme.borderStyle} ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; border-radius: ${theme.borderRadius}; + padding: 0; box-shadow: ${theme.boxShadow}; + z-index: 9997; font-family: ${theme.fontFamily}; color: ${theme.text}; + animation: slideIn 0.4s ease-out; + overflow-y: auto; /* Make stats panel scrollable */ ${theme.backdropFilter ? `backdrop-filter: ${theme.backdropFilter};` : ""} + transition: all 0.3s ease; + user-select: none; ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} } + /* FIX: Disable transition during drag to prevent lag */ + #wplace-stats-container.wplace-dragging { + transition: none; + } + .wplace-header { padding: ${CONFIG.currentTheme === "Neon Retro" ? "8px 12px" : "8px 12px"}; background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.secondary} 0%, #2a2a2a 100%)` - : theme.secondary - }; + ? `linear-gradient(135deg, ${theme.secondary} 0%, #2a2a2a 100%)` + : theme.secondary + }; color: ${theme.highlight}; font-size: ${CONFIG.currentTheme === "Neon Retro" ? "11px" : "13px"}; font-weight: ${CONFIG.currentTheme === "Neon Retro" ? "normal" : "700"}; + display: flex; + justify-content: space-between; + align-items: center; + cursor: move; + user-select: none; border-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "2px" : "1px"} solid ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.1)" : theme.text}; ${CONFIG.currentTheme === "Classic Autobot" ? "text-shadow: 0 1px 2px rgba(0,0,0,0.5);" : "text-transform: uppercase; letter-spacing: 1px;"} - font-family: ${theme.fontFamily}; + transition: background 0.2s ease; + position: relative; + z-index: 2; + ${theme.animations.glow ? "animation: neonGlow 2s ease-in-out infinite alternate;" : ""} } - .wplace-header-title { gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; } - .wplace-header-controls { gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; } + .wplace-header-title { + display: flex; + align-items: center; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; + } + + .wplace-header-controls { + display: flex; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; + } .wplace-header-btn { background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.1)" : theme.accent}; border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "none"}; color: ${theme.text}; + cursor: pointer; border-radius: ${CONFIG.currentTheme === "Classic Autobot" ? "4px" : "0"}; width: ${CONFIG.currentTheme === "Classic Autobot" ? "18px" : "auto"}; height: ${CONFIG.currentTheme === "Classic Autobot" ? "18px" : "auto"}; padding: ${CONFIG.currentTheme === "Neon Retro" ? "4px 6px" : "0"}; font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "10px"}; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; font-family: ${theme.fontFamily}; ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} } .wplace-header-btn:hover { background: ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; color: ${CONFIG.currentTheme === "Classic Autobot" ? theme.text : theme.primary}; + transform: ${CONFIG.currentTheme === "Classic Autobot" ? "scale(1.1)" : "none"}; ${CONFIG.currentTheme === "Neon Retro" ? `box-shadow: 0 0 10px ${theme.text};` : ""} } - .wplace-content { padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; } - .wplace-section { border-radius: ${theme.borderRadius}; } - .wplace-section-title { color: ${theme.highlight}; } + .wplace-content { + padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; + display: block; + position: relative; + z-index: 2; + } + .wplace-content.wplace-hidden { + display: none; + } + + .wplace-status-section { + margin-bottom: 12px; + padding: 8px; + background: rgba(255,255,255,0.03); + border-radius: ${theme.borderRadius}; + border: 1px solid rgba(255,255,255,0.1); + } + + .wplace-section { + margin-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; + padding: 12px; + background: rgba(255,255,255,0.03); + border-radius: ${theme.borderRadius}; + border: 1px solid rgba(255,255,255,0.1); + } + + .wplace-section-title { + font-size: 11px; + font-weight: 600; + margin-bottom: 8px; + color: ${theme.highlight}; + display: flex; + align-items: center; + gap: 6px; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .wplace-controls { + display: flex; + flex-direction: column; + gap: 8px; + } + .wplace-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + } + .wplace-row.single { + grid-template-columns: 1fr; + } .wplace-btn { padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px 8px" : "8px 12px"}; border: ${CONFIG.currentTheme === "Neon Retro" ? "2px solid" : "none"}; border-radius: ${theme.borderRadius}; font-weight: ${CONFIG.currentTheme === "Neon Retro" ? "normal" : "500"}; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; gap: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "6px"}; font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; + transition: all 0.3s ease; + position: relative; + overflow: hidden; font-family: ${theme.fontFamily}; ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px; image-rendering: pixelated;" : ""} background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.accent} 0%, #4a4a4a 100%)` - : theme.accent - }; + ? `linear-gradient(135deg, ${theme.accent} 0%, #4a4a4a 100%)` + : theme.accent + }; ${CONFIG.currentTheme === "Classic Autobot" ? "border: 1px solid rgba(255,255,255,0.1);" : ""} } - ${CONFIG.currentTheme === "Classic Autobot" ? ` - .wplace-btn::before { - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); - transition: left 0.5s ease; - } - .wplace-btn:hover:not(:disabled)::before { left: 100%; } - .wplace-btn:hover:not(:disabled) { - transform: translateY(-1px); - box-shadow: 0 4px 12px rgba(0,0,0,0.4); - } - ` : ` - .wplace-btn:hover:not(:disabled) { box-shadow: 0 0 15px currentColor; } - `} + ${CONFIG.currentTheme === "Classic Autobot" + ? ` + .wplace-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); + transition: left 0.5s ease; + } + .wplace-btn:hover:not(:disabled)::before { + left: 100%; + }` + : ` + .wplace-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.5s; + } + .wplace-btn:hover::before { + left: 100%; + }` + } + + .wplace-btn:hover:not(:disabled) { + transform: ${CONFIG.currentTheme === "Classic Autobot" ? "translateY(-1px)" : "none"}; + box-shadow: ${CONFIG.currentTheme === "Classic Autobot" ? "0 4px 12px rgba(0,0,0,0.4)" : "0 0 15px currentColor" + }; + ${theme.animations.pixelBlink ? "animation: pixelBlink 0.5s infinite;" : ""} + } + .wplace-btn:active:not(:disabled) { + transform: translateY(0); + } .wplace-btn-primary { background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.accent} 0%, #6a5acd 100%)` - : theme.accent - }; + ? `linear-gradient(135deg, ${theme.accent} 0%, #6a5acd 100%)` + : theme.accent + }; color: ${theme.text}; ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.text};` : ""} } - .wplace-btn-upload { background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.secondary} 0%, #4a4a4a 100%)` - : theme.purple - }; + ? `linear-gradient(135deg, ${theme.secondary} 0%, #4a4a4a 100%)` + : theme.purple + }; color: ${theme.text}; ${CONFIG.currentTheme === "Classic Autobot" - ? `border: 1px dashed ${theme.highlight};` - : `border-color: ${theme.text}; border-style: dashed;` - } + ? `border: 1px dashed ${theme.highlight};` + : `border-color: ${theme.text}; border-style: dashed;` + } } - .wplace-btn-start { background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.success} 0%, #228b22 100%)` - : theme.success - }; + ? `linear-gradient(135deg, ${theme.success} 0%, #228b22 100%)` + : theme.success + }; color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.success};` : ""} } - .wplace-btn-stop { background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.error} 0%, #dc143c 100%)` - : theme.error - }; + ? `linear-gradient(135deg, ${theme.error} 0%, #dc143c 100%)` + : theme.error + }; color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.text}; ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.error};` : ""} } - .wplace-btn-select { background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` - : theme.highlight - }; + ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` + : theme.highlight + }; color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.highlight};` : ""} } - .wplace-btn-file { background: ${CONFIG.currentTheme === "Classic Autobot" - ? "linear-gradient(135deg, #ff8c00 0%, #ff7f50 100%)" - : theme.warning - }; + ? "linear-gradient(135deg, #ff8c00 0%, #ff7f50 100%)" + : theme.warning + }; color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.warning};` : ""} } - - .wplace-btn:disabled { opacity: ${CONFIG.currentTheme === "Classic Autobot" ? "0.5" : "0.3"}; } + .wplace-btn:disabled { + opacity: ${CONFIG.currentTheme === "Classic Autobot" ? "0.5" : "0.3"}; + cursor: not-allowed; + transform: none !important; + ${theme.animations.pixelBlink ? "animation: none !important;" : ""} + box-shadow: none !important; + } + .wplace-btn:disabled::before { + display: none; + } + + .wplace-btn-overlay.active { + background: linear-gradient(135deg, #29b6f6 0%, #8e2de2 100%); + box-shadow: 0 0 15px #8e2de2; + } .wplace-stats { background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.03)" : theme.secondary}; @@ -3519,68 +3703,132 @@ } .wplace-stat-item { + display: flex; + justify-content: space-between; padding: ${CONFIG.currentTheme === "Neon Retro" ? "6px 0" : "4px 0"}; font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; + border-bottom: 1px solid rgba(255,255,255,0.05); ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} } - .wplace-stat-label { font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "10px"}; } - .wplace-stat-value { color: ${theme.highlight}; } + .wplace-stat-item:last-child { + border-bottom: none; + } + .wplace-stat-label { + display: flex; + align-items: center; + gap: 6px; + opacity: 0.9; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "10px"}; + } + .wplace-stat-value { + font-weight: 600; + color: ${theme.highlight}; + } + + .wplace-colors-section { + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid rgba(255,255,255,0.05); + } + + .wplace-stat-colors-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(16px, 1fr)); + gap: 4px; + margin-top: 8px; + padding: 4px; + background: rgba(0,0,0,0.2); + border-radius: 4px; + max-height: 80px; /* Limit height and allow scrolling */ + overflow-y: auto; + } + + .wplace-stat-color-swatch { + width: 16px; + height: 16px; + border-radius: 3px; + border: 1px solid rgba(255,255,255,0.1); + box-shadow: inset 0 0 2px rgba(0,0,0,0.5); + } .wplace-progress { + width: 100%; background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(0,0,0,0.3)" : theme.secondary}; border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "1px solid rgba(255,255,255,0.1)"}; border-radius: ${theme.borderRadius}; margin: ${CONFIG.currentTheme === "Neon Retro" ? "10px 0" : "8px 0"}; + overflow: hidden; height: ${CONFIG.currentTheme === "Neon Retro" ? "16px" : "6px"}; + position: relative; } - ${CONFIG.currentTheme === "Neon Retro" ? ` - .wplace-progress::before { - content: ''; - position: absolute; - top: 0; - left: 0; - right: 0; - bottom: 0; - background: - repeating-linear-gradient( - 45deg, - transparent, - transparent 2px, - rgba(0, 255, 65, 0.1) 2px, - rgba(0, 255, 65, 0.1) 4px - ); - pointer-events: none; - } - ` : ""} + ${CONFIG.currentTheme === "Neon Retro" + ? ` + .wplace-progress::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + repeating-linear-gradient( + 45deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.1) 2px, + rgba(0, 255, 65, 0.1) 4px + ); + pointer-events: none; + }` + : "" + } .wplace-progress-bar { height: ${CONFIG.currentTheme === "Neon Retro" ? "100%" : "6px"}; background: ${CONFIG.currentTheme === "Classic Autobot" - ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` - : `linear-gradient(90deg, ${theme.success}, ${theme.neon})` - }; + ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` + : `linear-gradient(90deg, ${theme.success}, ${theme.neon})` + }; transition: width ${CONFIG.currentTheme === "Neon Retro" ? "0.3s" : "0.5s"} ease; + position: relative; ${CONFIG.currentTheme === "Neon Retro" ? `box-shadow: 0 0 10px ${theme.success};` : ""} } - ${CONFIG.currentTheme === "Classic Autobot" ? ` - .wplace-progress-bar::after { - background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); - animation: shimmer 2s infinite; - } - ` : ` - .wplace-progress-bar::after { - background: ${theme.text}; - } - `} + ${CONFIG.currentTheme === "Classic Autobot" + ? ` + .wplace-progress-bar::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + animation: shimmer 2s infinite; + }` + : ` + .wplace-progress-bar::after { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 4px; + height: 100%; + background: ${theme.text}; + animation: pixelBlink 1s infinite; + }` + } .wplace-status { padding: ${CONFIG.currentTheme === "Neon Retro" ? "10px" : "6px"}; border: ${CONFIG.currentTheme === "Neon Retro" ? "2px solid" : "1px solid"}; border-radius: ${theme.borderRadius}; + text-align: center; font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} + position: relative; + overflow: hidden; } .status-default { @@ -3599,6 +3847,7 @@ border-color: ${theme.error}; color: ${CONFIG.currentTheme === "Classic Autobot" ? theme.error : theme.text}; box-shadow: 0 0 15px ${theme.error}; + ${theme.animations.pixelBlink ? "animation: pixelBlink 0.5s infinite;" : ""} } .status-warning { background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255, 165, 0, 0.1)" : theme.warning}; @@ -3607,17 +3856,71 @@ box-shadow: 0 0 15px ${theme.warning}; } - /* Settings and other dynamic elements */ .resize-container { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); background: ${theme.primary}; + padding: 20px; border: ${theme.borderWidth} ${theme.borderStyle} ${theme.text}; border-radius: ${theme.borderRadius}; - box-shadow: ${CONFIG.currentTheme === "Classic Autobot" ? "0 0 20px rgba(0,0,0,0.5)" : "0 0 30px rgba(0, 255, 65, 0.5)"}; + z-index: 10000; + box-shadow: ${CONFIG.currentTheme === "Classic Autobot" ? "0 0 20px rgba(0,0,0,0.5)" : "0 0 30px rgba(0, 255, 65, 0.5)" + }; + width: 90%; + max-width: 700px; + max-height: 90%; + overflow: auto; font-family: ${theme.fontFamily}; } - .resize-preview-wrapper { border: 1px solid ${theme.accent}; } - + .resize-preview-wrapper { + display: flex; + justify-content: center; + align-items: center; + border: 1px solid ${theme.accent}; + background: rgba(0,0,0,0.2); + margin: 15px 0; + height: 300px; + overflow: hidden; + } + + .resize-canvas-stack { position: relative; transform-origin: center center; display: inline-block; } + .resize-base-canvas, .resize-mask-canvas { + position: absolute; left: 0; top: 0; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + } + .resize-mask-canvas { pointer-events: auto; } + .resize-tools { display:flex; gap:8px; align-items:center; margin-top:8px; font-size:12px; } + .resize-tools button { padding:6px 10px; border-radius:6px; border:1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.06); color:#fff; cursor:pointer; } + .wplace-btn.active, + .wplace-btn[aria-pressed="true"] { + background: ${theme.highlight} !important; + color: ${theme.primary} !important; + border-color: ${theme.text} !important; + box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2) !important; + } + .wplace-btn.active i, + .wplace-btn[aria-pressed="true"] i { filter: drop-shadow(0 0 3px ${theme.primary}); } + .mask-mode-group .wplace-btn.active, + .mask-mode-group .wplace-btn[aria-pressed="true"] { + background: ${theme.highlight}; + color: ${theme.primary}; + border-color: ${theme.text}; + box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2); + } + + .resize-controls { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; + align-items: center; + } + .resize-controls label { font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "12px"}; ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} @@ -3625,55 +3928,517 @@ } .resize-slider { + width: 100%; height: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "4px"}; background: ${CONFIG.currentTheme === "Classic Autobot" ? "#ccc" : theme.secondary}; border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "none"}; border-radius: ${theme.borderRadius}; + outline: none; + -webkit-appearance: none; } - ${CONFIG.currentTheme === "Neon Retro" ? ` - .resize-slider::-webkit-slider-thumb { - width: 16px; - height: 16px; - background: ${theme.highlight}; - border: 2px solid ${theme.text}; - border-radius: 0; - box-shadow: 0 0 5px ${theme.highlight}; - } - - .resize-slider::-moz-range-thumb { - width: 16px; - height: 16px; - background: ${theme.highlight}; - border: 2px solid ${theme.text}; - border-radius: 0; - box-shadow: 0 0 5px ${theme.highlight}; - } - ` : ""} - - .wplace-slider::-webkit-slider-thumb { background: ${theme.highlight}; } - - ${CONFIG.currentTheme === "Neon Retro" ? ` - input[type="checkbox"] { - width: 16px; - height: 16px; - border: 2px solid ${theme.text}; - background: ${theme.secondary}; - } - input[type="checkbox"]:checked { background: ${theme.success}; } - input[type="checkbox"]:checked::after { color: ${theme.primary}; } - ` : ""} - - /* Additional theme-specific styles */ - .wplace-btn.active, - .wplace-btn[aria-pressed="true"] { - background: ${theme.highlight} !important; - color: ${theme.primary} !important; - border-color: ${theme.text} !important; + ${CONFIG.currentTheme === "Neon Retro" + ? ` + .resize-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: ${theme.highlight}; + border: 2px solid ${theme.text}; + border-radius: 0; + cursor: pointer; + box-shadow: 0 0 5px ${theme.highlight}; } + + .resize-slider::-moz-range-thumb { + width: 16px; + height: 16px; + background: ${theme.highlight}; + border: 2px solid ${theme.text}; + border-radius: 0; + cursor: pointer; + box-shadow: 0 0 5px ${theme.highlight}; + }` + : "" + } - .wplace-btn.active i, - .wplace-btn[aria-pressed="true"] i { filter: drop-shadow(0 0 3px ${theme.primary}); } + .resize-zoom-controls { + grid-column: 1 / -1; + display: flex; + align-items: center; + gap: 10px; + margin-top: 15px; + } + + .resize-buttons { + display: flex; + gap: 10px; + justify-content: center; + margin-top: 20px; + } + + .resize-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + z-index: 9999; + display: none; + } + .wplace-color-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); + gap: 10px; + padding-top: 8px; + max-height: 300px; + overflow-y: auto; + } + .wplace-color-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + } + .wplace-color-item-name { + font-size: 9px; + color: #ccc; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } + .wplace-color-swatch { + width: 22px; + height: 22px; + border: 1px solid rgba(255,255,255,0.2); + border-radius: 4px; + cursor: pointer; + transition: transform 0.1s ease, box-shadow 0.2s ease; + position: relative; + margin: 0 auto; + } + .wplace-color-swatch.unavailable { + border-color: #666; + border-style: dashed; + cursor: not-allowed; + } + .wplace-color-swatch:hover { + transform: scale(1.1); + z-index: 1; + } + .wplace-color-swatch:not(.active) { + opacity: 0.3; + filter: grayscale(80%); + } + .wplace-color-swatch.unavailable:not(.active) { + opacity: 0.2; + filter: grayscale(90%); + } + .wplace-color-swatch.active::after { + content: 'โœ”'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 12px; + font-weight: bold; + text-shadow: 0 0 3px black; + } + .wplace-color-divider { + border: none; + height: 1px; + background: rgba(255,255,255,0.1); + margin: 8px 0; + } + + .wplace-cooldown-control { + margin-top: 8px; + } + .wplace-cooldown-control label { + font-size: 11px; + margin-bottom: 4px; + display: block; + } + .wplace-slider-container { + display: flex; + align-items: center; + gap: 8px; + } + .wplace-slider { + flex: 1; + -webkit-appearance: none; + appearance: none; + height: 4px; + background: #444; + border-radius: 2px; + outline: none; + } + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + } + + + ${CONFIG.currentTheme === "Neon Retro" + ? ` + input[type="checkbox"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border: 2px solid ${theme.text}; + background: ${theme.secondary}; + margin-right: 8px; + position: relative; + cursor: pointer; + } + + input[type="checkbox"]:checked { + background: ${theme.success}; + } + + input[type="checkbox"]:checked::after { + content: 'โœ“'; + position: absolute; + top: -2px; + left: 1px; + color: ${theme.primary}; + font-size: 12px; + font-weight: bold; + } + + .fas, .fa { + filter: drop-shadow(0 0 3px currentColor); + } + + .wplace-speed-control { + margin-top: 12px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-speed-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-speed-label i { + margin-right: 6px; + color: ${theme.highlight}; + } + + .wplace-speed-slider-container { + display: flex; + align-items: center; + gap: 12px; + } + + .wplace-speed-slider { + flex: 1; + height: 6px; + border-radius: 3px; + background: ${theme.primary}; + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .wplace-speed-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-slider::-moz-range-thumb { + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-display { + display: flex; + align-items: center; + gap: 4px; + min-width: 90px; + justify-content: flex-end; + } + + #speedValue { + color: ${theme.highlight}; + font-weight: 600; + font-size: 14px; + } + + .wplace-speed-unit { + color: ${theme.text}; + font-size: 11px; + opacity: 0.8; + } + + #wplace-settings-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10001; + min-width: 400px; + max-width: 500px; + background: ${theme.primary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + box-shadow: ${theme.boxShadow}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-settings { + padding: 16px; + max-height: 400px; + overflow-y: auto; + } + + .wplace-setting-section { + margin-bottom: 20px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-setting-title { + display: flex; + align-items: center; + margin-bottom: 12px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-setting-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-setting-content { + color: ${theme.text}; + } + + .wplace-section { + margin-bottom: 20px; + padding: 15px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-section-title { + display: flex; + align-items: center; + margin-bottom: 15px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-section-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-speed-container { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 10px; + } + + .wplace-slider { + flex: 1; + height: 6px; + background: ${theme.accent}; + border-radius: 3px; + outline: none; + -webkit-appearance: none; + } + + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 18px; + height: 18px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + border: 2px solid ${theme.primary}; + } + + .wplace-speed-display { + background: ${theme.accent}; + padding: 5px 10px; + border-radius: 4px; + color: ${theme.text}; + font-weight: 600; + min-width: 80px; + text-align: center; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-select { + width: 100%; + padding: 8px 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + color: ${theme.text}; + font-size: 14px; + margin-bottom: 10px; + } + + .wplace-select:focus { + outline: none; + border-color: ${theme.highlight}; + } + + .wplace-description { + color: ${theme.text}; + font-size: 12px; + opacity: 0.8; + line-height: 1.4; + } + + .wplace-theme-custom { + margin-top: 15px; + padding: 15px; + background: ${theme.accent}; + border-radius: ${theme.borderRadius}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-custom-group { + margin-bottom: 15px; + } + + .wplace-custom-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-custom-label i { + margin-right: 8px; + color: ${theme.highlight}; + width: 16px; + } + + .wplace-color-input-group { + display: flex; + gap: 8px; + align-items: center; + } + + .wplace-color-input { + width: 50px; + height: 30px; + border: none; + border-radius: 4px; + cursor: pointer; + background: transparent; + } + + .wplace-color-text { + flex: 1; + padding: 6px 10px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: 4px; + color: ${theme.text}; + font-size: 12px; + font-family: monospace; + } + + .wplace-animation-controls { + display: flex; + flex-direction: column; + gap: 8px; + } + + .wplace-checkbox-label { + display: flex; + align-items: center; + gap: 8px; + color: ${theme.text}; + font-size: 12px; + cursor: pointer; + } + + .wplace-checkbox-label input[type="checkbox"] { + accent-color: ${theme.highlight}; + } + + .wplace-slider-container { + display: flex; + align-items: center; + gap: 10px; + } + + .wplace-slider-container .wplace-slider { + flex: 1; + } + + .wplace-slider-container span { + color: ${theme.text}; + font-size: 12px; + font-weight: 600; + min-width: 40px; + } + + .wplace-custom-actions { + display: flex; + gap: 10px; + margin-top: 20px; + border-top: 1px solid ${theme.accent}; + padding-top: 15px; + } + + .wplace-btn-secondary { + background: ${theme.accent}; + color: ${theme.text}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-btn-secondary:hover { + background: ${theme.secondary}; + }` + : "" + } ` document.head.appendChild(style) @@ -3832,12 +4597,12 @@ // Use the theme variable already declared at the top of createUI function const settingsContainer = document.createElement("div") settingsContainer.id = "wplace-settings-container" - + // Apply theme-based styling - const themeBackground = theme.primary ? - `linear-gradient(135deg, ${theme.primary} 0%, ${theme.secondary || theme.primary} 100%)` : - `linear-gradient(135deg, #667eea 0%, #764ba2 100%)` - + const themeBackground = theme.primary ? + `linear-gradient(135deg, ${theme.primary} 0%, ${theme.secondary || theme.primary} 100%)` : + `linear-gradient(135deg, #667eea 0%, #764ba2 100%)` + settingsContainer.style.cssText = ` position: fixed; top: 50%; @@ -4194,7 +4959,7 @@ box-shadow: 0 3px 10px rgba(0,0,0,0.1); "> ${Object.keys(CONFIG.THEMES).map(themeName => - `` + `` ).join('')} @@ -4518,7 +5283,7 @@ const loadBtn = container.querySelector("#loadBtn") const saveToFileBtn = container.querySelector("#saveToFileBtn") const loadFromFileBtn = container.querySelector("#loadFromFileBtn") - + // Disable load/upload buttons until initial setup is complete (startup only) if (loadBtn) { loadBtn.disabled = !state.initialSetupComplete; @@ -4532,7 +5297,7 @@ uploadBtn.disabled = !state.initialSetupComplete; uploadBtn.title = state.initialSetupComplete ? "" : "๐Ÿ”„ Waiting for initial setup to complete..."; } - + const minimizeBtn = container.querySelector("#minimizeBtn") const compactBtn = container.querySelector("#compactBtn") const statsBtn = container.querySelector("#statsBtn") @@ -4569,9 +5334,9 @@ function makeDraggable(element) { let pos1 = 0, - pos2 = 0, - pos3 = 0, - pos4 = 0 + pos2 = 0, + pos3 = 0, + pos4 = 0 let isDragging = false const header = element.querySelector(".wplace-header") || element.querySelector(".wplace-settings-header") @@ -4742,8 +5507,8 @@ if (notifOnChargesToggle) state.notifyOnChargesReached = !!notifOnChargesToggle.checked; if (notifOnlyUnfocusedToggle) state.notifyOnlyWhenUnfocused = !!notifOnlyUnfocusedToggle.checked; if (notifIntervalInput) { - const v = parseInt(notifIntervalInput.value, 10); - if (!isNaN(v) && v >= 1 && v <= 60) state.notificationIntervalMinutes = v; + const v = parseInt(notifIntervalInput.value, 10); + if (!isNaN(v) && v >= 1 && v <= 60) state.notificationIntervalMinutes = v; } saveBotSettings(); Utils.showAlert(Utils.t("settingsSaved"), "success"); @@ -4774,11 +5539,11 @@ const randomBatchControls = settingsContainer.querySelector("#randomBatchControls") const randomBatchMin = settingsContainer.querySelector("#randomBatchMin") const randomBatchMax = settingsContainer.querySelector("#randomBatchMax") - + if (batchModeSelect) { batchModeSelect.addEventListener("change", (e) => { state.batchMode = e.target.value - + // Switch between normal and random controls if (normalBatchControls && randomBatchControls) { if (e.target.value === 'random') { @@ -4789,13 +5554,13 @@ randomBatchControls.style.display = 'none' } } - + saveBotSettings() console.log(`๐Ÿ“ฆ Batch mode changed to: ${state.batchMode}`) Utils.showAlert(`Batch mode set to: ${state.batchMode === 'random' ? 'Random Range' : 'Normal Fixed Size'}`, "success") }) } - + if (randomBatchMin) { randomBatchMin.addEventListener("input", (e) => { const min = parseInt(e.target.value) @@ -4810,7 +5575,7 @@ } }) } - + if (randomBatchMax) { randomBatchMax.addEventListener("input", (e) => { const max = parseInt(e.target.value) @@ -4883,7 +5648,7 @@ }); } - // (Advanced color listeners moved outside to work with resize dialog) + // (Advanced color listeners moved outside to work with resize dialog) // (Advanced color listeners moved outside to work with resize dialog) // Notifications listeners const notifPermBtn = settingsContainer.querySelector("#notifRequestPermBtn"); @@ -4909,23 +5674,23 @@ const heightValue = resizeContainer.querySelector("#heightValue") const keepAspect = resizeContainer.querySelector("#keepAspect") const paintWhiteToggle = resizeContainer.querySelector("#paintWhiteToggle"); - const zoomSlider = resizeContainer.querySelector("#zoomSlider"); - const zoomValue = resizeContainer.querySelector('#zoomValue'); - const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); - const zoomOutBtn = resizeContainer.querySelector('#zoomOutBtn'); - const zoomFitBtn = resizeContainer.querySelector('#zoomFitBtn'); - const zoomActualBtn = resizeContainer.querySelector('#zoomActualBtn'); - const panModeBtn = resizeContainer.querySelector('#panModeBtn'); - const panStage = resizeContainer.querySelector('#resizePanStage'); - const canvasStack = resizeContainer.querySelector('#resizeCanvasStack'); - const baseCanvas = resizeContainer.querySelector('#resizeCanvas'); - const maskCanvas = resizeContainer.querySelector('#maskCanvas'); - const baseCtx = baseCanvas.getContext('2d'); - const maskCtx = maskCanvas.getContext('2d'); + const zoomSlider = resizeContainer.querySelector("#zoomSlider"); + const zoomValue = resizeContainer.querySelector('#zoomValue'); + const zoomInBtn = resizeContainer.querySelector('#zoomInBtn'); + const zoomOutBtn = resizeContainer.querySelector('#zoomOutBtn'); + const zoomFitBtn = resizeContainer.querySelector('#zoomFitBtn'); + const zoomActualBtn = resizeContainer.querySelector('#zoomActualBtn'); + const panModeBtn = resizeContainer.querySelector('#panModeBtn'); + const panStage = resizeContainer.querySelector('#resizePanStage'); + const canvasStack = resizeContainer.querySelector('#resizeCanvasStack'); + const baseCanvas = resizeContainer.querySelector('#resizeCanvas'); + const maskCanvas = resizeContainer.querySelector('#maskCanvas'); + const baseCtx = baseCanvas.getContext('2d'); + const maskCtx = maskCanvas.getContext('2d'); const confirmResize = resizeContainer.querySelector("#confirmResize") const cancelResize = resizeContainer.querySelector("#cancelResize") - const downloadPreviewBtn = resizeContainer.querySelector("#downloadPreviewBtn"); - const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn'); + const downloadPreviewBtn = resizeContainer.querySelector("#downloadPreviewBtn"); + const clearIgnoredBtn = resizeContainer.querySelector('#clearIgnoredBtn'); if (compactBtn) { compactBtn.addEventListener("click", () => { @@ -4962,9 +5727,9 @@ if (toggleOverlayBtn) { toggleOverlayBtn.addEventListener('click', () => { - const isEnabled = overlayManager.toggle(); - toggleOverlayBtn.classList.toggle('active', isEnabled); - toggleOverlayBtn.setAttribute('aria-pressed', isEnabled ? 'true' : 'false'); + const isEnabled = overlayManager.toggle(); + toggleOverlayBtn.classList.toggle('active', isEnabled); + toggleOverlayBtn.setAttribute('aria-pressed', isEnabled ? 'true' : 'false'); Utils.showAlert(`Overlay ${isEnabled ? 'enabled' : 'disabled'}.`, 'info'); }); } @@ -5009,7 +5774,7 @@ Utils.showAlert("๐Ÿ”„ Please wait for the initial setup to complete before loading progress.", "warning"); return; } - + const savedData = Utils.loadProgress() if (!savedData) { updateUI("noSavedData", "warning") @@ -5018,9 +5783,9 @@ } const confirmLoad = confirm( - `${Utils.t("savedDataFound")}\n\n` + - `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + - `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels`, + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${new Date(savedData.timestamp).toLocaleString()}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels`, ) if (confirmLoad) { @@ -5073,7 +5838,7 @@ Utils.showAlert("๐Ÿ”„ Please wait for the initial setup to complete before loading from file.", "warning"); return; } - + try { const success = await Utils.loadProgressFromFile() if (success) { @@ -5201,31 +5966,31 @@ } const aspectRatio = width / height; - const rs = state.resizeSettings; - widthSlider.max = width * 2; - heightSlider.max = height * 2; - let initialW = width; - let initialH = height; - if (rs && Number.isFinite(rs.width) && Number.isFinite(rs.height) && rs.width > 0 && rs.height > 0) { - initialW = rs.width; - initialH = rs.height; - } - // Clamp to slider ranges - initialW = Math.max(parseInt(widthSlider.min, 10) || 10, Math.min(initialW, parseInt(widthSlider.max, 10))); - initialH = Math.max(parseInt(heightSlider.min, 10) || 10, Math.min(initialH, parseInt(heightSlider.max, 10))); - widthSlider.value = initialW; - heightSlider.value = initialH; - widthValue.textContent = initialW; - heightValue.textContent = initialH; - zoomSlider.value = 1; - if (zoomValue) zoomValue.textContent = '100%'; + const rs = state.resizeSettings; + widthSlider.max = width * 2; + heightSlider.max = height * 2; + let initialW = width; + let initialH = height; + if (rs && Number.isFinite(rs.width) && Number.isFinite(rs.height) && rs.width > 0 && rs.height > 0) { + initialW = rs.width; + initialH = rs.height; + } + // Clamp to slider ranges + initialW = Math.max(parseInt(widthSlider.min, 10) || 10, Math.min(initialW, parseInt(widthSlider.max, 10))); + initialH = Math.max(parseInt(heightSlider.min, 10) || 10, Math.min(initialH, parseInt(heightSlider.max, 10))); + widthSlider.value = initialW; + heightSlider.value = initialH; + widthValue.textContent = initialW; + heightValue.textContent = initialH; + zoomSlider.value = 1; + if (zoomValue) zoomValue.textContent = '100%'; paintWhiteToggle.checked = state.paintWhitePixels; let _previewTimer = null; let _previewJobId = 0; let _isDraggingSize = false; let _zoomLevel = 1; - let _ditherWorkBuf = null; + let _ditherWorkBuf = null; let _ditherEligibleBuf = null; const ensureDitherBuffers = (n) => { if (!_ditherWorkBuf || _ditherWorkBuf.length !== n * 3) _ditherWorkBuf = new Float32Array(n * 3); @@ -5288,9 +6053,9 @@ widthValue.textContent = newWidth; heightValue.textContent = newHeight; - ensureMaskSize(newWidth, newHeight); - canvasStack.style.width = newWidth + 'px'; - canvasStack.style.height = newHeight + 'px'; + ensureMaskSize(newWidth, newHeight); + canvasStack.style.width = newWidth + 'px'; + canvasStack.style.height = newHeight + 'px'; baseCtx.imageSmoothingEnabled = false; if (!state.availableColors || state.availableColors.length === 0) { if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { @@ -5304,7 +6069,7 @@ updateZoomLayout(); return; } - if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { await baseProcessor.load(); } baseCtx.clearRect(0,0,newWidth,newHeight); @@ -5312,7 +6077,7 @@ const imgData = baseCtx.getImageData(0, 0, newWidth, newHeight); const data = imgData.data; - const tThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; + const tThresh = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; const applyFSDither = () => { const w = newWidth, h = newHeight; @@ -5389,9 +6154,9 @@ if (jobId !== _previewJobId) return; baseCtx.putImageData(imgData, 0, 0); - maskCtx.clearRect(0,0,maskCanvas.width,maskCanvas.height); - if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); - updateZoomLayout(); + maskCtx.clearRect(0,0,maskCanvas.width,maskCanvas.height); + if (_maskImageData) maskCtx.putImageData(_maskImageData, 0, 0); + updateZoomLayout(); }; const onWidthInput = () => { @@ -5399,13 +6164,13 @@ heightSlider.value = Math.round(parseInt(widthSlider.value, 10) / aspectRatio); } _updateResizePreview(); - const curW = parseInt(widthSlider.value, 10); - const curH = parseInt(heightSlider.value, 10); - state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; - saveBotSettings(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; + saveBotSettings(); // Auto-fit after size changes const fit = (typeof computeFitZoom === 'function') ? computeFitZoom() : 1; - if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); }; const onHeightInput = () => { @@ -5413,13 +6178,13 @@ widthSlider.value = Math.round(parseInt(heightSlider.value, 10) * aspectRatio); } _updateResizePreview(); - const curW = parseInt(widthSlider.value, 10); - const curH = parseInt(heightSlider.value, 10); - state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; - saveBotSettings(); + const curW = parseInt(widthSlider.value, 10); + const curH = parseInt(heightSlider.value, 10); + state.resizeSettings = { baseWidth: width, baseHeight: height, width: curW, height: curH }; + saveBotSettings(); // Auto-fit after size changes const fit = (typeof computeFitZoom === 'function') ? computeFitZoom() : 1; - if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); + if (!isNaN(fit) && isFinite(fit)) applyZoom(fit); }; paintWhiteToggle.onchange = (e) => { @@ -5474,8 +6239,8 @@ zoomSlider.addEventListener('input', () => { applyZoom(parseFloat(zoomSlider.value)); }); - if (zoomInBtn) zoomInBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) + 0.1)); - if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) - 0.1)); + if (zoomInBtn) zoomInBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) + 0.1)); + if (zoomOutBtn) zoomOutBtn.addEventListener('click', () => applyZoom(parseFloat(zoomSlider.value) - 0.1)); const computeFitZoom = () => { const wrapRect = panStage?.getBoundingClientRect(); if (!wrapRect) return 1; @@ -5486,8 +6251,8 @@ const scaleY = (wrapRect.height - margin) / h; return Math.max(0.05, Math.min(20, Math.min(scaleX, scaleY))); }; - if (zoomFitBtn) zoomFitBtn.addEventListener('click', () => { applyZoom(computeFitZoom()); centerInView(); }); - if (zoomActualBtn) zoomActualBtn.addEventListener('click', () => { applyZoom(1); centerInView(); }); + if (zoomFitBtn) zoomFitBtn.addEventListener('click', () => { applyZoom(computeFitZoom()); centerInView(); }); + if (zoomActualBtn) zoomActualBtn.addEventListener('click', () => { applyZoom(1); centerInView(); }); const centerInView = () => { if (!panStage) return; @@ -5499,12 +6264,12 @@ applyPan(); }; - let isPanning = false; let startX = 0, startY = 0, startPanX = 0, startPanY = 0; - let allowPan = false; // Space key - let panMode = false; // Explicit pan mode toggle for touch/one-button mice + let isPanning = false; let startX = 0, startY = 0, startPanX = 0, startPanY = 0; + let allowPan = false; // Space key + let panMode = false; // Explicit pan mode toggle for touch/one-button mice const isPanMouseButton = (e) => e.button === 1 || e.button === 2; - const setCursor = (val) => { if (panStage) panStage.style.cursor = val; }; - const isPanActive = (e) => panMode || allowPan || isPanMouseButton(e); + const setCursor = (val) => { if (panStage) panStage.style.cursor = val; }; + const isPanActive = (e) => panMode || allowPan || isPanMouseButton(e); const updatePanModeBtn = () => { if (!panModeBtn) return; panModeBtn.classList.toggle('active', panMode); @@ -5530,7 +6295,7 @@ panX = startPanX + dx; panY = startPanY + dy; applyPan(); }); window.addEventListener('mouseup', () => { if (isPanning) { isPanning = false; setCursor(allowPan ? 'grab' : ''); }}); - panStage.addEventListener('wheel', (e) => { + panStage.addEventListener('wheel', (e) => { if (!e.ctrlKey && !e.metaKey) return; e.preventDefault(); const rect = panStage.getBoundingClientRect(); @@ -5616,8 +6381,8 @@ heightSlider.addEventListener('pointerdown', markDragStart); widthSlider.addEventListener('pointerup', markDragEnd); heightSlider.addEventListener('pointerup', markDragEnd); - widthSlider.addEventListener("input", () => { onWidthInput(); schedulePreview(); }); - heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); }); + widthSlider.addEventListener("input", () => { onWidthInput(); schedulePreview(); }); + heightSlider.addEventListener("input", () => { onHeightInput(); schedulePreview(); }); // Mask painting UX: brush size, modes, row/column fills, and precise coords let draggingMask = false; @@ -5659,11 +6424,11 @@ rowColSizeValEl.textContent = rowColSizeEl.value; rowColSize = parseInt(rowColSizeEl.value, 10) || 1; } - if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore')); - if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore')); - if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle')); - // Initialize button state (default to toggle mode) - updateModeButtons(); + if (btnIgnore) btnIgnore.addEventListener('click', () => setMode('ignore')); + if (btnUnignore) btnUnignore.addEventListener('click', () => setMode('unignore')); + if (btnToggle) btnToggle.addEventListener('click', () => setMode('toggle')); + // Initialize button state (default to toggle mode) + updateModeButtons(); const mapClientToPixel = (clientX, clientY) => { // Compute without rounding until final step to avoid drift at higher zoom @@ -5718,12 +6483,12 @@ const w = baseCanvas.width, h = baseCanvas.height; ensureMask(w, h); if (y < 0 || y >= h) return; - + // Paint multiple rows based on rowColSize const halfSize = Math.floor(rowColSize / 2); const startY = Math.max(0, y - halfSize); const endY = Math.min(h - 1, y + halfSize); - + for (let rowY = startY; rowY <= endY; rowY++) { for (let x = 0; x < w; x++) { const idx = rowY * w + x; @@ -5750,12 +6515,12 @@ const w = baseCanvas.width, h = baseCanvas.height; ensureMask(w, h); if (x < 0 || x >= w) return; - + // Paint multiple columns based on rowColSize const halfSize = Math.floor(rowColSize / 2); const startX = Math.max(0, x - halfSize); const endX = Math.min(w - 1, x + halfSize); - + for (let colX = startX; colX <= endX; colX++) { for (let y = 0; y < h; y++) { const idx = y * w + colX; @@ -5801,14 +6566,14 @@ redrawMaskOverlay(); }; - maskCanvas.addEventListener('mousedown', (e) => { + maskCanvas.addEventListener('mousedown', (e) => { if (e.button === 1 || e.button === 2 || allowPan) return; // let pan handler manage draggingMask = true; handlePaint(e); }); - // Avoid hijacking touch gestures for panning/zooming - maskCanvas.addEventListener('touchstart', (e) => { /* let panStage handle */ }, { passive: true }); - maskCanvas.addEventListener('touchmove', (e) => { /* let panStage handle */ }, { passive: true }); - maskCanvas.addEventListener('touchend', (e) => { /* let panStage handle */ }, { passive: true }); + // Avoid hijacking touch gestures for panning/zooming + maskCanvas.addEventListener('touchstart', (e) => { /* let panStage handle */ }, { passive: true }); + maskCanvas.addEventListener('touchmove', (e) => { /* let panStage handle */ }, { passive: true }); + maskCanvas.addEventListener('touchend', (e) => { /* let panStage handle */ }, { passive: true }); window.addEventListener('mousemove', (e) => { if (draggingMask) handlePaint(e); }); window.addEventListener('mouseup', () => { if (draggingMask) { draggingMask = false; saveBotSettings(); }}); @@ -5839,15 +6604,15 @@ tempCanvas.width = newWidth; tempCanvas.height = newHeight; tempCtx.imageSmoothingEnabled = false; - if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { + if (baseProcessor !== processor && (!baseProcessor.img || !baseProcessor.canvas)) { await baseProcessor.load(); } tempCtx.drawImage(baseProcessor.img, 0, 0, newWidth, newHeight); - const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight); + const imgData = tempCtx.getImageData(0, 0, newWidth, newHeight); const data = imgData.data; const tThresh2 = state.customTransparencyThreshold || CONFIG.TRANSPARENCY_THRESHOLD; let totalValidPixels = 0; - const mask = (state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight) ? state.resizeIgnoreMask : null; + const mask = (state.resizeIgnoreMask && state.resizeIgnoreMask.length === newWidth * newHeight) ? state.resizeIgnoreMask : null; const applyFSDitherFinal = async () => { const w = newWidth, h = newHeight; @@ -5942,16 +6707,16 @@ state.totalPixels = totalValidPixels; state.paintedPixels = 0; - state.resizeSettings = { baseWidth: width, baseHeight: height, width: newWidth, height: newHeight }; + state.resizeSettings = { baseWidth: width, baseHeight: height, width: newWidth, height: newHeight }; saveBotSettings(); const finalImageBitmap = await createImageBitmap(tempCanvas); await overlayManager.setImage(finalImageBitmap); - overlayManager.enable(); - toggleOverlayBtn.classList.add('active'); - toggleOverlayBtn.setAttribute('aria-pressed', 'true'); + overlayManager.enable(); + toggleOverlayBtn.classList.add('active'); + toggleOverlayBtn.setAttribute('aria-pressed', 'true'); - // Keep state.imageData.processor as the original-based source; painting uses paletted pixels already stored + // Keep state.imageData.processor as the original-based source; painting uses paletted pixels already stored updateStats(); updateUI("resizeSuccess", "success", { width: newWidth, height: newHeight }); @@ -6002,7 +6767,7 @@ } function closeResizeDialog() { - try { if (typeof _resizeDialogCleanup === 'function') { _resizeDialogCleanup(); } } catch {} + try { if (typeof _resizeDialogCleanup === 'function') { _resizeDialogCleanup(); } } catch {} resizeOverlay.style.display = "none"; resizeContainer.style.display = "none"; _updateResizePreview = () => { }; @@ -6010,7 +6775,7 @@ try { if (_previewTimer) { clearTimeout(_previewTimer); _previewTimer = null; } } catch {} _maskImageData = null; _maskData = null; _dirty = null; _ditherWorkBuf = null; _ditherEligibleBuf = null; - _resizeDialogCleanup = null; + _resizeDialogCleanup = null; } if (uploadBtn) { @@ -6069,10 +6834,10 @@ state.paintedPixels = 0 state.imageLoaded = true state.lastPosition = { x: 0, y: 0 } - + // Initialize painted map for tracking Utils.initializePaintedMap(width, height); - + // New image: clear previous resize settings state.resizeSettings = null; // Also clear any previous ignore mask @@ -6132,9 +6897,9 @@ const tempFetch = async (url, options) => { if ( - typeof url === "string" && - url.includes("https://backend.wplace.live/s0/pixel/") && - options?.method?.toUpperCase() === "POST" + typeof url === "string" && + url.includes("https://backend.wplace.live/s0/pixel/") && + options?.method?.toUpperCase() === "POST" ) { try { const response = await originalFetch(url, options) @@ -6260,11 +7025,11 @@ const progress = Math.round((savedData.state.paintedPixels / savedData.state.totalPixels) * 100) Utils.showAlert( - `${Utils.t("savedDataFound")}\n\n` + - `Saved: ${savedDate}\n` + - `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + - `${Utils.t("clickLoadToContinue")}`, - "info", + `${Utils.t("savedDataFound")}\n\n` + + `Saved: ${savedDate}\n` + + `Progress: ${savedData.state.paintedPixels}/${savedData.state.totalPixels} pixels (${progress}%)\n` + + `${Utils.t("clickLoadToContinue")}`, + "info", ) } } @@ -6335,7 +7100,7 @@ break outerLoop } - + const idx = (y * width + x) * 4 const r = pixels[idx] const g = pixels[idx + 1] @@ -6370,15 +7135,15 @@ let pixelY = absY % 1000; if (!pixelBatch || - pixelBatch.regionX !== regionX + adderX || - pixelBatch.regionY !== regionY + adderY) { + pixelBatch.regionX !== regionX + adderX || + pixelBatch.regionY !== regionY + adderY) { if (pixelBatch && pixelBatch.pixels.length > 0) { console.log(`๐ŸŒ Sending region-change batch with ${pixelBatch.pixels.length} pixels (switching to region ${regionX + adderX},${regionY + adderY})`); const success = await sendBatchWithRetry(pixelBatch.pixels, pixelBatch.regionX, pixelBatch.regionY); - + if (success) { - pixelBatch.pixels.forEach((p) => { + pixelBatch.pixels.forEach((p) => { state.paintedPixels++; // Mark pixel as painted in map Utils.markPixelPainted(p.x, p.y, pixelBatch.regionX, pixelBatch.regionY); @@ -6414,7 +7179,7 @@ }; } - + try { const tileRegionX = pixelBatch ? (pixelBatch.regionX) : (regionX + adderX); const tileRegionY = pixelBatch ? (pixelBatch.regionY) : (regionY + adderY); @@ -6502,13 +7267,13 @@ current: state.currentCharges }); await updateStats(); - + // Allow auto save during cooldown Utils.performSmartSave(); - + await Utils.sleep(state.cooldown); } - + // Disable save button again after exiting wait loop (back to normal painting) if (!state.stopFlag) { saveBtn.disabled = true; @@ -6530,7 +7295,7 @@ state.currentCharges -= pixelBatch.pixels.length; // Final save with painted map Utils.saveProgress(); - + if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && pixelBatch.pixels.length > 0) { const delayPerPixel = 1000 / state.paintingSpeed // ms per pixel const totalDelay = Math.max(100, delayPerPixel * pixelBatch.pixels.length) // minimum 100ms @@ -6578,7 +7343,7 @@ // Helper function to calculate batch size based on mode function calculateBatchSize() { let targetBatchSize; - + if (state.batchMode === 'random') { // Generate random batch size within the specified range const min = Math.max(1, state.randomBatchMin); @@ -6589,11 +7354,11 @@ // Normal mode - use the fixed paintingSpeed value targetBatchSize = state.paintingSpeed; } - + // Always limit by available charges const maxAllowed = Math.floor(state.currentCharges); const finalBatchSize = Math.min(targetBatchSize, maxAllowed); - + return finalBatchSize; } @@ -6603,9 +7368,9 @@ while (attempt < maxRetries && !state.stopFlag) { attempt++; console.log(`๐Ÿ”„ Attempting to send batch (attempt ${attempt}/${maxRetries}) for region ${regionX},${regionY} with ${pixels.length} pixels`); - + const result = await sendPixelBatch(pixels, regionX, regionY); - + if (result === true) { console.log(`โœ… Batch succeeded on attempt ${attempt}`); return true; @@ -6631,19 +7396,19 @@ await Utils.sleep(baseDelay + jitter); } } - + if (attempt >= maxRetries) { console.error(`โŒ Batch failed after ${maxRetries} attempts (MAX_BATCH_RETRIES=${MAX_BATCH_RETRIES}). This will stop painting to prevent infinite loops.`); updateUI("paintingError", "error"); return false; } - + return false; } async function sendPixelBatch(pixelBatch, regionX, regionY) { let token = turnstileToken; - + // Generate new token if we don't have one if (!token) { try { @@ -6680,13 +7445,13 @@ let data = null try { data = await res.json() } catch (_) { } console.error("โŒ 403 Forbidden. Turnstile token might be invalid or expired.") - + // Try to generate a new token and retry once try { console.log("๐Ÿ”„ Regenerating Turnstile token after 403..."); token = await handleCaptcha(); turnstileToken = token; - + // Retry the request with new token const retryPayload = { coords, colors, t: token }; const retryRes = await fetch(`https://backend.wplace.live/s0/pixel/${regionX}/${regionY}`, { @@ -6695,16 +7460,16 @@ credentials: "include", body: JSON.stringify(retryPayload), }); - + if (retryRes.status === 403) { turnstileToken = null; tokenPromise = new Promise((resolve) => { _resolveToken = resolve }); return "token_error"; } - + const retryData = await retryRes.json(); return retryData?.painted === pixelBatch.length; - + } catch (retryError) { console.error("โŒ Token regeneration failed:", retryError); turnstileToken = null; @@ -6712,7 +7477,7 @@ return "token_error"; } } - + const data = await res.json() return data?.painted === pixelBatch.length } catch (e) { @@ -6734,19 +7499,19 @@ minimized: state.minimized, overlayOpacity: state.overlayOpacity, blueMarbleEnabled: document.getElementById('enableBlueMarbleToggle')?.checked, - ditheringEnabled: state.ditheringEnabled, - colorMatchingAlgorithm: state.colorMatchingAlgorithm, - enableChromaPenalty: state.enableChromaPenalty, - chromaPenaltyWeight: state.chromaPenaltyWeight, - customTransparencyThreshold: state.customTransparencyThreshold, - customWhiteThreshold: state.customWhiteThreshold, - paintWhitePixels: state.paintWhitePixels, - resizeSettings: state.resizeSettings, - originalImage: state.originalImage, - // Save ignore mask (as base64) with its dimensions - resizeIgnoreMask: (state.resizeIgnoreMask && state.resizeSettings && state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length) - ? { w: state.resizeSettings.width, h: state.resizeSettings.height, data: btoa(String.fromCharCode(...state.resizeIgnoreMask)) } - : null, + ditheringEnabled: state.ditheringEnabled, + colorMatchingAlgorithm: state.colorMatchingAlgorithm, + enableChromaPenalty: state.enableChromaPenalty, + chromaPenaltyWeight: state.chromaPenaltyWeight, + customTransparencyThreshold: state.customTransparencyThreshold, + customWhiteThreshold: state.customWhiteThreshold, + paintWhitePixels: state.paintWhitePixels, + resizeSettings: state.resizeSettings, + originalImage: state.originalImage, + // Save ignore mask (as base64) with its dimensions + resizeIgnoreMask: (state.resizeIgnoreMask && state.resizeSettings && state.resizeSettings.width * state.resizeSettings.height === state.resizeIgnoreMask.length) + ? { w: state.resizeSettings.width, h: state.resizeSettings.height, data: btoa(String.fromCharCode(...state.resizeIgnoreMask)) } + : null, // Notifications notificationsEnabled: state.notificationsEnabled, notifyOnChargesReached: state.notifyOnChargesReached, @@ -6779,31 +7544,31 @@ CONFIG.AUTO_CAPTCHA_ENABLED = settings.autoCaptchaEnabled ?? false; state.overlayOpacity = settings.overlayOpacity ?? CONFIG.OVERLAY.OPACITY_DEFAULT; state.blueMarbleEnabled = settings.blueMarbleEnabled ?? CONFIG.OVERLAY.BLUE_MARBLE_DEFAULT; - state.ditheringEnabled = settings.ditheringEnabled ?? false; - state.colorMatchingAlgorithm = settings.colorMatchingAlgorithm || 'lab'; - state.enableChromaPenalty = settings.enableChromaPenalty ?? true; - state.chromaPenaltyWeight = settings.chromaPenaltyWeight ?? 0.15; - state.customTransparencyThreshold = settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; - state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; - state.paintWhitePixels = settings.paintWhitePixels ?? true; - state.resizeSettings = settings.resizeSettings ?? null; - state.originalImage = settings.originalImage ?? null; + state.ditheringEnabled = settings.ditheringEnabled ?? false; + state.colorMatchingAlgorithm = settings.colorMatchingAlgorithm || 'lab'; + state.enableChromaPenalty = settings.enableChromaPenalty ?? true; + state.chromaPenaltyWeight = settings.chromaPenaltyWeight ?? 0.15; + state.customTransparencyThreshold = settings.customTransparencyThreshold ?? CONFIG.TRANSPARENCY_THRESHOLD; + state.customWhiteThreshold = settings.customWhiteThreshold ?? CONFIG.WHITE_THRESHOLD; + state.paintWhitePixels = settings.paintWhitePixels ?? true; + state.resizeSettings = settings.resizeSettings ?? null; + state.originalImage = settings.originalImage ?? null; // Notifications state.notificationsEnabled = settings.notificationsEnabled ?? CONFIG.NOTIFICATIONS.ENABLED; state.notifyOnChargesReached = settings.notifyOnChargesReached ?? CONFIG.NOTIFICATIONS.ON_CHARGES_REACHED; state.notifyOnlyWhenUnfocused = settings.notifyOnlyWhenUnfocused ?? CONFIG.NOTIFICATIONS.ONLY_WHEN_UNFOCUSED; state.notificationIntervalMinutes = settings.notificationIntervalMinutes ?? CONFIG.NOTIFICATIONS.REPEAT_MINUTES; - // Restore ignore mask if dims match current resizeSettings - if (settings.resizeIgnoreMask && settings.resizeIgnoreMask.data && state.resizeSettings && settings.resizeIgnoreMask.w === state.resizeSettings.width && settings.resizeIgnoreMask.h === state.resizeSettings.height) { - try { - const bin = atob(settings.resizeIgnoreMask.data); - const arr = new Uint8Array(bin.length); - for (let i=0;i { state.colorMatchingAlgorithm = e.target.value; saveBotSettings(); _updateResizePreview(); }); if (chromaToggle) chromaToggle.addEventListener('change', e => { state.enableChromaPenalty = e.target.checked; saveBotSettings(); _updateResizePreview(); }); if (chromaSlider && chromaValue) chromaSlider.addEventListener('input', e => { state.chromaPenaltyWeight = parseFloat(e.target.value)||0.15; chromaValue.textContent = state.chromaPenaltyWeight.toFixed(2); saveBotSettings(); _updateResizePreview(); }); if (transInput) transInput.addEventListener('change', e => { const v=parseInt(e.target.value,10); if(!isNaN(v)&&v>=0&&v<=255){ state.customTransparencyThreshold=v; CONFIG.TRANSPARENCY_THRESHOLD=v; saveBotSettings(); _updateResizePreview(); }}); if (whiteInput) whiteInput.addEventListener('change', e => { const v=parseInt(e.target.value,10); if(!isNaN(v)&&v>=200&&v<=255){ state.customWhiteThreshold=v; CONFIG.WHITE_THRESHOLD=v; saveBotSettings(); _updateResizePreview(); }}); - if (ditherToggle) ditherToggle.addEventListener('change', e => { state.ditheringEnabled = e.target.checked; saveBotSettings(); _updateResizePreview(); }); + if (ditherToggle) ditherToggle.addEventListener('change', e => { state.ditheringEnabled = e.target.checked; saveBotSettings(); _updateResizePreview(); }); if (resetBtn) resetBtn.addEventListener('click', () => { state.colorMatchingAlgorithm='lab'; state.enableChromaPenalty=true; state.chromaPenaltyWeight=0.15; state.customTransparencyThreshold=CONFIG.TRANSPARENCY_THRESHOLD=100; state.customWhiteThreshold=CONFIG.WHITE_THRESHOLD=250; saveBotSettings(); const a=document.getElementById('colorAlgorithmSelect'); if(a) a.value='lab'; const ct=document.getElementById('enableChromaPenaltyToggle'); if(ct) ct.checked=true; if(chromaSlider) chromaSlider.value=0.15; if(chromaValue) chromaValue.textContent='0.15'; if(transInput) transInput.value=100; if(whiteInput) whiteInput.value=250; _updateResizePreview(); Utils.showAlert('Advanced color settings reset.', 'success'); }); }; // Delay to ensure resize UI built setTimeout(advancedInit, 500); - + // Add cleanup on page unload window.addEventListener('beforeunload', () => { Utils.cleanupTurnstile(); }); }) -})() +})() \ No newline at end of file From 42ad959e63c8828e4d1d3dc4848b8a8cfafa5f8c Mon Sep 17 00:00:00 2001 From: Staninna <26458805+Staninna@users.noreply.github.com> Date: Mon, 25 Aug 2025 20:12:27 +0200 Subject: [PATCH 2/7] this one was also gone --- auto-image-styles.css | 749 ------------------------------------------ 1 file changed, 749 deletions(-) delete mode 100644 auto-image-styles.css diff --git a/auto-image-styles.css b/auto-image-styles.css deleted file mode 100644 index 5ac4a82..0000000 --- a/auto-image-styles.css +++ /dev/null @@ -1,749 +0,0 @@ -/* WPlace Auto-Image Bot Styles */ - -/* Animations */ -@keyframes neonGlow { - 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 pixelBlink { - 0%, 50% { opacity: 1; } - 51%, 100% { opacity: 0.7; } -} - -@keyframes scanline { - 0% { transform: translateY(-100%); } - 100% { transform: translateY(400px); } -} - -@keyframes pulse { - 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); } - 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 slideIn { - from { transform: translateY(-10px); opacity: 0; } - to { transform: translateY(0); opacity: 1; } -} - -@keyframes shimmer { - 0% { transform: translateX(-100%); } - 100% { transform: translateX(100%); } -} - -@keyframes slideDown { - from { transform: translateX(-50%) translateY(-20px); opacity: 0; } - to { transform: translateX(-50%) translateY(0); opacity: 1; } -} - -/* Main containers - will be dynamically styled based on theme */ -#wplace-image-bot-container { - position: fixed; - top: 20px; - left: 20px; - width: 280px; - max-height: calc(100vh - 40px); - padding: 0; - z-index: 9998; - animation: slideIn 0.4s ease-out; - overflow-y: auto; - overflow-x: hidden; - transition: all 0.3s ease; - user-select: none; -} - -#wplace-image-bot-container.wplace-dragging { - transition: none; - box-shadow: 0 12px 40px rgba(0,0,0,0.8), 0 0 0 2px rgba(255,255,255,0.2); - transform: scale(1.02); - z-index: 9999; -} - -#wplace-image-bot-container.wplace-minimized { - width: 200px; - height: auto; - overflow: hidden; -} - -#wplace-image-bot-container.wplace-compact { - width: 240px; -} - -/* Stats Container */ -#wplace-stats-container { - position: fixed; - top: 20px; - left: 330px; - width: 280px; - max-height: calc(100vh - 40px); - padding: 0; - z-index: 9997; - animation: slideIn 0.4s ease-out; - overflow-y: auto; - transition: all 0.3s ease; - user-select: none; -} - -#wplace-stats-container.wplace-dragging { - transition: none; -} - -/* Header styles */ -.wplace-header { - display: flex; - justify-content: space-between; - align-items: center; - cursor: move; - user-select: none; - transition: background 0.2s ease; - position: relative; - z-index: 2; -} - -.wplace-header-title { - display: flex; - align-items: center; -} - -.wplace-header-controls { - display: flex; -} - -.wplace-header-btn { - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.2s; -} - -.wplace-header-btn:hover { - transform: scale(1.1); -} - -/* Content areas */ -.wplace-content { - display: block; - position: relative; - z-index: 2; -} - -.wplace-content.wplace-hidden { - display: none; -} - -.wplace-status-section { - margin-bottom: 12px; - padding: 8px; - background: rgba(255,255,255,0.03); - border: 1px solid rgba(255,255,255,0.1); -} - -.wplace-section { - margin-bottom: 12px; - padding: 12px; - background: rgba(255,255,255,0.03); - border: 1px solid rgba(255,255,255,0.1); -} - -.wplace-section-title { - font-size: 11px; - font-weight: 600; - margin-bottom: 8px; - display: flex; - align-items: center; - gap: 6px; - text-transform: uppercase; - letter-spacing: 0.5px; -} - -/* Controls and buttons */ -.wplace-controls { - display: flex; - flex-direction: column; - gap: 8px; -} - -.wplace-row { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 8px; -} - -.wplace-row.single { - grid-template-columns: 1fr; -} - -.wplace-btn { - cursor: pointer; - display: flex; - align-items: center; - justify-content: center; - transition: all 0.3s ease; - position: relative; - overflow: hidden; -} - -.wplace-btn::before { - content: ''; - position: absolute; - top: 0; - left: -100%; - width: 100%; - height: 100%; - background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); - transition: left 0.5s; -} - -.wplace-btn:hover::before { - left: 100%; -} - -.wplace-btn:hover:not(:disabled) { - box-shadow: 0 0 15px currentColor; -} - -.wplace-btn:active:not(:disabled) { - transform: translateY(0); -} - -.wplace-btn:disabled { - opacity: 0.3; - cursor: not-allowed; - transform: none !important; - box-shadow: none !important; -} - -.wplace-btn:disabled::before { - display: none; -} - -.wplace-btn-overlay.active { - background: linear-gradient(135deg, #29b6f6 0%, #8e2de2 100%); - box-shadow: 0 0 15px #8e2de2; -} - -/* Stats display */ -.wplace-stats { - margin-bottom: 8px; -} - -.wplace-stat-item { - display: flex; - justify-content: space-between; - border-bottom: 1px solid rgba(255,255,255,0.05); -} - -.wplace-stat-item:last-child { - border-bottom: none; -} - -.wplace-stat-label { - display: flex; - align-items: center; - gap: 6px; - opacity: 0.9; -} - -.wplace-stat-value { - font-weight: 600; -} - -.wplace-colors-section { - margin-top: 10px; - padding-top: 8px; - border-top: 1px solid rgba(255,255,255,0.05); -} - -.wplace-stat-colors-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(16px, 1fr)); - gap: 4px; - margin-top: 8px; - padding: 4px; - background: rgba(0,0,0,0.2); - border-radius: 4px; - max-height: 80px; - overflow-y: auto; -} - -.wplace-stat-color-swatch { - width: 16px; - height: 16px; - border-radius: 3px; - border: 1px solid rgba(255,255,255,0.1); - box-shadow: inset 0 0 2px rgba(0,0,0,0.5); -} - -/* Progress bars */ -.wplace-progress { - width: 100%; - overflow: hidden; - position: relative; -} - -.wplace-progress-bar { - position: relative; - transition: width 0.5s ease; -} - -/* Status indicators */ -.wplace-status { - text-align: center; - position: relative; - overflow: hidden; -} - -.status-default { - background: rgba(255,255,255,0.1); -} - -.status-success { - background: rgba(0, 255, 0, 0.1); - box-shadow: 0 0 15px currentColor; -} - -.status-error { - background: rgba(255, 0, 0, 0.1); - box-shadow: 0 0 15px currentColor; -} - -.status-warning { - background: rgba(255, 165, 0, 0.1); - box-shadow: 0 0 15px currentColor; -} - -/* Resize container */ -.resize-container { - display: none; - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 10000; - width: 90%; - max-width: 700px; - max-height: 90%; - overflow: auto; -} - -.resize-preview-wrapper { - display: flex; - justify-content: center; - align-items: center; - background: rgba(0,0,0,0.2); - margin: 15px 0; - height: 300px; - overflow: hidden; -} - -.resize-canvas-stack { - position: relative; - transform-origin: center center; - display: inline-block; -} - -.resize-base-canvas, .resize-mask-canvas { - position: absolute; - left: 0; - top: 0; - image-rendering: pixelated; - image-rendering: -moz-crisp-edges; - image-rendering: crisp-edges; -} - -.resize-mask-canvas { - pointer-events: auto; -} - -.resize-tools { - display: flex; - gap: 8px; - align-items: center; - margin-top: 8px; - font-size: 12px; -} - -.resize-tools button { - padding: 6px 10px; - border-radius: 6px; - border: 1px solid rgba(255,255,255,0.2); - background: rgba(255,255,255,0.06); - color: #fff; - cursor: pointer; -} - -.wplace-btn.active, -.wplace-btn[aria-pressed="true"] { - box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2) !important; -} - -.mask-mode-group .wplace-btn.active, -.mask-mode-group .wplace-btn[aria-pressed="true"] { - box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2); -} - -.resize-controls { - display: grid; - grid-template-columns: 1fr 1fr; - gap: 15px; - align-items: center; -} - -.resize-slider { - width: 100%; - outline: none; - -webkit-appearance: none; -} - -.resize-zoom-controls { - grid-column: 1 / -1; - display: flex; - align-items: center; - gap: 10px; - margin-top: 15px; -} - -.resize-buttons { - display: flex; - gap: 10px; - justify-content: center; - margin-top: 20px; -} - -.resize-overlay { - position: fixed; - top: 0; - left: 0; - width: 100%; - height: 100%; - background: rgba(0, 0, 0, 0.8); - z-index: 9999; - display: none; -} - -/* Color grid */ -.wplace-color-grid { - display: grid; - grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); - gap: 10px; - padding-top: 8px; - max-height: 300px; - overflow-y: auto; -} - -.wplace-color-item { - display: flex; - flex-direction: column; - align-items: center; - gap: 4px; -} - -.wplace-color-item-name { - font-size: 9px; - color: #ccc; - text-align: center; - white-space: nowrap; - overflow: hidden; - text-overflow: ellipsis; - width: 100%; -} - -.wplace-color-swatch { - width: 22px; - height: 22px; - border: 1px solid rgba(255,255,255,0.2); - border-radius: 4px; - cursor: pointer; - transition: transform 0.1s ease, box-shadow 0.2s ease; - position: relative; - margin: 0 auto; -} - -.wplace-color-swatch.unavailable { - border-color: #666; - border-style: dashed; - cursor: not-allowed; -} - -.wplace-color-swatch:hover { - transform: scale(1.1); - z-index: 1; -} - -.wplace-color-swatch:not(.active) { - opacity: 0.3; - filter: grayscale(80%); -} - -.wplace-color-swatch.unavailable:not(.active) { - opacity: 0.2; - filter: grayscale(90%); -} - -.wplace-color-swatch.active::after { - content: 'โœ”'; - position: absolute; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - color: white; - font-size: 12px; - font-weight: bold; - text-shadow: 0 0 3px black; -} - -.wplace-color-divider { - border: none; - height: 1px; - background: rgba(255,255,255,0.1); - margin: 8px 0; -} - -/* Cooldown controls */ -.wplace-cooldown-control { - margin-top: 8px; -} - -.wplace-cooldown-control label { - font-size: 11px; - margin-bottom: 4px; - display: block; -} - -.wplace-slider-container { - display: flex; - align-items: center; - gap: 8px; -} - -.wplace-slider { - flex: 1; - -webkit-appearance: none; - appearance: none; - height: 4px; - background: #444; - border-radius: 2px; - outline: none; -} - -.wplace-slider::-webkit-slider-thumb { - -webkit-appearance: none; - width: 14px; - height: 14px; - border-radius: 50%; - cursor: pointer; -} - -/* Settings container */ -#wplace-settings-container { - position: fixed; - top: 50%; - left: 50%; - transform: translate(-50%, -50%); - z-index: 10001; - min-width: 400px; - max-width: 500px; -} - -.wplace-settings { - padding: 16px; - max-height: 400px; - overflow-y: auto; -} - -.wplace-setting-section { - margin-bottom: 20px; - padding: 12px; -} - -.wplace-setting-title { - display: flex; - align-items: center; - margin-bottom: 12px; - font-size: 14px; - font-weight: 600; -} - -.wplace-setting-title i { - margin-right: 8px; -} - -.wplace-setting-content { - /* Content styling will be theme-dependent */ -} - -.wplace-speed-control { - margin-top: 12px; - padding: 12px; -} - -.wplace-speed-label { - display: flex; - align-items: center; - margin-bottom: 8px; - font-size: 13px; - font-weight: 600; -} - -.wplace-speed-label i { - margin-right: 6px; -} - -.wplace-speed-slider-container { - display: flex; - align-items: center; - gap: 12px; -} - -.wplace-speed-slider { - flex: 1; - height: 6px; - border-radius: 3px; - outline: none; - cursor: pointer; - -webkit-appearance: none; - appearance: none; -} - -.wplace-speed-slider::-webkit-slider-thumb { - -webkit-appearance: none; - appearance: none; - width: 18px; - height: 18px; - border-radius: 50%; - cursor: pointer; - border: 2px solid; -} - -.wplace-speed-slider::-moz-range-thumb { - width: 18px; - height: 18px; - border-radius: 50%; - cursor: pointer; - border: 2px solid; -} - -.wplace-speed-display { - display: flex; - align-items: center; - gap: 4px; - min-width: 90px; - justify-content: flex-end; -} - -#speedValue { - font-weight: 600; - font-size: 14px; -} - -.wplace-speed-unit { - font-size: 11px; - opacity: 0.8; -} - -.wplace-speed-container { - display: flex; - align-items: center; - gap: 15px; - margin-bottom: 10px; -} - -.wplace-speed-display { - padding: 5px 10px; - border-radius: 4px; - font-weight: 600; - min-width: 80px; - text-align: center; -} - -.wplace-select { - width: 100%; - padding: 8px 12px; - font-size: 14px; - margin-bottom: 10px; -} - -.wplace-select:focus { - outline: none; -} - -.wplace-description { - font-size: 12px; - opacity: 0.8; - line-height: 1.4; -} - -.wplace-theme-custom { - margin-top: 15px; - padding: 15px; -} - -.wplace-custom-group { - margin-bottom: 15px; -} - -.wplace-custom-label { - display: flex; - align-items: center; - margin-bottom: 8px; - font-size: 13px; - font-weight: 600; -} - -.wplace-custom-label i { - margin-right: 8px; - width: 16px; -} - -.wplace-color-input-group { - display: flex; - gap: 8px; - align-items: center; -} - -.wplace-color-input { - width: 50px; - height: 30px; - border: none; - border-radius: 4px; - cursor: pointer; - background: transparent; -} - -.wplace-color-text { - flex: 1; - padding: 6px 10px; - border-radius: 4px; - font-size: 12px; - font-family: monospace; -} - -.wplace-animation-controls { - display: flex; - flex-direction: column; - gap: 8px; -} - -.wplace-checkbox-label { - display: flex; - align-items: center; - gap: 8px; - font-size: 12px; - cursor: pointer; -} - -.wplace-custom-actions { - display: flex; - gap: 10px; - margin-top: 20px; - border-top: 1px solid; - padding-top: 15px; -} - -/* Theme-specific overrides will be applied dynamically via JavaScript */ \ No newline at end of file From db315ef13ee189d38870e164ee6a5448140e032d Mon Sep 17 00:00:00 2001 From: Camilo404 Date: Tue, 26 Aug 2025 00:52:07 -0500 Subject: [PATCH 3/7] feat: Add Spanish language support - Added ES.md Spanish README translation - Added complete Spanish translations to Auto-Image.js --- Auto-Image.js | 75 ++++++++++++++++++++++++++++++ ES.md | 126 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 201 insertions(+) create mode 100644 ES.md diff --git a/Auto-Image.js b/Auto-Image.js index b4ea565..f1d9da1 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -1038,6 +1038,80 @@ automation: "์ž๋™ํ™”", noChargesThreshold: "โŒ› ํšŸ์ˆ˜๊ฐ€ {threshold} ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ์ค‘. ํ˜„์žฌ {current}. ๋‹ค์Œ {time} ํ›„...", }, + es: { + title: "WPlace Auto-Imagen", + toggleOverlay: "Alternar Superposiciรณn", + scanColors: "Escanear Colores", + uploadImage: "Subir Imagen", + resizeImage: "Redimensionar Imagen", + selectPosition: "Seleccionar Posiciรณn", + startPainting: "Iniciar Pintura", + stopPainting: "Detener Pintura", + checkingColors: "๐Ÿ” Verificando colores disponibles...", + noColorsFound: "โŒ ยกAbre la paleta de colores en el sitio y vuelve a intentar!", + colorsFound: "โœ… {count} colores disponibles encontrados. 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 a que pintes el pรญxel de referencia...", + positionSet: "โœ… ยกPosiciรณn establecida exitosamente!", + positionTimeout: "โŒ Tiempo agotado para seleccionar posiciรณn", + startPaintingMsg: "๐ŸŽจ Iniciando pintura...", + paintingProgress: "๐Ÿงฑ Progreso: {painted}/{total} pรญxeles...", + noCharges: "โŒ› Sin cargas. Esperando {time}...", + paintingStopped: "โน๏ธ Pintura detenida por el usuario", + paintingComplete: "โœ… ยกPintura completa! {count} pรญxeles pintados.", + paintingError: "โŒ Error durante la pintura", + missingRequirements: "โŒ Carga una imagen y selecciona una posiciรณn primero", + 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 tokens Turnstile...", + tokenReady: "โœ… Generador de tokens listo - ยกya puedes empezar a pintar!", + tokenRetryLater: "โš ๏ธ El generador de tokens volverรก a intentar cuando sea necesario", + resizeSuccess: "โœ… Imagen redimensionada a {width}x{height}", + paintingPaused: "โธ๏ธ Pintura pausada en la posiciรณn X: {x}, Y: {y}", + captchaNeeded: "โ— Fallรณ la generaciรณn de token. Por favor intenta 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 exitosamente", + fileSaved: "โœ… Progreso guardado en archivo exitosamente", + fileLoaded: "โœ… Progreso cargado desde archivo exitosamente", + 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 archivo", + invalidFileFormat: "โŒ Formato de archivo invรกlido", + paintingSpeed: "Velocidad de Pintura", + pixelsPerSecond: "pรญxeles/segundo", + speedSetting: "Velocidad: {speed} pรญxeles/seg", + settings: "Configuraciรณn", + botSettings: "Configuraciรณn del Bot", + close: "Cerrar", + language: "Idioma", + themeSettings: "Configuraciรณn de Tema", + themeSettingsDesc: "Elige tu tema de colores preferido para la interfaz.", + languageSelectDesc: "Selecciona tu idioma preferido. Los cambios tendrรกn efecto inmediatamente.", + autoCaptcha: "Solucionador Automรกtico de CAPTCHA (Turnstile)", + autoCaptchaDesc: "Genera automรกticamente tokens Turnstile usando el generador integrado. Se recurre a la automatizaciรณn del navegador si es necesario.", + applySettings: "Aplicar Configuraciรณn", + settingsSaved: "โœ… ยกConfiguraciรณn guardada exitosamente!", + cooldownSettings: "Configuraciรณn de Enfriamiento", + waitCharges: "Esperar hasta que las cargas alcancen", + captchaSolving: "๐Ÿ”‘ Generando token Turnstile...", + captchaFailed: "โŒ Fallรณ la generaciรณn de token Turnstile. Probando mรฉtodo alternativo...", + automation: "Automatizaciรณn", + noChargesThreshold: "โŒ› Esperando que las cargas alcancen {threshold}. Actualmente {current}. Siguiente en {time}...", + }, } // GLOBAL STATE @@ -4990,6 +5064,7 @@ + diff --git a/ES.md b/ES.md new file mode 100644 index 0000000..a4691b9 --- /dev/null +++ b/ES.md @@ -0,0 +1,126 @@ +

+ image +

+ +

WPlace AutoBOT

+

+Este proyecto es un fork de https://github.com/DarkModde/WPlace-AutoBOT +

+

+ Considera unirte a nuestro discord: https://discord.gg/knkNRYyQcm +

+

+ ยกScripts prรกcticos con menรบs intuitivos para hacer tu vida mรกs fรกcil en WPlace!
+ Perfectos para quienes quieren subir de nivel automรกticamente o construir pixel arts masivos โ€” sin perder tiempo. + + Advertencia: Este es un script puramente educativo para demostrar lo que se puede hacer en wplace.live, no somos responsables de lo que suceda si usas el script. +

+ +
+ +

+ Si te preguntas, ยกsรญ! Estos scripts fueron desarrollados por un equipo conformado por brasileรฑos, vietnamitas e indonesios. Jajajaj! +

+ Brazil +   + Vietnam +   + Indonesia +

+

+ +--- + +

๐Ÿš€โ”ƒCรณmo usar los scripts:

+ +

+ Es sรบper fรกcil: copia uno de los cรณdigos de abajo, pรฉgalo en la barra de marcadores de tu navegador,
+ y ejecรบtalo mientras estรฉs en wplace.live. +

+ +
+ +### ๐ŸŽฏโ”ƒAuto-Farm +#### EL AUTOFARM USA CARGAS PARA CONSEGUIR NIVELES, NO DIBUJA UNA IMAGEN POR TI. POR FAVOR USA AUTO-IMAGE PARA ESO +```js +javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval); +``` + +### ๐Ÿ–ผ๏ธโ”ƒAuto-Image + +```js +javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval); +``` + +
+

๐Ÿ“–โ”ƒTutoriales

+ +--- + +![Parte 1](https://i.imgur.com/yneG5if.png) + +--- + +![Parte 2](https://i.imgur.com/ZRpU0wZ.png) + +--- + +![Parte 3](https://i.imgur.com/lfjfcEw.png) + +
+ + +
+ +> [!IMPORTANT] +>

1. Ninguno de los scripts funcionarรก si cierras la pestaรฑa del navegador. Debes mantener la pestaรฑa de WPlace abierta, incluso si estรก en segundo plano.

+>

2. Nunca ejecutes ambos scripts en la misma pรกgina โ€” esto puede causar errores en tu WPlace. Si sucede, ยกsolo refresca la pรกgina!

+>

3. Si el script no detecta los colores disponibles, solo haz clic en el botรณn "Pintar" para mostrarlos โ€” solo entonces inicia el Auto-Image.

+ +
+ +### โœจโ”ƒCaracterรญsticas + +- [x] Farmea automรกticamente gotas/niveles +- [x] Omisiรณn correcta de pรญxeles de color +- [x] Superposiciรณn (ya no necesitas bluemarble) +- [x] Pintar en mรบltiples tiles +- [x] Solucionador de AutoCaptcha con generador de tokens Turnstile +- [x] Procesamiento avanzado de colores +- [x] Soporte multiidioma +- [x] Mรบltiples cuentas a travรฉs de funciรณn de guardar y cargar +- [x] Funciona en mรณvil :P + + +--- + + +--- + +

+ Demostraciรณn del Script +

+ +--- + +### ๐Ÿ“‹โ”ƒPor hacer + +- [x] Hacer que AutoFarm funcione +- [ ] Fusionar ambos scripts en uno +- [ ] Cambiador de mรบltiples cuentas (actualmente en desarrollo) +- [ ] Sistema de cola de mรบltiples cuentas +- [x] Agregar sistema de superposiciรณn +- [x] Agregar soporte de tiles cruzados +- [x] Agregar solucionador de AutoCaptcha +- [x] Agregar omisiรณn correcta de pรญxeles de color +- [ ] Separar CSS + +--- + +- Contribuidores: + Contribuidores + + +

+ +

From 0f2914353d583c3874d271c2b2ddc83a4456785c Mon Sep 17 00:00:00 2001 From: Yaroslav <63865083+dnrovs@users.noreply.github.com> Date: Tue, 26 Aug 2025 19:32:15 +0300 Subject: [PATCH 4/7] =?UTF-8?q?=F0=9F=93=8C=E2=94=83README=20Ukrainian?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- UK.md | 125 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 UK.md diff --git a/UK.md b/UK.md new file mode 100644 index 0000000..4703d2e --- /dev/null +++ b/UK.md @@ -0,0 +1,125 @@ +

+ image +

+ +

WPlace AutoBOT

+

+ะฆะตะน ะฟั€ะพั”ะบั‚ ั” ั„ะพั€ะบะพะผ ะท https://github.com/DarkModde/WPlace-AutoBOT +

+

+ ะ—ะฐะฒั–ั‚ะฐะนั‚ะต ะฝะฐ ะฝะฐัˆ Discord: https://discord.gg/knkNRYyQcm +

+

+ ะŸั€ะฐะบั‚ะธั‡ะฝั– ัะบั€ะธะฟั‚ะธ ะท ั–ะฝั‚ัƒั—ั‚ะธะฒะฝะธะผ ะผะตะฝัŽ, ั‰ะพะฑ ะทั€ะพะฑะธั‚ะธ ะฒะฐัˆะต ะถะธั‚ั‚ั ะฝะฐ WPlace ะฟั€ะพัั‚ั–ัˆะธะผ!
+ ะ†ะดะตะฐะปัŒะฝะพ ะดะปั ั‚ะธั…, ั…ั‚ะพ ั…ะพั‡ะต ะฐะฒั‚ะพะผะฐั‚ะธั‡ะฝะพ ะฟั€ะพะบะฐั‡ัƒะฒะฐั‚ะธ ั€ั–ะฒะตะฝัŒ ะฐะฑะพ ะฑัƒะดัƒะฒะฐั‚ะธ ะผะฐัะธะฒะฝั– ะฟั–ะบัะตะปัŒ-ะฐั€ั‚ะธ โ€” ะฑะตะท ะฒะธั‚ั€ะฐั‚ะธ ั‡ะฐััƒ. + + โš ๏ธ ะฃะฒะฐะณะฐ: ะฆะต ะพัะฒั–ั‚ะฝั–ะน ัะบั€ะธะฟั‚, ัั‚ะฒะพั€ะตะฝะธะน ะดะปั ะดะตะผะพะฝัั‚ั€ะฐั†ั–ั— ะผะพะถะปะธะฒะพัั‚ะตะน ะฝะฐ wplace.live. ะœะธ ะฝะต ะฝะตัะตะผะพ ะฒั–ะดะฟะพะฒั–ะดะฐะปัŒะฝะพัั‚ั– ะทะฐ ะฝะฐัะปั–ะดะบะธ ะนะพะณะพ ะฒะธะบะพั€ะธัั‚ะฐะฝะฝั. +

+ +
+ +

+ ะฏะบั‰ะพ ะฒะธ ั†ั–ะบะฐะฒะธั‚ะตัั โ€” ั‚ะฐะบ! ะกะบั€ะธะฟั‚ะธ ะฑัƒะปะธ ั€ะพะทั€ะพะฑะปะตะฝั– ะบะพะผะฐะฝะดะพัŽ ะท ะ‘ั€ะฐะทะธะปั–ั—, ะ’โ€™ั”ั‚ะฝะฐะผัƒ ั‚ะฐ ะ†ะฝะดะพะฝะตะทั–ั—. HUEHUE! +

+ Brazil +   + Vietnam +   + Indonesia +

+

+ +--- + +

๐Ÿš€โ”ƒะฏะบ ะบะพั€ะธัั‚ัƒะฒะฐั‚ะธัั ัะบั€ะธะฟั‚ะฐะผะธ:

+ +

+ ะฆะต ะดัƒะถะต ะฟั€ะพัั‚ะพ: ัะบะพะฟั–ัŽะนั‚ะต ะพะดะธะฝ ะท ะบะพะดั–ะฒ ะฝะธะถั‡ะต, ะฒัั‚ะฐะฒั‚ะต ะนะพะณะพ ัƒ ะฟะฐะฝะตะปัŒ ะทะฐะบะปะฐะดะพะบ ะฒะฐัˆะพะณะพ ะฑั€ะฐัƒะทะตั€ะฐ,
+ ั‚ะฐ ะทะฐะฟัƒัั‚ั–ั‚ัŒ, ะบะพะปะธ ะฟะตั€ะตะฑัƒะฒะฐั”ั‚ะต ะฝะฐ wplace.live. +

+ +
+ +### ๐ŸŽฏโ”ƒะะฒั‚ะพ-ะคะฐั€ะผ +#### AUTOFARM ะ’ะ˜ะšะžะ ะ˜ะกะขะžะ’ะฃะ„ ะงะะ ะ”ะ–ะ† ะ”ะ›ะฏ ะžะขะ ะ˜ะœะะะะฏ ะ ะ†ะ’ะะ†ะ’, ะ’ะ†ะ ะะ• ะœะะ›ะฎะ„ ะ—ะžะ‘ะ ะะ–ะ•ะะะฏ. ะ”ะ›ะฏ ะฆะฌะžะ“ะž ะ’ะ˜ะšะžะ ะ˜ะกะขะžะ’ะฃะ™ะขะ• AUTO-IMAGE +```js +javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval); +``` + +### ๐Ÿ–ผ๏ธโ”ƒะะฒั‚ะพ-ะ—ะพะฑั€ะฐะถะตะฝะฝั + +```js +javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval); +``` + +
+

๐Ÿ“–โ”ƒะ†ะฝัั‚ั€ัƒะบั†ั–ั—

+ +--- + +![ะงะฐัั‚ะธะฝะฐ 1](https://i.imgur.com/yneG5if.png) + +--- + +![ะงะฐัั‚ะธะฝะฐ 2](https://i.imgur.com/ZRpU0wZ.png) + +--- + +![ะงะฐัั‚ะธะฝะฐ 3](https://i.imgur.com/lfjfcEw.png) + +
+ + +
+ +> [!IMPORTANT] +>

1. ะ–ะพะดะตะฝ ัะบั€ะธะฟั‚ ะฝะต ะฟั€ะฐั†ัŽะฒะฐั‚ะธะผะต, ัะบั‰ะพ ะฒะธ ะทะฐะบั€ะธั”ั‚ะต ะฒะบะปะฐะดะบัƒ ะฑั€ะฐัƒะทะตั€ะฐ. ะ’ะบะปะฐะดะบะฐ WPlace ะผะฐั” ะทะฐะปะธัˆะฐั‚ะธัั ะฒั–ะดะบั€ะธั‚ะพัŽ, ะฝะฐะฒั–ั‚ัŒ ัƒ ั„ะพะฝั–.

+>

2. ะั–ะบะพะปะธ ะฝะต ะทะฐะฟัƒัะบะฐะนั‚ะต ะพะฑะธะดะฒะฐ ัะบั€ะธะฟั‚ะธ ะฝะฐ ะพะดะฝั–ะน ัั‚ะพั€ั–ะฝั†ั– โ€” ั†ะต ะผะพะถะต ะทะปะฐะผะฐั‚ะธ WPlace. ะฏะบั‰ะพ ั†ะต ัั‚ะฐะปะพัั โ€” ะฟั€ะพัั‚ะพ ะพะฝะพะฒั–ั‚ัŒ ัั‚ะพั€ั–ะฝะบัƒ!

+>

3. ะฏะบั‰ะพ ัะบั€ะธะฟั‚ ะฝะต ะฒะธะทะฝะฐั‡ะฐั” ะดะพัั‚ัƒะฟะฝั– ะบะพะปัŒะพั€ะธ, ะฝะฐั‚ะธัะฝั–ั‚ัŒ ะบะฝะพะฟะบัƒ ยซPaintยป, ั‰ะพะฑ ะฒะพะฝะธ ะทโ€™ัะฒะธะปะธัั โ€” ะปะธัˆะต ั‚ะพะดั– ะทะฐะฟัƒัะบะฐะนั‚ะต Auto-Imager.

+ +
+ +### โœจโ”ƒะœะพะถะปะธะฒะพัั‚ั– + +- [x] ะะฒั‚ะพะผะฐั‚ะธั‡ะฝะต ั„ะฐั€ะผะปะตะฝะฝั ะบั€ะฐะฟะตะปัŒ/ั€ั–ะฒะฝั–ะฒ +- [x] ะŸั€ะพะฟัƒัะบ ะฟั–ะบัะตะปั–ะฒ ะท ะฟั€ะฐะฒะธะปัŒะฝะธะผ ะบะพะปัŒะพั€ะพะผ +- [x] ะžะฒะตั€ะปะตะน (ะฑั–ะปัŒัˆะต ะฝะต ะฟะพั‚ั€ั–ะฑะตะฝ bluemarble) +- [x] ะœะฐะปัŽะฒะฐะฝะฝั ะฟะพ ะบั–ะปัŒะบะพั… ั‚ะฐะนะปะฐั… +- [x] ะะฒั‚ะพ-ั€ะพะทะฒโ€™ัะทัƒะฒะฐั‡ ะบะฐะฟั‡ั– ะท ะณะตะฝะตั€ะฐั‚ะพั€ะพะผ ั‚ะพะบะตะฝั–ะฒ Turnstile +- [x] ะ ะพะทัˆะธั€ะตะฝะฐ ะพะฑั€ะพะฑะบะฐ ะบะพะปัŒะพั€ั–ะฒ +- [x] ะŸั–ะดั‚ั€ะธะผะบะฐ ะบั–ะปัŒะบะพั… ะผะพะฒ +- [x] ะ‘ะฐะณะฐั‚ะพะฐะบะฐัƒะฝั‚ะฝั–ัั‚ัŒ ั‡ะตั€ะตะท ั„ัƒะฝะบั†ั–ั— ะทะฑะตั€ะตะถะตะฝะฝั ั‚ะฐ ะทะฐะฒะฐะฝั‚ะฐะถะตะฝะฝั +- [x] ะŸั€ะฐั†ัŽั” ะฝะฐ ะผะพะฑั–ะปัŒะฝะธั… :P + + +--- + +--- + +

+ ะ”ะตะผะพะฝัั‚ั€ะฐั†ั–ั ัะบั€ะธะฟั‚ะฐ +

+ +--- + +### ๐Ÿ“‹โ”ƒะกะฟะธัะพะบ ะทะฐะฒะดะฐะฝัŒ + +- [x] ะ—ะฐะฟัƒัะบ AutoFarm +- [ ] ะžะฑโ€™ั”ะดะฝะฐะฝะฝั ะพะฑะพั… ัะบั€ะธะฟั‚ั–ะฒ ะฒ ะพะดะธะฝ +- [ ] ะŸะตั€ะตะผะธะบะฐั‡ ะฐะบะฐัƒะฝั‚ั–ะฒ (ัƒ ั€ะพะทั€ะพะฑั†ั–) +- [ ] ะกะธัั‚ะตะผะฐ ั‡ะตั€ะณะธ ะฐะบะฐัƒะฝั‚ั–ะฒ +- [x] ะ”ะพะดะฐั‚ะธ ัะธัั‚ะตะผัƒ ะพะฒะตั€ะปะตัŽ +- [x] ะ”ะพะดะฐั‚ะธ ะฟั–ะดั‚ั€ะธะผะบัƒ ะบั€ะพั-ั‚ะฐะนะปั–ะฒ +- [x] ะ”ะพะดะฐั‚ะธ ะฐะฒั‚ะพ-ั€ะพะทะฒโ€™ัะทัƒะฒะฐั‡ ะบะฐะฟั‡ั– +- [x] ะ”ะพะดะฐั‚ะธ ะฟั€ะพะฟัƒัะบ ะฟั€ะฐะฒะธะปัŒะฝะธั… ะฟั–ะบัะตะปั–ะฒ +- [ ] ะ’ะธะฝะตัั‚ะธ CSS ะพะบั€ะตะผะพ + +--- + +- ะšะพะฝั‚ั€ะธะฑโ€™ัŽั‚ะพั€ะธ: + Contributors + + +

+ +

From dc010bd85a13e9be806dc2c3df6f29a8b180bd07 Mon Sep 17 00:00:00 2001 From: Yaroslav <63865083+dnrovs@users.noreply.github.com> Date: Tue, 26 Aug 2025 19:34:31 +0300 Subject: [PATCH 5/7] =?UTF-8?q?=F0=9F=8C=90=E2=94=83Added=20Ukrainian=20lo?= =?UTF-8?q?cale=20and=20changed=20language=20detection=20function?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Auto-Image.js | 117 +++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 92 insertions(+), 25 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index b4ea565..c9f4c29 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -212,13 +212,19 @@ } const loadLanguagePreference = () => { - try { - const saved = localStorage.getItem("wplace_language") - if (saved && TEXT[saved]) { - state.language = saved - } - } catch (e) { - console.warn("Could not load language preference:", e) + const savedLanguage = localStorage.getItem("wplace_language") + + const browserLocale = navigator.language + const browserLanguage = browserLocale.split("-")[0] + + if (TEXT[savedLanguage]) { // trying to load saved language + state.language = savedLanguage + } else if (TEXT[browserLocale]) { // trying to load full locale match (e.g. "zh-CN", "zh-TW" etc) + state.language = browserLocale + localStorage.setItem("wplace_language", browserLocale) + } else if (TEXT[browserLanguage]) { // trying to load base language match (e.g. "en" for "en-US" or "en-GB" etc) + state.language = browserLanguage + localStorage.setItem("wplace_language", browserLanguage) } } @@ -734,7 +740,7 @@ automation: "Otomasyon", noChargesThreshold: "โŒ› Haklarฤฑn {threshold} seviyesine ulaลŸmasฤฑ bekleniyor. ลžu anda {current}. Sonraki {time} iรงinde...", }, - zh: { + "zh-CN": { title: "WPlace ่‡ชๅŠจๅ›พๅƒ", toggleOverlay: "ๅˆ‡ๆข่ฆ†็›–ๅฑ‚", scanColors: "ๆ‰ซๆ้ขœ่‰ฒ", @@ -810,7 +816,7 @@ automation: "่‡ชๅŠจๅŒ–", noChargesThreshold: "โŒ› ็ญ‰ๅพ…ๆฌกๆ•ฐ่พพๅˆฐ {threshold}ใ€‚ๅฝ“ๅ‰ {current}ใ€‚ไธ‹ๆฌกๅœจ {time}...", }, - "zh-tw": { + "zh-TW": { title: "WPlace ่‡ชๅ‹•ๅœ–ๅƒ", toggleOverlay: "ๅˆ‡ๆ›่ฆ†่“‹ๅฑค", scanColors: "ๆŽƒๆ้ก่‰ฒ", @@ -1038,6 +1044,80 @@ automation: "์ž๋™ํ™”", noChargesThreshold: "โŒ› ํšŸ์ˆ˜๊ฐ€ {threshold} ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ์ค‘. ํ˜„์žฌ {current}. ๋‹ค์Œ {time} ํ›„...", }, + uk: { + 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: "โœ… ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ัƒัะฟั–ัˆะฝะพ ะทะฑะตั€ะตะถะตะฝะพ!", + cooldownSettings: "ะะฐะปะฐัˆั‚ัƒะฒะฐะฝะฝั ะฒั–ะดะฝะพะฒะปะตะฝะฝั", + waitCharges: "ะžั‡ั–ะบัƒะฒะฐั‚ะธ, ะดะพะบะธ ะบั–ะปัŒะบั–ัั‚ัŒ ะทะฐั€ัะดั–ะฒ ะดะพััะณะฝะต", + captchaSolving: "๐Ÿ”‘ ะ“ะตะฝะตั€ะฐั†ั–ั ั‚ะพะบะตะฝะฐ Turnstile...", + captchaFailed: "โŒ ะะต ะฒะดะฐะปะพัั ะทะณะตะฝะตั€ัƒะฒะฐั‚ะธ ั‚ะพะบะตะฝ Turnstile. ะ’ะธะบะพั€ะธัั‚ะพะฒัƒัŽ ะทะฐะฟะฐัะฝะธะน ะผะตั‚ะพะด...", + automation: "ะะฒั‚ะพะผะฐั‚ะธะทะฐั†ั–ั", + noChargesThreshold: "โŒ› ะžั‡ั–ะบัƒะฒะฐะฝะฝั, ะดะพะบะธ ะทะฐั€ัะดะธ ะดะพััะณะฝัƒั‚ัŒ {threshold}. ะ—ะฐั€ะฐะท {current}. ะะฐัั‚ัƒะฟะฝะต ั‡ะตั€ะตะท {time}...", + }, } // GLOBAL STATE @@ -1620,18 +1700,6 @@ } }); - async function detectLanguage() { - try { - const response = await fetch("https://backend.wplace.live/me", { - credentials: "include", - }) - const data = await response.json() - state.language = data.language === "pt" ? "pt" : "en" - } catch { - state.language = navigator.language.startsWith("pt") ? "pt" : "en" - } - } - // UTILITY FUNCTIONS const Utils = { sleep: (ms) => new Promise((r) => setTimeout(r, ms)), @@ -3261,8 +3329,6 @@ async function createUI() { - await detectLanguage() - const existingContainer = document.getElementById("wplace-image-bot-container") const existingStats = document.getElementById("wplace-stats-container") const existingSettings = document.getElementById("wplace-settings-container") @@ -4989,12 +5055,13 @@ + - - + + From 67c78c5d8a8dbd17c191a1107ec0ecd942dbd80e Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Wed, 27 Aug 2025 15:10:31 +0800 Subject: [PATCH 6/7] =?UTF-8?q?Revert=20"feat:=20Add=20Spanish=20language?= =?UTF-8?q?=20support=20-=20Added=20ES.md=20Spanish=20README=20trans?= =?UTF-8?q?=E2=80=A6"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- Auto-Image.js | 75 ------------------------------ ES.md | 126 -------------------------------------------------- 2 files changed, 201 deletions(-) delete mode 100644 ES.md diff --git a/Auto-Image.js b/Auto-Image.js index f1d9da1..b4ea565 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -1038,80 +1038,6 @@ automation: "์ž๋™ํ™”", noChargesThreshold: "โŒ› ํšŸ์ˆ˜๊ฐ€ {threshold} ์— ๋„๋‹ฌํ•  ๋•Œ๊นŒ์ง€ ๋Œ€๊ธฐ ์ค‘. ํ˜„์žฌ {current}. ๋‹ค์Œ {time} ํ›„...", }, - es: { - title: "WPlace Auto-Imagen", - toggleOverlay: "Alternar Superposiciรณn", - scanColors: "Escanear Colores", - uploadImage: "Subir Imagen", - resizeImage: "Redimensionar Imagen", - selectPosition: "Seleccionar Posiciรณn", - startPainting: "Iniciar Pintura", - stopPainting: "Detener Pintura", - checkingColors: "๐Ÿ” Verificando colores disponibles...", - noColorsFound: "โŒ ยกAbre la paleta de colores en el sitio y vuelve a intentar!", - colorsFound: "โœ… {count} colores disponibles encontrados. 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 a que pintes el pรญxel de referencia...", - positionSet: "โœ… ยกPosiciรณn establecida exitosamente!", - positionTimeout: "โŒ Tiempo agotado para seleccionar posiciรณn", - startPaintingMsg: "๐ŸŽจ Iniciando pintura...", - paintingProgress: "๐Ÿงฑ Progreso: {painted}/{total} pรญxeles...", - noCharges: "โŒ› Sin cargas. Esperando {time}...", - paintingStopped: "โน๏ธ Pintura detenida por el usuario", - paintingComplete: "โœ… ยกPintura completa! {count} pรญxeles pintados.", - paintingError: "โŒ Error durante la pintura", - missingRequirements: "โŒ Carga una imagen y selecciona una posiciรณn primero", - 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 tokens Turnstile...", - tokenReady: "โœ… Generador de tokens listo - ยกya puedes empezar a pintar!", - tokenRetryLater: "โš ๏ธ El generador de tokens volverรก a intentar cuando sea necesario", - resizeSuccess: "โœ… Imagen redimensionada a {width}x{height}", - paintingPaused: "โธ๏ธ Pintura pausada en la posiciรณn X: {x}, Y: {y}", - captchaNeeded: "โ— Fallรณ la generaciรณn de token. Por favor intenta 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 exitosamente", - fileSaved: "โœ… Progreso guardado en archivo exitosamente", - fileLoaded: "โœ… Progreso cargado desde archivo exitosamente", - 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 archivo", - invalidFileFormat: "โŒ Formato de archivo invรกlido", - paintingSpeed: "Velocidad de Pintura", - pixelsPerSecond: "pรญxeles/segundo", - speedSetting: "Velocidad: {speed} pรญxeles/seg", - settings: "Configuraciรณn", - botSettings: "Configuraciรณn del Bot", - close: "Cerrar", - language: "Idioma", - themeSettings: "Configuraciรณn de Tema", - themeSettingsDesc: "Elige tu tema de colores preferido para la interfaz.", - languageSelectDesc: "Selecciona tu idioma preferido. Los cambios tendrรกn efecto inmediatamente.", - autoCaptcha: "Solucionador Automรกtico de CAPTCHA (Turnstile)", - autoCaptchaDesc: "Genera automรกticamente tokens Turnstile usando el generador integrado. Se recurre a la automatizaciรณn del navegador si es necesario.", - applySettings: "Aplicar Configuraciรณn", - settingsSaved: "โœ… ยกConfiguraciรณn guardada exitosamente!", - cooldownSettings: "Configuraciรณn de Enfriamiento", - waitCharges: "Esperar hasta que las cargas alcancen", - captchaSolving: "๐Ÿ”‘ Generando token Turnstile...", - captchaFailed: "โŒ Fallรณ la generaciรณn de token Turnstile. Probando mรฉtodo alternativo...", - automation: "Automatizaciรณn", - noChargesThreshold: "โŒ› Esperando que las cargas alcancen {threshold}. Actualmente {current}. Siguiente en {time}...", - }, } // GLOBAL STATE @@ -5064,7 +4990,6 @@ - diff --git a/ES.md b/ES.md deleted file mode 100644 index a4691b9..0000000 --- a/ES.md +++ /dev/null @@ -1,126 +0,0 @@ -

- image -

- -

WPlace AutoBOT

-

-Este proyecto es un fork de https://github.com/DarkModde/WPlace-AutoBOT -

-

- Considera unirte a nuestro discord: https://discord.gg/knkNRYyQcm -

-

- ยกScripts prรกcticos con menรบs intuitivos para hacer tu vida mรกs fรกcil en WPlace!
- Perfectos para quienes quieren subir de nivel automรกticamente o construir pixel arts masivos โ€” sin perder tiempo. - - Advertencia: Este es un script puramente educativo para demostrar lo que se puede hacer en wplace.live, no somos responsables de lo que suceda si usas el script. -

- -
- -

- Si te preguntas, ยกsรญ! Estos scripts fueron desarrollados por un equipo conformado por brasileรฑos, vietnamitas e indonesios. Jajajaj! -

- Brazil -   - Vietnam -   - Indonesia -

-

- ---- - -

๐Ÿš€โ”ƒCรณmo usar los scripts:

- -

- Es sรบper fรกcil: copia uno de los cรณdigos de abajo, pรฉgalo en la barra de marcadores de tu navegador,
- y ejecรบtalo mientras estรฉs en wplace.live. -

- -
- -### ๐ŸŽฏโ”ƒAuto-Farm -#### EL AUTOFARM USA CARGAS PARA CONSEGUIR NIVELES, NO DIBUJA UNA IMAGEN POR TI. POR FAVOR USA AUTO-IMAGE PARA ESO -```js -javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Farm.js").then(t=>t.text()).then(eval); -``` - -### ๐Ÿ–ผ๏ธโ”ƒAuto-Image - -```js -javascript:fetch("https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/Auto-Image.js").then(t=>t.text()).then(eval); -``` - -
-

๐Ÿ“–โ”ƒTutoriales

- ---- - -![Parte 1](https://i.imgur.com/yneG5if.png) - ---- - -![Parte 2](https://i.imgur.com/ZRpU0wZ.png) - ---- - -![Parte 3](https://i.imgur.com/lfjfcEw.png) - -
- - -
- -> [!IMPORTANT] ->

1. Ninguno de los scripts funcionarรก si cierras la pestaรฑa del navegador. Debes mantener la pestaรฑa de WPlace abierta, incluso si estรก en segundo plano.

->

2. Nunca ejecutes ambos scripts en la misma pรกgina โ€” esto puede causar errores en tu WPlace. Si sucede, ยกsolo refresca la pรกgina!

->

3. Si el script no detecta los colores disponibles, solo haz clic en el botรณn "Pintar" para mostrarlos โ€” solo entonces inicia el Auto-Image.

- -
- -### โœจโ”ƒCaracterรญsticas - -- [x] Farmea automรกticamente gotas/niveles -- [x] Omisiรณn correcta de pรญxeles de color -- [x] Superposiciรณn (ya no necesitas bluemarble) -- [x] Pintar en mรบltiples tiles -- [x] Solucionador de AutoCaptcha con generador de tokens Turnstile -- [x] Procesamiento avanzado de colores -- [x] Soporte multiidioma -- [x] Mรบltiples cuentas a travรฉs de funciรณn de guardar y cargar -- [x] Funciona en mรณvil :P - - ---- - - ---- - -

- Demostraciรณn del Script -

- ---- - -### ๐Ÿ“‹โ”ƒPor hacer - -- [x] Hacer que AutoFarm funcione -- [ ] Fusionar ambos scripts en uno -- [ ] Cambiador de mรบltiples cuentas (actualmente en desarrollo) -- [ ] Sistema de cola de mรบltiples cuentas -- [x] Agregar sistema de superposiciรณn -- [x] Agregar soporte de tiles cruzados -- [x] Agregar solucionador de AutoCaptcha -- [x] Agregar omisiรณn correcta de pรญxeles de color -- [ ] Separar CSS - ---- - -- Contribuidores: - Contribuidores - - -

- -

From af752829b86d95bf83150b23fd457ff996b8b660 Mon Sep 17 00:00:00 2001 From: MiraclePatrickLumowa Date: Wed, 27 Aug 2025 15:56:26 +0800 Subject: [PATCH 7/7] Revert "Decoupled css" --- Auto-Image.js | 1891 ++++++++++++++++++++++++++++++------ NL.md | 2 +- README.md | 2 +- RU.md | 2 +- auto-image-styles.css | 1952 -------------------------------------- themes/classic-light.css | 361 ------- themes/classic.css | 646 ------------- themes/neon.css | 357 ------- 8 files changed, 1604 insertions(+), 3609 deletions(-) delete mode 100644 auto-image-styles.css delete mode 100644 themes/classic-light.css delete mode 100644 themes/classic.css delete mode 100644 themes/neon.css diff --git a/Auto-Image.js b/Auto-Image.js index c30b847..b4ea565 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -146,27 +146,6 @@ pixelBlink: false, }, }, - "Classic Light": { - primary: "#ffffff", - secondary: "#f8f9fa", - accent: "#e9ecef", - text: "#212529", - highlight: "#6f42c1", - success: "#28a745", - error: "#dc3545", - warning: "#ffc107", - fontFamily: "'Segoe UI', Roboto, sans-serif", - borderRadius: "12px", - borderStyle: "solid", - borderWidth: "1px", - boxShadow: "0 8px 32px rgba(0,0,0,0.15), 0 0 0 1px rgba(0,0,0,0.08)", - backdropFilter: "blur(10px)", - animations: { - glow: false, - scanline: false, - pixelBlink: false, - }, - }, "Neon Retro": { primary: "#1a1a2e", secondary: "#16213e", @@ -198,56 +177,20 @@ const getCurrentTheme = () => CONFIG.THEMES[CONFIG.currentTheme] const switchTheme = (themeName) => { - if (CONFIG.THEMES[themeName]) { - CONFIG.currentTheme = themeName - saveThemePreference() + if (CONFIG.THEMES[themeName]) { + CONFIG.currentTheme = themeName + saveThemePreference() - // APPLY THEME VARS/CLASS (new) - applyTheme() + // Remove existing theme styles + const existingStyle = document.querySelector('style[data-wplace-theme="true"]') + if (existingStyle) { + existingStyle.remove() + } - // Recreate UI (kept for now) - createUI() + // Recreate UI with new theme (cleanup is handled in createUI) + createUI() + } } -} - -// Add this helper (place it after getCurrentTheme/switchTheme definitions) -function applyTheme() { - const theme = getCurrentTheme(); - // Toggle theme class on documentElement so CSS vars cascade to our UI - document.documentElement.classList.remove('wplace-theme--classic', 'wplace-theme--classic-light', 'wplace-theme--neon'); - - let themeClass = 'wplace-theme--classic'; // default - if (CONFIG.currentTheme === 'Neon Retro') { - themeClass = 'wplace-theme--neon'; - } else if (CONFIG.currentTheme === 'Classic Light') { - themeClass = 'wplace-theme--classic-light'; - } - - document.documentElement.classList.add(themeClass); - - // Also set CSS variables explicitly in case you want runtime overrides - const root = document.documentElement; - const setVar = (k, v) => { try { root.style.setProperty(k, v); } catch {} }; - - setVar('--wplace-primary', theme.primary); - setVar('--wplace-secondary', theme.secondary); - setVar('--wplace-accent', theme.accent); - setVar('--wplace-text', theme.text); - setVar('--wplace-highlight', theme.highlight); - setVar('--wplace-success', theme.success); - setVar('--wplace-error', theme.error); - setVar('--wplace-warning', theme.warning); - - // Typography + look - setVar('--wplace-font', theme.fontFamily || "'Segoe UI', Roboto, sans-serif"); - setVar('--wplace-radius', ('' + (theme.borderRadius || '12px'))); - setVar('--wplace-border-style', (('' + (theme.borderStyle || 'solid')))); - setVar('--wplace-border-width', (('' + (theme.borderWidth || '1px')))); - setVar('--wplace-backdrop', (('' + (theme.backdropFilter || 'blur(10px)')))); - setVar('--wplace-border-color', 'rgba(255,255,255,0.1)'); -} - - const saveThemePreference = () => { try { @@ -1757,7 +1700,18 @@ function applyTheme() { } this._turnstileContainer = document.createElement('div'); - this._turnstileContainer.className = 'wplace-turnstile-hidden'; + this._turnstileContainer.style.cssText = ` + position: fixed !important; + left: -99999px !important; + top: -99999px !important; + width: 1px !important; + height: 1px !important; + pointer-events: none !important; + opacity: 0 !important; + visibility: hidden !important; + z-index: -99999 !important; + overflow: hidden !important; + `; this._turnstileContainer.setAttribute('aria-hidden', 'true'); this._turnstileContainer.id = 'turnstile-widget-container'; document.body.appendChild(this._turnstileContainer); @@ -1773,19 +1727,35 @@ function applyTheme() { const overlay = document.createElement('div'); overlay.id = 'turnstile-overlay-container'; - overlay.className = 'wplace-turnstile-overlay wplace-overlay-hidden'; + overlay.style.cssText = ` + position: fixed !important; + bottom: 20px !important; + right: 20px !important; + z-index: 99999 !important; + background: rgba(0,0,0,0.9) !important; + border-radius: 12px !important; + padding: 20px !important; + box-shadow: 0 8px 32px rgba(0,0,0,0.4) !important; + backdrop-filter: blur(10px) !important; + border: 1px solid rgba(255,255,255,0.2) !important; + color: white !important; + font-family: 'Segoe UI', sans-serif !important; + display: none !important; + max-width: 350px !important; + min-width: 300px !important; + `; const title = document.createElement('div'); title.textContent = 'Cloudflare Turnstile โ€” please complete the check if shown'; - title.className = 'wplace-turnstile-title'; + title.style.cssText = 'font: 600 12px/1.3 "Segoe UI",sans-serif; margin-bottom: 8px; opacity: 0.9;'; const host = document.createElement('div'); host.id = 'turnstile-overlay-host'; - host.className = 'wplace-turnstile-host'; + host.style.cssText = 'width: 100%; min-height: 70px;'; const hideBtn = document.createElement('button'); hideBtn.textContent = 'Hide'; - hideBtn.className = 'wplace-turnstile-hide-btn'; + hideBtn.style.cssText = 'position:absolute; top:6px; right:6px; font-size:11px; background:transparent; color:#fff; border:1px solid rgba(255,255,255,0.2); border-radius:6px; padding:2px 6px; cursor:pointer;'; hideBtn.addEventListener('click', () => overlay.remove()); overlay.appendChild(title); @@ -1900,7 +1870,6 @@ function applyTheme() { } const overlay = this.ensureTurnstileOverlayContainer(); - overlay.classList.remove('wplace-overlay-hidden'); overlay.style.display = 'block'; const host = overlay.querySelector('#turnstile-overlay-host'); @@ -1909,7 +1878,6 @@ function applyTheme() { // 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 @@ -1921,8 +1889,7 @@ function applyTheme() { theme: 'light', callback: (token) => { clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; + overlay.style.display = 'none'; console.log('โœ… Interactive Turnstile completed successfully'); if (typeof token === 'string' && token.length > 20) { @@ -1934,8 +1901,7 @@ function applyTheme() { }, 'error-callback': (error) => { clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); - overlay.style.display = 'none'; + overlay.style.display = 'none'; console.warn('โŒ Interactive Turnstile error:', error); resolve(null); }, @@ -1946,7 +1912,6 @@ function applyTheme() { if (!widgetId) { clearTimeout(timeout); - overlay.classList.add('wplace-overlay-hidden'); overlay.style.display = 'none'; console.warn('โŒ Failed to create interactive Turnstile widget'); resolve(null); @@ -2119,7 +2084,40 @@ function applyTheme() { showAlert: (message, type = "info") => { const alertDiv = document.createElement("div") - alertDiv.className = `wplace-alert-base wplace-alert-${type}` + alertDiv.style.cssText = ` + position: fixed; + top: 20px; + left: 50%; + transform: translateX(-50%); + padding: 12px 20px; + border-radius: 8px; + color: white; + font-weight: 600; + z-index: 10001; + max-width: 400px; + text-align: center; + box-shadow: 0 4px 12px rgba(0,0,0,0.3); + animation: slideDown 0.3s ease-out; + font-family: 'Segoe UI', sans-serif; + ` + + const colors = { + info: "background: linear-gradient(135deg, #3498db, #2980b9);", + success: "background: linear-gradient(135deg, #27ae60, #229954);", + warning: "background: linear-gradient(135deg, #f39c12, #e67e22);", + error: "background: linear-gradient(135deg, #e74c3c, #c0392b);", + } + + alertDiv.style.cssText += colors[type] || colors.info + + const style = document.createElement("style") + style.textContent = ` + @keyframes slideDown { + from { transform: translateX(-50%) translateY(-20px); opacity: 0; } + to { transform: translateX(-50%) translateY(0); opacity: 1; } + } + ` + document.head.appendChild(style) alertDiv.textContent = message document.body.appendChild(alertDiv) @@ -2128,6 +2126,7 @@ function applyTheme() { alertDiv.style.animation = "slideDown 0.3s ease-out reverse" setTimeout(() => { document.body.removeChild(alertDiv) + document.head.removeChild(style) }, 300) }, 4000) }, @@ -3280,7 +3279,6 @@ function applyTheme() { loadLanguagePreference() const theme = getCurrentTheme() - applyTheme() // <- new: set CSS vars and theme class before building UI const fontAwesome = document.createElement("link") fontAwesome.rel = "stylesheet" @@ -3294,12 +3292,1155 @@ function applyTheme() { document.head.appendChild(googleFonts) } - // Link external CSS files - const cssLink = document.createElement('link'); - cssLink.rel = 'stylesheet'; - cssLink.href = 'https://staninna.github.io/WPlace-AutoBOT/auto-image-styles.css'; // TODO: Before merge change to https://raw.githubusercontent.com/Wplace-AutoBot/WPlace-AutoBOT/refs/heads/main/auto-image-styles.css - cssLink.setAttribute('data-wplace-theme', 'true'); - document.head.appendChild(cssLink); + const style = document.createElement("style") + style.setAttribute("data-wplace-theme", "true") + + style.textContent = ` + ${theme.animations.glow + ? ` + @keyframes neonGlow { + 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; + } + }` + : "" + } + + ${theme.animations.pixelBlink + ? ` + @keyframes pixelBlink { + 0%, 50% { opacity: 1; } + 51%, 100% { opacity: 0.7; } + }` + : "" + } + + ${theme.animations.scanline + ? ` + @keyframes scanline { + 0% { transform: translateY(-100%); } + 100% { transform: translateY(400px); } + }` + : "" + } + + @keyframes pulse { + 0% { box-shadow: 0 0 0 0 rgba(0, 255, 0, 0.7); } + 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 slideIn { + from { transform: translateY(-10px); opacity: 0; } + to { transform: translateY(0); opacity: 1; } + } + @keyframes shimmer { + 0% { transform: translateX(-100%); } + 100% { transform: translateX(100%); } + } + + #wplace-image-bot-container { + position: fixed; + top: 20px; + left: 20px; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; + max-height: calc(100vh - 40px); + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` + : theme.primary + }; + border: ${theme.borderWidth} ${theme.borderStyle} ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; + border-radius: ${theme.borderRadius}; + padding: 0; + box-shadow: ${theme.boxShadow}; + z-index: 9998; + font-family: ${theme.fontFamily}; + color: ${theme.text}; + animation: slideIn 0.4s ease-out; + overflow-y: auto; /* Allow scrolling for main panel */ + overflow-x: hidden; + ${theme.backdropFilter ? `backdrop-filter: ${theme.backdropFilter};` : ""} + transition: all 0.3s ease; + user-select: none; + ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} + } + + ${theme.animations.scanline + ? ` + #wplace-image-bot-container::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + height: 2px; + background: linear-gradient(90deg, transparent, ${theme.neon}, transparent); + animation: scanline 3s linear infinite; + z-index: 1; + pointer-events: none; + }` + : "" + } + + ${CONFIG.currentTheme === "Neon Retro" + ? ` + #wplace-image-bot-container::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + repeating-linear-gradient( + 0deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.03) 2px, + rgba(0, 255, 65, 0.03) 4px + ); + pointer-events: none; + z-index: 1; + }` + : "" + } + + #wplace-image-bot-container.wplace-dragging { + transition: none; + box-shadow: 0 12px 40px rgba(0,0,0,0.8), 0 0 0 2px rgba(255,255,255,0.2); + transform: scale(1.02); + z-index: 9999; + } + #wplace-image-bot-container.wplace-minimized { + width: 200px; + height: auto; + overflow: hidden; + } + #wplace-image-bot-container.wplace-compact { + width: 240px; + } + + /* Stats Container */ + #wplace-stats-container { + position: fixed; + top: 20px; + left: 330px; + width: ${CONFIG.currentTheme === "Neon Retro" ? "280px" : "280px"}; + max-height: calc(100vh - 40px); + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.primary} 0%, #1a1a1a 100%)` + : theme.primary + }; + border: ${theme.borderWidth} ${theme.borderStyle} ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; + border-radius: ${theme.borderRadius}; + padding: 0; + box-shadow: ${theme.boxShadow}; + z-index: 9997; + font-family: ${theme.fontFamily}; + color: ${theme.text}; + animation: slideIn 0.4s ease-out; + overflow-y: auto; /* Make stats panel scrollable */ + ${theme.backdropFilter ? `backdrop-filter: ${theme.backdropFilter};` : ""} + transition: all 0.3s ease; + user-select: none; + ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} + } + + /* FIX: Disable transition during drag to prevent lag */ + #wplace-stats-container.wplace-dragging { + transition: none; + } + + .wplace-header { + padding: ${CONFIG.currentTheme === "Neon Retro" ? "8px 12px" : "8px 12px"}; + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.secondary} 0%, #2a2a2a 100%)` + : theme.secondary + }; + color: ${theme.highlight}; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "11px" : "13px"}; + font-weight: ${CONFIG.currentTheme === "Neon Retro" ? "normal" : "700"}; + display: flex; + justify-content: space-between; + align-items: center; + cursor: move; + user-select: none; + border-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "2px" : "1px"} solid ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.1)" : theme.text}; + ${CONFIG.currentTheme === "Classic Autobot" ? "text-shadow: 0 1px 2px rgba(0,0,0,0.5);" : "text-transform: uppercase; letter-spacing: 1px;"} + transition: background 0.2s ease; + position: relative; + z-index: 2; + ${theme.animations.glow ? "animation: neonGlow 2s ease-in-out infinite alternate;" : ""} + } + + .wplace-header-title { + display: flex; + align-items: center; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; + } + + .wplace-header-controls { + display: flex; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "6px" : "6px"}; + } + + .wplace-header-btn { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.1)" : theme.accent}; + border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "none"}; + color: ${theme.text}; + cursor: pointer; + border-radius: ${CONFIG.currentTheme === "Classic Autobot" ? "4px" : "0"}; + width: ${CONFIG.currentTheme === "Classic Autobot" ? "18px" : "auto"}; + height: ${CONFIG.currentTheme === "Classic Autobot" ? "18px" : "auto"}; + padding: ${CONFIG.currentTheme === "Neon Retro" ? "4px 6px" : "0"}; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "10px"}; + display: flex; + align-items: center; + justify-content: center; + transition: all 0.2s; + font-family: ${theme.fontFamily}; + ${CONFIG.currentTheme === "Neon Retro" ? "image-rendering: pixelated;" : ""} + } + .wplace-header-btn:hover { + background: ${CONFIG.currentTheme === "Classic Autobot" ? theme.accent : theme.text}; + color: ${CONFIG.currentTheme === "Classic Autobot" ? theme.text : theme.primary}; + transform: ${CONFIG.currentTheme === "Classic Autobot" ? "scale(1.1)" : "none"}; + ${CONFIG.currentTheme === "Neon Retro" ? `box-shadow: 0 0 10px ${theme.text};` : ""} + } + + .wplace-content { + padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; + display: block; + position: relative; + z-index: 2; + } + .wplace-content.wplace-hidden { + display: none; + } + + .wplace-status-section { + margin-bottom: 12px; + padding: 8px; + background: rgba(255,255,255,0.03); + border-radius: ${theme.borderRadius}; + border: 1px solid rgba(255,255,255,0.1); + } + + .wplace-section { + margin-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "12px"}; + padding: 12px; + background: rgba(255,255,255,0.03); + border-radius: ${theme.borderRadius}; + border: 1px solid rgba(255,255,255,0.1); + } + + .wplace-section-title { + font-size: 11px; + font-weight: 600; + margin-bottom: 8px; + color: ${theme.highlight}; + display: flex; + align-items: center; + gap: 6px; + text-transform: uppercase; + letter-spacing: 0.5px; + } + + .wplace-controls { + display: flex; + flex-direction: column; + gap: 8px; + } + .wplace-row { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 8px; + } + .wplace-row.single { + grid-template-columns: 1fr; + } + + .wplace-btn { + padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px 8px" : "8px 12px"}; + border: ${CONFIG.currentTheme === "Neon Retro" ? "2px solid" : "none"}; + border-radius: ${theme.borderRadius}; + font-weight: ${CONFIG.currentTheme === "Neon Retro" ? "normal" : "500"}; + cursor: pointer; + display: flex; + align-items: center; + justify-content: center; + gap: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "6px"}; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; + transition: all 0.3s ease; + position: relative; + overflow: hidden; + font-family: ${theme.fontFamily}; + ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px; image-rendering: pixelated;" : ""} + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.accent} 0%, #4a4a4a 100%)` + : theme.accent + }; + ${CONFIG.currentTheme === "Classic Autobot" ? "border: 1px solid rgba(255,255,255,0.1);" : ""} + } + + ${CONFIG.currentTheme === "Classic Autobot" + ? ` + .wplace-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.1), transparent); + transition: left 0.5s ease; + } + .wplace-btn:hover:not(:disabled)::before { + left: 100%; + }` + : ` + .wplace-btn::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 100%; + background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.2), transparent); + transition: left 0.5s; + } + .wplace-btn:hover::before { + left: 100%; + }` + } + + .wplace-btn:hover:not(:disabled) { + transform: ${CONFIG.currentTheme === "Classic Autobot" ? "translateY(-1px)" : "none"}; + box-shadow: ${CONFIG.currentTheme === "Classic Autobot" ? "0 4px 12px rgba(0,0,0,0.4)" : "0 0 15px currentColor" + }; + ${theme.animations.pixelBlink ? "animation: pixelBlink 0.5s infinite;" : ""} + } + .wplace-btn:active:not(:disabled) { + transform: translateY(0); + } + + .wplace-btn-primary { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.accent} 0%, #6a5acd 100%)` + : theme.accent + }; + color: ${theme.text}; + ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.text};` : ""} + } + .wplace-btn-upload { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.secondary} 0%, #4a4a4a 100%)` + : theme.purple + }; + color: ${theme.text}; + ${CONFIG.currentTheme === "Classic Autobot" + ? `border: 1px dashed ${theme.highlight};` + : `border-color: ${theme.text}; border-style: dashed;` + } + } + .wplace-btn-start { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.success} 0%, #228b22 100%)` + : theme.success + }; + color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; + ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.success};` : ""} + } + .wplace-btn-stop { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.error} 0%, #dc143c 100%)` + : theme.error + }; + color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.text}; + ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.error};` : ""} + } + .wplace-btn-select { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` + : theme.highlight + }; + color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; + ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.highlight};` : ""} + } + .wplace-btn-file { + background: ${CONFIG.currentTheme === "Classic Autobot" + ? "linear-gradient(135deg, #ff8c00 0%, #ff7f50 100%)" + : theme.warning + }; + color: ${CONFIG.currentTheme === "Classic Autobot" ? "white" : theme.primary}; + ${CONFIG.currentTheme === "Neon Retro" ? `border-color: ${theme.warning};` : ""} + } + .wplace-btn:disabled { + opacity: ${CONFIG.currentTheme === "Classic Autobot" ? "0.5" : "0.3"}; + cursor: not-allowed; + transform: none !important; + ${theme.animations.pixelBlink ? "animation: none !important;" : ""} + box-shadow: none !important; + } + .wplace-btn:disabled::before { + display: none; + } + + .wplace-btn-overlay.active { + background: linear-gradient(135deg, #29b6f6 0%, #8e2de2 100%); + box-shadow: 0 0 15px #8e2de2; + } + + .wplace-stats { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.03)" : theme.secondary}; + padding: ${CONFIG.currentTheme === "Neon Retro" ? "12px" : "8px"}; + border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "1px solid rgba(255,255,255,0.1)"}; + border-radius: ${theme.borderRadius}; + margin-bottom: ${CONFIG.currentTheme === "Neon Retro" ? "15px" : "8px"}; + ${CONFIG.currentTheme === "Neon Retro" ? "box-shadow: inset 0 0 10px rgba(0, 255, 65, 0.1);" : ""} + } + + .wplace-stat-item { + display: flex; + justify-content: space-between; + padding: ${CONFIG.currentTheme === "Neon Retro" ? "6px 0" : "4px 0"}; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; + border-bottom: 1px solid rgba(255,255,255,0.05); + ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} + } + .wplace-stat-item:last-child { + border-bottom: none; + } + .wplace-stat-label { + display: flex; + align-items: center; + gap: 6px; + opacity: 0.9; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "10px"}; + } + .wplace-stat-value { + font-weight: 600; + color: ${theme.highlight}; + } + + .wplace-colors-section { + margin-top: 10px; + padding-top: 8px; + border-top: 1px solid rgba(255,255,255,0.05); + } + + .wplace-stat-colors-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(16px, 1fr)); + gap: 4px; + margin-top: 8px; + padding: 4px; + background: rgba(0,0,0,0.2); + border-radius: 4px; + max-height: 80px; /* Limit height and allow scrolling */ + overflow-y: auto; + } + + .wplace-stat-color-swatch { + width: 16px; + height: 16px; + border-radius: 3px; + border: 1px solid rgba(255,255,255,0.1); + box-shadow: inset 0 0 2px rgba(0,0,0,0.5); + } + + .wplace-progress { + width: 100%; + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(0,0,0,0.3)" : theme.secondary}; + border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "1px solid rgba(255,255,255,0.1)"}; + border-radius: ${theme.borderRadius}; + margin: ${CONFIG.currentTheme === "Neon Retro" ? "10px 0" : "8px 0"}; + overflow: hidden; + height: ${CONFIG.currentTheme === "Neon Retro" ? "16px" : "6px"}; + position: relative; + } + + ${CONFIG.currentTheme === "Neon Retro" + ? ` + .wplace-progress::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: + repeating-linear-gradient( + 45deg, + transparent, + transparent 2px, + rgba(0, 255, 65, 0.1) 2px, + rgba(0, 255, 65, 0.1) 4px + ); + pointer-events: none; + }` + : "" + } + + .wplace-progress-bar { + height: ${CONFIG.currentTheme === "Neon Retro" ? "100%" : "6px"}; + background: ${CONFIG.currentTheme === "Classic Autobot" + ? `linear-gradient(135deg, ${theme.highlight} 0%, #9370db 100%)` + : `linear-gradient(90deg, ${theme.success}, ${theme.neon})` + }; + transition: width ${CONFIG.currentTheme === "Neon Retro" ? "0.3s" : "0.5s"} ease; + position: relative; + ${CONFIG.currentTheme === "Neon Retro" ? `box-shadow: 0 0 10px ${theme.success};` : ""} + } + + ${CONFIG.currentTheme === "Classic Autobot" + ? ` + .wplace-progress-bar::after { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + background: linear-gradient(90deg, transparent, rgba(255,255,255,0.3), transparent); + animation: shimmer 2s infinite; + }` + : ` + .wplace-progress-bar::after { + content: ''; + position: absolute; + top: 0; + right: 0; + width: 4px; + height: 100%; + background: ${theme.text}; + animation: pixelBlink 1s infinite; + }` + } + + .wplace-status { + padding: ${CONFIG.currentTheme === "Neon Retro" ? "10px" : "6px"}; + border: ${CONFIG.currentTheme === "Neon Retro" ? "2px solid" : "1px solid"}; + border-radius: ${theme.borderRadius}; + text-align: center; + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "11px"}; + ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} + position: relative; + overflow: hidden; + } + + .status-default { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255,255,255,0.1)" : theme.accent}; + border-color: ${theme.text}; + color: ${theme.text}; + } + .status-success { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(0, 255, 0, 0.1)" : theme.success}; + border-color: ${theme.success}; + color: ${CONFIG.currentTheme === "Classic Autobot" ? theme.success : theme.primary}; + box-shadow: 0 0 15px ${theme.success}; + } + .status-error { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255, 0, 0, 0.1)" : theme.error}; + border-color: ${theme.error}; + color: ${CONFIG.currentTheme === "Classic Autobot" ? theme.error : theme.text}; + box-shadow: 0 0 15px ${theme.error}; + ${theme.animations.pixelBlink ? "animation: pixelBlink 0.5s infinite;" : ""} + } + .status-warning { + background: ${CONFIG.currentTheme === "Classic Autobot" ? "rgba(255, 165, 0, 0.1)" : theme.warning}; + border-color: ${theme.warning}; + color: ${CONFIG.currentTheme === "Classic Autobot" ? "orange" : theme.primary}; + box-shadow: 0 0 15px ${theme.warning}; + } + + .resize-container { + display: none; + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: ${theme.primary}; + padding: 20px; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.text}; + border-radius: ${theme.borderRadius}; + z-index: 10000; + box-shadow: ${CONFIG.currentTheme === "Classic Autobot" ? "0 0 20px rgba(0,0,0,0.5)" : "0 0 30px rgba(0, 255, 65, 0.5)" + }; + width: 90%; + max-width: 700px; + max-height: 90%; + overflow: auto; + font-family: ${theme.fontFamily}; + } + + .resize-preview-wrapper { + display: flex; + justify-content: center; + align-items: center; + border: 1px solid ${theme.accent}; + background: rgba(0,0,0,0.2); + margin: 15px 0; + height: 300px; + overflow: hidden; + } + + .resize-canvas-stack { position: relative; transform-origin: center center; display: inline-block; } + .resize-base-canvas, .resize-mask-canvas { + position: absolute; left: 0; top: 0; + image-rendering: pixelated; + image-rendering: -moz-crisp-edges; + image-rendering: crisp-edges; + } + .resize-mask-canvas { pointer-events: auto; } + .resize-tools { display:flex; gap:8px; align-items:center; margin-top:8px; font-size:12px; } + .resize-tools button { padding:6px 10px; border-radius:6px; border:1px solid rgba(255,255,255,0.2); background: rgba(255,255,255,0.06); color:#fff; cursor:pointer; } + .wplace-btn.active, + .wplace-btn[aria-pressed="true"] { + background: ${theme.highlight} !important; + color: ${theme.primary} !important; + border-color: ${theme.text} !important; + box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2) !important; + } + .wplace-btn.active i, + .wplace-btn[aria-pressed="true"] i { filter: drop-shadow(0 0 3px ${theme.primary}); } + .mask-mode-group .wplace-btn.active, + .mask-mode-group .wplace-btn[aria-pressed="true"] { + background: ${theme.highlight}; + color: ${theme.primary}; + border-color: ${theme.text}; + box-shadow: 0 0 8px rgba(0,0,0,0.25) inset, 0 0 6px rgba(0,0,0,0.2); + } + + .resize-controls { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 15px; + align-items: center; + } + + .resize-controls label { + font-size: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "12px"}; + ${CONFIG.currentTheme === "Neon Retro" ? "text-transform: uppercase; letter-spacing: 1px;" : ""} + color: ${theme.text}; + } + + .resize-slider { + width: 100%; + height: ${CONFIG.currentTheme === "Neon Retro" ? "8px" : "4px"}; + background: ${CONFIG.currentTheme === "Classic Autobot" ? "#ccc" : theme.secondary}; + border: ${CONFIG.currentTheme === "Neon Retro" ? `2px solid ${theme.text}` : "none"}; + border-radius: ${theme.borderRadius}; + outline: none; + -webkit-appearance: none; + } + + ${CONFIG.currentTheme === "Neon Retro" + ? ` + .resize-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 16px; + height: 16px; + background: ${theme.highlight}; + border: 2px solid ${theme.text}; + border-radius: 0; + cursor: pointer; + box-shadow: 0 0 5px ${theme.highlight}; + } + + .resize-slider::-moz-range-thumb { + width: 16px; + height: 16px; + background: ${theme.highlight}; + border: 2px solid ${theme.text}; + border-radius: 0; + cursor: pointer; + box-shadow: 0 0 5px ${theme.highlight}; + }` + : "" + } + + .resize-zoom-controls { + grid-column: 1 / -1; + display: flex; + align-items: center; + gap: 10px; + margin-top: 15px; + } + + .resize-buttons { + display: flex; + gap: 10px; + justify-content: center; + margin-top: 20px; + } + + .resize-overlay { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + z-index: 9999; + display: none; + } + .wplace-color-grid { + display: grid; + grid-template-columns: repeat(auto-fill, minmax(60px, 1fr)); + gap: 10px; + padding-top: 8px; + max-height: 300px; + overflow-y: auto; + } + .wplace-color-item { + display: flex; + flex-direction: column; + align-items: center; + gap: 4px; + } + .wplace-color-item-name { + font-size: 9px; + color: #ccc; + text-align: center; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + width: 100%; + } + .wplace-color-swatch { + width: 22px; + height: 22px; + border: 1px solid rgba(255,255,255,0.2); + border-radius: 4px; + cursor: pointer; + transition: transform 0.1s ease, box-shadow 0.2s ease; + position: relative; + margin: 0 auto; + } + .wplace-color-swatch.unavailable { + border-color: #666; + border-style: dashed; + cursor: not-allowed; + } + .wplace-color-swatch:hover { + transform: scale(1.1); + z-index: 1; + } + .wplace-color-swatch:not(.active) { + opacity: 0.3; + filter: grayscale(80%); + } + .wplace-color-swatch.unavailable:not(.active) { + opacity: 0.2; + filter: grayscale(90%); + } + .wplace-color-swatch.active::after { + content: 'โœ”'; + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + color: white; + font-size: 12px; + font-weight: bold; + text-shadow: 0 0 3px black; + } + .wplace-color-divider { + border: none; + height: 1px; + background: rgba(255,255,255,0.1); + margin: 8px 0; + } + + .wplace-cooldown-control { + margin-top: 8px; + } + .wplace-cooldown-control label { + font-size: 11px; + margin-bottom: 4px; + display: block; + } + .wplace-slider-container { + display: flex; + align-items: center; + gap: 8px; + } + .wplace-slider { + flex: 1; + -webkit-appearance: none; + appearance: none; + height: 4px; + background: #444; + border-radius: 2px; + outline: none; + } + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 14px; + height: 14px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + } + + + ${CONFIG.currentTheme === "Neon Retro" + ? ` + input[type="checkbox"] { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + width: 16px; + height: 16px; + border: 2px solid ${theme.text}; + background: ${theme.secondary}; + margin-right: 8px; + position: relative; + cursor: pointer; + } + + input[type="checkbox"]:checked { + background: ${theme.success}; + } + + input[type="checkbox"]:checked::after { + content: 'โœ“'; + position: absolute; + top: -2px; + left: 1px; + color: ${theme.primary}; + font-size: 12px; + font-weight: bold; + } + + .fas, .fa { + filter: drop-shadow(0 0 3px currentColor); + } + + .wplace-speed-control { + margin-top: 12px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-speed-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-speed-label i { + margin-right: 6px; + color: ${theme.highlight}; + } + + .wplace-speed-slider-container { + display: flex; + align-items: center; + gap: 12px; + } + + .wplace-speed-slider { + flex: 1; + height: 6px; + border-radius: 3px; + background: ${theme.primary}; + outline: none; + cursor: pointer; + -webkit-appearance: none; + appearance: none; + } + + .wplace-speed-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-slider::-moz-range-thumb { + width: 18px; + height: 18px; + border-radius: 50%; + background: ${theme.highlight}; + cursor: pointer; + border: 2px solid ${theme.text}; + box-shadow: ${theme.boxShadow}; + } + + .wplace-speed-display { + display: flex; + align-items: center; + gap: 4px; + min-width: 90px; + justify-content: flex-end; + } + + #speedValue { + color: ${theme.highlight}; + font-weight: 600; + font-size: 14px; + } + + .wplace-speed-unit { + color: ${theme.text}; + font-size: 11px; + opacity: 0.8; + } + + #wplace-settings-container { + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + z-index: 10001; + min-width: 400px; + max-width: 500px; + background: ${theme.primary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + box-shadow: ${theme.boxShadow}; + backdrop-filter: ${theme.backdropFilter}; + } + + .wplace-settings { + padding: 16px; + max-height: 400px; + overflow-y: auto; + } + + .wplace-setting-section { + margin-bottom: 20px; + padding: 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-setting-title { + display: flex; + align-items: center; + margin-bottom: 12px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-setting-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-setting-content { + color: ${theme.text}; + } + + .wplace-section { + margin-bottom: 20px; + padding: 15px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + } + + .wplace-section-title { + display: flex; + align-items: center; + margin-bottom: 15px; + color: ${theme.text}; + font-size: 14px; + font-weight: 600; + } + + .wplace-section-title i { + margin-right: 8px; + color: ${theme.highlight}; + } + + .wplace-speed-container { + display: flex; + align-items: center; + gap: 15px; + margin-bottom: 10px; + } + + .wplace-slider { + flex: 1; + height: 6px; + background: ${theme.accent}; + border-radius: 3px; + outline: none; + -webkit-appearance: none; + } + + .wplace-slider::-webkit-slider-thumb { + -webkit-appearance: none; + width: 18px; + height: 18px; + background: ${theme.highlight}; + border-radius: 50%; + cursor: pointer; + border: 2px solid ${theme.primary}; + } + + .wplace-speed-display { + background: ${theme.accent}; + padding: 5px 10px; + border-radius: 4px; + color: ${theme.text}; + font-weight: 600; + min-width: 80px; + text-align: center; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-select { + width: 100%; + padding: 8px 12px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: ${theme.borderRadius}; + color: ${theme.text}; + font-size: 14px; + margin-bottom: 10px; + } + + .wplace-select:focus { + outline: none; + border-color: ${theme.highlight}; + } + + .wplace-description { + color: ${theme.text}; + font-size: 12px; + opacity: 0.8; + line-height: 1.4; + } + + .wplace-theme-custom { + margin-top: 15px; + padding: 15px; + background: ${theme.accent}; + border-radius: ${theme.borderRadius}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-custom-group { + margin-bottom: 15px; + } + + .wplace-custom-label { + display: flex; + align-items: center; + margin-bottom: 8px; + color: ${theme.text}; + font-size: 13px; + font-weight: 600; + } + + .wplace-custom-label i { + margin-right: 8px; + color: ${theme.highlight}; + width: 16px; + } + + .wplace-color-input-group { + display: flex; + gap: 8px; + align-items: center; + } + + .wplace-color-input { + width: 50px; + height: 30px; + border: none; + border-radius: 4px; + cursor: pointer; + background: transparent; + } + + .wplace-color-text { + flex: 1; + padding: 6px 10px; + background: ${theme.secondary}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.accent}; + border-radius: 4px; + color: ${theme.text}; + font-size: 12px; + font-family: monospace; + } + + .wplace-animation-controls { + display: flex; + flex-direction: column; + gap: 8px; + } + + .wplace-checkbox-label { + display: flex; + align-items: center; + gap: 8px; + color: ${theme.text}; + font-size: 12px; + cursor: pointer; + } + + .wplace-checkbox-label input[type="checkbox"] { + accent-color: ${theme.highlight}; + } + + .wplace-slider-container { + display: flex; + align-items: center; + gap: 10px; + } + + .wplace-slider-container .wplace-slider { + flex: 1; + } + + .wplace-slider-container span { + color: ${theme.text}; + font-size: 12px; + font-weight: 600; + min-width: 40px; + } + + .wplace-custom-actions { + display: flex; + gap: 10px; + margin-top: 20px; + border-top: 1px solid ${theme.accent}; + padding-top: 15px; + } + + .wplace-btn-secondary { + background: ${theme.accent}; + color: ${theme.text}; + border: ${theme.borderWidth} ${theme.borderStyle} ${theme.highlight}; + } + + .wplace-btn-secondary:hover { + background: ${theme.secondary}; + }` + : "" + } + ` + document.head.appendChild(style) const container = document.createElement("div") container.id = "wplace-image-bot-container" @@ -3462,13 +4603,19 @@ function applyTheme() { `linear-gradient(135deg, ${theme.primary} 0%, ${theme.secondary || theme.primary} 100%)` : `linear-gradient(135deg, #667eea 0%, #764ba2 100%)` - settingsContainer.className = 'wplace-settings-container-base'; - // Apply theme-specific background - settingsContainer.style.background = themeBackground; - settingsContainer.style.cssText += ` + settingsContainer.style.cssText = ` + position: fixed; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: ${themeBackground}; + border: ${theme.borderWidth || '1px'} ${theme.borderStyle || 'solid'} ${theme.accent || 'rgba(255,255,255,0.1)'}; + border-radius: ${theme.borderRadius || '16px'}; + padding: 0; + z-index: 10002; + display: none; min-width: 420px; max-width: 480px; - z-index: 99999; color: ${theme.text || 'white'}; font-family: ${theme.fontFamily || "'Segoe UI', Tahoma, Geneva, Verdana, sans-serif"}; box-shadow: ${theme.boxShadow || '0 20px 40px rgba(0,0,0,0.3), 0 0 0 1px rgba(255,255,255,0.1)'}; @@ -3479,85 +4626,171 @@ function applyTheme() { box-shadow: ${theme.boxShadow || '0 20px 40px rgba(0,0,0,0.3)'}, 0 0 30px ${theme.highlight || theme.neon || '#00ffff'}; ` : ''} - `; + ` settingsContainer.innerHTML = ` -
-
-

- +
+ ${theme.animations?.scanline ? ` +
+ ` : ''} +
+

+ ${Utils.t("settings")}

- +
-
+
-
-