mirror of
https://github.com/Ark0N/Codeman.git
synced 2026-10-09 08:59:40 +02:00
fix(tiles): a tile focus no longer leaves a glow listener on its tab
_selectTiledSession added a once animationend listener to the focused tile's tab on every focus. On every skin but OG the glow is `animation: none`, so animationend never fires: the listeners piled up on the tab (and the class stayed). It now glows a tab only when it is not glowing already, so a tab holds at most one; on OG the animation ends, the class goes and the next focus glows again. Measured (50 tile focus changes, daylight-blue): animationend listeners on the tabs 0 -> 49 before, 0 -> 6 (one per tab) after. Over the leak run's 20 grid open/close cycles the page's listener count grew 1003 -> 1042; this is the part CDP could attribute. Live, this code: 50 tile focus changes leave 6 animationend listeners on the tabs, one per tab. The single view's copy of the same block (app.js selectSession) has the same leak on every non-OG skin; it is a separate, pre-existing copy and is left alone here. Test: ten focus changes leave one listener; after animationend the next focus glows again; mutation-checked. Scope: PR 2 (tile-grid.js). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1535,7 +1535,10 @@ Object.assign(CodemanApp.prototype, {
|
|||||||
this.closeSessionSidebarOnHandheld?.();
|
this.closeSessionSidebarOnHandheld?.();
|
||||||
this.renderSessionTabs?.();
|
this.renderSessionTabs?.();
|
||||||
const activeTab = document.querySelector(`.session-tab.active[data-id="${sessionId}"]`);
|
const activeTab = document.querySelector(`.session-tab.active[data-id="${sessionId}"]`);
|
||||||
if (activeTab) {
|
// Not while it still glows: on every skin but OG the glow is `animation:
|
||||||
|
// none`, so animationend never comes and each tile focus used to leave
|
||||||
|
// another listener behind on the tab (49 after 50 focus changes).
|
||||||
|
if (activeTab && !activeTab.classList.contains('tab-glow')) {
|
||||||
activeTab.classList.add('tab-glow');
|
activeTab.classList.add('tab-glow');
|
||||||
activeTab.addEventListener('animationend', () => activeTab.classList.remove('tab-glow'), { once: true });
|
activeTab.addEventListener('animationend', () => activeTab.classList.remove('tab-glow'), { once: true });
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,7 +19,9 @@
|
|||||||
*/
|
*/
|
||||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||||
import {
|
import {
|
||||||
|
FakeEl,
|
||||||
FakeTile,
|
FakeTile,
|
||||||
|
bySelector,
|
||||||
idleCallbacks,
|
idleCallbacks,
|
||||||
localStore,
|
localStore,
|
||||||
makeGridApp,
|
makeGridApp,
|
||||||
@@ -63,6 +65,29 @@ describe('selectSession with the grid open', () => {
|
|||||||
expect(app._focusedPane().sessionId).toBe('s-b');
|
expect(app._focusedPane().sessionId).toBe('s-b');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('focus changes leave at most one glow listener on a tab, and the glow runs again once it ended', async () => {
|
||||||
|
// On every skin but OG the glow is `animation: none`: animationend never
|
||||||
|
// fires, and every focus used to add one more once-listener to the tab.
|
||||||
|
const app = openGrid();
|
||||||
|
const tab = new FakeEl();
|
||||||
|
tab.className = 'session-tab active';
|
||||||
|
bySelector.set('.session-tab.active[data-id="s-b"]', tab);
|
||||||
|
for (let i = 0; i < 5; i++) {
|
||||||
|
await app.selectSession('s-b');
|
||||||
|
await app.selectSession('s-a');
|
||||||
|
}
|
||||||
|
expect(tab.classList.contains('tab-glow')).toBe(true);
|
||||||
|
expect(tab.listeners.animationend).toHaveLength(1);
|
||||||
|
|
||||||
|
// OG: the animation ends, the class goes, and the next focus glows again.
|
||||||
|
tab.dispatch('animationend');
|
||||||
|
tab.listeners.animationend = [];
|
||||||
|
expect(tab.classList.contains('tab-glow')).toBe(false);
|
||||||
|
await app.selectSession('s-b');
|
||||||
|
expect(tab.classList.contains('tab-glow')).toBe(true);
|
||||||
|
expect(tab.listeners.animationend).toHaveLength(1);
|
||||||
|
});
|
||||||
|
|
||||||
it('a user-initiated focus acknowledges the idle alert; an `auto` one does not', async () => {
|
it('a user-initiated focus acknowledges the idle alert; an `auto` one does not', async () => {
|
||||||
const app = openGrid();
|
const app = openGrid();
|
||||||
await app.selectSession('s-b');
|
await app.selectSession('s-b');
|
||||||
|
|||||||
Reference in New Issue
Block a user