feat: add terminal navigation action shortcut

This commit is contained in:
Federico Jaramillo Martinez
2026-05-20 15:15:36 +02:00
parent 3da2fcf251
commit e5bc87b7c1
4 changed files with 58 additions and 1 deletions
+5
View File
@@ -0,0 +1,5 @@
---
"@jmfederico/pi-web": patch
---
Add a Go to Terminal action with a keyboard shortcut and clarify that plugin shortcuts are default keybindings attached to actions.
+9 -1
View File
@@ -326,7 +326,15 @@ Notes:
- Other `state` fields may exist at runtime, but they are Pi Web internals and can change quickly.
- `enabled` is evaluated when the action palette asks for actions.
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
- `shortcut` is displayed/handled the same way app actions are; choose shortcuts carefully to avoid conflicts.
#### Keyboard shortcuts
- App-level keyboard shortcuts must be attached to actions. Pi Web does not support standalone plugin keyboard commands; contribute an action first, then add a `shortcut` if it needs a keybinding.
- `shortcut` is the action's default keybinding. It is displayed in the action palette and handled by the global shortcut dispatcher when the action is enabled.
- Use modified shortcuts such as `mod+shift+p`; plain letter shortcuts are intentionally ignored so normal typing is never captured.
- Future Pi Web versions may allow users to override or disable action shortcuts by action id, so plugins should treat `shortcut` as a default rather than a guaranteed final binding.
- Choose shortcuts carefully to avoid conflicts. There is no user-facing shortcut override or conflict resolver yet.
- Local text input, terminal input, list navigation, and dialog keys such as Enter, Escape, and arrow keys do not need to be plugin actions unless they are app-level commands.
### Workspace panels
+8
View File
@@ -70,6 +70,14 @@ export function createCoreActions(): PluginAction[] {
enabled: hasGitWorkspace,
run: (context) => { context.selectMainView("core:workspace.git"); },
},
{
id: "view.terminal",
title: "Go to Terminal",
shortcut: "mod+4",
group: "Navigation",
enabled: hasWorkspace,
run: (context) => { context.selectMainView("core:workspace.terminal"); },
},
{
id: "workspace.refresh-files",
title: "Refresh Files",
+36
View File
@@ -66,7 +66,9 @@ describe("PluginRegistry", () => {
const active = registry.getActions(createContext({ selectedWorkspace: testWorkspace() }).context);
expect(inactive.find((action) => action.id === "core:view.files")?.enabled).toBe(false);
expect(inactive.find((action) => action.id === "core:view.terminal")?.enabled).toBe(false);
expect(active.find((action) => action.id === "core:view.files")?.enabled).toBe(true);
expect(active.find((action) => action.id === "core:view.terminal")?.enabled).toBe(true);
});
it("routes refresh current to the active core workspace panel", () => {
@@ -83,6 +85,40 @@ describe("PluginRegistry", () => {
expect(calls).toEqual(["refreshGit"]);
});
it("exposes terminal navigation as a shortcut-backed action", () => {
const registry = new PluginRegistry();
registry.register({ id: "core", plugin: corePlugin });
const { context, calls } = createContext({ selectedWorkspace: testWorkspace() });
const action = registry.getActions(context).find((candidate) => candidate.id === "core:view.terminal");
expect(action?.shortcut).toBe("mod+4");
if (action !== undefined) void action.run();
expect(calls).toEqual(["selectMainView:core:workspace.terminal"]);
});
it("keeps built-in keyboard shortcuts unique and action-backed", () => {
const registry = new PluginRegistry();
registry.register({ id: "core", plugin: corePlugin });
const shortcuts = registry.getActions(createContext({ selectedWorkspace: testWorkspace() }).context)
.filter((action) => action.shortcut !== undefined)
.map((action) => [action.id, action.shortcut]);
expect(shortcuts).toEqual([
["core:actions.show", "mod+k"],
["core:view.chat", "mod+1"],
["core:view.files", "mod+2"],
["core:view.git", "mod+3"],
["core:view.terminal", "mod+4"],
["core:workspace.refresh-files", "mod+shift+f"],
["core:workspace.refresh-git", "mod+shift+g"],
["core:workspace.refresh-current", "mod+shift+r"],
["core:session.start", "mod+enter"],
["core:session.stop", "mod+."],
]);
expect(new Set(shortcuts.map(([, shortcut]) => shortcut)).size).toBe(shortcuts.length);
});
it("collects built-in Pi Web themes from an in-app plugin", () => {
const registry = new PluginRegistry();
registry.register({ id: "themes", plugin: themePackPlugin });