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.
@@ -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.
@@ -1,5 +1,5 @@
1
1
  ---
2
- title: Client API
2
+ title: Client API Reference
3
3
  ---
4
4
 
5
5
  ## defineOvermuxClient
@@ -2,93 +2,208 @@
2
2
  title: Theming and CSS
3
3
  ---
4
4
 
5
- Your app owns its UI and CSS. Overmux provides scoped component styles and optional CSS variables for customization.
5
+ Your Overmux UI owns its CSS. Overmux provides shared theme variables, light/dark defaults, and theme scopes for individual sections.
6
6
 
7
- ## Appearance
7
+ ## Load your CSS
8
8
 
9
- Set `appearance` in `defineOvermuxClient({ appearance, commands, component })`. Types are exported from `overmux/client`.
9
+ Import your stylesheet from your browser entry point:
10
10
 
11
- | Property | Values | Default | Behavior |
12
- | --- | --- | --- | --- |
13
- | `scheme` | `"light"`, `"dark"`, `"system"` | `"system"` | Selects the fallback palette and CSS `color-scheme`. System follows `prefers-color-scheme`. |
14
- | `contrast` | `"normal"`, `"high"`, `"auto"` | `"auto"` | High selects system-color fallbacks; auto responds to `prefers-contrast: more`. |
11
+ ```tsx
12
+ import { OvermuxHost } from "overmux/client";
13
+ import { createRoot } from "react-dom/client";
14
+
15
+ import definition from "./app";
16
+ import "./styles.css";
17
+
18
+ createRoot(document.getElementById("root")!).render(
19
+ <OvermuxHost definition={definition} />,
20
+ );
21
+ ```
15
22
 
16
- Browser forced colors apply regardless of `contrast`, with `forced-color-adjust: auto`. Explicit color tokens override palette fallbacks; test custom colors in each mode. Appearance does not persist preferences or configure third-party themes.
23
+ Overmux components load their own styles automatically. Use ordinary CSS and component `className` props to customize them. Overmux defaults use CSS layers, so normal unlayered application styles take precedence.
17
24
 
18
- ## Tokens
25
+ ## Choose light or dark
19
26
 
20
- Set public tokens on `:root` for the whole document, or on a theme scope for one subtree. `OvermuxStyle` provides typed inline styles for these tokens alongside React CSS properties.
27
+ Set appearance in your client definition:
21
28
 
22
- | Token | CSS value | Purpose |
23
- | --- | --- | --- |
24
- | `--om-color-canvas` | Color | Page background |
25
- | `--om-color-surface` | Color | Main surface |
26
- | `--om-color-panel` | Color | Secondary background |
27
- | `--om-color-fg` | Color | Primary text |
28
- | `--om-color-muted` | Color | Secondary text |
29
- | `--om-color-border` | Color | Borders and separators |
30
- | `--om-color-accent` | Color | Interactive accents |
31
- | `--om-color-accent-fg` | Color | Foreground on accent backgrounds, where consumed |
32
- | `--om-color-danger` | Color | Errors and destructive states |
33
- | `--om-color-success` | Color | Success and added-content states |
34
- | `--om-font-sans` | Font-family list | Interface text |
35
- | `--om-font-mono` | Font-family list | Code and diagnostics |
36
- | `--om-spacing` | Length | Spacing unit; components may scale it |
37
- | `--om-radius` | Length | Corner radius; components may scale it |
38
- | `--om-elevation` | Box-shadow | Panel and popover shadows |
39
- | `--om-focus-ring` | Box-shadow | Focus-ring shadows |
40
- | `--om-motion-duration` | Time | Transition duration |
29
+ ```tsx
30
+ import { defineOvermuxClient } from "overmux/client";
31
+
32
+ export default defineOvermuxClient({
33
+ commands: {},
34
+ component: App,
35
+ appearance: {
36
+ scheme: "dark",
37
+ contrast: "auto",
38
+ },
39
+ });
40
+ ```
41
41
 
42
- Tokens apply only where components consume them. Fallbacks vary by component; runtime palette variables beginning with `--_om-` are private. **Public tokens are not populated with default palette values:** app CSS using `var(--om-color-canvas)` must define it or provide a fallback. Explicit tokens inherit normally and do not change automatically with the scheme.
42
+ - `scheme`: `"system"` (default), `"light"`, or `"dark"`.
43
+ - `contrast`: `"auto"` (default), `"normal"`, or `"high"`.
43
44
 
44
- ## `OvermuxThemeScope`
45
+ `"system"` follows the system color preference. `"auto"` follows the system preference for increased contrast.
45
46
 
46
- Import from `overmux/client`. The host already supplies an outer scope; add scopes for local themes or portal placement.
47
+ Appearance applies to your app and Overmux-owned UI inside `OvermuxHost`. It changes built-in defaults, not custom CSS variables.
48
+
49
+ ## Set colors, fonts, and other theme variables
50
+
51
+ Define shared variables in your stylesheet. This example supplies a custom dark palette and every shared theme variable:
52
+
53
+ ```css
54
+ :root {
55
+ --om-color-canvas: #111318;
56
+ --om-color-surface: #191c24;
57
+ --om-color-panel: #222735;
58
+ --om-color-fg: #eef0f6;
59
+ --om-color-muted: #a3adc2;
60
+ --om-color-border: #394156;
61
+ --om-color-accent: #b59aff;
62
+ --om-color-accent-fg: #111318;
63
+ --om-color-danger: #ff8b82;
64
+ --om-color-success: #65d6a2;
65
+
66
+ --om-font-sans: "Inter", sans-serif;
67
+ --om-font-mono: "JetBrains Mono", monospace;
68
+
69
+ --om-spacing: 0.75rem;
70
+ --om-radius: 0.5rem;
71
+ --om-elevation: 0 0.75rem 2rem rgb(0 0 0 / 30%);
72
+ --om-focus-ring: 0 0 0 3px rgb(181 154 255 / 30%);
73
+ --om-motion-duration: 120ms;
74
+ }
75
+ ```
76
+
77
+ ### Colors
78
+
79
+ - `--om-color-canvas`: page background.
80
+ - `--om-color-surface`: content surface background.
81
+ - `--om-color-panel`: panel and grouped-content background.
82
+ - `--om-color-fg`: primary text.
83
+ - `--om-color-muted`: secondary text.
84
+ - `--om-color-border`: borders and separators.
85
+ - `--om-color-accent`: highlighted controls and actions.
86
+ - `--om-color-accent-fg`: text on an accent background.
87
+ - `--om-color-danger`: errors and destructive states.
88
+ - `--om-color-success`: success states.
89
+
90
+ ### Fonts
91
+
92
+ - `--om-font-sans`: interface font family.
93
+ - `--om-font-mono`: monospace font family.
94
+
95
+ Load custom fonts yourself; these variables only select them.
96
+
97
+ #### Loading custom fonts
98
+
99
+ Load fonts with `@font-face`, then reference their family names in your theme variables:
100
+
101
+ ```css
102
+ @font-face {
103
+ font-family: "My Font";
104
+ src: url("./fonts/my-font.woff2") format("woff2");
105
+ font-display: swap;
106
+ }
107
+
108
+ :root {
109
+ --om-font-sans: "My Font", sans-serif;
110
+ }
111
+ ```
112
+
113
+ For a monospace font, use the same approach with `--om-font-mono`.
114
+
115
+ ### Spacing and effects
116
+
117
+ - `--om-spacing`: base spacing used by components.
118
+ - `--om-radius`: base corner radius.
119
+ - `--om-elevation`: elevated-surface box shadow.
120
+ - `--om-focus-ring`: additional focus box shadow.
121
+ - `--om-motion-duration`: transition duration.
122
+
123
+ Components decide which variables they use.
124
+
125
+ ### Applying your theme
126
+
127
+ Variables on `:root` reach both your application and Overmux-owned UI, including recovery, update, and settings screens. The separate login screen does not load userland CSS.
128
+
129
+ Use the same variables in your own components:
130
+
131
+ ```css
132
+ body {
133
+ margin: 0;
134
+ background: var(--om-color-canvas);
135
+ }
136
+
137
+ .panel {
138
+ color: var(--om-color-fg);
139
+ background: var(--om-color-surface);
140
+ border: 1px solid var(--om-color-border);
141
+ border-radius: var(--om-radius);
142
+ padding: var(--om-spacing);
143
+ }
144
+ ```
145
+
146
+ Overmux does not paint your application's page background for you.
147
+
148
+ Public `--om-*` variables are override inputs: Overmux does not populate them with its built-in palette. Define them before using them in your own CSS, or supply CSS fallbacks.
149
+
150
+ Custom values remain in effect when `scheme` changes. If you supply a custom palette, you also own its light/dark variants and any saved theme preference.
151
+
152
+ ## Theme one section
153
+
154
+ Wrap a section in `OvermuxThemeScope`:
47
155
 
48
156
  ```tsx
49
- <OvermuxThemeScope scheme="light" style={{ "--om-color-accent": "purple" }}>
157
+ import { OvermuxThemeScope } from "overmux/client";
158
+
159
+ <OvermuxThemeScope
160
+ scheme="light"
161
+ contrast="normal"
162
+ className="preview"
163
+ style={{ "--om-color-accent": "rebeccapurple" }}
164
+ >
50
165
  <Preview />
51
- </OvermuxThemeScope>
166
+ </OvermuxThemeScope>;
52
167
  ```
53
168
 
54
- | Prop | Type | Default |
55
- | --- | --- | --- |
56
- | `children` | `ReactNode` | Required |
57
- | `scheme` | `OvermuxScheme` | `"system"` |
58
- | `contrast` | `OvermuxContrast` | `"auto"` |
59
- | `className` | `string` | Unset |
60
- | `style` | `OvermuxStyle` | Unset |
169
+ The scope renders a wrapper `<div>` and applies appearance to its contents.
61
170
 
62
- Renders a `div` with `data-om-scope`, `data-om-scheme`, `data-om-contrast`, and an internal overlay container. Sets foreground, font family, and `color-scheme`, but does not paint a background.
171
+ Custom CSS variables inherit normally. A light scope inside a custom dark palette will still inherit those custom colors unless you override them.
63
172
 
64
- Nested scopes resolve their own defaults, not their parent's appearance props. Public token overrides still inherit and take precedence over the nested palette. Tokens placed inside the app cannot affect ancestor host UI.
173
+ `OvermuxHost` already creates an application-wide scope. A scope inside your app affects only that section, not surrounding Overmux-owned UI.
65
174
 
66
- ## `OvermuxPortal`
175
+ ## Keep overlays themed
67
176
 
68
- Import from `overmux/client`. Use `<OvermuxPortal><Popup /></OvermuxPortal>` to render overlays within the nearest theme scope.
177
+ Use `OvermuxPortal` for menus, dialogs, and other overlays:
69
178
 
70
- | Context | Destination |
71
- | --- | --- |
72
- | Inside a scope | Nearest scope's internal overlay container |
73
- | Outside a scope, in a browser | `document.body` |
74
- | Scope container not mounted, or no document | Nothing rendered |
75
- | `external={{ container, scopeAttributesAndVariables: "caller-owned" }}` | Supplied element; caller owns its scope attributes and variables |
179
+ ```tsx
180
+ import { OvermuxPortal, OvermuxThemeScope } from "overmux/client";
181
+
182
+ <OvermuxThemeScope scheme="dark">
183
+ <Editor />
184
+ <OvermuxPortal>
185
+ <EditorMenu />
186
+ </OvermuxPortal>
187
+ </OvermuxThemeScope>;
188
+ ```
76
189
 
77
- The overlay container is a sibling of the scope's children. Put shared overlay tokens on the scope, not a descendant around the portal call site. External containers receive no automatic copying of scope attributes or variables. Portals supply placement, not positioning, focus trapping, or dismissal.
190
+ The overlay renders inside the nearest theme scope, preserving its appearance and inherited variables.
78
191
 
79
- ## CSS and integrations
192
+ If you supply an external container, you own its theme attributes and CSS variables:
80
193
 
81
- Import app CSS from browser code with `import "./styles.css"`. Theme scopes, the UI package, and Git, Pi, xterm, and Zellij React components import their own styles. Layout, resets, and font loading remain app-owned.
194
+ ```tsx
195
+ <OvermuxPortal
196
+ external={{
197
+ container: overlayElement,
198
+ scopeAttributesAndVariables: "caller-owned",
199
+ }}
200
+ >
201
+ <EditorMenu />
202
+ </OvermuxPortal>;
203
+ ```
82
204
 
83
- Built-in styles use `@scope` and the `om.components` cascade layer, with top-level ordering `theme, base, om`. Normal unlayered app CSS takes precedence over layered styles. Theme rules stop at nested scopes, but global app selectors and inherited properties can still affect components. Browser support for `@scope` and cascade layers is required.
205
+ ## Packages
84
206
 
85
- | Component family | Shared styling | Separate configuration |
86
- | --- | --- | --- |
87
- | Runtime host UI | Theme tokens where consumed | Client `appearance` |
88
- | UI split views | Border and accent tokens | App owns pane contents |
89
- | Git React UI | Colors, fonts, spacing, radius | Diff options and themes; `registerGitDiffTheme` registers custom themes |
90
- | Pi React UI | Colors, fonts, spacing, radius, elevation | Code highlighting uses Tokyo Night |
91
- | xterm | Wrapper background: `--om-xterm-terminal-background` | `options.theme` for terminal colors; options such as `fontFamily` for fonts |
92
- | tmux / Zellij terminal wrappers | xterm wrapper styles | Forwarded xterm props |
207
+ Packages use shared `--om-*` variables where applicable. They may also expose additional CSS variables, `className`, `classNames`, or other styling props.
93
208
 
94
- Scope appearance does not automatically update terminal palettes or syntax-highlighting themes.
209
+ Some rendered content has separate theme options rather than inheriting CSS colors. See each package's documentation for its styling controls.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "overmux",
3
- "version": "0.0.5",
3
+ "version": "0.0.6",
4
4
  "homepage": "https://github.com/richardgill/overmux",
5
5
  "repository": {
6
6
  "type": "git",
@@ -52,7 +52,7 @@
52
52
  "web-push": "3.6.7",
53
53
  "ws": "8.21.3",
54
54
  "zod": "4.4.3",
55
- "@overmux/keybindings": "0.0.3"
55
+ "@overmux/keybindings": "0.0.4"
56
56
  },
57
57
  "devDependencies": {
58
58
  "@playwright/test": "1.58.0",