feat: prevent scrolling of containers when using focused number inputs (#4629)
* feat: prevent scrolling of containers when using focused number inputs #4607 * feat: enhance number input wheel behavior with slider sync and Firefox support * refactor: optimize number input wheel handler with native DOM methods and type checks * Refactor: Move into dom-handlers folder * refactor: use optional chaining for slider element selection * Simplify file paths * Unlock scroll to edit in misc controls * refactor: throttle wheel event handler updates * refactor: Extract value update into a function * fix: NaN-aware value clamping * fix: Add sanity checks for input value calculations --------- Co-authored-by: Wolfsblvt <wolfsblvt@gmail.com>
This commit is contained in:
+4
-4
@@ -253,7 +253,7 @@
|
||||
</div>
|
||||
<div id="common-gen-settings-block" class="width100p">
|
||||
<div id="pro-settings-block" class="flex-container gap10h5v justifyCenter">
|
||||
<div id="amount_gen_block" class="alignitemscenter flex-container marginBot5 flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<div id="amount_gen_block" class="range-block-range-and-counter alignitemscenter flex-container marginBot5 flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<small data-i18n="response legth(tokens)">Response (tokens)</small>
|
||||
<input class="neo-range-slider" type="range" id="amount_gen" name="volume" min="16" max="2048" step="1">
|
||||
<div data-randomization-disabled="true" class="wide100p">
|
||||
@@ -284,7 +284,7 @@
|
||||
</label>
|
||||
</div>
|
||||
</div>
|
||||
<div id="max_context_block" class="alignitemscenter flex-container marginBot5 flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<div id="max_context_block" class="range-block-range-and-counter alignitemscenter flex-container marginBot5 flexFlowColumn flexBasis48p flexGrow flexShrink gap0">
|
||||
<small data-i18n="context size(tokens)">Context (tokens)</small>
|
||||
<input class="neo-range-slider" type="range" id="max_context" name="volume" min="512" max="8192" step="64">
|
||||
<div data-randomization-disabled="true" class="wide100p">
|
||||
@@ -648,7 +648,7 @@
|
||||
Max Response Length (tokens)
|
||||
</div>
|
||||
<div class="wide100p">
|
||||
<input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536">
|
||||
<input type="number" id="openai_max_tokens" name="openai_max_tokens" class="text_pole" min="1" max="65536" step="1">
|
||||
</div>
|
||||
</div>
|
||||
<div class="range-block" data-source="openai,custom,xai,aimlapi,moonshot,azure_openai">
|
||||
@@ -4214,7 +4214,7 @@
|
||||
Token Padding
|
||||
</small>
|
||||
</div>
|
||||
<input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" />
|
||||
<input id="token_padding" class="text_pole textarea_compact" type="number" min="-2048" max="2048" step="1" />
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
|
||||
@@ -270,6 +270,7 @@ import { getSystemMessageByType, initSystemMessages, SAFETY_CHAT, sendSystemMess
|
||||
import { event_types, eventSource } from './scripts/events.js';
|
||||
import { initAccessibility } from './scripts/a11y.js';
|
||||
import { applyStreamFadeIn } from './scripts/util/stream-fadein.js';
|
||||
import { initDomHandlers } from './scripts/dom-handlers.js';
|
||||
|
||||
// API OBJECT FOR EXTERNAL WIRING
|
||||
globalThis.SillyTavern = {
|
||||
@@ -640,6 +641,7 @@ async function firstLoadInit() {
|
||||
|
||||
showLoader();
|
||||
registerPromptManagerMigration();
|
||||
initDomHandlers();
|
||||
initStandaloneMode();
|
||||
initLibraryShims();
|
||||
addShowdownPatch(showdown);
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
import { throttle } from './utils.js';
|
||||
|
||||
export function initDomHandlers() {
|
||||
handleInputWheel();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trap mouse wheel inside of focused number inputs to prevent scrolling their containers.
|
||||
* Instead of firing wheel events, manually update both slider and input values.
|
||||
* This also makes wheel work inside Firefox.
|
||||
*/
|
||||
function handleInputWheel() {
|
||||
const minInterval = 25; // ms
|
||||
|
||||
/**
|
||||
* Update input and slider values based on wheel delta
|
||||
* @param {HTMLInputElement} input The number input element
|
||||
* @param {HTMLInputElement|null} slider The associated range input element, if any
|
||||
* @param {number} deltaY The wheel deltaY value
|
||||
*/
|
||||
function updateValue(input, slider, deltaY) {
|
||||
const currentValue = parseFloat(input.value);
|
||||
const step = parseFloat(input.step);
|
||||
const min = parseFloat(input.min);
|
||||
const max = parseFloat(input.max);
|
||||
|
||||
// Sanity checks before trying to calculate new value
|
||||
if (isNaN(currentValue) || isNaN(step) || step <= 0 || deltaY === 0) return;
|
||||
|
||||
// Calculate new value based on wheel movement delta (negative = up, positive = down)
|
||||
let newValue = currentValue + (deltaY > 0 ? -step : step);
|
||||
// Ensure it's a multiple of step
|
||||
newValue = Math.round(newValue / step) * step;
|
||||
// Ensure it's within the min and max range (NaN-aware)
|
||||
newValue = !isNaN(min) ? Math.max(newValue, min) : newValue;
|
||||
newValue = !isNaN(max) ? Math.min(newValue, max) : newValue;
|
||||
// Simple fix for floating point precision issues
|
||||
newValue = Math.round(newValue * 1e10) / 1e10;
|
||||
|
||||
// Update both input and slider values
|
||||
input.value = newValue.toString();
|
||||
if (slider) slider.value = newValue.toString();
|
||||
// Trigger input event (just ONE) to update any listeners
|
||||
const inputEvent = new Event('input', { bubbles: true });
|
||||
input.dispatchEvent(inputEvent);
|
||||
}
|
||||
|
||||
const updateValueThrottled = throttle(updateValue, minInterval);
|
||||
|
||||
document.addEventListener('wheel', (e) => {
|
||||
// Try to carefully narrow down if we even need to fire this handler
|
||||
const input = document.activeElement instanceof HTMLInputElement ? document.activeElement : null;
|
||||
if (input && input.type === 'number' && input.hasAttribute('step')) {
|
||||
const parent = input.closest('.range-block-range-and-counter') ?? input.closest('div') ?? input.parentElement;
|
||||
const slider = /** @type {HTMLInputElement} */ (parent?.querySelector('input[type="range"]'));
|
||||
|
||||
// Stop propagation for either target
|
||||
if (e.target === input || (slider && e.target === slider)) {
|
||||
e.stopPropagation();
|
||||
e.preventDefault();
|
||||
|
||||
updateValueThrottled(input, slider, e.deltaY);
|
||||
}
|
||||
}
|
||||
}, { passive: false });
|
||||
}
|
||||
Reference in New Issue
Block a user