overmux 0.0.5 → 0.0.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -0
- package/README.md +16 -2
- package/dist/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
- package/dist/docs/400-reference/200-configuration.md +1 -1
- package/dist/docs/400-reference/500-server/500-api.md +1 -1
- package/dist/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
- package/dist/docs/400-reference/600-client/007-deep-links.md +87 -0
- package/dist/docs/400-reference/600-client/010-commands.md +189 -0
- package/dist/docs/400-reference/600-client/020-shortcuts.md +171 -0
- package/dist/docs/400-reference/600-client/100-api.md +1 -1
- package/dist/docs/400-reference/600-client/200-theming.md +179 -64
- package/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
- package/docs/400-reference/200-configuration.md +1 -1
- package/docs/400-reference/500-server/500-api.md +1 -1
- package/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
- package/docs/400-reference/600-client/007-deep-links.md +87 -0
- package/docs/400-reference/600-client/010-commands.md +189 -0
- package/docs/400-reference/600-client/020-shortcuts.md +171 -0
- package/docs/400-reference/600-client/100-api.md +1 -1
- package/docs/400-reference/600-client/200-theming.md +179 -64
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,14 @@
|
|
|
1
1
|
# overmux
|
|
2
2
|
|
|
3
|
+
## 0.0.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [`a84e0c8`](https://github.com/richardgill/overmux/commit/a84e0c82d6d259a650af7c52b18dfb79c76562f6) Thanks [@richardgill](https://github.com/richardgill)! - Republish all npm packages with the latest changes.
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`a84e0c8`](https://github.com/richardgill/overmux/commit/a84e0c82d6d259a650af7c52b18dfb79c76562f6)]:
|
|
10
|
+
- @overmux/keybindings@0.0.4
|
|
11
|
+
|
|
3
12
|
## 0.0.5
|
|
4
13
|
|
|
5
14
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
Overmux is a local or self-hosted web UI for terminal sessions, agents, and Git workspaces.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Installation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
Install the CLI with Mise:
|
|
8
|
+
|
|
9
|
+
```sh
|
|
10
|
+
mise use --global npm:overmux
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
Or with pnpm:
|
|
14
|
+
|
|
15
|
+
```sh
|
|
16
|
+
pnpm add --global overmux
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Documentation
|
|
20
|
+
|
|
21
|
+
See the [Overmux documentation](./docs/000-index.md).
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Set Up with Packages
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
To understand how Overmux works and how to configure your Overmux, start with the [reference docs](/docs/reference/project-structure). They cover your project structure, configuration, server, and browser UI.
|
|
6
|
+
|
|
7
|
+
To get the most out of Overmux you'll want to use some packages.
|
|
8
|
+
|
|
9
|
+
## What are packages?
|
|
10
|
+
|
|
11
|
+
Packages are building blocks for your Overmux. They can provide server functionality, UI components, or both.
|
|
12
|
+
|
|
13
|
+
Overmux packages are just standard npm packages you install and use in your Overmux project.
|
|
14
|
+
|
|
15
|
+
## Packages to start with
|
|
16
|
+
|
|
17
|
+
### Terminals with xterm and tmux
|
|
18
|
+
|
|
19
|
+
Use these two packages together to add interactive terminals backed by persistent tmux sessions.
|
|
20
|
+
|
|
21
|
+
- **[tmux](/docs/packages/tmux)** connects your Overmux to your machine's tmux sessions, windows, and panes. It provides live state, terminal streaming, and operations for controlling tmux, along with React components and hooks for your UI.
|
|
22
|
+
- **[xterm](/docs/packages/xterm)** renders an [xterm.js](https://xtermjs.org/) terminal in your browser. It handles terminal display and input, but doesn't start a shell or manage sessions.
|
|
23
|
+
|
|
24
|
+
Together, they let you interact with your terminals through Overmux UI while tmux keeps your sessions running when you disconnect.
|
|
25
|
+
|
|
26
|
+
Follow the package docs for installation and wiring examples.
|
|
27
|
+
|
|
28
|
+
### Source control with git
|
|
29
|
+
|
|
30
|
+
The **[git package](/docs/packages/git)** adds repository changes and diffs to your Overmux, with ready-made UI components for browsing them.
|
|
31
|
+
|
|
32
|
+
You choose which repository paths your server can access. You can also enable actions such as staging, unstaging, and discarding changes; write permissions are off by default.
|
|
33
|
+
|
|
34
|
+
## Explore more packages
|
|
35
|
+
|
|
36
|
+
Depending on your setup, you might also want:
|
|
37
|
+
|
|
38
|
+
- **[Zellij](/docs/packages/zellij)** for a Zellij-backed terminal setup instead of tmux. Experimental; compatibility is not guaranteed.
|
|
39
|
+
- **[Pi](/docs/packages/pi)** for viewing AI agent conversations and interacting with running agents. Experimental; compatibility is not guaranteed.
|
|
40
|
+
- **[JSONL store](/docs/packages/jsonl-store)** for storing schema-validated records in a local file.
|
|
41
|
+
|
|
42
|
+
Start with the pieces you need. Each package's documentation explains what it provides and how to connect it to your Overmux.
|
|
@@ -111,7 +111,7 @@ IDs must be 1-253 lowercase ASCII characters, start and end with a letter or dig
|
|
|
111
111
|
|
|
112
112
|
An ID function runs once at startup using the actual listening port. Every address serving the same running instance reports the same ID. Distinct instances need distinct IDs; IDs are not credentials.
|
|
113
113
|
|
|
114
|
-
See [Deep
|
|
114
|
+
See [Deep links](./600-client/007-deep-links.md) and [`overmux instance`](/docs/reference/cli/instance).
|
|
115
115
|
|
|
116
116
|
### `vite`
|
|
117
117
|
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Setting up your UI
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Your Overmux UI is a React application served and built with Vite. You own its layout, components, and styles; `OvermuxHost` provides the runtime for Overmux’s client APIs.
|
|
6
|
+
|
|
7
|
+
Run `overmux init` to generate a starter project with the UI setup below.
|
|
8
|
+
|
|
9
|
+
## File layout
|
|
10
|
+
|
|
11
|
+
```text
|
|
12
|
+
overmux.config.ts
|
|
13
|
+
vite.config.ts
|
|
14
|
+
src/ui/
|
|
15
|
+
index.html
|
|
16
|
+
main.tsx
|
|
17
|
+
app.tsx
|
|
18
|
+
styles.css
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Configure Vite
|
|
22
|
+
|
|
23
|
+
Set your UI directory as Vite’s root and enable React:
|
|
24
|
+
|
|
25
|
+
```ts title="vite.config.ts"
|
|
26
|
+
import react from "@vitejs/plugin-react";
|
|
27
|
+
import { defineConfig } from "vite";
|
|
28
|
+
|
|
29
|
+
export default defineConfig({
|
|
30
|
+
root: "src/ui",
|
|
31
|
+
plugins: [react()],
|
|
32
|
+
build: {
|
|
33
|
+
emptyOutDir: true,
|
|
34
|
+
outDir: "../../dist",
|
|
35
|
+
},
|
|
36
|
+
});
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
Point your Overmux configuration at this file, keeping your existing server and authentication settings:
|
|
40
|
+
|
|
41
|
+
```ts title="overmux.config.ts"
|
|
42
|
+
import { defineOvermuxConfig } from "overmux";
|
|
43
|
+
import server from "./src/server/index";
|
|
44
|
+
|
|
45
|
+
export default defineOvermuxConfig({
|
|
46
|
+
server,
|
|
47
|
+
auth: { mode: "cli-login" },
|
|
48
|
+
vite: "./vite.config.ts",
|
|
49
|
+
productionWebAssetsDir: "./dist",
|
|
50
|
+
});
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Vite’s output directory is relative to its UI root; `productionWebAssetsDir` points to the same directory from your Overmux configuration.
|
|
54
|
+
|
|
55
|
+
## The HTML entry point
|
|
56
|
+
|
|
57
|
+
Provide a root element and load your React entry point:
|
|
58
|
+
|
|
59
|
+
```html title="src/ui/index.html"
|
|
60
|
+
<!doctype html>
|
|
61
|
+
<html lang="en">
|
|
62
|
+
<head>
|
|
63
|
+
<meta charset="UTF-8" />
|
|
64
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
65
|
+
<title>My Overmux</title>
|
|
66
|
+
</head>
|
|
67
|
+
<body>
|
|
68
|
+
<div id="root"></div>
|
|
69
|
+
<script type="module" src="/main.tsx"></script>
|
|
70
|
+
</body>
|
|
71
|
+
</html>
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Mount OvermuxHost
|
|
75
|
+
|
|
76
|
+
Mount your client definition inside `OvermuxHost`. It supplies the runtime required by hooks such as `useCommand` and `useCommands`.
|
|
77
|
+
|
|
78
|
+
```tsx title="src/ui/main.tsx"
|
|
79
|
+
import { OvermuxHost } from "overmux/client";
|
|
80
|
+
import { createRoot } from "react-dom/client";
|
|
81
|
+
|
|
82
|
+
import { client } from "./app";
|
|
83
|
+
import "./styles.css";
|
|
84
|
+
|
|
85
|
+
const root = document.querySelector("#root");
|
|
86
|
+
if (!root) {
|
|
87
|
+
throw new Error("Missing root element");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
createRoot(root).render(<OvermuxHost definition={client} />);
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
## Define your client
|
|
94
|
+
|
|
95
|
+
Use `defineOvermuxClient` to connect your root React component and command registry:
|
|
96
|
+
|
|
97
|
+
```tsx title="src/ui/app.tsx"
|
|
98
|
+
import { defineOvermuxClient } from "overmux/client";
|
|
99
|
+
|
|
100
|
+
const App = () => <main>My Overmux UI</main>;
|
|
101
|
+
|
|
102
|
+
export const client = defineOvermuxClient({
|
|
103
|
+
component: App,
|
|
104
|
+
commands: {},
|
|
105
|
+
});
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
`App` is an ordinary React component. Add your own components, state, and layout, or compose components from ecosystem packages.
|
|
109
|
+
|
|
110
|
+
Start with an empty command registry. See [Commands](./commands) when you want named actions for menus, command palettes, or keyboard shortcuts.
|
|
111
|
+
|
|
112
|
+
## Add styles
|
|
113
|
+
|
|
114
|
+
Import your application CSS from `main.tsx`:
|
|
115
|
+
|
|
116
|
+
```css title="src/ui/styles.css"
|
|
117
|
+
body {
|
|
118
|
+
margin: 0;
|
|
119
|
+
font-family: system-ui, sans-serif;
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
main {
|
|
123
|
+
padding: 1rem;
|
|
124
|
+
}
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Individual ecosystem components may require additional stylesheet imports.
|
|
128
|
+
|
|
129
|
+
## Run your Overmux server
|
|
130
|
+
|
|
131
|
+
Run your instance through Overmux so the UI has access to its server:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
overmux serve --config ./overmux.config.ts
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
Open the UI URL shown by the server. Vite updates the UI as you edit your React components and styles.
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Deep links
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Your Overmux UI is a website, so each page has a URL. Deep links let you open those same routes in a specific Overmux instance using `overmux://` instead of an `http://` web address.
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
overmux://work-laptop/my-page?tab=logs
|
|
9
|
+
instance UI route
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
## Instances
|
|
13
|
+
|
|
14
|
+
An **instance** is one Overmux server. Its ID defaults to `<hostname>-<port>`. Set [`instanceId`](../200-configuration.md#instanceid) in `overmux.config.ts` to give it a stable identity.
|
|
15
|
+
|
|
16
|
+
The instance ID is not a network address. Desktop remembers how to reach an instance when you connect to it.
|
|
17
|
+
|
|
18
|
+
Run [`overmux instance --json`](../700-cli/350-instance.md) to get the running server’s `instanceId` and `deepLinkPrefix`.
|
|
19
|
+
|
|
20
|
+
## Creating links
|
|
21
|
+
|
|
22
|
+
Append your Overmux UI’s route to the instance’s deep-link prefix.
|
|
23
|
+
|
|
24
|
+
Server handlers receive `instance` through their context argument:
|
|
25
|
+
|
|
26
|
+
```ts
|
|
27
|
+
import { defineOperation } from "overmux/server";
|
|
28
|
+
import { z } from "zod";
|
|
29
|
+
|
|
30
|
+
export const getPageLink = defineOperation({
|
|
31
|
+
input: z.void(),
|
|
32
|
+
output: z.string(),
|
|
33
|
+
handle: (_input, { instance }) =>
|
|
34
|
+
`${instance.getDeepLinkPrefix()}/my-page?tab=logs`,
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
In React, use `useInstance` from your `createOvermuxHooks` setup. It re-renders when the server’s identity becomes available or changes:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
import { useInstance } from "./overmux";
|
|
42
|
+
|
|
43
|
+
const PageLink = () => {
|
|
44
|
+
const instance = useInstance();
|
|
45
|
+
if (!instance) return null;
|
|
46
|
+
|
|
47
|
+
return (
|
|
48
|
+
<a href={`${instance.deepLinkPrefix}/my-page?tab=logs`}>
|
|
49
|
+
View logs
|
|
50
|
+
</a>
|
|
51
|
+
);
|
|
52
|
+
};
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
`useInstance()` returns `undefined` until the server identifies itself.
|
|
56
|
+
|
|
57
|
+
## Routing in your Overmux UI
|
|
58
|
+
|
|
59
|
+
Your Overmux UI owns routing. We recommend [TanStack Router](https://tanstack.com/router/latest) for routing with React. See [Tech stack recommendations](./300-tech-stack-recommendations.md).
|
|
60
|
+
|
|
61
|
+
Overmux passes the path, query, and fragment to your client’s `navigate` callback. Connect this callback to your router to navigate without reloading the page. Without that callback, Overmux loads the route normally.
|
|
62
|
+
|
|
63
|
+
With an existing TanStack Router instance exported from `./router`:
|
|
64
|
+
|
|
65
|
+
```tsx
|
|
66
|
+
import { RouterProvider } from "@tanstack/react-router";
|
|
67
|
+
import { defineOvermuxClient } from "overmux/client";
|
|
68
|
+
import { router } from "./router";
|
|
69
|
+
|
|
70
|
+
const App = () => <RouterProvider router={router} />;
|
|
71
|
+
|
|
72
|
+
export const client = defineOvermuxClient({
|
|
73
|
+
commands: {},
|
|
74
|
+
component: App,
|
|
75
|
+
navigate: (route) => router.navigate({ href: route }),
|
|
76
|
+
});
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
For `overmux://work-laptop/my-page?tab=logs#latest`, `route` is `/my-page?tab=logs#latest`.
|
|
80
|
+
|
|
81
|
+
## Opening links
|
|
82
|
+
|
|
83
|
+
- **Inside your Overmux UI or PWA:** same-instance links navigate locally. No OS protocol handler is needed.
|
|
84
|
+
- **Desktop:** opening a link launches or focuses Desktop. Links can also switch to another known instance after confirmation. Connect to that instance first so Desktop knows its address.
|
|
85
|
+
- **Browser/PWA:** links to different or not-yet-discovered instances are blocked.
|
|
86
|
+
|
|
87
|
+
Installing the PWA does not register it as an OS-wide `overmux://` handler. Links clicked outside your Overmux UI require Overmux Desktop.
|
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Commands
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Commands let you centralize a collection of named actions in your Overmux UI, such as “Close pane” or “Open settings”, so you can build a command palette, menu, or similar feature. They could also be triggered by [keyboard shortcuts](./shortcuts) or buttons.
|
|
6
|
+
|
|
7
|
+
Packages may also provide commands that you can use in your UI.
|
|
8
|
+
|
|
9
|
+
## Registering a command
|
|
10
|
+
|
|
11
|
+
Declare a command with `defineCommandRegistry`, then register its behavior inside a React component with `useCommand`.
|
|
12
|
+
|
|
13
|
+
### Basic registration
|
|
14
|
+
|
|
15
|
+
The handler can access the component’s state:
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
import { useState } from "react";
|
|
19
|
+
import {
|
|
20
|
+
defineCommandRegistry,
|
|
21
|
+
defineOvermuxClient,
|
|
22
|
+
useCommand,
|
|
23
|
+
} from "overmux/client";
|
|
24
|
+
import type { serverConfig } from "./server";
|
|
25
|
+
|
|
26
|
+
const commands = defineCommandRegistry<typeof serverConfig>()({
|
|
27
|
+
openSettings: {
|
|
28
|
+
title: "Open settings",
|
|
29
|
+
},
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const App = () => {
|
|
33
|
+
const [settingsOpen, setSettingsOpen] = useState(false);
|
|
34
|
+
|
|
35
|
+
useCommand(commands.openSettings, {
|
|
36
|
+
run: () => setSettingsOpen(true),
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
return settingsOpen ? <Settings /> : <Workspace />;
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const client = defineOvermuxClient({
|
|
43
|
+
commands,
|
|
44
|
+
component: App,
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`openSettings` is the command’s ID; `title` is its display name. `Settings` and `Workspace` represent your own components.
|
|
49
|
+
|
|
50
|
+
The handler is registered while `App` is mounted and removed when it unmounts. Registration does not execute the command.
|
|
51
|
+
|
|
52
|
+
### Disabling a command
|
|
53
|
+
|
|
54
|
+
Set `enabled` to prevent execution while keeping the command registered and visible through `useCommands()`:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
useCommand(commands.openSettings, {
|
|
58
|
+
enabled: !settingsOpen,
|
|
59
|
+
run: () => setSettingsOpen(true),
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`enabled` defaults to `true`.
|
|
64
|
+
|
|
65
|
+
### Passing parameters to a shared handler
|
|
66
|
+
|
|
67
|
+
Declare `params` with a Zod schema and provide `run` in the registry when the behavior can live outside React:
|
|
68
|
+
|
|
69
|
+
```tsx
|
|
70
|
+
import { z } from "zod";
|
|
71
|
+
|
|
72
|
+
const commands = defineCommandRegistry<typeof serverConfig>()({
|
|
73
|
+
killTmuxPane: {
|
|
74
|
+
title: "Kill pane",
|
|
75
|
+
params: z.object({ paneId: z.string() }),
|
|
76
|
+
run: ({ params, overmuxServerApi }) =>
|
|
77
|
+
overmuxServerApi.executeOperation("killTmuxPane", params),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
This assumes your server defines a `killTmuxPane` operation accepting `{ paneId: string }`. The handler runs client-side and calls that server operation.
|
|
83
|
+
|
|
84
|
+
The component supplies the current values:
|
|
85
|
+
|
|
86
|
+
```tsx
|
|
87
|
+
useCommand(commands.killTmuxPane, {
|
|
88
|
+
params: { paneId: pane.id },
|
|
89
|
+
});
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
Parameters are type-checked and validated before execution. A registration can supply its own `run` to override the shared handler; that local handler takes no arguments and can read component state directly.
|
|
93
|
+
|
|
94
|
+
### Conditional registration
|
|
95
|
+
|
|
96
|
+
Pass `skipToken` when a parameterized command’s required context is missing:
|
|
97
|
+
|
|
98
|
+
```tsx
|
|
99
|
+
import { skipToken } from "overmux/client";
|
|
100
|
+
|
|
101
|
+
useCommand(
|
|
102
|
+
commands.killTmuxPane,
|
|
103
|
+
pane ? { params: { paneId: pane.id } } : skipToken,
|
|
104
|
+
);
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
Unlike `enabled: false`, this skips registration entirely. Without another registration, the command will not appear in `useCommands()`. Keep the hook call unconditional.
|
|
108
|
+
|
|
109
|
+
### Registering a command in multiple components
|
|
110
|
+
|
|
111
|
+
Use `element` to associate each registration with a UI region:
|
|
112
|
+
|
|
113
|
+
```tsx
|
|
114
|
+
const paneRef = useRef<HTMLDivElement>(null);
|
|
115
|
+
|
|
116
|
+
useCommand(commands.killTmuxPane, {
|
|
117
|
+
params: { paneId: pane.id },
|
|
118
|
+
element: paneRef,
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
return <div ref={paneRef}>...</div>;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Import `useRef` from React. When the command is triggered, Overmux prefers the registration whose element contains keyboard focus. For nested elements, the innermost wins.
|
|
125
|
+
|
|
126
|
+
If none contains focus, the most recently registered handler wins. `element` is a preference, not a restriction; use `enabled` to control availability. If the selected registration is disabled, nothing runs.
|
|
127
|
+
|
|
128
|
+
### Adding keyboard shortcuts
|
|
129
|
+
|
|
130
|
+
Set `defaultBindings` on a command declaration to give it keyboard triggers. See [Shortcuts](./shortcuts) for bindings and client overrides.
|
|
131
|
+
|
|
132
|
+
## Listing all registered commands
|
|
133
|
+
|
|
134
|
+
Use `useCommands()` to build a menu or command palette with all your registered commands.
|
|
135
|
+
|
|
136
|
+
```tsx
|
|
137
|
+
import { useCommands } from "overmux/client";
|
|
138
|
+
|
|
139
|
+
const CommandMenu = () => {
|
|
140
|
+
const commands = useCommands();
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<div>
|
|
144
|
+
{commands.map((command) => (
|
|
145
|
+
<button
|
|
146
|
+
key={command.id}
|
|
147
|
+
disabled={!command.enabled}
|
|
148
|
+
onClick={() => void command.execute()}
|
|
149
|
+
>
|
|
150
|
+
{command.title}
|
|
151
|
+
</button>
|
|
152
|
+
))}
|
|
153
|
+
</div>
|
|
154
|
+
);
|
|
155
|
+
};
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
Each entry exposes `id`, `title`, `bindings`, `enabled`, and `execute()`. The list includes disabled commands but excludes commands without a registration.
|
|
159
|
+
|
|
160
|
+
Each command appears once, even if multiple components register it. `execute()` selects the handler using the current focus and runs it only if enabled.
|
|
161
|
+
|
|
162
|
+
## Using commands from packages
|
|
163
|
+
|
|
164
|
+
Packages can export commands to include alongside your own:
|
|
165
|
+
|
|
166
|
+
```tsx
|
|
167
|
+
import { sourceControlCommands } from "@overmux/git/react";
|
|
168
|
+
|
|
169
|
+
export const client = defineOvermuxClient({
|
|
170
|
+
commands: {
|
|
171
|
+
...commands,
|
|
172
|
+
...sourceControlCommands,
|
|
173
|
+
},
|
|
174
|
+
component: App,
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
Adding commands to the registry does not register their handlers. For this package, pass the commands to `SourceControlView`:
|
|
179
|
+
|
|
180
|
+
```tsx
|
|
181
|
+
<SourceControlView
|
|
182
|
+
{...sourceControlProps}
|
|
183
|
+
commandHandles={sourceControlCommands}
|
|
184
|
+
/>
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
The view registers handlers for navigating changed files and scrolling diffs. They then appear in `useCommands()` alongside your own registered commands.
|
|
188
|
+
|
|
189
|
+
Keep command IDs unique when combining registries, and pass the same command objects to the client and the component.
|
|
@@ -0,0 +1,171 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Shortcuts
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
Shortcuts trigger [commands](./commands) from the keyboard. Define bindings on commands, then customize them in your client configuration.
|
|
6
|
+
|
|
7
|
+
## Adding shortcuts
|
|
8
|
+
|
|
9
|
+
Set `defaultBindings` on a command:
|
|
10
|
+
|
|
11
|
+
```tsx
|
|
12
|
+
const commands = defineCommandRegistry<typeof serverConfig>()({
|
|
13
|
+
openSettings: {
|
|
14
|
+
title: "Open settings",
|
|
15
|
+
defaultBindings: ["Mod+,"],
|
|
16
|
+
},
|
|
17
|
+
});
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The command must have a registered, enabled handler. See [Registering a command](./commands#registering-a-command).
|
|
21
|
+
|
|
22
|
+
## Binding syntax
|
|
23
|
+
|
|
24
|
+
A binding combines optional modifiers with a key:
|
|
25
|
+
|
|
26
|
+
```tsx
|
|
27
|
+
defaultBindings: ["Mod+K"]
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Use `Control`, `Alt`, `Shift`, or `Meta` for explicit modifiers. `Mod` means `Meta` (Command) on macOS and `Control` elsewhere.
|
|
31
|
+
|
|
32
|
+
Key names include uppercase letters, digits, `F1`–`F12`, and named keys such as `Enter`, `Escape`, `Space`, `Tab`, and `ArrowLeft`. Supported punctuation includes `/`, `[`, `]`, `\`, `=`, `-`, `,`, `.`, `;`, `:`, backtick, `'`, and `§`.
|
|
33
|
+
|
|
34
|
+
Modifiers use a fixed order: `Control+Alt+Shift+Meta`. With `Mod`, use `Mod+Alt+Shift`. Omit modifiers you do not need.
|
|
35
|
+
|
|
36
|
+
Use `Shift` with letters, function keys, or named keys, not digits or punctuation. For a colon, use `":"`, not `"Shift+;"`.
|
|
37
|
+
|
|
38
|
+
Multiple bindings provide alternative ways to trigger the same command:
|
|
39
|
+
|
|
40
|
+
```tsx
|
|
41
|
+
defaultBindings: ["Mod+K", "F2"]
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Bindings match the key reported by the keyboard layout, not a physical key position. Holding a key does not repeatedly execute its command.
|
|
45
|
+
|
|
46
|
+
## Key sequences (chords)
|
|
47
|
+
|
|
48
|
+
Nest an array to require keys pressed in order:
|
|
49
|
+
|
|
50
|
+
```tsx
|
|
51
|
+
defaultBindings: [["F12", "X"]]
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
Press F12, then X to trigger the command. Each step can include modifiers:
|
|
55
|
+
|
|
56
|
+
```tsx
|
|
57
|
+
defaultBindings: [["Control+K", "Control+C"]]
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Overmux waits up to one second between steps. Escape cancels the pending sequence. A nonmatching key ends it.
|
|
61
|
+
|
|
62
|
+
Avoid assigning a complete shortcut to another sequence’s prefix: the complete shortcut runs immediately rather than waiting for more keys.
|
|
63
|
+
|
|
64
|
+
## Overriding shortcuts
|
|
65
|
+
|
|
66
|
+
Use `shortcutOverrides` in your client configuration, keyed by command ID:
|
|
67
|
+
|
|
68
|
+
```tsx
|
|
69
|
+
export const client = defineOvermuxClient({
|
|
70
|
+
commands,
|
|
71
|
+
component: App,
|
|
72
|
+
shortcutOverrides: {
|
|
73
|
+
openSettings: ["Mod+Shift+O"],
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
An override replaces all default bindings for that command. An empty array removes its keyboard shortcuts without disabling the command:
|
|
79
|
+
|
|
80
|
+
```tsx
|
|
81
|
+
shortcutOverrides: {
|
|
82
|
+
openSettings: [],
|
|
83
|
+
}
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Conditional shortcuts
|
|
87
|
+
|
|
88
|
+
Wrap a binding with `when.media` to activate it only while a CSS media query matches:
|
|
89
|
+
|
|
90
|
+
```tsx
|
|
91
|
+
defaultBindings: [
|
|
92
|
+
{
|
|
93
|
+
binding: "Mod+,",
|
|
94
|
+
when: { media: "(min-width: 800px)" },
|
|
95
|
+
},
|
|
96
|
+
]
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Conditional bindings work in both `defaultBindings` and `shortcutOverrides`, and update when the media query changes.
|
|
100
|
+
|
|
101
|
+
## Text inputs and terminals
|
|
102
|
+
|
|
103
|
+
Shortcuts do not run while an ordinary input, textarea, select, or editable text element has focus.
|
|
104
|
+
|
|
105
|
+
Terminals are an exception: modified keys, function keys, and multi-key sequences can trigger commands while typing. Plain single-key shortcuts are also allowed in terminal copy mode.
|
|
106
|
+
|
|
107
|
+
Matched shortcuts prevent the key’s normal browser or terminal behavior. Browser or operating-system shortcuts that never reach the page cannot be handled by Overmux.
|
|
108
|
+
|
|
109
|
+
### Replaying unmatched sequences
|
|
110
|
+
|
|
111
|
+
By default, keys intercepted for an incomplete sequence are discarded when it times out or fails to match.
|
|
112
|
+
|
|
113
|
+
Configure a prefix to replay those keys into a registered input instead. For example, suppose F12, then X closes a pane:
|
|
114
|
+
|
|
115
|
+
```tsx
|
|
116
|
+
const commands = defineCommandRegistry<typeof serverConfig>()({
|
|
117
|
+
closePane: {
|
|
118
|
+
title: "Close pane",
|
|
119
|
+
defaultBindings: [["F12", "X"]],
|
|
120
|
+
},
|
|
121
|
+
});
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
Enable replay for F12 in your client configuration:
|
|
125
|
+
|
|
126
|
+
```tsx
|
|
127
|
+
export const client = defineOvermuxClient({
|
|
128
|
+
commands,
|
|
129
|
+
component: App,
|
|
130
|
+
chordPrefixes: [
|
|
131
|
+
{ binding: "F12", unmatched: "replay-to-focused-input" },
|
|
132
|
+
],
|
|
133
|
+
});
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
With the command’s handler registered and enabled, while a terminal has focus:
|
|
137
|
+
|
|
138
|
+
- **F12 → X:** closes the pane; neither key reaches the terminal.
|
|
139
|
+
- **F12 → Y:** no shortcut matches, so both F12 and Y are forwarded to the terminal.
|
|
140
|
+
- **F12 → wait one second:** F12 is forwarded to the terminal.
|
|
141
|
+
- **F12 → Escape:** cancels; neither key reaches the terminal.
|
|
142
|
+
|
|
143
|
+
This lets Overmux share a prefix with a terminal application rather than always swallowing it.
|
|
144
|
+
|
|
145
|
+
The tmux and Zellij terminal components already register replay targets. Custom terminal integrations can register one with:
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
import { useShortcutInputTarget } from "overmux/client";
|
|
149
|
+
|
|
150
|
+
useShortcutInputTarget({
|
|
151
|
+
container: containerRef,
|
|
152
|
+
input: inputRef,
|
|
153
|
+
});
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`container` identifies the focused UI region; `input` receives replayed keyboard events. If regions are nested, the innermost containing focus is selected when the sequence begins. Without a matching input target, intercepted keys cannot be replayed.
|
|
157
|
+
|
|
158
|
+
## Displaying bindings
|
|
159
|
+
|
|
160
|
+
Use `formatShortcutBinding` to format a binding for the current platform:
|
|
161
|
+
|
|
162
|
+
```tsx
|
|
163
|
+
import { formatShortcutBinding } from "overmux/client";
|
|
164
|
+
|
|
165
|
+
formatShortcutBinding("Mod+K");
|
|
166
|
+
formatShortcutBinding(["F12", "X"]);
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
On macOS, modifiers appear as symbols such as `⌘`; sequences display their steps separated by spaces.
|
|
170
|
+
|
|
171
|
+
Entries returned by [`useCommands()`](./commands#listing-all-registered-commands) expose their active, overridden bindings through `bindings`. Format those values to show shortcuts alongside command titles.
|