fix(numpad): segno perso e raffica HID concatenata a cifre manuali (#11, #27)

toggleSign() usciva subito a display vuoto: il segno del calibro, che arriva
prima delle cifre, veniva scartato. Ora '-'/'+' impostano il segno in modo
esplicito (non toggle) quando arrivano da tastiera/HID; toggleSign() (bottone
manuale +/-) funziona anche a vuoto.

_trackBurst riconosce l'inizio di una raffica solo dal secondo carattere:
prima ripuliva il display in ritardo, concatenando cifre digitate a mano.
Ora la raffica ripulisce il display fino al proprio inizio quando la pausa
prima di essa è > 300ms. Parte intera oltre 6 cifre: rifiutata con evento
numpad-error invece di essere troncata in silenzio.

Test node in tests/js/test_numpad.js, lanciato da pytest
(tests/test_numpad_js.py, skip se node manca).

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
2026-09-21 22:40:21 +02:00
parent 1576fa190b
commit d40c74870e
3 changed files with 216 additions and 7 deletions
+56 -7
View File
@@ -25,6 +25,8 @@ function numpad(options) {
_lastKeyTime: 0, // Timestamp of last keystroke
_burstCount: 0, // Consecutive fast keystrokes
_enterWasFast: false, // Enter arrived in the same burst as the digits
_rejected: false, // Current reading refused (e.g. overflow); ignore the rest of it
_runStart: null, // {value, gap} snapshot taken at the first key of the current run
/**
* Get the display value with sign
@@ -55,6 +57,8 @@ function numpad(options) {
* @param {string} d - The digit to add (0-9)
*/
addDigit(d) {
if (this._rejected) return;
// Validate: don't exceed max digits
const parts = this.value.split('.');
@@ -63,7 +67,15 @@ function numpad(options) {
if (parts[1] && parts[1].length >= this.maxDecDigits) return;
} else {
// Check integer part length
if (parts[0] && parts[0].length >= this.maxIntDigits) return;
if (parts[0] && parts[0].length >= this.maxIntDigits) {
// A caliper reading whose integer part does not fit must not be
// quietly cut down to something that does: refuse the whole
// reading instead of saving a truncated number. Manual typing
// (outside a burst) keeps the old, harmless behaviour of simply
// not accepting more digits.
if (this._burstCount >= 2) this._rejectReading('integer_overflow');
return;
}
}
// Prevent leading zeros (except "0.")
@@ -79,6 +91,8 @@ function numpad(options) {
* Add a decimal point to the current value
*/
addDecimal() {
if (this._rejected) return;
// Don't add decimal if one already exists
if (this.hasDecimal) return;
@@ -90,10 +104,11 @@ function numpad(options) {
},
/**
* Toggle the sign of the current value
* Toggle the sign of the current value. Works on an empty display too -
* the manual +/- key is pressed before the first digit as often as after
* one, and used to be silently dropped in that case.
*/
toggleSign() {
if (!this.value) return;
this.negative = !this.negative;
},
@@ -119,6 +134,8 @@ function numpad(options) {
this.negative = false;
this.hasDecimal = false;
this._enterWasFast = false;
this._rejected = false;
this._runStart = null;
},
/**
@@ -138,6 +155,7 @@ function numpad(options) {
* Confirm the current value and dispatch event
*/
confirm() {
this._rejected = false;
if (!this.hasValue) return;
const val = this.numericValue;
@@ -239,11 +257,32 @@ function numpad(options) {
this._enterWasFast = gap < 80;
this.confirm();
}
// Minus sign - toggle sign
// Minus / plus - the caliper's own sign character. Unlike the manual
// +/- key (toggleSign(), bound to the on-screen button) these are
// absolute rather than a toggle: a caliper transmission says which
// sign the reading is, it never flips one already on screen.
else if (e.key === '-') {
e.preventDefault();
this.toggleSign();
this.negative = true;
}
else if (e.key === '+') {
e.preventDefault();
this.negative = false;
}
},
/**
* Refuse the reading in progress: say why with an event the page can show,
* and wipe the display so the rejected digits do not linger as if valid.
* Further keystrokes of the same burst are ignored until Enter or a clear.
*/
_rejectReading(reason) {
this.$dispatch('numpad-error', { reason: reason });
this.value = '';
this.negative = false;
this.hasDecimal = false;
this._enterWasFast = false;
this._rejected = true;
},
/**
@@ -253,12 +292,22 @@ function numpad(options) {
*/
_trackBurst() {
const now = performance.now();
const gap = now - this._lastKeyTime;
const gap = this._lastKeyTime > 0 ? now - this._lastKeyTime : Infinity;
if (this._lastKeyTime > 0 && gap < 80) {
if (gap < 80) {
this._burstCount++;
// The burst is only recognisable from its second character. If the
// pause before the first one was long enough to be a human keystroke,
// whatever was on display before this run began was typed by hand:
// drop it now instead of letting the caliper's own digits
// concatenate onto it.
if (this._burstCount === 2 && this._runStart && this._runStart.gap > 300) {
this.value = this.value.slice(this._runStart.value.length);
this.hasDecimal = this.value.includes('.');
}
} else {
this._burstCount = 1;
this._runStart = { value: this.value, gap: gap };
}
this._lastKeyTime = now;