feat(coding-agent): make scoped models and tree filter shortcuts configurable (#3343)

- Add app.models.{save,enableAll,clearAll,toggleProvider,reorderUp,reorderDown}
- Add app.tree.filter.{default,noTools,userOnly,labeledOnly,all,cycleForward,cycleBackward}
- Migrate scoped-models-selector cancel to tui.select.cancel
- Prefer reserved actions on key collision in extension shortcut conflict check
This commit is contained in:
Marek Pazik
2026-04-20 20:35:30 +08:00
committed by GitHub
parent d554409b1f
commit d4e2e563ae
7 changed files with 163 additions and 25 deletions

View File

@@ -180,6 +180,29 @@ describe("ExtensionRunner", () => {
warnSpy.mockRestore();
});
it("blocks shortcuts when reserved key is also bound to non-reserved actions", async () => {
const extCode = `
export default function(pi) {
pi.registerShortcut("ctrl+p", {
description: "Conflicts with shared reserved default",
handler: async () => {},
});
}
`;
fs.writeFileSync(path.join(extensionsDir, "shared-reserved.ts"), extCode);
const warnSpy = vi.spyOn(console, "warn").mockImplementation(() => {});
const result = await discoverAndLoadExtensions([], tempDir, tempDir);
const runner = new ExtensionRunner(result.extensions, result.runtime, tempDir, sessionManager, modelRegistry);
const shortcuts = runner.getShortcuts(defaultKeybindings);
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining("conflicts with built-in"));
expect(shortcuts.has("ctrl+p")).toBe(false);
warnSpy.mockRestore();
});
it("blocks shortcuts when reserved action has multiple keys", async () => {
const extCode = `
export default function(pi) {

View File

@@ -1,6 +1,7 @@
import type { TUI } from "@mariozechner/pi-tui";
import { setKeybindings, type TUI } from "@mariozechner/pi-tui";
import stripAnsi from "strip-ansi";
import { afterEach, beforeAll, describe, expect, it } from "vitest";
import { afterEach, beforeAll, beforeEach, describe, expect, it } from "vitest";
import { KeybindingsManager } from "../../../src/core/keybindings.js";
import { ModelSelectorComponent } from "../../../src/modes/interactive/components/model-selector.js";
import { ScopedModelsSelectorComponent } from "../../../src/modes/interactive/components/scoped-models-selector.js";
import { initTheme } from "../../../src/modes/interactive/theme/theme.js";
@@ -23,6 +24,11 @@ describe("issue #3217 scoped model ordering", () => {
initTheme("dark");
});
beforeEach(() => {
// Ensure test isolation: keybindings are a global singleton
setKeybindings(new KeybindingsManager());
});
afterEach(() => {
while (harnesses.length > 0) {
harnesses.pop()?.cleanup();