Archived
feat: add terminal navigation action shortcut
This commit is contained in:
@@ -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
@@ -326,7 +326,15 @@ Notes:
|
|||||||
- Other `state` fields may exist at runtime, but they are Pi Web internals and can change quickly.
|
- 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.
|
- `enabled` is evaluated when the action palette asks for actions.
|
||||||
- `selectWorkspaceTool()` expects a qualified panel id such as `my-plugin:workspace.info`.
|
- `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
|
### Workspace panels
|
||||||
|
|
||||||
|
|||||||
@@ -70,6 +70,14 @@ export function createCoreActions(): PluginAction[] {
|
|||||||
enabled: hasGitWorkspace,
|
enabled: hasGitWorkspace,
|
||||||
run: (context) => { context.selectMainView("core:workspace.git"); },
|
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",
|
id: "workspace.refresh-files",
|
||||||
title: "Refresh Files",
|
title: "Refresh Files",
|
||||||
|
|||||||
@@ -66,7 +66,9 @@ describe("PluginRegistry", () => {
|
|||||||
const active = registry.getActions(createContext({ selectedWorkspace: testWorkspace() }).context);
|
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.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.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", () => {
|
it("routes refresh current to the active core workspace panel", () => {
|
||||||
@@ -83,6 +85,40 @@ describe("PluginRegistry", () => {
|
|||||||
expect(calls).toEqual(["refreshGit"]);
|
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", () => {
|
it("collects built-in Pi Web themes from an in-app plugin", () => {
|
||||||
const registry = new PluginRegistry();
|
const registry = new PluginRegistry();
|
||||||
registry.register({ id: "themes", plugin: themePackPlugin });
|
registry.register({ id: "themes", plugin: themePackPlugin });
|
||||||
|
|||||||
Reference in New Issue
Block a user