Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
81 changes: 80 additions & 1 deletion docs/docs/keybindings.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,8 @@ Chords are shown with a + between the keys. You have 2 seconds to hit the 2nd ch
| <Kbd k="Ctrl:Shift:0"/> | Focus WaveAI input |
| <Kbd k="Ctrl:Shift:1-9"/> | Switch to block number |
| <Kbd k="Ctrl:Shift:Arrows"/> / <Kbd k="Ctrl:Shift:h/j/k/l"/> | Move left, right, up, down between blocks |
| <Kbd k="Ctrl:Shift:]"/> | Cycle block focus forward (CW) |
| <Kbd k="Ctrl:Shift:["/> | Cycle block focus backward (CCW) |
| <Kbd k="Ctrl:Shift:x"/> | Replace the current block with a launcher block |
| <Kbd k="Cmd:1-9"/> | Switch to tab number |
| <Kbd k="Cmd:["/> / <Kbd k="Shift:Cmd:["/> | Switch tab left |
Expand Down Expand Up @@ -105,7 +107,84 @@ Chords are shown with a + between the keys. You have 2 seconds to hit the 2nd ch
| <Kbd k="Shift:PageUp"/> | Scroll up one page |
| <Kbd k="Shift:PageDown"/>| Scroll down one page |

## Customizeable Systemwide Global Hotkey
## Customizing Keybindings

You can override, remap, or disable any default keybinding by editing `keybindings.json` in the Wave config directory (`~/.config/waveterm/keybindings.json`). You can also edit this file from within Wave by opening the Config editor and selecting "Keybindings" in the sidebar.

The file uses a VS Code-style array format. Each entry maps a key combination to an action ID. Only overrides are needed — all defaults apply automatically.

### Key Syntax

Key combinations use colon-separated format:

- **Modifiers:** `Cmd` (macOS Command / Windows-Linux Meta), `Ctrl`, `Shift`, `Alt` (macOS Option), `Meta`
- **Special keys:** `ArrowUp`, `ArrowDown`, `ArrowLeft`, `ArrowRight`, `Home`, `End`, `Escape`, `Enter`, `Tab`, `Space`, `Backspace`, `Delete`
- **Letters and digits:** Lowercase (`a`–`z`), digits (`0`–`9`)

### Examples

**Rebind a key:** Change "new tab" from <Kbd k="Cmd:t"/> to <Kbd k="Cmd:Shift:t"/>:
```json
[
{ "key": "Cmd:Shift:t", "command": "tab:new" }
]
```

**Disable a keybinding:** Remove <Kbd k="Cmd:w"/> close block:
```json
[
{ "command": "-block:close" }
]
```

You can also set `key` to `null` to unbind:
```json
[
{ "key": null, "command": "block:close" }
]
```

**Swap two keys:**
```json
[
{ "key": "Cmd:d", "command": "block:splitdown" },
{ "key": "Cmd:Shift:d", "command": "block:splitright" }
]
```

### Action IDs

| Action ID | Default Key | Description |
| --- | --- | --- |
| `tab:new` | <Kbd k="Cmd:t"/> | Open a new tab |
| `tab:close` | <Kbd k="Cmd:Shift:w"/> | Close the current tab |
| `tab:prev` | <Kbd k="Cmd:["/> | Switch to previous tab |
| `tab:next` | <Kbd k="Cmd:]"/> | Switch to next tab |
| `tab:switchto1`–`tab:switchto9` | <Kbd k="Cmd:1"/>–<Kbd k="Cmd:9"/> | Switch to tab N |
| `block:new` | <Kbd k="Cmd:n"/> | Open a new block |
| `block:close` | <Kbd k="Cmd:w"/> | Close the current block |
| `block:splitright` | <Kbd k="Cmd:d"/> | Split right |
| `block:splitdown` | <Kbd k="Cmd:Shift:d"/> | Split down |
| `block:magnify` | <Kbd k="Cmd:m"/> | Magnify/unmagnify block |
| `block:refocus` | <Kbd k="Cmd:i"/> | Refocus the current block |
| `block:navup/navdown/navleft/navright` | <Kbd k="Ctrl:Shift:Arrows"/> | Navigate between blocks |
| `block:navcw` | <Kbd k="Ctrl:Shift:]"/> | Cycle block focus forward (CW) |
| `block:navccw` | <Kbd k="Ctrl:Shift:["/> | Cycle block focus backward (CCW) |
| `block:switchto1`–`block:switchto9` | <Kbd k="Ctrl:Shift:1"/>–<Kbd k="Ctrl:Shift:9"/> | Switch to block N |
| `block:switchtoai` | <Kbd k="Ctrl:Shift:0"/> | Focus WaveAI input |
| `block:replacewithlauncher` | <Kbd k="Ctrl:Shift:x"/> | Replace block with launcher |
| `app:search` | <Kbd k="Cmd:f"/> | Find/search |
| `app:openconnection` | <Kbd k="Cmd:g"/> | Open connection switcher |
| `app:toggleaipanel` | <Kbd k="Cmd:Shift:a"/> | Toggle WaveAI panel |
| `app:togglewidgetssidebar` | <Kbd k="Cmd:b"/> | Toggle widgets sidebar |
| `app:settings` | <Kbd k="Cmd:,"/> | Open settings |
| `term:togglemultiinput` | <Kbd k="Ctrl:Shift:i"/> | Toggle terminal multi-input |
| `generic:cancel` | <Kbd k="Escape"/> | Close modals/search |
| `block:splitchord` | <Kbd k="Ctrl:Shift:s"/> | Initiate split chord |

Changes take effect immediately — no restart required.

## Customizable Systemwide Global Hotkey

Wave allows setting a custom global hotkey to focus your most recent window from anywhere in your computer. For more information on this, see [the config docs](./config#customizable-systemwide-global-hotkey).

Expand Down
6 changes: 6 additions & 0 deletions frontend/app/monaco/schemaendpoints.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import aipresetsSchema from "../../../schema/aipresets.json";
import backgroundsSchema from "../../../schema/backgrounds.json";
import connectionsSchema from "../../../schema/connections.json";
import keybindingsSchema from "../../../schema/keybindings.json";
import settingsSchema from "../../../schema/settings.json";
import waveaiSchema from "../../../schema/waveai.json";
import widgetsSchema from "../../../schema/widgets.json";
Expand Down Expand Up @@ -45,6 +46,11 @@ const MonacoSchemas: SchemaInfo[] = [
fileMatch: ["*/WAVECONFIGPATH/widgets.json"],
schema: widgetsSchema,
},
{
uri: "wave://schema/keybindings.json",
fileMatch: ["*/WAVECONFIGPATH/keybindings.json"],
schema: keybindingsSchema,
},
];

export { MonacoSchemas };
13 changes: 13 additions & 0 deletions frontend/app/store/global-atoms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,18 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
const settingsAtom = atom((get) => {
return get(fullConfigAtom)?.settings ?? {};
}) as Atom<SettingsType>;
const keybindingsAtom = atom((get) => {
const fullConfig = get(fullConfigAtom);
if (!fullConfig?.keybindings) return [];
try {
const raw = fullConfig.keybindings;
const parsed = typeof raw === "string" ? JSON.parse(raw) : raw;
return Array.isArray(parsed) ? parsed : [];
} catch {
console.warn("Failed to parse keybindings.json");
return [];
}
});
const hasCustomAIPresetsAtom = atom((get) => {
const fullConfig = get(fullConfigAtom);
if (!fullConfig?.presets) {
Expand Down Expand Up @@ -136,6 +148,7 @@ function initGlobalAtoms(initOpts: GlobalInitOptions) {
fullConfigAtom,
waveaiModeConfigAtom,
settingsAtom,
keybindingsAtom,
hasCustomAIPresetsAtom,
hasConfigErrors,
staticTabId: staticTabIdAtom,
Expand Down
Loading