fix(applauncher): throttle inactivity timer resets in menus, optimize sound slider updates, and adjust keyboard layout/keys

This commit is contained in:
2026-01-06 09:58:40 +00:00
parent dcc3e18474
commit 79f41a6001
6 changed files with 64 additions and 9 deletions

View File

@@ -1,5 +1,13 @@
# Changelog # Changelog
## 2026-01-06 - 3.34.2 - fix(applauncher)
throttle inactivity timer resets in menus, optimize sound slider updates, and adjust keyboard layout/keys
- Add lastActivityTime and throttle resetInactivityTimer to only reset if 5+ seconds have passed in battery, sound, and wifi menus to reduce frequent resets from continuous input.
- Remove @mousemove listener in menu containers and rely on mousedown + throttled resets to lower event noise.
- Debounce slider mousemove handling in sound menu using requestAnimationFrame and pendingPercentage to batch setVolume calls and cancel RAF on mouseup, preventing excessive volume updates.
- Add up/down arrow keys to virtual keyboard, reduce space key width from 4 to 3, and add .key.wide-3 CSS class to support the new sizing.
## 2026-01-06 - 3.34.1 - fix(elements/applauncher) ## 2026-01-06 - 3.34.1 - fix(elements/applauncher)
add eco app launcher components, wifi/sound/battery menus, demos and new eco-screensaver; replace dees-screensaver (breaking API change) add eco app launcher components, wifi/sound/battery menus, demos and new eco-screensaver; replace dees-screensaver (breaking API change)

View File

@@ -3,6 +3,6 @@
*/ */
export const commitinfo = { export const commitinfo = {
name: '@ecobridge.xyz/catalog', name: '@ecobridge.xyz/catalog',
version: '3.34.1', version: '3.34.2',
description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.' description: 'A comprehensive library that provides dynamic web components for building sophisticated and modern web applications using JavaScript and TypeScript.'
} }

View File

@@ -248,6 +248,7 @@ export class EcoApplauncherBatterymenu extends DeesElement {
private boundHandleClickOutside = this.handleClickOutside.bind(this); private boundHandleClickOutside = this.handleClickOutside.bind(this);
private inactivityTimeout: ReturnType<typeof setTimeout> | null = null; private inactivityTimeout: ReturnType<typeof setTimeout> | null = null;
private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute
private lastActivityTime = 0;
public render(): TemplateResult { public render(): TemplateResult {
const fillClass = this.getFillClass(); const fillClass = this.getFillClass();
@@ -255,7 +256,6 @@ export class EcoApplauncherBatterymenu extends DeesElement {
return html` return html`
<div class="menu-container" <div class="menu-container"
@click=${(e: MouseEvent) => e.stopPropagation()} @click=${(e: MouseEvent) => e.stopPropagation()}
@mousemove=${this.resetInactivityTimer}
@mousedown=${this.resetInactivityTimer} @mousedown=${this.resetInactivityTimer}
> >
<div class="battery-display"> <div class="battery-display">
@@ -340,6 +340,12 @@ export class EcoApplauncherBatterymenu extends DeesElement {
} }
private resetInactivityTimer(): void { private resetInactivityTimer(): void {
const now = Date.now();
// Throttle: only reset if 5+ seconds since last reset
if (now - this.lastActivityTime < 5000) {
return;
}
this.lastActivityTime = now;
this.clearInactivityTimer(); this.clearInactivityTimer();
if (this.open) { if (this.open) {
this.inactivityTimeout = setTimeout(() => { this.inactivityTimeout = setTimeout(() => {

View File

@@ -78,8 +78,10 @@ const qwertyLayout: IKeyConfig[][] = [
[ [
{ key: '123', display: '123', width: 1.5, type: 'layout', action: 'numbers' }, { key: '123', display: '123', width: 1.5, type: 'layout', action: 'numbers' },
{ key: 'globe', display: '🌐', type: 'special' }, { key: 'globe', display: '🌐', type: 'special' },
{ key: 'space', display: '', width: 4, type: 'space' }, { key: 'space', display: '', width: 3, type: 'space' },
{ key: 'left', display: '←', type: 'special', action: 'arrow-left' }, { key: 'left', display: '←', type: 'special', action: 'arrow-left' },
{ key: 'up', display: '↑', type: 'special', action: 'arrow-up' },
{ key: 'down', display: '↓', type: 'special', action: 'arrow-down' },
{ key: 'right', display: '→', type: 'special', action: 'arrow-right' }, { key: 'right', display: '→', type: 'special', action: 'arrow-right' },
{ key: 'enter', display: '↵', width: 1.5, type: 'special' }, { key: 'enter', display: '↵', width: 1.5, type: 'special' },
], ],
@@ -102,8 +104,10 @@ const numbersLayout: IKeyConfig[][] = [
[ [
{ key: 'ABC', display: 'ABC', width: 1.5, type: 'layout', action: 'qwerty' }, { key: 'ABC', display: 'ABC', width: 1.5, type: 'layout', action: 'qwerty' },
{ key: 'globe', display: '🌐', type: 'special' }, { key: 'globe', display: '🌐', type: 'special' },
{ key: 'space', display: '', width: 4, type: 'space' }, { key: 'space', display: '', width: 3, type: 'space' },
{ key: 'left', display: '←', type: 'special', action: 'arrow-left' }, { key: 'left', display: '←', type: 'special', action: 'arrow-left' },
{ key: 'up', display: '↑', type: 'special', action: 'arrow-up' },
{ key: 'down', display: '↓', type: 'special', action: 'arrow-down' },
{ key: 'right', display: '→', type: 'special', action: 'arrow-right' }, { key: 'right', display: '→', type: 'special', action: 'arrow-right' },
{ key: 'enter', display: '↵', width: 1.5, type: 'special' }, { key: 'enter', display: '↵', width: 1.5, type: 'special' },
], ],
@@ -126,8 +130,10 @@ const symbolsLayout: IKeyConfig[][] = [
[ [
{ key: 'ABC', display: 'ABC', width: 1.5, type: 'layout', action: 'qwerty' }, { key: 'ABC', display: 'ABC', width: 1.5, type: 'layout', action: 'qwerty' },
{ key: 'globe', display: '🌐', type: 'special' }, { key: 'globe', display: '🌐', type: 'special' },
{ key: 'space', display: '', width: 4, type: 'space' }, { key: 'space', display: '', width: 3, type: 'space' },
{ key: 'left', display: '←', type: 'special', action: 'arrow-left' }, { key: 'left', display: '←', type: 'special', action: 'arrow-left' },
{ key: 'up', display: '↑', type: 'special', action: 'arrow-up' },
{ key: 'down', display: '↓', type: 'special', action: 'arrow-down' },
{ key: 'right', display: '→', type: 'special', action: 'arrow-right' }, { key: 'right', display: '→', type: 'special', action: 'arrow-right' },
{ key: 'enter', display: '↵', width: 1.5, type: 'special' }, { key: 'enter', display: '↵', width: 1.5, type: 'special' },
], ],
@@ -245,6 +251,11 @@ export class EcoApplauncherKeyboard extends DeesElement {
max-width: 100px; max-width: 100px;
} }
.key.wide-3 {
flex: 3;
max-width: 140px;
}
.key.wide-4 { .key.wide-4 {
flex: 4; flex: 4;
max-width: 180px; max-width: 180px;

View File

@@ -249,6 +249,7 @@ export class EcoApplauncherSoundmenu extends DeesElement {
private isDragging = false; private isDragging = false;
private inactivityTimeout: ReturnType<typeof setTimeout> | null = null; private inactivityTimeout: ReturnType<typeof setTimeout> | null = null;
private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute
private lastActivityTime = 0;
public render(): TemplateResult { public render(): TemplateResult {
const volumeIcon = this.getVolumeIcon(); const volumeIcon = this.getVolumeIcon();
@@ -256,7 +257,6 @@ export class EcoApplauncherSoundmenu extends DeesElement {
return html` return html`
<div class="menu-container" <div class="menu-container"
@click=${(e: MouseEvent) => e.stopPropagation()} @click=${(e: MouseEvent) => e.stopPropagation()}
@mousemove=${this.resetInactivityTimer}
@mousedown=${this.resetInactivityTimer} @mousedown=${this.resetInactivityTimer}
> >
<div class="menu-header"> <div class="menu-header">
@@ -368,16 +368,34 @@ export class EcoApplauncherSoundmenu extends DeesElement {
private handleSliderMouseDown(e: MouseEvent): void { private handleSliderMouseDown(e: MouseEvent): void {
this.isDragging = true; this.isDragging = true;
const slider = e.currentTarget as HTMLElement; const slider = e.currentTarget as HTMLElement;
let rafId: number | null = null;
let pendingPercentage: number | null = null;
const updateVolume = () => {
if (pendingPercentage !== null) {
this.setVolume(pendingPercentage);
pendingPercentage = null;
}
rafId = null;
};
const handleMouseMove = (moveEvent: MouseEvent) => { const handleMouseMove = (moveEvent: MouseEvent) => {
if (!this.isDragging) return; if (!this.isDragging) return;
const rect = slider.getBoundingClientRect(); const rect = slider.getBoundingClientRect();
const percentage = Math.round(((moveEvent.clientX - rect.left) / rect.width) * 100); pendingPercentage = Math.max(0, Math.min(100, Math.round(((moveEvent.clientX - rect.left) / rect.width) * 100)));
this.setVolume(Math.max(0, Math.min(100, percentage))); if (!rafId) {
rafId = requestAnimationFrame(updateVolume);
}
}; };
const handleMouseUp = () => { const handleMouseUp = () => {
this.isDragging = false; this.isDragging = false;
if (rafId) {
cancelAnimationFrame(rafId);
}
if (pendingPercentage !== null) {
this.setVolume(pendingPercentage);
}
document.removeEventListener('mousemove', handleMouseMove); document.removeEventListener('mousemove', handleMouseMove);
document.removeEventListener('mouseup', handleMouseUp); document.removeEventListener('mouseup', handleMouseUp);
}; };
@@ -421,6 +439,12 @@ export class EcoApplauncherSoundmenu extends DeesElement {
} }
private resetInactivityTimer(): void { private resetInactivityTimer(): void {
const now = Date.now();
// Throttle: only reset if 5+ seconds since last reset
if (now - this.lastActivityTime < 5000) {
return;
}
this.lastActivityTime = now;
this.clearInactivityTimer(); this.clearInactivityTimer();
if (this.open) { if (this.open) {
this.inactivityTimeout = setTimeout(() => { this.inactivityTimeout = setTimeout(() => {

View File

@@ -231,12 +231,12 @@ export class EcoApplauncherWifimenu extends DeesElement {
private boundHandleClickOutside = this.handleClickOutside.bind(this); private boundHandleClickOutside = this.handleClickOutside.bind(this);
private inactivityTimeout: ReturnType<typeof setTimeout> | null = null; private inactivityTimeout: ReturnType<typeof setTimeout> | null = null;
private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute private readonly INACTIVITY_TIMEOUT = 60000; // 1 minute
private lastActivityTime = 0;
public render(): TemplateResult { public render(): TemplateResult {
return html` return html`
<div class="menu-container" <div class="menu-container"
@click=${(e: MouseEvent) => e.stopPropagation()} @click=${(e: MouseEvent) => e.stopPropagation()}
@mousemove=${this.resetInactivityTimer}
@mousedown=${this.resetInactivityTimer} @mousedown=${this.resetInactivityTimer}
> >
<div class="menu-header"> <div class="menu-header">
@@ -348,6 +348,12 @@ export class EcoApplauncherWifimenu extends DeesElement {
} }
private resetInactivityTimer(): void { private resetInactivityTimer(): void {
const now = Date.now();
// Throttle: only reset if 5+ seconds since last reset
if (now - this.lastActivityTime < 5000) {
return;
}
this.lastActivityTime = now;
this.clearInactivityTimer(); this.clearInactivityTimer();
if (this.open) { if (this.open) {
this.inactivityTimeout = setTimeout(() => { this.inactivityTimeout = setTimeout(() => {