overmux 0.0.4 → 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.
Files changed (151) hide show
  1. package/CHANGELOG.md +43 -0
  2. package/README.md +16 -2
  3. package/dist/bin.js +140 -127
  4. package/dist/bin.js.map +1 -1
  5. package/dist/docs/000-index.md +2 -4
  6. package/dist/docs/100-introduction/200-how-overmux-works.md +126 -2
  7. package/dist/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
  8. package/dist/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
  9. package/dist/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
  10. package/dist/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
  11. package/dist/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
  12. package/dist/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
  13. package/dist/docs/400-reference/100-project-structure.md +83 -0
  14. package/dist/docs/400-reference/200-configuration.md +257 -0
  15. package/dist/docs/400-reference/300-storage-locations.md +46 -0
  16. package/dist/docs/400-reference/400-authentication-and-security.md +37 -0
  17. package/dist/docs/400-reference/500-server/000-index.md +13 -0
  18. package/dist/docs/400-reference/500-server/100-resources.md +191 -0
  19. package/dist/docs/400-reference/500-server/200-operations.md +111 -0
  20. package/dist/docs/400-reference/500-server/300-streams.md +140 -0
  21. package/dist/docs/400-reference/500-server/400-notifications.md +32 -0
  22. package/dist/docs/400-reference/500-server/500-api.md +118 -0
  23. package/dist/docs/400-reference/600-client/000-index.md +9 -0
  24. package/dist/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
  25. package/dist/docs/400-reference/600-client/007-deep-links.md +87 -0
  26. package/dist/docs/400-reference/600-client/010-commands.md +189 -0
  27. package/dist/docs/400-reference/600-client/020-shortcuts.md +171 -0
  28. package/dist/docs/400-reference/600-client/100-api.md +370 -0
  29. package/dist/docs/400-reference/600-client/200-theming.md +209 -0
  30. package/dist/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
  31. package/dist/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
  32. package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/100-serve.md +9 -4
  33. package/{docs/400-reference/400-cli → dist/docs/400-reference/700-cli}/200-auth.md +1 -4
  34. package/dist/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
  35. package/dist/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
  36. package/dist/docs/400-reference/700-cli/600-docs.md +128 -0
  37. package/dist/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
  38. package/dist/docs/500-hosted-pages.md +0 -1
  39. package/dist/exports/client.d.ts +5 -14
  40. package/dist/exports/client.d.ts.map +1 -1
  41. package/dist/exports/client.js +141 -46
  42. package/dist/exports/client.js.map +1 -1
  43. package/dist/exports/{index-DS70rKzo.d.ts → index-Cz3xkCa4.d.ts} +50 -36
  44. package/dist/exports/index-Cz3xkCa4.d.ts.map +1 -0
  45. package/dist/exports/index.d.ts +1 -1
  46. package/dist/exports/index.js +19 -5
  47. package/dist/exports/index.js.map +1 -1
  48. package/dist/exports/{notifications-av0FK0yZ.js → notifications-BFAD3QQl.js} +29 -4
  49. package/dist/exports/notifications-BFAD3QQl.js.map +1 -0
  50. package/dist/exports/server.d.ts +1 -44
  51. package/dist/exports/server.d.ts.map +1 -1
  52. package/dist/exports/server.js +9 -1061
  53. package/dist/exports/server.js.map +1 -1
  54. package/dist/internal/server/coordinator/server-child.js +47 -43
  55. package/dist/internal/server/coordinator/server-child.js.map +1 -1
  56. package/docs/000-index.md +2 -4
  57. package/docs/100-introduction/200-how-overmux-works.md +126 -2
  58. package/docs/100-introduction/{300-why-overmux.md → 300-why-i-built-overmux.md} +1 -1
  59. package/docs/200-getting-started/100-install-and-run-overmux.md +2 -2
  60. package/docs/200-getting-started/400-secure-with-https/200-tailscale-serve.md +3 -3
  61. package/docs/200-getting-started/400-secure-with-https/300-cloudflare-tunnel.md +2 -2
  62. package/docs/200-getting-started/400-secure-with-https/400-self-hosted-reverse-proxy.md +2 -2
  63. package/docs/200-getting-started/500-set-up-overmux-with-packages.md +42 -0
  64. package/docs/400-reference/100-project-structure.md +83 -0
  65. package/docs/400-reference/200-configuration.md +257 -0
  66. package/docs/400-reference/300-storage-locations.md +46 -0
  67. package/docs/400-reference/400-authentication-and-security.md +37 -0
  68. package/docs/400-reference/500-server/000-index.md +13 -0
  69. package/docs/400-reference/500-server/100-resources.md +191 -0
  70. package/docs/400-reference/500-server/200-operations.md +111 -0
  71. package/docs/400-reference/500-server/300-streams.md +140 -0
  72. package/docs/400-reference/500-server/400-notifications.md +32 -0
  73. package/docs/400-reference/500-server/500-api.md +118 -0
  74. package/docs/400-reference/600-client/000-index.md +9 -0
  75. package/docs/400-reference/600-client/005-setting-up-your-ui.md +137 -0
  76. package/docs/400-reference/600-client/007-deep-links.md +87 -0
  77. package/docs/400-reference/600-client/010-commands.md +189 -0
  78. package/docs/400-reference/600-client/020-shortcuts.md +171 -0
  79. package/docs/400-reference/600-client/100-api.md +370 -0
  80. package/docs/400-reference/600-client/200-theming.md +209 -0
  81. package/docs/400-reference/600-client/300-tech-stack-recommendations.md +12 -0
  82. package/docs/400-reference/{400-cli → 700-cli}/050-init.md +2 -4
  83. package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/100-serve.md +9 -4
  84. package/{dist/docs/400-reference/400-cli → docs/400-reference/700-cli}/200-auth.md +1 -4
  85. package/docs/400-reference/{400-cli → 700-cli}/300-call.md +1 -4
  86. package/docs/400-reference/{400-cli → 700-cli}/350-instance.md +1 -4
  87. package/docs/400-reference/700-cli/600-docs.md +128 -0
  88. package/docs/400-reference/{400-cli → 700-cli}/700-desktop.md +1 -3
  89. package/docs/500-hosted-pages.md +0 -1
  90. package/package.json +4 -3
  91. package/src/internal/cli/app.ts +3 -5
  92. package/src/internal/cli/commands/docs-ai-context.ts +33 -0
  93. package/src/internal/cli/commands/docs.ts +19 -2
  94. package/src/internal/cli/commands/init-template.ts +1 -1
  95. package/src/internal/cli/commands/serve.ts +3 -0
  96. package/src/internal/cli/login.ts +9 -9
  97. package/src/internal/client/client-definition.ts +6 -8
  98. package/src/internal/client/host/deep-link-navigation.ts +75 -0
  99. package/src/internal/client/host/overmux-host.tsx +9 -0
  100. package/src/internal/client/index.ts +0 -6
  101. package/src/internal/client/overmux-react.ts +71 -40
  102. package/src/internal/server/auth/auth-service.ts +2 -2
  103. package/src/internal/server/auth/instance-control.ts +65 -14
  104. package/src/internal/server/coordinator/ipc-protocol.ts +0 -1
  105. package/src/internal/server/runtime/create-runtime.ts +5 -3
  106. package/src/internal/server/runtime/runtime-instance.ts +5 -4
  107. package/src/internal/server/runtime/runtime-operations.ts +9 -3
  108. package/src/internal/server/runtime/runtime-resources.ts +12 -32
  109. package/src/internal/server/runtime/runtime-streams.ts +5 -6
  110. package/src/internal/server/server-logger.ts +5 -10
  111. package/src/internal/server/server-startup-options.ts +5 -10
  112. package/src/internal/server/start-application-server.ts +4 -7
  113. package/src/public/ai-context.ts +7 -29
  114. package/src/public/client.ts +0 -6
  115. package/src/public/config.ts +156 -31
  116. package/src/public/server.ts +1 -16
  117. package/dist/docs/100-introduction/100-what-is-overmux.md +0 -7
  118. package/dist/docs/300-fundamentals/100-project-structure.md +0 -23
  119. package/dist/docs/300-fundamentals/200-configuration.md +0 -3
  120. package/dist/docs/300-fundamentals/300-theming.md +0 -54
  121. package/dist/docs/300-fundamentals/400-server.md +0 -3
  122. package/dist/docs/300-fundamentals/500-client.md +0 -3
  123. package/dist/docs/300-fundamentals/600-operations.md +0 -3
  124. package/dist/docs/300-fundamentals/700-resources.md +0 -3
  125. package/dist/docs/300-fundamentals/800-streams.md +0 -3
  126. package/dist/docs/300-fundamentals/900-authentication-and-security.md +0 -3
  127. package/dist/docs/400-reference/100-configuration.md +0 -23
  128. package/dist/docs/400-reference/200-server-api.md +0 -21
  129. package/dist/docs/400-reference/300-client-api.md +0 -39
  130. package/dist/docs/400-reference/400-cli/400-check.md +0 -20
  131. package/dist/docs/400-reference/400-cli/500-ai-context.md +0 -102
  132. package/dist/docs/400-reference/400-cli/600-docs.md +0 -23
  133. package/dist/exports/index-DS70rKzo.d.ts.map +0 -1
  134. package/dist/exports/notifications-av0FK0yZ.js.map +0 -1
  135. package/docs/100-introduction/100-what-is-overmux.md +0 -7
  136. package/docs/300-fundamentals/100-project-structure.md +0 -23
  137. package/docs/300-fundamentals/200-configuration.md +0 -3
  138. package/docs/300-fundamentals/300-theming.md +0 -54
  139. package/docs/300-fundamentals/400-server.md +0 -3
  140. package/docs/300-fundamentals/500-client.md +0 -3
  141. package/docs/300-fundamentals/600-operations.md +0 -3
  142. package/docs/300-fundamentals/700-resources.md +0 -3
  143. package/docs/300-fundamentals/800-streams.md +0 -3
  144. package/docs/300-fundamentals/900-authentication-and-security.md +0 -3
  145. package/docs/400-reference/100-configuration.md +0 -23
  146. package/docs/400-reference/200-server-api.md +0 -21
  147. package/docs/400-reference/300-client-api.md +0 -39
  148. package/docs/400-reference/400-cli/400-check.md +0 -20
  149. package/docs/400-reference/400-cli/500-ai-context.md +0 -102
  150. package/docs/400-reference/400-cli/600-docs.md +0 -23
  151. package/src/internal/cli/commands/ai.ts +0 -44
@@ -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.