mirror of
https://github.com/badlogic/pi-mono.git
synced 2026-11-05 23:37:25 +00:00
fix(coding-agent): load user keybindings in pi config (#11788)
`pi config` ignored `~/.pi/agent/keybindings.json`. `selectConfig()` never called `setKeybindings()`, so the config selector's `getKeybindings()` fell back to the default TUI bindings. Remaps such as `"tui.select.down": ["down", "ctrl+n"]` did nothing there, although they work in `/settings` and the `--resume` picker. `selectConfig()` now installs `KeybindingsManager.create(options.agentDir)` before it builds the UI, the same way the session picker and startup UI do. **Testing** - Added `test/config-selector-keybindings.test.ts`. It runs `selectConfig()` with a stubbed terminal and component and an agent dir whose `keybindings.json` remaps `tui.select.up` and `tui.select.down`, then checks that the active keybindings include `ctrl+p` and `ctrl+n`. It fails without the change and passes with it. - Manual check: ran `pi config` in tmux with that `keybindings.json`. Before the change, Ctrl+N did nothing. After it, Ctrl+N moves the selection like Down. - `npm run check` reports only two errors, both missing modules in `packages/schema/test/bench/candidates.ts`. They occur on `main` without this change. Fixes #11651 Requested by @badlogic Co-authored-by: triagent <triagent@localhost>
This commit is contained in:
parent
081a6e87f6
commit
b0c64f33c8
2 changed files with 78 additions and 1 deletions
|
|
@ -2,7 +2,8 @@
|
|||
* TUI config selector for `pi config` command
|
||||
*/
|
||||
|
||||
import { ProcessTerminal, type TUI, TuiMainScreen } from "@earendil-works/pi-tui";
|
||||
import { ProcessTerminal, setKeybindings, type TUI, TuiMainScreen } from "@earendil-works/pi-tui";
|
||||
import { KeybindingsManager } from "../core/keybindings.ts";
|
||||
import type { SettingsManager } from "../core/settings-manager.ts";
|
||||
import { ConfigSelectorComponent, type ScopedResolvedPaths } from "../modes/interactive/components/config-selector.ts";
|
||||
import { initTheme, stopThemeWatcher } from "../modes/interactive/theme/theme.ts";
|
||||
|
|
@ -20,6 +21,7 @@ export interface ConfigSelectorOptions {
|
|||
export async function selectConfig(options: ConfigSelectorOptions): Promise<void> {
|
||||
// Initialize theme before showing TUI
|
||||
initTheme(options.settingsManager.getTheme(), true);
|
||||
setKeybindings(KeybindingsManager.create(options.agentDir));
|
||||
|
||||
return new Promise((resolve) => {
|
||||
const ui: TUI = new TuiMainScreen(
|
||||
|
|
|
|||
|
|
@ -0,0 +1,75 @@
|
|||
import { mkdtempSync, rmSync, writeFileSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import { join } from "node:path";
|
||||
import { getKeybindings, setKeybindings } from "@earendil-works/pi-tui";
|
||||
import { afterEach, describe, expect, it, vi } from "vitest";
|
||||
|
||||
vi.mock("@earendil-works/pi-tui", async (importOriginal) => {
|
||||
const actual = await importOriginal<typeof import("@earendil-works/pi-tui")>();
|
||||
class FakeTui {
|
||||
terminal = { rows: 24 };
|
||||
setClearOnShrink() {}
|
||||
addChild() {}
|
||||
setFocus() {}
|
||||
start() {}
|
||||
stop() {}
|
||||
requestRender() {}
|
||||
}
|
||||
return { ...actual, ProcessTerminal: class {}, TuiMainScreen: FakeTui };
|
||||
});
|
||||
|
||||
vi.mock("../src/modes/interactive/components/config-selector.ts", () => ({
|
||||
ConfigSelectorComponent: class {
|
||||
constructor(...args: unknown[]) {
|
||||
const onClose = args[4] as () => void;
|
||||
queueMicrotask(onClose);
|
||||
}
|
||||
getResourceList() {
|
||||
return {};
|
||||
}
|
||||
},
|
||||
}));
|
||||
|
||||
vi.mock("../src/modes/interactive/theme/theme.ts", () => ({
|
||||
initTheme: () => {},
|
||||
stopThemeWatcher: () => {},
|
||||
}));
|
||||
|
||||
import { selectConfig } from "../src/cli/config-selector.ts";
|
||||
import { KeybindingsManager } from "../src/core/keybindings.ts";
|
||||
import type { SettingsManager } from "../src/core/settings-manager.ts";
|
||||
|
||||
describe("selectConfig keybindings", () => {
|
||||
let agentDir: string | undefined;
|
||||
|
||||
afterEach(() => {
|
||||
if (agentDir) rmSync(agentDir, { recursive: true, force: true });
|
||||
agentDir = undefined;
|
||||
setKeybindings(new KeybindingsManager({}));
|
||||
});
|
||||
|
||||
it("uses the user's keybindings.json", async () => {
|
||||
agentDir = mkdtempSync(join(tmpdir(), "pi-config-keys-"));
|
||||
writeFileSync(
|
||||
join(agentDir, "keybindings.json"),
|
||||
JSON.stringify({ "tui.select.up": ["up", "ctrl+p"], "tui.select.down": ["down", "ctrl+n"] }),
|
||||
);
|
||||
const settingsManager = {
|
||||
getTheme: () => undefined,
|
||||
getShowHardwareCursor: () => false,
|
||||
getClearOnShrink: () => false,
|
||||
} as unknown as SettingsManager;
|
||||
|
||||
await selectConfig({
|
||||
resolvedPaths: {} as never,
|
||||
settingsManager,
|
||||
cwd: agentDir,
|
||||
agentDir,
|
||||
writeScope: "global",
|
||||
projectModeAvailable: false,
|
||||
});
|
||||
|
||||
expect(getKeybindings().getKeys("tui.select.down")).toContain("ctrl+n");
|
||||
expect(getKeybindings().getKeys("tui.select.up")).toContain("ctrl+p");
|
||||
});
|
||||
});
|
||||
Loading…
Add table
Add a link
Reference in a new issue