From 4acfdf4452f419dff05428afacc0018d410f7cb3 Mon Sep 17 00:00:00 2001 From: kyuketski Date: Sun, 31 Aug 2025 04:02:24 +0300 Subject: [PATCH] fix(ui): correct charge counter rounding and status updates - Fix charge counter inconsistencies between Status block and Stats section. - Implement rounding with a 0.95 threshold instead of always flooring. - Prevent Stats counter from showing -1 when painting reaches the last pixel. - Ensure Status UI updates correctly during painting progress (instead of being stuck on "Starting painting"). - Refactor charge calculation code for clarity and maintainability. - Standardize dynamic sleep interval handling with a minimum threshold of 100ms. Benefits: - Accurate and consistent charge display across UI components. - More reliable progress updates during painting. - Cleaner, more maintainable codebase. --- Auto-Image.js | 157 ++++++++++++++++++++++++++++++++++---------------- 1 file changed, 107 insertions(+), 50 deletions(-) diff --git a/Auto-Image.js b/Auto-Image.js index b0a0885..dd309f6 100644 --- a/Auto-Image.js +++ b/Auto-Image.js @@ -31,8 +31,7 @@ OPACITY_DEFAULT: 0.2, BLUE_MARBLE_DEFAULT: false, ditheringEnabled: false, - }, - // --- START: Color data from colour-converter.js --- + }, // --- START: Color data from colour-converter.js --- // New color structure with proper ID mapping COLOR_MAP: { 0: { id: 1, name: 'Black', rgb: { r: 0, g: 0, b: 0 } }, @@ -99,8 +98,7 @@ 61: { id: 59, name: 'Slate', rgb: { r: 109, g: 117, b: 141 } }, 62: { id: 60, name: 'Light Slate', rgb: { r: 179, g: 185, b: 209 } }, 63: { id: 0, name: 'Transparent', rgb: null }, - }, - // --- END: Color data --- + }, // --- END: Color data --- // Optimized CSS Classes for reuse CSS_CLASSES: { BUTTON_PRIMARY: ` @@ -329,7 +327,9 @@ ) { loadedTranslations[language] = translations; console.log( - `📚 Loaded ${language} translations successfully from CDN (${Object.keys(translations).length} keys)` + `📚 Loaded ${language} translations successfully from CDN (${ + Object.keys(translations).length + } keys)` ); return translations; } else { @@ -1295,7 +1295,7 @@ dynamicSleep: async function (getRemainingMsFn, interval = 500) { let remaining = getRemainingMsFn(); - while (remaining > 0) { + while (remaining > 100) { await this.sleep(Math.min(interval, remaining)); remaining = getRemainingMsFn(); } @@ -1320,12 +1320,7 @@ if (hours > 0) return `${hours}h ${minutes}m ${seconds}s`; if (minutes > 0) return `${minutes}m ${seconds}s`; return `${seconds}s`; - }, // Turnstile Generator Integration - Optimized with widget reuse and proper cleanup - turnstileLoaded: false, - _turnstileContainer: null, - _turnstileOverlay: null, - _turnstileWidgetId: null, - _lastSitekey: null, + }, /** * Calculate the range of tile coordinates (in region space) that cover a given image area. @@ -1356,7 +1351,12 @@ endTileX: startRegionX + Math.floor((endPixelX - 1) / tileSize), endTileY: startRegionY + Math.floor((endPixelY - 1) / tileSize), }; - }, + }, // Turnstile Generator Integration - Optimized with widget reuse and proper cleanup + turnstileLoaded: false, + _turnstileContainer: null, + _turnstileOverlay: null, + _turnstileWidgetId: null, + _lastSitekey: null, async loadTurnstile() { // If Turnstile is already present, just resolve. @@ -2872,7 +2872,7 @@ const shouldRepeat = now - (state._lastChargesNotifyAt || 0) >= repeatMs; if (shouldEdge || shouldRepeat) { const msg = Utils.t('chargesReadyMessage', { - current: Math.floor(state.currentCharges), + current: state.currentCharges, max: state.maxCharges, threshold: state.cooldownChargeThreshold, }); @@ -2977,7 +2977,9 @@ // Don't re-fetch colors here, use what was captured when user clicked upload if (!state.availableColors || state.availableColors.length === 0) { // If no colors have been captured yet, show message - colorsContainer.innerHTML = `
${Utils.t('uploadImageFirst')}
`; + colorsContainer.innerHTML = `
${Utils.t( + 'uploadImageFirst' + )}
`; return; } @@ -3349,7 +3351,9 @@
🖼️ Image Management
- @@ -3411,7 +3415,9 @@ ${Utils.t('saveData')} - @@ -3421,7 +3427,9 @@ ${Utils.t('saveToFile')} - @@ -3442,7 +3450,9 @@ ${Utils.t('paintingStats')}
-
@@ -3778,15 +3790,21 @@
${Utils.t('repeatEvery')} @@ -3794,8 +3812,12 @@ ${Utils.t('minutesPl')}
- - + +
@@ -3974,11 +3996,19 @@ ${Utils.t('paintTransparentPixels')}
- + - - - + + + @@ -4897,20 +4927,29 @@ const elapsed = Date.now() - startTime; const remainingMs = Math.max(0, (max - current) * cooldownMs - elapsed); - let timeText = Utils.msToTimeText(remainingMs); + const timeText = Utils.msToTimeText(remainingMs); - const chargesGained = Math.ceil(elapsed / cooldownMs); - const currentCharges = Math.min(current + chargesGained, max); + // total charges including elapsed time + const chargesGained = elapsed / cooldownMs; + const rawCharges = current + chargesGained; + const cappedCharges = Math.min(rawCharges, max); - state.currentCharges = Math.floor(currentCharges); + // rounding with 0.95 threshold + let displayCharges; + const fraction = cappedCharges - Math.floor(cappedCharges); + if (fraction >= 0.95) { + displayCharges = Math.ceil(cappedCharges); + } else { + displayCharges = Math.floor(cappedCharges); + } + + state.currentCharges = displayCharges; const chargesEl = document.getElementById('wplace-stat-charges-value'); const secondsInMinute = Math.ceil(remainingMs / 1000) % 60; if (chargesEl && (secondsInMinute === 0 || secondsInMinute === 30)) { - chargesEl.innerHTML = `${Math.floor( - state.currentCharges - )} / ${state.maxCharges}`; + chargesEl.innerHTML = `${state.currentCharges} / ${state.maxCharges}`; } - if (currentCharges >= max) { + if (state.currentCharges >= max) { fullChargeEl.innerHTML = `FULL`; clearInterval(state.fullChargeInterval); state.fullChargeInterval = null; @@ -5003,7 +5042,10 @@ colorSwatchesHTML = state.availableColors .map((color) => { const rgbString = `rgb(${color.rgb.join(',')})`; - return `
`; + return `
`; }) .join(''); } @@ -5021,7 +5063,7 @@ ${Utils.t('charges')}
- ${Math.floor(state.currentCharges)} / ${state.maxCharges} + ${state.currentCharges} / ${state.maxCharges}
@@ -5036,7 +5078,10 @@ state.colorsChecked ? `
-
${Utils.t('availableColors', { count: state.availableColors.length })}
+
${Utils.t( + 'availableColors', + { count: state.availableColors.length } + )}
${colorSwatchesHTML}
@@ -6483,11 +6528,9 @@ if (state.chargesThresholdInterval) { clearInterval(state.chargesThresholdInterval); } - updateUI('startPaintingMsg', 'success'); return; // Threshold already reached } - const current = state.fullChargeData ? state.fullChargeData.current : state.currentCharges; const timeText = Utils.msToTimeText(remainingMs); updateUI( @@ -6495,7 +6538,7 @@ 'warning', { threshold, - current: Math.floor(current), + current: state.currentCharges, time: timeText, }, true @@ -6671,6 +6714,10 @@ }); state.currentCharges -= batchSize; updateStats(); + updateUI('paintingProgress', 'default', { + painted: state.paintedPixels, + total: state.totalPixels, + }); Utils.performSmartSave(); if (CONFIG.PAINTING_SPEED_ENABLED && state.paintingSpeed > 0 && batchSize > 0) { @@ -6793,6 +6840,7 @@ clearInterval(state.chargesThresholdInterval); } updateUI('paintingPaused', 'warning', { x, y }); + // noinspection UnnecessaryLabelOnBreakStatementJS break outerLoop; } @@ -6848,6 +6896,7 @@ console.error(`❌ Batch failed permanently after retries. Stopping painting.`); state.stopFlag = true; updateUI('paintingBatchFailed', 'error'); + // noinspection UnnecessaryLabelOnBreakStatementJS break outerLoop; } } @@ -6884,16 +6933,21 @@ continue; } console.debug( - `[COMPARE] Pixel at 📍 (${pixelX}, ${pixelY}) in region (${regionX + adderX}, ${regionY + adderY})\n` + + `[COMPARE] Pixel at 📍 (${pixelX}, ${pixelY}) in region (${ + regionX + adderX + }, ${regionY + adderY})\n` + ` ├── Current color: rgb(${er}, ${eg}, ${eb}) (id: ${existingColor.id})\n` + ` ├── Target color: rgb(${targetPixelInfo.r}, ${targetPixelInfo.g}, ${targetPixelInfo.b}) (id: ${targetColorId})\n` + - ` └── Status: ${isMatch ? '✅ Already painted → SKIP' : '🔴 Needs paint → PAINT'}\n` + ` └── Status: ${ + isMatch ? '✅ Already painted → SKIP' : '🔴 Needs paint → PAINT' + }\n` ); } } catch (e) { console.error(`[DEBUG] Error checking existing pixel at (${pixelX}, ${pixelY}):`, e); updateUI('paintingPixelCheckFailed', 'error', { x: pixelX, y: pixelY }); state.stopFlag = true; + // noinspection UnnecessaryLabelOnBreakStatementJS break outerLoop; } @@ -6919,6 +6973,7 @@ console.error(`❌ Batch failed permanently after retries. Stopping painting.`); state.stopFlag = true; updateUI('paintingBatchFailed', 'error'); + // noinspection UnnecessaryLabelOnBreakStatementJS break outerLoop; } @@ -6927,21 +6982,23 @@ while (state.currentCharges < state.cooldownChargeThreshold && !state.stopFlag) { await updateStats(); + if (state.currentCharges >= state.cooldownChargeThreshold) { NotificationManager.maybeNotifyChargesReached(true); break; } - saveBtn.disabled = false; startChargesThresholdTicker(); - Utils.performSmartSave(); - await Utils.dynamicSleep(() => getRemainingMsToThreshold(state.cooldownChargeThreshold)); + await Utils.dynamicSleep( + () => getRemainingMsToThreshold(state.cooldownChargeThreshold), + 1000 + ); } - if (!state.stopFlag) saveBtn.disabled = true; if (state.stopFlag) { + // noinspection UnnecessaryLabelOnBreakStatementJS break outerLoop; } } @@ -7014,7 +7071,7 @@ } // Always limit by available charges - const maxAllowed = Math.floor(state.currentCharges); + const maxAllowed = state.currentCharges; const finalBatchSize = Math.min(targetBatchSize, maxAllowed); return finalBatchSize;