-
-
${CONFIG.PAINTING_SPEED.DEFAULT} (batch size)
+
+
+
+
+ ${CONFIG.PAINTING_SPEED.DEFAULT}
+
+
${CONFIG.PAINTING_SPEED.MIN}
@@ -3969,7 +4029,7 @@
}
}
- #speedSlider::-webkit-slider-thumb, #overlayOpacitySlider::-webkit-slider-thumb {
+ #speedSlider::-webkit-slider-thumb, #cooldownSlider::-webkit-slider-thumb, #overlayOpacitySlider::-webkit-slider-thumb {
-webkit-appearance: none;
width: 18px;
height: 18px;
@@ -3980,12 +4040,12 @@
transition: all 0.2s ease;
}
- #speedSlider::-webkit-slider-thumb:hover, #overlayOpacitySlider::-webkit-slider-thumb:hover {
+ #speedSlider::-webkit-slider-thumb:hover, #cooldownSlider::-webkit-slider-thumb:hover, #overlayOpacitySlider::-webkit-slider-thumb:hover {
transform: scale(1.2);
box-shadow: 0 4px 8px rgba(0,0,0,0.4), 0 0 0 3px #4facfe;
}
- #speedSlider::-moz-range-thumb, #overlayOpacitySlider::-moz-range-thumb {
+ #speedSlider::-moz-range-thumb, #cooldownSlider::-moz-range-thumb, #overlayOpacitySlider::-moz-range-thumb {
width: 18px;
height: 18px;
border-radius: 50%;
@@ -4032,6 +4092,25 @@
.wplace-settings-header:active {
background: rgba(255,255,255,0.2) !important;
}
+
+ /* Custom Scrollbar for Content Area */
+ .wplace-content::-webkit-scrollbar {
+ width: 6px;
+ }
+
+ .wplace-content::-webkit-scrollbar-track {
+ background: rgba(255,255,255,0.1);
+ border-radius: 3px;
+ }
+
+ .wplace-content::-webkit-scrollbar-thumb {
+ background: rgba(255,255,255,0.3);
+ border-radius: 3px;
+ }
+
+ .wplace-content::-webkit-scrollbar-thumb:hover {
+ background: rgba(255,255,255,0.5);
+ }
`;
@@ -4283,6 +4362,10 @@
const closeStatsBtn = statsContainer.querySelector('#closeStatsBtn');
const refreshChargesBtn = statsContainer.querySelector('#refreshChargesBtn');
const cooldownSlider = container.querySelector('#cooldownSlider');
+ const cooldownSliderValue = container.querySelector('#cooldownSliderValue');
+ const cooldownInput = container.querySelector('#cooldownInput');
+ const cooldownDecrease = container.querySelector('#cooldownDecrease');
+ const cooldownIncrease = container.querySelector('#cooldownIncrease');
const cooldownValue = container.querySelector('#cooldownValue');
if (!uploadBtn || !selectPosBtn || !startBtn || !stopBtn) {
@@ -4609,11 +4692,19 @@
);
if (overlayOpacitySlider && overlayOpacityValue) {
- overlayOpacitySlider.addEventListener('input', (e) => {
- const opacity = parseFloat(e.target.value);
+ const updateOpacity = (newValue) => {
+ const opacity = parseFloat(newValue);
state.overlayOpacity = opacity;
+ overlayOpacitySlider.value = opacity;
overlayOpacityValue.textContent = `${Math.round(opacity * 100)}%`;
+ };
+
+ overlayOpacitySlider.addEventListener('input', (e) => {
+ updateOpacity(e.target.value);
});
+
+ // Add scroll-to-adjust for overlay opacity slider
+ Utils.createScrollToAdjust(overlayOpacitySlider, updateOpacity, 0, 1, 0.05);
}
if (settingsPaintWhiteToggle) {
@@ -4644,16 +4735,49 @@
});
}
- // Speed slider event listener
+ // Speed controls - both slider and input
const speedSlider = settingsContainer.querySelector('#speedSlider');
+ const speedSliderValue = settingsContainer.querySelector('#speedSliderValue');
+ const speedInput = settingsContainer.querySelector('#speedInput');
+ const speedDecrease = settingsContainer.querySelector('#speedDecrease');
+ const speedIncrease = settingsContainer.querySelector('#speedIncrease');
const speedValue = settingsContainer.querySelector('#speedValue');
- if (speedSlider && speedValue) {
- speedSlider.addEventListener('input', (e) => {
- const speed = parseInt(e.target.value, 10);
+
+ if (speedSlider && speedSliderValue && speedInput && speedValue && speedDecrease && speedIncrease) {
+ const updateSpeed = (newValue) => {
+ const speed = Math.max(CONFIG.PAINTING_SPEED.MIN, Math.min(CONFIG.PAINTING_SPEED.MAX, parseInt(newValue)));
state.paintingSpeed = speed;
- speedValue.textContent = `${speed} (batch size)`;
+
+ // Update both controls without duplication
+ speedSlider.value = speed;
+ speedInput.value = speed;
+ speedSliderValue.textContent = `${speed}`;
+
saveBotSettings();
+ };
+
+ // Slider event listener
+ speedSlider.addEventListener('input', (e) => {
+ updateSpeed(e.target.value);
});
+
+ // Number input event listener
+ speedInput.addEventListener('input', (e) => {
+ updateSpeed(e.target.value);
+ });
+
+ // Decrease button
+ speedDecrease.addEventListener('click', () => {
+ updateSpeed(parseInt(speedInput.value) - 1);
+ });
+
+ // Increase button
+ speedIncrease.addEventListener('click', () => {
+ updateSpeed(parseInt(speedInput.value) + 1);
+ });
+
+ // Add scroll-to-adjust for speed slider
+ Utils.createScrollToAdjust(speedSlider, updateSpeed, CONFIG.PAINTING_SPEED.MIN, CONFIG.PAINTING_SPEED.MAX, 1);
}
if (enableBlueMarbleToggle) {
@@ -5094,9 +5218,12 @@
intervalMs
);
- if (cooldownSlider.max !== state.maxCharges) {
+ if (cooldownSlider && cooldownSlider.max !== state.maxCharges) {
cooldownSlider.max = state.maxCharges;
}
+ if (cooldownInput && cooldownInput.max !== state.maxCharges) {
+ cooldownInput.max = state.maxCharges;
+ }
let imageStatsHTML = '';
if (state.imageLoaded) {
@@ -6582,14 +6709,42 @@
setTimeout(checkSavedProgress, 1000);
- if (cooldownSlider && cooldownValue) {
- cooldownSlider.addEventListener('input', (e) => {
- const threshold = parseInt(e.target.value);
+ if (cooldownSlider && cooldownSliderValue && cooldownInput && cooldownValue && cooldownDecrease && cooldownIncrease) {
+ const updateCooldown = (newValue) => {
+ const threshold = Math.max(1, Math.min(state.maxCharges || 999, parseInt(newValue)));
state.cooldownChargeThreshold = threshold;
- cooldownValue.textContent = threshold;
+
+ // Update both controls
+ cooldownSlider.value = threshold;
+ cooldownInput.value = threshold;
+ cooldownSliderValue.textContent = threshold;
+
saveBotSettings();
NotificationManager.resetEdgeTracking(); // prevent spurious notify after threshold change
+ };
+
+ // Slider event listener
+ cooldownSlider.addEventListener('input', (e) => {
+ updateCooldown(e.target.value);
});
+
+ // Number input event listener
+ cooldownInput.addEventListener('input', (e) => {
+ updateCooldown(e.target.value);
+ });
+
+ // Decrease button
+ cooldownDecrease.addEventListener('click', () => {
+ updateCooldown(parseInt(cooldownInput.value) - 1);
+ });
+
+ // Increase button
+ cooldownIncrease.addEventListener('click', () => {
+ updateCooldown(parseInt(cooldownInput.value) + 1);
+ });
+
+ // Add scroll-to-adjust for cooldown slider
+ Utils.createScrollToAdjust(cooldownSlider, updateCooldown, 1, state.maxCharges, 1);
}
loadBotSettings();
@@ -7473,9 +7628,13 @@
}
const speedSlider = document.getElementById('speedSlider');
+ const speedInput = document.getElementById('speedInput');
+ const speedSliderValue = document.getElementById('speedSliderValue');
if (speedSlider) speedSlider.value = state.paintingSpeed;
+ if (speedInput) speedInput.value = state.paintingSpeed;
+ if (speedSliderValue) speedSliderValue.textContent = `${state.paintingSpeed} (batch size)`;
const speedValue = document.getElementById('speedValue');
- if (speedValue) speedValue.textContent = `${state.paintingSpeed} (batch size)`;
+ if (speedValue) speedValue.textContent = Utils.t('batchSize');
const enableSpeedToggle = document.getElementById('enableSpeedToggle');
if (enableSpeedToggle) enableSpeedToggle.checked = CONFIG.PAINTING_SPEED_ENABLED;
@@ -7507,9 +7666,13 @@
// AUTO_CAPTCHA_ENABLED is always true - no toggle to set
const cooldownSlider = document.getElementById('cooldownSlider');
+ const cooldownInput = document.getElementById('cooldownInput');
+ const cooldownSliderValue = document.getElementById('cooldownSliderValue');
if (cooldownSlider) cooldownSlider.value = state.cooldownChargeThreshold;
+ if (cooldownInput) cooldownInput.value = state.cooldownChargeThreshold;
+ if (cooldownSliderValue) cooldownSliderValue.textContent = state.cooldownChargeThreshold;
const cooldownValue = document.getElementById('cooldownValue');
- if (cooldownValue) cooldownValue.textContent = state.cooldownChargeThreshold;
+ if (cooldownValue) cooldownValue.textContent = Utils.t('charges');
const overlayOpacitySlider = document.getElementById('overlayOpacitySlider');
if (overlayOpacitySlider) overlayOpacitySlider.value = state.overlayOpacity;
diff --git a/themes/classic-light.css b/themes/classic-light.css
index 3761527..301f931 100644
--- a/themes/classic-light.css
+++ b/themes/classic-light.css
@@ -468,6 +468,104 @@
background: rgb(0 0 0 / 10%) !important;
}
+/* Dual control layout styling for classic light theme */
+.wplace-theme-classic-light .wplace-dual-control-compact {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ margin: 8px 0;
+ flex-wrap: wrap;
+}
+
+.wplace-theme-classic-light .wplace-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ min-width: 160px;
+}
+
+.wplace-theme-classic-light .wplace-speed-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ min-width: 160px;
+}
+
+.wplace-theme-classic-light .wplace-input-group-compact {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ flex-shrink: 0;
+}
+
+.wplace-theme-classic-light .wplace-input-btn-compact {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ color: white;
+ border: none;
+ border-radius: 4px;
+ width: 22px;
+ height: 22px;
+ cursor: pointer;
+ font-weight: bold;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+.wplace-theme-classic-light .wplace-input-btn-compact:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
+ background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
+}
+
+.wplace-theme-classic-light .wplace-input-btn-compact:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+.wplace-theme-classic-light .wplace-number-input-compact {
+ background: #fff;
+ border: 1px solid rgb(0 0 0 / 20%);
+ border-radius: 4px;
+ color: var(--wplace-text);
+ padding: 4px 8px;
+ font-size: 12px;
+ width: 50px;
+ text-align: center;
+ transition: all 0.2s ease;
+ height: 22px;
+}
+
+.wplace-theme-classic-light .wplace-number-input-compact:focus {
+ outline: none;
+ border-color: #4facfe;
+ box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3);
+ background: #f8f9fa;
+}
+
+.wplace-theme-classic-light .wplace-input-label-compact {
+ color: var(--wplace-text);
+ font-size: 11px;
+ margin-left: 4px;
+ white-space: nowrap;
+}
+
+.wplace-theme-classic-light .wplace-batch-size-header {
+ margin-bottom: 8px;
+}
+
+.wplace-theme-classic-light .wplace-batch-size-label {
+ color: var(--wplace-highlight);
+ font-weight: 500;
+ font-size: 13px;
+}
+
/* Color swatches with light styling */
.wplace-theme-classic-light .wplace-color-swatch {
border: 1px solid rgb(0 0 0 / 20%) !important;
diff --git a/themes/classic.css b/themes/classic.css
index f71efe3..4d9129f 100644
--- a/themes/classic.css
+++ b/themes/classic.css
@@ -590,6 +590,116 @@
border-radius: 8px;
}
+/* Dual control layout styling for classic theme */
+:root .wplace-dual-control-compact,
+.wplace-theme-classic .wplace-dual-control-compact {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 12px;
+ margin: 8px 0;
+ flex-wrap: wrap;
+}
+
+:root .wplace-slider-container-compact,
+.wplace-theme-classic .wplace-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ min-width: 160px;
+}
+
+:root .wplace-speed-slider-container-compact,
+.wplace-theme-classic .wplace-speed-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 8px;
+ flex: 1;
+ min-width: 160px;
+}
+
+:root .wplace-input-group-compact,
+.wplace-theme-classic .wplace-input-group-compact {
+ display: flex;
+ align-items: center;
+ gap: 4px;
+ flex-shrink: 0;
+}
+
+:root .wplace-input-btn-compact,
+.wplace-theme-classic .wplace-input-btn-compact {
+ background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
+ color: white;
+ border: none;
+ border-radius: 4px;
+ width: 22px;
+ height: 22px;
+ cursor: pointer;
+ font-weight: bold;
+ font-size: 12px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+:root .wplace-input-btn-compact:hover,
+.wplace-theme-classic .wplace-input-btn-compact:hover {
+ transform: translateY(-1px);
+ box-shadow: 0 4px 8px rgba(0,0,0,0.3);
+ background: linear-gradient(135deg, #00f2fe 0%, #4facfe 100%);
+}
+
+:root .wplace-input-btn-compact:active,
+.wplace-theme-classic .wplace-input-btn-compact:active {
+ transform: translateY(0);
+ box-shadow: 0 2px 4px rgba(0,0,0,0.2);
+}
+
+:root .wplace-number-input-compact,
+.wplace-theme-classic .wplace-number-input-compact {
+ background: rgba(255,255,255,0.1);
+ border: 1px solid rgba(255,255,255,0.2);
+ border-radius: 4px;
+ color: white;
+ padding: 4px 8px;
+ font-size: 12px;
+ width: 50px;
+ text-align: center;
+ transition: all 0.2s ease;
+ height: 22px;
+}
+
+:root .wplace-number-input-compact:focus,
+.wplace-theme-classic .wplace-number-input-compact:focus {
+ outline: none;
+ border-color: #4facfe;
+ box-shadow: 0 0 0 3px rgba(79, 172, 254, 0.3);
+ background: rgba(255,255,255,0.15);
+}
+
+:root .wplace-input-label-compact,
+.wplace-theme-classic .wplace-input-label-compact {
+ color: rgba(255,255,255,0.8);
+ font-size: 11px;
+ margin-left: 4px;
+ white-space: nowrap;
+}
+
+:root .wplace-batch-size-header,
+.wplace-theme-classic .wplace-batch-size-header {
+ margin-bottom: 8px;
+}
+
+:root .wplace-batch-size-label,
+.wplace-theme-classic .wplace-batch-size-label {
+ color: var(--wplace-highlight);
+ font-weight: 500;
+ font-size: 13px;
+}
+
/* Resize dialog styling for classic theme */
:root .resize-container,
.wplace-theme-classic .resize-container {
diff --git a/themes/neon.css b/themes/neon.css
index 9ce3c5c..0a1b9b9 100644
--- a/themes/neon.css
+++ b/themes/neon.css
@@ -392,6 +392,106 @@
}
}
+/* Dual control layout styling for neon theme */
+.wplace-theme-neon .wplace-dual-control-compact {
+ display: flex;
+ flex-direction: row;
+ align-items: center;
+ gap: 8px;
+ margin: 6px 0;
+ flex-wrap: wrap;
+}
+
+.wplace-theme-neon .wplace-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex: 1;
+ min-width: 140px;
+}
+
+.wplace-theme-neon .wplace-speed-slider-container-compact {
+ display: flex;
+ align-items: center;
+ gap: 6px;
+ flex: 1;
+ min-width: 140px;
+}
+
+.wplace-theme-neon .wplace-input-group-compact {
+ display: flex;
+ align-items: center;
+ gap: 3px;
+ flex-shrink: 0;
+}
+
+.wplace-theme-neon .wplace-input-btn-compact {
+ background: var(--wplace-secondary) !important;
+ border: 1px solid var(--wplace-text) !important;
+ border-radius: 0 !important;
+ color: var(--wplace-text) !important;
+ width: 18px;
+ height: 18px;
+ cursor: pointer;
+ font-weight: bold;
+ font-size: 10px;
+ display: flex;
+ align-items: center;
+ justify-content: center;
+ transition: all 0.2s ease;
+ text-shadow: 0 0 5px var(--wplace-text) !important;
+ font-family: var(--wplace-font) !important;
+}
+
+.wplace-theme-neon .wplace-input-btn-compact:hover {
+ box-shadow: 0 0 10px var(--wplace-text) !important;
+ animation: pixel-blink 0.5s infinite !important;
+}
+
+.wplace-theme-neon .wplace-number-input-compact {
+ background: var(--wplace-secondary) !important;
+ border: 1px solid var(--wplace-text) !important;
+ border-radius: 0 !important;
+ color: var(--wplace-text) !important;
+ padding: 2px 4px;
+ font-size: 10px;
+ width: 40px;
+ text-align: center;
+ transition: all 0.2s ease;
+ height: 18px;
+ font-family: var(--wplace-font) !important;
+ text-shadow: 0 0 5px var(--wplace-text) !important;
+}
+
+.wplace-theme-neon .wplace-number-input-compact:focus {
+ outline: none;
+ box-shadow: 0 0 10px var(--wplace-text) !important;
+ background: rgb(0 255 65 / 15%) !important;
+}
+
+.wplace-theme-neon .wplace-input-label-compact {
+ color: var(--wplace-text) !important;
+ font-size: 9px;
+ margin-left: 3px;
+ white-space: nowrap;
+ text-shadow: 0 0 5px var(--wplace-text) !important;
+ font-family: var(--wplace-font) !important;
+ text-transform: uppercase;
+}
+
+.wplace-theme-neon .wplace-batch-size-header {
+ margin-bottom: 6px;
+}
+
+.wplace-theme-neon .wplace-batch-size-label {
+ color: var(--wplace-text) !important;
+ font-weight: 500;
+ font-size: 11px;
+ text-shadow: 0 0 8px var(--wplace-text) !important;
+ font-family: var(--wplace-font) !important;
+ text-transform: uppercase;
+}
+
/* Resize dialog styling for neon theme */
.wplace-theme-neon .resize-container {
background: #1a1a2e !important;