@brimveyn/aimux-config 0.3.0 → 0.3.2
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/README.md +152 -99
- package/package.json +1 -1
- package/src/actions.ts +17 -24
- package/src/defaults.ts +60 -26
- package/src/index.ts +6 -0
- package/src/keymap-builder.ts +5 -3
- package/src/resolver.ts +1 -0
- package/src/types.ts +60 -16
package/README.md
CHANGED
|
@@ -1,160 +1,213 @@
|
|
|
1
1
|
# @brimveyn/aimux-config
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Typed configuration package for [`@brimveyn/aimux`](https://github.com/BrimVeyn/aimux).
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Use this package to author `aimux.config.ts` or `aimux.config.js` inside an
|
|
6
|
+
`aimux` profile directory.
|
|
7
|
+
|
|
8
|
+
## What This Package Does
|
|
9
|
+
|
|
10
|
+
`@brimveyn/aimux-config` provides:
|
|
11
|
+
|
|
12
|
+
- `defineConfig()` for typed config authoring
|
|
13
|
+
- the keymap builder API
|
|
14
|
+
- the built-in action catalog
|
|
15
|
+
- theme helpers and built-in theme IDs
|
|
16
|
+
- exported types for config, modes, state, and tooling
|
|
17
|
+
|
|
18
|
+
It does not launch the app. The `aimux` runtime loads and consumes the resolved
|
|
19
|
+
config.
|
|
6
20
|
|
|
7
21
|
## Install
|
|
8
22
|
|
|
23
|
+
Install it inside an `aimux` profile directory:
|
|
24
|
+
|
|
9
25
|
```bash
|
|
10
|
-
mkdir -p ~/.config/aimux
|
|
26
|
+
mkdir -p ~/.config/aimux/default
|
|
27
|
+
cd ~/.config/aimux/default
|
|
11
28
|
bun init -y
|
|
12
29
|
bun add -d @brimveyn/aimux-config
|
|
13
30
|
```
|
|
14
31
|
|
|
15
|
-
Then create
|
|
32
|
+
Then create:
|
|
33
|
+
|
|
34
|
+
```text
|
|
35
|
+
~/.config/aimux/default/aimux.config.ts
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you use another profile, replace `default` with that profile name.
|
|
39
|
+
|
|
40
|
+
## Minimal Example
|
|
16
41
|
|
|
17
42
|
```ts
|
|
18
|
-
import { defineConfig, actions
|
|
43
|
+
import { defineConfig, actions } from '@brimveyn/aimux-config'
|
|
19
44
|
|
|
20
45
|
export default defineConfig({
|
|
21
|
-
|
|
46
|
+
sessionBar: {
|
|
47
|
+
position: 'top',
|
|
48
|
+
visible: true,
|
|
49
|
+
},
|
|
22
50
|
|
|
23
51
|
keymaps: (k) =>
|
|
24
|
-
k
|
|
25
|
-
.leader('<Space>')
|
|
26
|
-
.timeout(300)
|
|
27
|
-
.mode('navigation', (m) =>
|
|
28
|
-
m
|
|
29
|
-
.map('j', actions.nextTab)
|
|
30
|
-
.map('k', actions.prevTab)
|
|
31
|
-
.map('<leader>g', actions.sessionPicker)
|
|
32
|
-
.group('<leader>t', 'tabs', (g) =>
|
|
33
|
-
g.map('n', actions.newTab).map('r', actions.renameTab).map('x', actions.closeTab)
|
|
34
|
-
)
|
|
35
|
-
),
|
|
52
|
+
k.mode('navigation', (m) => m.map('<C-p>', actions.sessionPicker, 'Session picker')),
|
|
36
53
|
})
|
|
37
54
|
```
|
|
38
55
|
|
|
39
|
-
|
|
56
|
+
This example only uses surfaces that are wired into the runtime today.
|
|
40
57
|
|
|
41
|
-
|
|
42
|
-
| ----------------- | ------------------------------ |
|
|
43
|
-
| `j` | Bare character `j` |
|
|
44
|
-
| `J` | Shift+J (uppercase letter) |
|
|
45
|
-
| `<C-n>` | Ctrl+N |
|
|
46
|
-
| `<M-x>` / `<A-x>` | Meta/Alt+X |
|
|
47
|
-
| `<C-M-a>` | Ctrl+Alt+A |
|
|
48
|
-
| `<CR>` | Return/Enter |
|
|
49
|
-
| `<Esc>` | Escape |
|
|
50
|
-
| `<Space>` | Spacebar |
|
|
51
|
-
| `<Tab>` | Tab |
|
|
52
|
-
| `<BS>` | Backspace |
|
|
53
|
-
| `<Up>` `<Down>` | Arrow keys |
|
|
54
|
-
| `<leader>` | Configured leader chord |
|
|
55
|
-
| `dd` | Multi-key sequence (d, then d) |
|
|
56
|
-
| `<leader>tn` | Leader, then t, then n |
|
|
58
|
+
## Key Notation
|
|
57
59
|
|
|
58
|
-
|
|
60
|
+
| Notation | Meaning |
|
|
61
|
+
| ---------------------------------- | -------------------------- |
|
|
62
|
+
| `j` | bare character |
|
|
63
|
+
| `J` | shifted letter |
|
|
64
|
+
| `<C-n>` | Ctrl+N |
|
|
65
|
+
| `<M-x>` or `<A-x>` | Meta or Alt + X |
|
|
66
|
+
| `<C-M-a>` | Ctrl+Alt+A |
|
|
67
|
+
| `<CR>` | Enter |
|
|
68
|
+
| `<Esc>` | Escape |
|
|
69
|
+
| `<Tab>` | Tab |
|
|
70
|
+
| `<BS>` | Backspace |
|
|
71
|
+
| `<Space>` | Space |
|
|
72
|
+
| `<Up>` `<Down>` `<Left>` `<Right>` | arrow keys |
|
|
73
|
+
| `<leader>` | configured leader chord |
|
|
74
|
+
| `dd` | multi-key sequence |
|
|
75
|
+
| `<leader>tn` | leader, then `t`, then `n` |
|
|
59
76
|
|
|
60
|
-
|
|
77
|
+
Ambiguous prefixes are resolved after the configured timeout.
|
|
61
78
|
|
|
62
|
-
|
|
79
|
+
## Keymap Builder
|
|
63
80
|
|
|
64
81
|
```ts
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
backends?: Record<string, BackendConfig> // stub for future use
|
|
69
|
-
sidebar?: SidebarConfig // stub
|
|
70
|
-
hooks?: HooksConfig // stub
|
|
71
|
-
snippets?: SnippetDef[] // stub
|
|
72
|
-
})
|
|
82
|
+
k.leader(key)
|
|
83
|
+
.timeout(ms)
|
|
84
|
+
.mode(id | ids[], configure)
|
|
73
85
|
```
|
|
74
86
|
|
|
75
|
-
|
|
87
|
+
Mode builder methods:
|
|
76
88
|
|
|
77
89
|
```ts
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.
|
|
90
|
+
m.map(keys, action, description?)
|
|
91
|
+
.unmap(keys)
|
|
92
|
+
.group(prefix, name, configure)
|
|
93
|
+
.passthrough()
|
|
81
94
|
```
|
|
82
95
|
|
|
83
|
-
|
|
96
|
+
Example with groups and multi-mode bindings:
|
|
84
97
|
|
|
85
98
|
```ts
|
|
86
|
-
|
|
99
|
+
import { defineConfig, actions } from '@brimveyn/aimux-config'
|
|
100
|
+
|
|
101
|
+
export default defineConfig({
|
|
102
|
+
keymaps: (k) =>
|
|
103
|
+
k
|
|
104
|
+
.mode('navigation', (m) =>
|
|
105
|
+
m
|
|
106
|
+
.group('<leader>t', 'tabs', (g) =>
|
|
107
|
+
g
|
|
108
|
+
.map('n', actions.newTab, 'New tab')
|
|
109
|
+
.map('r', actions.renameTab, 'Rename tab')
|
|
110
|
+
.map('x', actions.closeTab, 'Close tab')
|
|
111
|
+
)
|
|
112
|
+
.unmap('r')
|
|
113
|
+
)
|
|
114
|
+
.mode(['navigation', 'terminal-input'], (m) =>
|
|
115
|
+
m.map('<C-s>', actions.snippetPicker, 'Snippet picker')
|
|
116
|
+
),
|
|
117
|
+
})
|
|
87
118
|
```
|
|
88
119
|
|
|
89
|
-
|
|
120
|
+
## Important Runtime Note About the Leader Key
|
|
90
121
|
|
|
91
|
-
|
|
92
|
-
m.map(keys, action) // bind a key/sequence to an action
|
|
93
|
-
.unmap(keys) // remove a default binding
|
|
94
|
-
.group(prefix, name, g) // sugar for leader-prefixed sub-trees
|
|
95
|
-
.passthrough() // for text-input modes: unmatched keys route to text input
|
|
96
|
-
```
|
|
122
|
+
The shipped runtime defaults use `Ctrl+W` as the leader key.
|
|
97
123
|
|
|
98
|
-
|
|
124
|
+
The builder itself starts from `<Space>` internally, but the app merges user
|
|
125
|
+
config on top of shipped defaults from `@brimveyn/aimux`. In practice:
|
|
99
126
|
|
|
100
|
-
|
|
127
|
+
- if you omit `.leader(...)`, the shipped leader stays `Ctrl+W`
|
|
128
|
+
- if you set another leader such as `<C-a>`, it overrides the shipped leader
|
|
129
|
+
- do not rely on `.leader('<Space>')` to switch the runtime leader to Space
|
|
101
130
|
|
|
102
|
-
|
|
103
|
-
.
|
|
104
|
-
.map('n', actions.newTab)
|
|
105
|
-
.map('r', actions.renameTab)
|
|
106
|
-
.map('x', actions.closeTab))
|
|
107
|
-
```
|
|
131
|
+
See [`../../docs/guide/keymaps.md`](../../docs/guide/keymaps.md) for the full
|
|
132
|
+
explanation.
|
|
108
133
|
|
|
109
134
|
## Actions
|
|
110
135
|
|
|
111
|
-
Pre-built actions
|
|
136
|
+
Pre-built actions include:
|
|
112
137
|
|
|
113
|
-
|
|
138
|
+
- tabs: `nextTab`, `prevTab`, `newTab`, `renameTab`, `closeTab`, `restartTab`
|
|
139
|
+
- sessions: `sessionPicker`, `switchSessionByIndex(n)` and session modal actions
|
|
140
|
+
- snippets: `snippetPicker`, snippet editor and filter actions
|
|
141
|
+
- themes: `themePicker`, `previewTheme`, `confirmTheme`, `restoreTheme`
|
|
142
|
+
- panes: `splitVertical`, `splitHorizontal`, `focusPane`, `resizePane`, `closePane`
|
|
143
|
+
- UI: `toggleSidebar`, `toggleSessionBar`, `toggleGitPane`,
|
|
144
|
+
`resizeGitPane(delta)`, `setGitPaneMode(mode)`, `setGitPanePosition(position)`
|
|
145
|
+
- modes: `enterInsert`, `leaveTerminalInput`, `closeModal`, `helpModal`
|
|
146
|
+
- git: enter git mode, stage, delete or unstage, commit, push
|
|
114
147
|
|
|
115
|
-
|
|
148
|
+
You can also bind a custom `ActionFn` for dynamic behavior.
|
|
116
149
|
|
|
117
|
-
|
|
150
|
+
## Themes
|
|
118
151
|
|
|
119
|
-
|
|
152
|
+
Built-in theme IDs:
|
|
120
153
|
|
|
121
|
-
|
|
154
|
+
- `aimux`
|
|
155
|
+
- `dracula`
|
|
156
|
+
- `dracula-at-night`
|
|
157
|
+
- `everforest`
|
|
158
|
+
- `tokyo-night`
|
|
159
|
+
- `gruvbox-dark`
|
|
160
|
+
- `catppuccin-mocha`
|
|
161
|
+
- `nord`
|
|
162
|
+
- `solarized-dark`
|
|
163
|
+
- `one-dark`
|
|
164
|
+
- `kanagawa`
|
|
122
165
|
|
|
123
|
-
|
|
166
|
+
Helpers:
|
|
124
167
|
|
|
125
168
|
```ts
|
|
126
|
-
.
|
|
127
|
-
|
|
128
|
-
const tabId = ctx.state.activeTabId
|
|
129
|
-
if (!tabId) return null
|
|
130
|
-
return {
|
|
131
|
-
actions: [{ type: 'close-active-tab' }],
|
|
132
|
-
effects: [{ type: 'close-tab', tabId }],
|
|
133
|
-
}
|
|
134
|
-
}))
|
|
169
|
+
themes.extend(baseThemeId, overrides)
|
|
170
|
+
themes.create(colors)
|
|
135
171
|
```
|
|
136
172
|
|
|
137
|
-
|
|
173
|
+
Typed theme config is currently only `Partially supported` by the runtime.
|
|
138
174
|
|
|
139
|
-
|
|
140
|
-
import { themes } from '@brimveyn/aimux-config'
|
|
175
|
+
## Support Status
|
|
141
176
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
177
|
+
| Surface | Status | Notes |
|
|
178
|
+
| ------------ | ------------------- | --------------------------------------------------------------------- |
|
|
179
|
+
| `keymaps` | Supported | Fully registered by the runtime |
|
|
180
|
+
| `sessionBar` | Supported | Used during app initialization |
|
|
181
|
+
| `gitPane` | Supported | Placement and rendering of the git file list (see docs reference) |
|
|
182
|
+
| `theme` | Partially supported | Package surface exists, but runtime startup uses `aimux.json.themeId` |
|
|
183
|
+
| `backends` | Typed surface only | Runtime wiring deferred |
|
|
184
|
+
| `sidebar` | Typed surface only | Type exists, runtime not currently driven by this field |
|
|
185
|
+
| `hooks` | Typed surface only | Type exists, runtime use not currently wired |
|
|
186
|
+
| `snippets` | Typed surface only | Runtime currently uses `aimux-snippets.json` |
|
|
147
187
|
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
})
|
|
188
|
+
## Backends Subpath
|
|
189
|
+
|
|
190
|
+
The package also exports:
|
|
191
|
+
|
|
192
|
+
```ts
|
|
193
|
+
@brimveyn/aimux-config/backends
|
|
155
194
|
```
|
|
156
195
|
|
|
157
|
-
|
|
196
|
+
Current helpers:
|
|
197
|
+
|
|
198
|
+
- `claudeBackend()`
|
|
199
|
+
- `codexBackend()`
|
|
200
|
+
|
|
201
|
+
These helpers are documented as stubs. Do not treat them as a fully supported
|
|
202
|
+
runtime backend override system yet.
|
|
203
|
+
|
|
204
|
+
## More Documentation
|
|
205
|
+
|
|
206
|
+
- [`../../docs/reference/config-reference.md`](../../docs/reference/config-reference.md)
|
|
207
|
+
- [`../../docs/guide/keymaps.md`](../../docs/guide/keymaps.md)
|
|
208
|
+
- [`../../docs/guide/themes.md`](../../docs/guide/themes.md)
|
|
209
|
+
- [`../../docs/concepts/config-and-state.md`](../../docs/concepts/config-and-state.md)
|
|
210
|
+
- [`../../docs/concepts/profiles.md`](../../docs/concepts/profiles.md)
|
|
158
211
|
|
|
159
212
|
## License
|
|
160
213
|
|
package/package.json
CHANGED
package/src/actions.ts
CHANGED
|
@@ -35,8 +35,16 @@ export const themePicker: KeyResult = r(
|
|
|
35
35
|
export const helpModal: KeyResult = r([{ type: 'open-help-modal' }], [], 'modal.help')
|
|
36
36
|
|
|
37
37
|
export const toggleSidebar: KeyResult = r([{ type: 'toggle-sidebar' }])
|
|
38
|
-
export const
|
|
38
|
+
export const toggleGitPane: KeyResult = r([{ type: 'toggle-git-pane' }])
|
|
39
39
|
export const toggleSessionBar: KeyResult = r([{ type: 'toggle-session-bar' }])
|
|
40
|
+
|
|
41
|
+
export function setGitPaneMode(mode: 'embedded' | 'pane'): KeyResult {
|
|
42
|
+
return r([{ mode, type: 'set-git-pane-mode' }])
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export function setGitPanePosition(position: 'top' | 'bottom' | 'left' | 'right'): KeyResult {
|
|
46
|
+
return r([{ position, type: 'set-git-pane-position' }])
|
|
47
|
+
}
|
|
40
48
|
export const enterGitMode: KeyResult = r([{ type: 'enter-git-mode' }], [], 'git-mode')
|
|
41
49
|
|
|
42
50
|
export function switchSessionByIndex(index: number): KeyResult {
|
|
@@ -97,8 +105,8 @@ export function resizeSidebar(delta: number): KeyResult {
|
|
|
97
105
|
return r([{ delta, type: 'resize-sidebar' }])
|
|
98
106
|
}
|
|
99
107
|
|
|
100
|
-
export function
|
|
101
|
-
return r([{ delta, type: 'resize-git-
|
|
108
|
+
export function resizeGitPane(delta: number): KeyResult {
|
|
109
|
+
return r([{ delta, type: 'resize-git-pane' }])
|
|
102
110
|
}
|
|
103
111
|
|
|
104
112
|
export function focusPane(direction: 'left' | 'right' | 'up' | 'down'): KeyResult {
|
|
@@ -200,6 +208,12 @@ export const beginSnippetFilter: KeyResult = r(
|
|
|
200
208
|
'modal.snippet-picker.filtering'
|
|
201
209
|
)
|
|
202
210
|
|
|
211
|
+
export const beginHelpFilter: KeyResult = r(
|
|
212
|
+
[{ type: 'begin-help-filter' }],
|
|
213
|
+
[],
|
|
214
|
+
'modal.help.filtering'
|
|
215
|
+
)
|
|
216
|
+
|
|
203
217
|
export const confirmSplit: KeyResult = r([], [{ type: 'confirm-split' }])
|
|
204
218
|
|
|
205
219
|
export const restoreTheme: KeyResult = r(
|
|
@@ -252,21 +266,6 @@ export const confirmUpdateSelection: KeyResult = r(
|
|
|
252
266
|
'navigation'
|
|
253
267
|
)
|
|
254
268
|
|
|
255
|
-
// Layout-specific
|
|
256
|
-
export const exitLayoutToInput: KeyResult = r(
|
|
257
|
-
[{ focusMode: 'terminal-input', type: 'set-focus-mode' }],
|
|
258
|
-
[],
|
|
259
|
-
'terminal-input'
|
|
260
|
-
)
|
|
261
|
-
|
|
262
|
-
export const exitLayoutToNavigation: ActionFn = (ctx: ModeContext) => {
|
|
263
|
-
const actions: KeyResult['actions'] = [{ focusMode: 'navigation', type: 'set-focus-mode' }]
|
|
264
|
-
if (!ctx.state.sidebar.visible) {
|
|
265
|
-
actions.push({ type: 'toggle-sidebar' })
|
|
266
|
-
}
|
|
267
|
-
return r(actions, [], 'navigation')
|
|
268
|
-
}
|
|
269
|
-
|
|
270
269
|
export const closePane: ActionFn = (ctx: ModeContext) => {
|
|
271
270
|
const tabId = ctx.state.activeTabId
|
|
272
271
|
if (!tabId) return null
|
|
@@ -299,12 +298,6 @@ export const leaveTerminalInput: KeyResult = r(
|
|
|
299
298
|
'navigation'
|
|
300
299
|
)
|
|
301
300
|
|
|
302
|
-
export const enterLayoutMode: KeyResult = r(
|
|
303
|
-
[{ focusMode: 'layout', type: 'set-focus-mode' }],
|
|
304
|
-
[],
|
|
305
|
-
'layout'
|
|
306
|
-
)
|
|
307
|
-
|
|
308
301
|
export const toggleSidebarFromInput: KeyResult = r([{ type: 'toggle-sidebar' }])
|
|
309
302
|
|
|
310
303
|
// Session name modal
|
package/src/defaults.ts
CHANGED
|
@@ -27,10 +27,10 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
27
27
|
.map('<C-t>', actions.themePicker, 'Theme picker')
|
|
28
28
|
.map('<C-h>', actions.resizeSidebar(-2), 'Sidebar narrower')
|
|
29
29
|
.map('<C-l>', actions.resizeSidebar(2), 'Sidebar wider')
|
|
30
|
-
.map('G', actions.
|
|
30
|
+
.map('G', actions.toggleGitPane, 'Toggle git pane')
|
|
31
31
|
.map('<C-d>', actions.enterGitMode, 'Enter git mode')
|
|
32
|
-
.map('<C-j>', actions.
|
|
33
|
-
.map('<C-k>', actions.
|
|
32
|
+
.map('<C-j>', actions.resizeGitPane(-0.05), 'Git pane smaller')
|
|
33
|
+
.map('<C-k>', actions.resizeGitPane(0.05), 'Git pane larger')
|
|
34
34
|
.map('J', actions.reorderTab(1), 'Move tab right')
|
|
35
35
|
.map('j', actions.nextTab, 'Next tab')
|
|
36
36
|
.map('K', actions.reorderTab(-1), 'Move tab left')
|
|
@@ -46,8 +46,18 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
46
46
|
.mode('terminal-input', (m) =>
|
|
47
47
|
m
|
|
48
48
|
.map('<C-z>', actions.leaveTerminalInput, 'Leave insert')
|
|
49
|
-
.map('<Leader>', actions.enterLayoutMode, 'Layout mode')
|
|
50
49
|
.map('<C-b>', actions.toggleSidebarFromInput, 'Toggle sidebar')
|
|
50
|
+
.map('<Leader>h', actions.focusPane('left'), 'Focus left')
|
|
51
|
+
.map('<Leader>j', actions.focusPane('down'), 'Focus down')
|
|
52
|
+
.map('<Leader>k', actions.focusPane('up'), 'Focus up')
|
|
53
|
+
.map('<Leader>l', actions.focusPane('right'), 'Focus right')
|
|
54
|
+
.map('<Leader>H', actions.resizePane(-1, 'vertical'), 'Shrink ←', { repeatable: true })
|
|
55
|
+
.map('<Leader>L', actions.resizePane(1, 'vertical'), 'Grow →', { repeatable: true })
|
|
56
|
+
.map('<Leader>K', actions.resizePane(-1, 'horizontal'), 'Shrink ↑', { repeatable: true })
|
|
57
|
+
.map('<Leader>J', actions.resizePane(1, 'horizontal'), 'Grow ↓', { repeatable: true })
|
|
58
|
+
.map('<Leader>|', actions.splitVertical, 'Split vertical')
|
|
59
|
+
.map('<Leader>-', actions.splitHorizontal, 'Split horizontal')
|
|
60
|
+
.map('<Leader>q', actions.closePane, 'Close pane')
|
|
51
61
|
)
|
|
52
62
|
|
|
53
63
|
// -----------------------------------------------------------------------
|
|
@@ -67,27 +77,6 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
67
77
|
.map('<Leader>9', actions.switchSessionByIndex(9), 'Session 9')
|
|
68
78
|
)
|
|
69
79
|
|
|
70
|
-
// -----------------------------------------------------------------------
|
|
71
|
-
// Layout mode
|
|
72
|
-
// -----------------------------------------------------------------------
|
|
73
|
-
.mode('layout', (m) =>
|
|
74
|
-
m
|
|
75
|
-
.map('<Esc>', actions.exitLayoutToInput, 'Back to insert')
|
|
76
|
-
.map('<Leader>', actions.exitLayoutToInput, 'Back to insert')
|
|
77
|
-
.map('<C-z>', actions.exitLayoutToNavigation, 'Back to nav')
|
|
78
|
-
.map('H', actions.resizePane(-1, 'vertical'), 'Shrink ←')
|
|
79
|
-
.map('L', actions.resizePane(1, 'vertical'), 'Grow →')
|
|
80
|
-
.map('K', actions.resizePane(-1, 'horizontal'), 'Shrink ↑')
|
|
81
|
-
.map('J', actions.resizePane(1, 'horizontal'), 'Grow ↓')
|
|
82
|
-
.map('h', actions.focusPane('left'), 'Focus left')
|
|
83
|
-
.map('j', actions.focusPane('down'), 'Focus down')
|
|
84
|
-
.map('k', actions.focusPane('up'), 'Focus up')
|
|
85
|
-
.map('l', actions.focusPane('right'), 'Focus right')
|
|
86
|
-
.map('|', actions.splitVertical, 'Split vertical')
|
|
87
|
-
.map('-', actions.splitHorizontal, 'Split horizontal')
|
|
88
|
-
.map('q', actions.closePane, 'Close pane')
|
|
89
|
-
)
|
|
90
|
-
|
|
91
80
|
// -----------------------------------------------------------------------
|
|
92
81
|
// Git mode
|
|
93
82
|
// -----------------------------------------------------------------------
|
|
@@ -109,7 +98,30 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
109
98
|
// -----------------------------------------------------------------------
|
|
110
99
|
// Modal: help
|
|
111
100
|
// -----------------------------------------------------------------------
|
|
112
|
-
.mode('modal.help', (m) =>
|
|
101
|
+
.mode('modal.help', (m) =>
|
|
102
|
+
m
|
|
103
|
+
.map('<Esc>', actions.closeModal, 'Close')
|
|
104
|
+
.map('j', actions.moveModalSelection(1), 'Next')
|
|
105
|
+
.map('k', actions.moveModalSelection(-1), 'Prev')
|
|
106
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
107
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
108
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
109
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
110
|
+
.map('/', actions.beginHelpFilter, 'Filter')
|
|
111
|
+
)
|
|
112
|
+
|
|
113
|
+
// -----------------------------------------------------------------------
|
|
114
|
+
// Modal: help filtering
|
|
115
|
+
// -----------------------------------------------------------------------
|
|
116
|
+
.mode('modal.help.filtering', (m) =>
|
|
117
|
+
m
|
|
118
|
+
.map('<Esc>', actions.cancelCommandEdit('modal.help'), 'Cancel')
|
|
119
|
+
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
120
|
+
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
121
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
122
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
123
|
+
.passthrough()
|
|
124
|
+
)
|
|
113
125
|
|
|
114
126
|
// -----------------------------------------------------------------------
|
|
115
127
|
// Modal: theme-picker
|
|
@@ -121,6 +133,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
121
133
|
.map('k', actions.previewTheme(-1), 'Prev')
|
|
122
134
|
.map('<Down>', actions.previewTheme(1))
|
|
123
135
|
.map('<Up>', actions.previewTheme(-1))
|
|
136
|
+
.map('<C-n>', actions.previewTheme(1))
|
|
137
|
+
.map('<C-p>', actions.previewTheme(-1))
|
|
124
138
|
.map('<CR>', actions.confirmTheme, 'Confirm')
|
|
125
139
|
)
|
|
126
140
|
|
|
@@ -138,6 +152,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
138
152
|
.map('<Left>', actions.moveModalSelection(-1))
|
|
139
153
|
.map('<Down>', actions.moveModalSelection(1))
|
|
140
154
|
.map('<Up>', actions.moveModalSelection(-1))
|
|
155
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
156
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
141
157
|
.map('<CR>', actions.confirmUpdateSelection, 'Confirm')
|
|
142
158
|
)
|
|
143
159
|
|
|
@@ -161,6 +177,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
161
177
|
.map('k', actions.moveModalSelection(-1), 'Prev')
|
|
162
178
|
.map('<Down>', actions.moveModalSelection(1))
|
|
163
179
|
.map('<Up>', actions.moveModalSelection(-1))
|
|
180
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
181
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
164
182
|
.map('<CR>', actions.launchSelectedAssistant, 'Launch')
|
|
165
183
|
.map('e', actions.beginCommandEdit, 'Edit command')
|
|
166
184
|
)
|
|
@@ -174,6 +192,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
174
192
|
.map('<CR>', actions.commitCommandEdit, 'Save')
|
|
175
193
|
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
176
194
|
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
195
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
196
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
177
197
|
.passthrough()
|
|
178
198
|
)
|
|
179
199
|
|
|
@@ -187,6 +207,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
187
207
|
.map('k', actions.moveModalSelection(-1), 'Prev')
|
|
188
208
|
.map('<Down>', actions.moveModalSelection(1))
|
|
189
209
|
.map('<Up>', actions.moveModalSelection(-1))
|
|
210
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
211
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
190
212
|
.map('<CR>', actions.confirmSelectedSession, 'Open')
|
|
191
213
|
.map('n', actions.openCreateSessionModal, 'New')
|
|
192
214
|
.map('r', actions.openRenameSelectedSession, 'Rename')
|
|
@@ -203,6 +225,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
203
225
|
.map('<CR>', actions.confirmSelectedSession, 'Open')
|
|
204
226
|
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
205
227
|
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
228
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
229
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
206
230
|
.passthrough()
|
|
207
231
|
)
|
|
208
232
|
|
|
@@ -226,6 +250,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
226
250
|
.map('<CR>', actions.confirmCreateSession, 'Confirm')
|
|
227
251
|
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
228
252
|
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
253
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
254
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
229
255
|
.passthrough()
|
|
230
256
|
)
|
|
231
257
|
|
|
@@ -239,6 +265,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
239
265
|
.map('k', actions.moveModalSelection(-1), 'Prev')
|
|
240
266
|
.map('<Down>', actions.moveModalSelection(1))
|
|
241
267
|
.map('<Up>', actions.moveModalSelection(-1))
|
|
268
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
269
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
242
270
|
.map('<CR>', actions.pasteSelectedSnippet, 'Send')
|
|
243
271
|
.map('a', actions.pasteSnippetToGroup, 'Send to group')
|
|
244
272
|
.map('n', actions.openSnippetEditor, 'New')
|
|
@@ -258,6 +286,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
258
286
|
.map('<C-a>', actions.snippetFilterPasteToGroup, 'Send to group')
|
|
259
287
|
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
260
288
|
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
289
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
290
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
261
291
|
.passthrough()
|
|
262
292
|
)
|
|
263
293
|
|
|
@@ -271,6 +301,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
271
301
|
.map('<CR>', actions.saveSnippetEditor, 'Save')
|
|
272
302
|
.map('<C-n>', actions.moveModalSelection(1), 'Next')
|
|
273
303
|
.map('<C-p>', actions.moveModalSelection(-1), 'Prev')
|
|
304
|
+
.map('<Down>', actions.moveModalSelection(1))
|
|
305
|
+
.map('<Up>', actions.moveModalSelection(-1))
|
|
274
306
|
.passthrough()
|
|
275
307
|
)
|
|
276
308
|
|
|
@@ -284,6 +316,8 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
284
316
|
.map('k', actions.moveModalSelection(-1), 'Prev')
|
|
285
317
|
.map('<Down>', actions.moveModalSelection(1))
|
|
286
318
|
.map('<Up>', actions.moveModalSelection(-1))
|
|
319
|
+
.map('<C-n>', actions.moveModalSelection(1))
|
|
320
|
+
.map('<C-p>', actions.moveModalSelection(-1))
|
|
287
321
|
.map('<CR>', actions.confirmSplit, 'Confirm')
|
|
288
322
|
)
|
|
289
323
|
|
package/src/index.ts
CHANGED
package/src/keymap-builder.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
Action,
|
|
3
3
|
BindingDef,
|
|
4
|
+
BindingOptions,
|
|
4
5
|
GroupBuilderApi,
|
|
5
6
|
KeymapBuilderApi,
|
|
6
7
|
ModeBindingBuilderApi,
|
|
@@ -21,11 +22,12 @@ export class GroupBuilder implements GroupBuilderApi {
|
|
|
21
22
|
private readonly groupName: string
|
|
22
23
|
) {}
|
|
23
24
|
|
|
24
|
-
map(keys: string, action: Action, description?: string): this {
|
|
25
|
+
map(keys: string, action: Action, description?: string, opts?: BindingOptions): this {
|
|
25
26
|
this.bindings.push({
|
|
26
27
|
description,
|
|
27
28
|
group: this.groupName,
|
|
28
29
|
keys: `${this.prefix}${keys}`,
|
|
30
|
+
repeatable: opts?.repeatable,
|
|
29
31
|
result: action,
|
|
30
32
|
})
|
|
31
33
|
return this
|
|
@@ -48,8 +50,8 @@ export class ModeBindingBuilder implements ModeBindingBuilderApi {
|
|
|
48
50
|
private readonly removals: string[] = []
|
|
49
51
|
private _passthrough = false
|
|
50
52
|
|
|
51
|
-
map(keys: string, action: Action, description?: string): this {
|
|
52
|
-
this.bindings.push({ description, keys, result: action })
|
|
53
|
+
map(keys: string, action: Action, description?: string, opts?: BindingOptions): this {
|
|
54
|
+
this.bindings.push({ description, keys, repeatable: opts?.repeatable, result: action })
|
|
53
55
|
return this
|
|
54
56
|
}
|
|
55
57
|
|
package/src/resolver.ts
CHANGED
package/src/types.ts
CHANGED
|
@@ -11,7 +11,6 @@
|
|
|
11
11
|
export type ModeId =
|
|
12
12
|
| 'navigation'
|
|
13
13
|
| 'terminal-input'
|
|
14
|
-
| 'layout'
|
|
15
14
|
| 'git-mode'
|
|
16
15
|
| 'modal.new-tab'
|
|
17
16
|
| 'modal.new-tab.command-edit'
|
|
@@ -25,6 +24,7 @@ export type ModeId =
|
|
|
25
24
|
| 'modal.snippet-editor'
|
|
26
25
|
| 'modal.theme-picker'
|
|
27
26
|
| 'modal.help'
|
|
27
|
+
| 'modal.help.filtering'
|
|
28
28
|
| 'modal.split-picker'
|
|
29
29
|
| 'modal.git-commit'
|
|
30
30
|
| 'modal.update-available'
|
|
@@ -35,13 +35,7 @@ export type BuiltinAssistantId = 'claude' | 'codex' | 'opencode' | 'terminal'
|
|
|
35
35
|
export type AssistantId = BuiltinAssistantId | (string & {})
|
|
36
36
|
export type TabStatus = 'starting' | 'running' | 'disconnected' | 'exited' | 'error'
|
|
37
37
|
export type TabActivity = 'busy' | 'idle'
|
|
38
|
-
export type FocusMode =
|
|
39
|
-
| 'navigation'
|
|
40
|
-
| 'terminal-input'
|
|
41
|
-
| 'modal'
|
|
42
|
-
| 'command-edit'
|
|
43
|
-
| 'layout'
|
|
44
|
-
| 'git'
|
|
38
|
+
export type FocusMode = 'navigation' | 'terminal-input' | 'modal' | 'command-edit' | 'git'
|
|
45
39
|
export type SplitDirection = 'horizontal' | 'vertical'
|
|
46
40
|
|
|
47
41
|
// ─── Terminal data shapes ─────────────────────────────────────────────────────
|
|
@@ -109,8 +103,6 @@ export interface WorkspaceSnapshotV1 {
|
|
|
109
103
|
sidebar: {
|
|
110
104
|
visible: boolean
|
|
111
105
|
width: number
|
|
112
|
-
gitPanelVisible?: boolean
|
|
113
|
-
gitPanelRatio?: number
|
|
114
106
|
}
|
|
115
107
|
tabs: PersistedTabSnapshot[]
|
|
116
108
|
layoutTree?: LayoutNode
|
|
@@ -163,8 +155,15 @@ export interface SidebarState {
|
|
|
163
155
|
width: number
|
|
164
156
|
minWidth: number
|
|
165
157
|
maxWidth: number
|
|
166
|
-
|
|
167
|
-
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
export interface GitPaneState {
|
|
161
|
+
visible: boolean
|
|
162
|
+
mode: 'embedded' | 'pane'
|
|
163
|
+
position: 'top' | 'bottom' | 'left' | 'right'
|
|
164
|
+
ratio: number
|
|
165
|
+
path: GitPanePathConfig
|
|
166
|
+
diffCount: GitPaneDiffCountConfig
|
|
168
167
|
}
|
|
169
168
|
|
|
170
169
|
export type GitFileStatus = 'M' | 'A' | 'D' | 'R' | 'C' | 'U' | '?'
|
|
@@ -242,6 +241,7 @@ export interface ModalThemePicker extends ModalBase {
|
|
|
242
241
|
}
|
|
243
242
|
export interface ModalHelp extends ModalBase {
|
|
244
243
|
type: 'help'
|
|
244
|
+
entryCount: number
|
|
245
245
|
}
|
|
246
246
|
export interface ModalSplitPicker extends ModalBase {
|
|
247
247
|
type: 'split-picker'
|
|
@@ -310,6 +310,7 @@ export interface AppState {
|
|
|
310
310
|
snippets: SnippetRecord[]
|
|
311
311
|
focusMode: FocusMode
|
|
312
312
|
sidebar: SidebarState
|
|
313
|
+
gitPane: GitPaneState
|
|
313
314
|
modal: ModalState
|
|
314
315
|
layout: LayoutState
|
|
315
316
|
customCommands: Record<AssistantId, string>
|
|
@@ -341,6 +342,8 @@ export type ModalAction =
|
|
|
341
342
|
| { type: 'open-snippet-picker' }
|
|
342
343
|
| { type: 'open-snippet-editor'; snippetId?: string }
|
|
343
344
|
| { type: 'begin-snippet-filter' }
|
|
345
|
+
| { type: 'begin-help-filter' }
|
|
346
|
+
| { type: 'set-help-entry-count'; count: number }
|
|
344
347
|
| { type: 'open-theme-picker' }
|
|
345
348
|
| { type: 'open-update-available-modal'; currentVersion: string; latestVersion: string }
|
|
346
349
|
|
|
@@ -410,8 +413,10 @@ export type UIAction =
|
|
|
410
413
|
| { type: 'resize-sidebar'; delta: number }
|
|
411
414
|
| { type: 'set-focus-mode'; focusMode: FocusMode }
|
|
412
415
|
| { type: 'set-terminal-size'; cols: number; rows: number }
|
|
413
|
-
| { type: 'toggle-git-
|
|
414
|
-
| { type: 'resize-git-
|
|
416
|
+
| { type: 'toggle-git-pane' }
|
|
417
|
+
| { type: 'resize-git-pane'; delta: number }
|
|
418
|
+
| { type: 'set-git-pane-mode'; mode: 'embedded' | 'pane' }
|
|
419
|
+
| { type: 'set-git-pane-position'; position: 'top' | 'bottom' | 'left' | 'right' }
|
|
415
420
|
| { type: 'set-pending-chords'; chords: string[] | null }
|
|
416
421
|
| { type: 'toggle-session-bar' }
|
|
417
422
|
| { type: 'set-session-bar-position'; position: SessionBarPosition }
|
|
@@ -587,8 +592,12 @@ export type Action = KeyResult | ActionFn
|
|
|
587
592
|
|
|
588
593
|
// ─── Keymap builder API types ─────────────────────────────────────────────────
|
|
589
594
|
|
|
595
|
+
export interface BindingOptions {
|
|
596
|
+
repeatable?: boolean
|
|
597
|
+
}
|
|
598
|
+
|
|
590
599
|
export interface GroupBuilderApi {
|
|
591
|
-
map(keys: string, action: Action, description?: string): GroupBuilderApi
|
|
600
|
+
map(keys: string, action: Action, description?: string, opts?: BindingOptions): GroupBuilderApi
|
|
592
601
|
group(
|
|
593
602
|
prefix: string,
|
|
594
603
|
name: string,
|
|
@@ -597,7 +606,12 @@ export interface GroupBuilderApi {
|
|
|
597
606
|
}
|
|
598
607
|
|
|
599
608
|
export interface ModeBindingBuilderApi {
|
|
600
|
-
map(
|
|
609
|
+
map(
|
|
610
|
+
keys: string,
|
|
611
|
+
action: Action,
|
|
612
|
+
description?: string,
|
|
613
|
+
opts?: BindingOptions
|
|
614
|
+
): ModeBindingBuilderApi
|
|
601
615
|
unmap(keys: string): ModeBindingBuilderApi
|
|
602
616
|
group(
|
|
603
617
|
prefix: string,
|
|
@@ -623,12 +637,40 @@ export interface SessionBarConfig {
|
|
|
623
637
|
position?: SessionBarPosition
|
|
624
638
|
}
|
|
625
639
|
|
|
640
|
+
// ─── Git pane config (discriminated union) ────────────────────────────────────
|
|
641
|
+
|
|
642
|
+
export type GitPanePathConfig =
|
|
643
|
+
| { enabled: false }
|
|
644
|
+
| { enabled: true; pathFn?: (path: string) => string }
|
|
645
|
+
|
|
646
|
+
export type GitPaneDiffCountConfig = { enabled: boolean }
|
|
647
|
+
|
|
648
|
+
interface GitPaneBaseConfig {
|
|
649
|
+
visible?: boolean
|
|
650
|
+
ratio?: number
|
|
651
|
+
path?: GitPanePathConfig
|
|
652
|
+
diffCount?: GitPaneDiffCountConfig
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export interface GitPaneEmbeddedConfig extends GitPaneBaseConfig {
|
|
656
|
+
mode?: 'embedded'
|
|
657
|
+
position?: 'top' | 'bottom'
|
|
658
|
+
}
|
|
659
|
+
|
|
660
|
+
export interface GitPanePaneConfig extends GitPaneBaseConfig {
|
|
661
|
+
mode: 'pane'
|
|
662
|
+
position?: 'left' | 'right'
|
|
663
|
+
}
|
|
664
|
+
|
|
665
|
+
export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
|
|
666
|
+
|
|
626
667
|
export interface AimuxUserConfig {
|
|
627
668
|
theme?: ThemeId | ThemeDefinition
|
|
628
669
|
keymaps?: (k: KeymapBuilderApi) => KeymapBuilderApi
|
|
629
670
|
backends?: Record<string, BackendConfig>
|
|
630
671
|
sidebar?: SidebarConfig
|
|
631
672
|
sessionBar?: SessionBarConfig
|
|
673
|
+
gitPane?: GitPaneConfig
|
|
632
674
|
hooks?: HooksConfig
|
|
633
675
|
snippets?: SnippetDef[]
|
|
634
676
|
}
|
|
@@ -640,6 +682,7 @@ export interface BindingDef {
|
|
|
640
682
|
result: Action
|
|
641
683
|
group?: string
|
|
642
684
|
description?: string
|
|
685
|
+
repeatable?: boolean
|
|
643
686
|
}
|
|
644
687
|
|
|
645
688
|
export interface ModeKeymapDef {
|
|
@@ -660,6 +703,7 @@ export interface ResolvedConfig {
|
|
|
660
703
|
backends: Record<string, BackendConfig>
|
|
661
704
|
sidebar: SidebarConfig
|
|
662
705
|
sessionBar: SessionBarConfig
|
|
706
|
+
gitPane: GitPaneConfig
|
|
663
707
|
hooks: HooksConfig
|
|
664
708
|
snippets: SnippetDef[]
|
|
665
709
|
}
|