refactor: replace store-based keyboard action requests with direct dispatcher and simplify agent attention clearing

This commit is contained in:
Mohamed Boudra
2026-03-06 14:49:06 +07:00
parent 885b575d14
commit 5b270444d5
25 changed files with 617 additions and 1355 deletions
@@ -0,0 +1,152 @@
import { beforeEach, describe, expect, it, vi } from "vitest";
import {
createKeyboardActionDispatcher,
type KeyboardActionDefinition,
} from "./keyboard-action-dispatcher";
describe("keyboard-action-dispatcher", () => {
let dispatcher: ReturnType<typeof createKeyboardActionDispatcher>;
beforeEach(() => {
dispatcher = createKeyboardActionDispatcher();
});
it("dispatches to the highest-priority active handler", () => {
const calls: string[] = [];
const action: KeyboardActionDefinition = {
id: "message-input.dictation-toggle",
scope: "message-input",
};
dispatcher.registerHandler({
handlerId: "low",
actions: [action.id],
enabled: true,
priority: 100,
isActive: () => true,
handle: () => {
calls.push("low");
return true;
},
});
dispatcher.registerHandler({
handlerId: "high",
actions: [action.id],
enabled: true,
priority: 200,
isActive: () => true,
handle: () => {
calls.push("high");
return true;
},
});
const handled = dispatcher.dispatch(action);
expect(handled).toBe(true);
expect(calls).toEqual(["high"]);
});
it("skips disabled and inactive handlers", () => {
const handle = vi.fn(() => true);
const action: KeyboardActionDefinition = {
id: "message-input.dictation-toggle",
scope: "message-input",
};
dispatcher.registerHandler({
handlerId: "disabled",
actions: [action.id],
enabled: false,
priority: 300,
isActive: () => true,
handle,
});
dispatcher.registerHandler({
handlerId: "inactive",
actions: [action.id],
enabled: true,
priority: 200,
isActive: () => false,
handle,
});
dispatcher.registerHandler({
handlerId: "active",
actions: [action.id],
enabled: true,
priority: 100,
isActive: () => true,
handle,
});
const handled = dispatcher.dispatch(action);
expect(handled).toBe(true);
expect(handle).toHaveBeenCalledTimes(1);
});
it("tries lower-priority handlers when a higher one does not consume the action", () => {
const calls: string[] = [];
const action: KeyboardActionDefinition = {
id: "message-input.focus",
scope: "message-input",
};
dispatcher.registerHandler({
handlerId: "first",
actions: [action.id],
enabled: true,
priority: 200,
isActive: () => true,
handle: () => {
calls.push("first");
return false;
},
});
dispatcher.registerHandler({
handlerId: "second",
actions: [action.id],
enabled: true,
priority: 100,
isActive: () => true,
handle: () => {
calls.push("second");
return true;
},
});
const handled = dispatcher.dispatch(action);
expect(handled).toBe(true);
expect(calls).toEqual(["first", "second"]);
});
it("does not dispatch after a handler is unregistered", () => {
const handle = vi.fn(() => true);
const action: KeyboardActionDefinition = {
id: "message-input.dictation-cancel",
scope: "message-input",
};
const unregister = dispatcher.registerHandler({
handlerId: "handler",
actions: [action.id],
enabled: true,
priority: 100,
isActive: () => true,
handle,
});
unregister();
const handled = dispatcher.dispatch(action);
expect(handled).toBe(false);
expect(handle).not.toHaveBeenCalled();
});
});