fix: resolve /clear command and ESC key lag caused by hooks system

- Make hook events fire-and-forget in clearCommand to avoid blocking UI
- Move context.ui.clear() before resetChat for immediate responsiveness
- Add hasHooksForEvent() fast-path check to HookSystem and Config
- Skip MessageBus round-trips in client.ts when no hooks are registered
- Add comprehensive unit tests for all changes

Fixes #2651
This commit is contained in:
LaZzyMan 2026-03-25 14:01:24 +08:00
parent 3776825c2d
commit 05062fd689
8 changed files with 297 additions and 25 deletions

View file

@ -1582,4 +1582,37 @@ describe('Model Switching and Config Updates', () => {
const updatedConfig = config.getContentGeneratorConfig();
expect(updatedConfig['contextWindowSize']).toBe(128_000);
});
describe('hasHooksForEvent', () => {
it('should return false when hookSystem is not initialized', () => {
const config = new Config(baseParams);
expect(config.hasHooksForEvent('Stop')).toBe(false);
});
it('should delegate to hookSystem.hasHooksForEvent when hookSystem exists', () => {
const config = new Config(baseParams);
const mockHasHooksForEvent = vi.fn().mockReturnValue(true);
const mockHookSystem = {
hasHooksForEvent: mockHasHooksForEvent,
};
// @ts-expect-error - accessing private for testing
config['hookSystem'] = mockHookSystem;
expect(config.hasHooksForEvent('UserPromptSubmit')).toBe(true);
expect(mockHasHooksForEvent).toHaveBeenCalledWith('UserPromptSubmit');
});
it('should return false when hookSystem has no hooks for the event', () => {
const config = new Config(baseParams);
const mockHasHooksForEvent = vi.fn().mockReturnValue(false);
const mockHookSystem = {
hasHooksForEvent: mockHasHooksForEvent,
};
// @ts-expect-error - accessing private for testing
config['hookSystem'] = mockHookSystem;
expect(config.hasHooksForEvent('Stop')).toBe(false);
expect(mockHasHooksForEvent).toHaveBeenCalledWith('Stop');
});
});
});

View file

@ -1769,6 +1769,15 @@ export class Config {
return this.hookSystem;
}
/**
* Fast-path check: returns true only when hooks are enabled AND there are
* registered hooks for the given event name. Callers can use this to skip
* expensive MessageBus round-trips when no hooks are configured.
*/
hasHooksForEvent(eventName: string): boolean {
return this.hookSystem?.hasHooksForEvent(eventName) ?? false;
}
/**
* Check if hooks are enabled.
*/