@brimveyn/aimux-config 0.4.6 → 0.5.0
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 +34 -44
- package/package.json +1 -1
- package/src/actions.ts +89 -9
- package/src/auto-commit-runtime.ts +18 -0
- package/src/defaults.ts +30 -1
- package/src/index.ts +5 -0
- package/src/resolver.ts +87 -4
- package/src/types.ts +151 -10
package/README.md
CHANGED
|
@@ -44,17 +44,21 @@ import { defineConfig, actions } from '@brimveyn/aimux-config'
|
|
|
44
44
|
|
|
45
45
|
export default defineConfig({
|
|
46
46
|
sessionBar: {
|
|
47
|
-
|
|
48
|
-
|
|
47
|
+
initialPosition: 'top',
|
|
48
|
+
initialVisible: true,
|
|
49
49
|
},
|
|
50
50
|
|
|
51
51
|
keymaps: (k) =>
|
|
52
|
-
k.mode('navigation', (m) => m.map('<C-p>', actions.sessionPicker, '
|
|
52
|
+
k.mode('navigation', (m) => m.map('<C-p>', actions.sessionPicker, 'Workspace picker')),
|
|
53
53
|
})
|
|
54
54
|
```
|
|
55
55
|
|
|
56
56
|
This example only uses surfaces that are wired into the runtime today.
|
|
57
57
|
|
|
58
|
+
Important: typed config expresses startup intent. Fields such as
|
|
59
|
+
`sessionBar.initialVisible` or `gitPane.initialMode` are reapplied on every
|
|
60
|
+
launch; runtime UI changes do not write back into `aimux.config.ts`.
|
|
61
|
+
|
|
58
62
|
## Key Notation
|
|
59
63
|
|
|
60
64
|
| Notation | Meaning |
|
|
@@ -136,7 +140,7 @@ explanation.
|
|
|
136
140
|
Pre-built actions include:
|
|
137
141
|
|
|
138
142
|
- tabs: `nextTab`, `prevTab`, `newTab`, `renameTab`, `closeTab`, `restartTab`
|
|
139
|
-
-
|
|
143
|
+
- workspaces: `sessionPicker`, `switchSessionByIndex(n)` and workspace modal actions
|
|
140
144
|
- snippets: `snippetPicker`, snippet editor and filter actions
|
|
141
145
|
- themes: `themePicker`, `previewTheme`, `confirmTheme`, `restoreTheme`
|
|
142
146
|
- panes: `splitVertical`, `splitHorizontal`, `focusPane`, `resizePane`, `closePane`
|
|
@@ -149,58 +153,44 @@ You can also bind a custom `ActionFn` for dynamic behavior.
|
|
|
149
153
|
|
|
150
154
|
## Themes
|
|
151
155
|
|
|
152
|
-
|
|
153
|
-
(`aimux`, `dracula-at-night`) ship alongside the full Shiki catalog — 67
|
|
154
|
-
themes total. The same `Theme` object powers UI colors and code highlighting.
|
|
156
|
+
`theme` is a startup override surface, not the persisted selected theme.
|
|
155
157
|
|
|
156
|
-
|
|
158
|
+
Use it to choose the built-in light or dark family when there is no persisted
|
|
159
|
+
`aimux.json.themeId`, and to apply palette overrides on top of the chosen base
|
|
160
|
+
theme:
|
|
157
161
|
|
|
158
162
|
```ts
|
|
159
|
-
import { defineConfig
|
|
163
|
+
import { defineConfig } from '@brimveyn/aimux-config'
|
|
160
164
|
|
|
161
165
|
export default defineConfig({
|
|
162
|
-
theme:
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}),
|
|
169
|
-
// Or drop a full VSCode theme JSON verbatim:
|
|
170
|
-
'custom': themes.full({
|
|
171
|
-
name: 'custom',
|
|
172
|
-
displayName: 'Custom',
|
|
173
|
-
type: 'dark',
|
|
174
|
-
fg: '#eee',
|
|
175
|
-
bg: '#1a1a1a',
|
|
176
|
-
colors: {
|
|
177
|
-
/* VSCode workbench keys */
|
|
178
|
-
},
|
|
179
|
-
settings: [
|
|
180
|
-
/* TextMate token rules */
|
|
181
|
-
],
|
|
182
|
-
}),
|
|
166
|
+
theme: {
|
|
167
|
+
initialMode: 'dark',
|
|
168
|
+
paletteOverrides: {
|
|
169
|
+
primary: '#7dd3fc',
|
|
170
|
+
warning: '#fbbf24',
|
|
171
|
+
},
|
|
183
172
|
},
|
|
184
173
|
})
|
|
185
174
|
```
|
|
186
175
|
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
176
|
+
The runtime theme picker persists the selected theme id separately in
|
|
177
|
+
`aimux.json`, and that persisted choice wins on restart. See
|
|
178
|
+
[`../../docs/guide/themes.md`](../../docs/guide/themes.md) for picker behavior,
|
|
179
|
+
palette guidance, and precedence details.
|
|
190
180
|
|
|
191
181
|
## Support Status
|
|
192
182
|
|
|
193
|
-
| Surface | Status | Notes
|
|
194
|
-
| ------------ | ------------------ |
|
|
195
|
-
| `keymaps` | Supported | Fully registered by the runtime
|
|
196
|
-
| `sessionBar` | Supported |
|
|
197
|
-
| `gitPane` | Supported |
|
|
198
|
-
| `theme` | Supported |
|
|
199
|
-
| `themes` | Supported | User themes; appear in the picker and power synthesized highlighting
|
|
200
|
-
| `backends` | Typed surface only | Runtime wiring deferred
|
|
201
|
-
| `sidebar` | Typed surface only | Type exists, runtime not currently driven by this field
|
|
202
|
-
| `hooks` | Typed surface only | Type exists, runtime use not currently wired
|
|
203
|
-
| `snippets` | Typed surface only | Runtime currently uses `aimux-snippets.json`
|
|
183
|
+
| Surface | Status | Notes |
|
|
184
|
+
| ------------ | ------------------ | ------------------------------------------------------------------------------------ |
|
|
185
|
+
| `keymaps` | Supported | Fully registered by the runtime |
|
|
186
|
+
| `sessionBar` | Supported | Startup overrides; app-managed runtime state still persists separately |
|
|
187
|
+
| `gitPane` | Supported | Startup overrides for placement and rendering; runtime state persists separately |
|
|
188
|
+
| `theme` | Supported | `theme.initialMode` is a startup override; persisted `aimux.json.themeId` still wins |
|
|
189
|
+
| `themes` | Supported | User themes; appear in the picker and power synthesized highlighting |
|
|
190
|
+
| `backends` | Typed surface only | Runtime wiring deferred |
|
|
191
|
+
| `sidebar` | Typed surface only | Type exists, runtime not currently driven by this field |
|
|
192
|
+
| `hooks` | Typed surface only | Type exists, runtime use not currently wired |
|
|
193
|
+
| `snippets` | Typed surface only | Runtime currently uses `aimux-snippets.json` |
|
|
204
194
|
|
|
205
195
|
## Backends Subpath
|
|
206
196
|
|
package/package.json
CHANGED
package/src/actions.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
1
|
import type { ActionFn, AppAction, KeyResult, ModeContext, ModeId } from './types'
|
|
2
2
|
|
|
3
|
+
import { isAutoCommitEnabled } from './auto-commit-runtime'
|
|
4
|
+
|
|
5
|
+
const AUTO_COMMIT_DISABLED_MESSAGE =
|
|
6
|
+
'auto-commit is disabled — set autoCommit: { enabled: true } in aimux.config.ts'
|
|
7
|
+
|
|
3
8
|
function r(
|
|
4
9
|
actions: KeyResult['actions'] = [],
|
|
5
10
|
effects: KeyResult['effects'] = [],
|
|
@@ -545,7 +550,12 @@ export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
|
|
|
545
550
|
},
|
|
546
551
|
])
|
|
547
552
|
}
|
|
548
|
-
|
|
553
|
+
const sessionId = ctx.state.currentSessionId ?? undefined
|
|
554
|
+
return r(
|
|
555
|
+
[...clearPendingDelete(ctx), { sessionId, type: 'open-git-commit-modal' }],
|
|
556
|
+
[],
|
|
557
|
+
'modal.git-commit'
|
|
558
|
+
)
|
|
549
559
|
}
|
|
550
560
|
|
|
551
561
|
export const gitPush: ActionFn = (ctx: ModeContext) => {
|
|
@@ -564,18 +574,88 @@ export const gitPush: ActionFn = (ctx: ModeContext) => {
|
|
|
564
574
|
// Modal: git-commit
|
|
565
575
|
// ---------------------------------------------------------------------------
|
|
566
576
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (modal.type !== 'git-commit')
|
|
572
|
-
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
573
|
-
}
|
|
577
|
+
function extractGitCommitFields(modal: ModeContext['state']['modal']): {
|
|
578
|
+
title: string
|
|
579
|
+
body: string
|
|
580
|
+
} | null {
|
|
581
|
+
if (modal.type !== 'git-commit') return null
|
|
574
582
|
const editBuffer = modal.editBuffer ?? ''
|
|
575
583
|
const activeIsTitle = modal.activeField === 'title'
|
|
576
584
|
const title = (activeIsTitle ? editBuffer : modal.contentBuffer).trim()
|
|
577
585
|
const body = (activeIsTitle ? modal.contentBuffer : editBuffer).trim()
|
|
578
|
-
return
|
|
586
|
+
return { body, title }
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
export const gitCommitCancel: ActionFn = (ctx: ModeContext) => {
|
|
590
|
+
const modal = ctx.state.modal
|
|
591
|
+
if (modal.type === 'git-commit' && modal.stage === 'confirm') {
|
|
592
|
+
return r([{ type: 'git-commit-leave-confirm' }], [], 'modal.git-commit')
|
|
593
|
+
}
|
|
594
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
export const gitCommitEnterConfirm: ActionFn = (ctx: ModeContext) => {
|
|
598
|
+
const modal = ctx.state.modal
|
|
599
|
+
if (modal.type !== 'git-commit') return null
|
|
600
|
+
if (!isAutoCommitEnabled()) {
|
|
601
|
+
return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
|
|
602
|
+
}
|
|
603
|
+
const fields = extractGitCommitFields(modal)
|
|
604
|
+
const hasTitle = !!fields && fields.title.length > 0
|
|
605
|
+
if (hasTitle) {
|
|
606
|
+
return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
|
|
607
|
+
}
|
|
608
|
+
const sessionId = ctx.state.currentSessionId
|
|
609
|
+
if (!sessionId) {
|
|
610
|
+
return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
|
|
611
|
+
}
|
|
612
|
+
return r(
|
|
613
|
+
[{ sessionId, type: 'git-commit-enter-generating' }],
|
|
614
|
+
[{ sessionId, type: 'generate-auto-commit-now' }],
|
|
615
|
+
'modal.git-commit.generating'
|
|
616
|
+
)
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
export const gitCommitLeaveConfirm: KeyResult = r(
|
|
620
|
+
[{ type: 'git-commit-leave-confirm' }],
|
|
621
|
+
[],
|
|
622
|
+
'modal.git-commit'
|
|
623
|
+
)
|
|
624
|
+
|
|
625
|
+
export const gitCommitLeaveGenerating: ActionFn = (ctx: ModeContext) => {
|
|
626
|
+
const sessionId = ctx.state.currentSessionId
|
|
627
|
+
const actionsList: AppAction[] = [{ type: 'git-commit-leave-generating' }]
|
|
628
|
+
if (sessionId) {
|
|
629
|
+
actionsList.push({ sessionId, type: 'auto-commit-clear' })
|
|
630
|
+
}
|
|
631
|
+
return r(actionsList, [], 'modal.git-commit')
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
export const gitCommitAutoAccept: ActionFn = (ctx: ModeContext) => {
|
|
635
|
+
if (!isAutoCommitEnabled()) {
|
|
636
|
+
return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
|
|
637
|
+
}
|
|
638
|
+
const fields = extractGitCommitFields(ctx.state.modal)
|
|
639
|
+
if (!fields) {
|
|
640
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
641
|
+
}
|
|
642
|
+
return r(
|
|
643
|
+
[{ type: 'close-modal' }],
|
|
644
|
+
[{ body: fields.body, title: fields.title, type: 'git-commit-auto' }],
|
|
645
|
+
'git-mode'
|
|
646
|
+
)
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export const gitCommitSubmit: ActionFn = (ctx: ModeContext) => {
|
|
650
|
+
const fields = extractGitCommitFields(ctx.state.modal)
|
|
651
|
+
if (!fields) {
|
|
652
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
653
|
+
}
|
|
654
|
+
return r(
|
|
655
|
+
[{ type: 'close-modal' }],
|
|
656
|
+
[{ body: fields.body, title: fields.title, type: 'git-commit' }],
|
|
657
|
+
'git-mode'
|
|
658
|
+
)
|
|
579
659
|
}
|
|
580
660
|
|
|
581
661
|
export const gitCommitReturnKey: ActionFn = (ctx: ModeContext) => {
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Module-level flag that mirrors `autoCommit.enabled` from the resolved config.
|
|
3
|
+
*
|
|
4
|
+
* Actions (which run outside React and can't subscribe to a React context) read
|
|
5
|
+
* this via `isAutoCommitEnabled()`. The app sets it once at startup via
|
|
6
|
+
* `setAutoCommitEnabled(resolvedConfig.autoCommit.enabled)` before any children
|
|
7
|
+
* render, so downstream readers always see the correct value.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
let enabled = false
|
|
11
|
+
|
|
12
|
+
export function setAutoCommitEnabled(value: boolean): void {
|
|
13
|
+
enabled = value
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export function isAutoCommitEnabled(): boolean {
|
|
17
|
+
return enabled
|
|
18
|
+
}
|
package/src/defaults.ts
CHANGED
|
@@ -1,8 +1,17 @@
|
|
|
1
|
-
import type { ResolvedKeymapConfig } from './types'
|
|
1
|
+
import type { AutoCommitConfig, ResolvedKeymapConfig } from './types'
|
|
2
2
|
|
|
3
3
|
import * as actions from './actions'
|
|
4
4
|
import { KeymapBuilder } from './keymap-builder'
|
|
5
5
|
|
|
6
|
+
export const DEFAULT_AUTO_COMMIT_CONFIG: AutoCommitConfig = {
|
|
7
|
+
enabled: false,
|
|
8
|
+
models: {
|
|
9
|
+
claude: 'claude-haiku-4-5',
|
|
10
|
+
codex: 'gpt-5-mini',
|
|
11
|
+
},
|
|
12
|
+
timeoutMs: 60_000,
|
|
13
|
+
}
|
|
14
|
+
|
|
6
15
|
/**
|
|
7
16
|
* Build the default keymap — 1:1 transcription of all existing handlers.
|
|
8
17
|
*/
|
|
@@ -276,10 +285,30 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
276
285
|
m
|
|
277
286
|
.map('<Esc>', actions.gitCommitCancel, 'Cancel')
|
|
278
287
|
.map('<C-CR>', actions.gitCommitSubmit, 'Commit')
|
|
288
|
+
.map('<C-a>', actions.gitCommitEnterConfirm, 'Auto-commit (stage all)')
|
|
279
289
|
.map('<Tab>', actions.switchField, 'Next field')
|
|
280
290
|
.map('<CR>', actions.gitCommitReturnKey, 'Newline / confirm')
|
|
281
291
|
.passthrough()
|
|
282
292
|
)
|
|
283
293
|
|
|
294
|
+
// -----------------------------------------------------------------------
|
|
295
|
+
// Modal: git-commit confirm (auto-commit stage all + commit)
|
|
296
|
+
// -----------------------------------------------------------------------
|
|
297
|
+
.mode('modal.git-commit.confirm', (m) =>
|
|
298
|
+
m
|
|
299
|
+
.map('<Esc>', actions.gitCommitLeaveConfirm, 'Cancel auto-commit')
|
|
300
|
+
.map('<CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
|
|
301
|
+
.map('<C-CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
|
|
302
|
+
.map('<Tab>', actions.switchField, 'Next field')
|
|
303
|
+
.passthrough()
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
// -----------------------------------------------------------------------
|
|
307
|
+
// Modal: git-commit generating (waiting for LLM)
|
|
308
|
+
// -----------------------------------------------------------------------
|
|
309
|
+
.mode('modal.git-commit.generating', (m) =>
|
|
310
|
+
m.map('<Esc>', actions.gitCommitLeaveGenerating, 'Cancel generation')
|
|
311
|
+
)
|
|
312
|
+
|
|
284
313
|
return kb._build()
|
|
285
314
|
}
|
package/src/index.ts
CHANGED
|
@@ -22,6 +22,7 @@ export { paletteToShikiTheme } from './palette-to-shiki'
|
|
|
22
22
|
export { GroupBuilder, KeymapBuilder, ModeBindingBuilder } from './keymap-builder'
|
|
23
23
|
export { getDefaultKeymapConfig } from './defaults'
|
|
24
24
|
export { resolveConfig } from './resolver'
|
|
25
|
+
export { isAutoCommitEnabled, setAutoCommitEnabled } from './auto-commit-runtime'
|
|
25
26
|
|
|
26
27
|
// Type exports
|
|
27
28
|
export type {
|
|
@@ -33,8 +34,11 @@ export type {
|
|
|
33
34
|
AimuxThemeConfig,
|
|
34
35
|
// User-facing config
|
|
35
36
|
AimuxUserConfig,
|
|
37
|
+
AIUsageTool,
|
|
38
|
+
AIUsageToolConfig,
|
|
36
39
|
AppAction,
|
|
37
40
|
AppState,
|
|
41
|
+
AutoCommitConfig,
|
|
38
42
|
BackendConfig,
|
|
39
43
|
BindingDef,
|
|
40
44
|
FocusMode,
|
|
@@ -68,6 +72,7 @@ export type {
|
|
|
68
72
|
SnippetDef,
|
|
69
73
|
SnippetRecord,
|
|
70
74
|
SplitDirection,
|
|
75
|
+
StatusBarConfig,
|
|
71
76
|
TabSession,
|
|
72
77
|
Theme,
|
|
73
78
|
ThemeId,
|
package/src/resolver.ts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
AimuxUserConfig,
|
|
3
|
+
AutoCommitConfig,
|
|
3
4
|
ModeId,
|
|
4
5
|
ModeKeymapDef,
|
|
5
6
|
ResolvedConfig,
|
|
6
7
|
ResolvedKeymapConfig,
|
|
7
8
|
} from './types'
|
|
8
9
|
|
|
9
|
-
import { getDefaultKeymapConfig } from './defaults'
|
|
10
|
+
import { DEFAULT_AUTO_COMMIT_CONFIG, getDefaultKeymapConfig } from './defaults'
|
|
10
11
|
import { KeymapBuilder } from './keymap-builder'
|
|
11
12
|
|
|
12
13
|
/**
|
|
@@ -16,15 +17,97 @@ import { KeymapBuilder } from './keymap-builder'
|
|
|
16
17
|
export function resolveConfig(userConfig: AimuxUserConfig): ResolvedConfig {
|
|
17
18
|
const keymaps = resolveKeymaps(userConfig)
|
|
18
19
|
|
|
20
|
+
const autoCommit: AutoCommitConfig = {
|
|
21
|
+
enabled: userConfig.autoCommit?.enabled ?? DEFAULT_AUTO_COMMIT_CONFIG.enabled,
|
|
22
|
+
models: { ...DEFAULT_AUTO_COMMIT_CONFIG.models, ...userConfig.autoCommit?.models },
|
|
23
|
+
timeoutMs: userConfig.autoCommit?.timeoutMs ?? DEFAULT_AUTO_COMMIT_CONFIG.timeoutMs,
|
|
24
|
+
}
|
|
25
|
+
|
|
19
26
|
return {
|
|
27
|
+
autoCommit,
|
|
20
28
|
backends: userConfig.backends ?? {},
|
|
21
|
-
gitPane: userConfig.gitPane
|
|
29
|
+
gitPane: resolveGitPane(userConfig.gitPane),
|
|
22
30
|
hooks: userConfig.hooks ?? {},
|
|
23
31
|
keymaps,
|
|
24
|
-
sessionBar: userConfig.sessionBar
|
|
32
|
+
sessionBar: resolveSessionBar(userConfig.sessionBar),
|
|
25
33
|
sidebar: userConfig.sidebar ?? {},
|
|
26
34
|
snippets: userConfig.snippets ?? [],
|
|
27
|
-
|
|
35
|
+
statusBar: userConfig.statusBar ?? {},
|
|
36
|
+
theme: resolveTheme(userConfig.theme),
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function resolveTheme(userConfig: AimuxUserConfig['theme']): ResolvedConfig['theme'] {
|
|
41
|
+
if (!userConfig) return undefined
|
|
42
|
+
return {
|
|
43
|
+
initialMode: userConfig.initialMode ?? userConfig.mode,
|
|
44
|
+
paletteOverrides: userConfig.paletteOverrides,
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function resolveSessionBar(
|
|
49
|
+
userConfig: AimuxUserConfig['sessionBar']
|
|
50
|
+
): ResolvedConfig['sessionBar'] {
|
|
51
|
+
if (!userConfig) return {}
|
|
52
|
+
return {
|
|
53
|
+
initialPosition: userConfig.initialPosition ?? userConfig.position,
|
|
54
|
+
initialVisible: userConfig.initialVisible ?? userConfig.visible,
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function resolvePaneInitialPosition(
|
|
59
|
+
userConfig: NonNullable<AimuxUserConfig['gitPane']>
|
|
60
|
+
): 'left' | 'right' | undefined {
|
|
61
|
+
if (userConfig.initialPosition === 'left' || userConfig.initialPosition === 'right') {
|
|
62
|
+
return userConfig.initialPosition
|
|
63
|
+
}
|
|
64
|
+
if (userConfig.position === 'left' || userConfig.position === 'right') {
|
|
65
|
+
return userConfig.position
|
|
66
|
+
}
|
|
67
|
+
return undefined
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function resolveEmbeddedInitialPosition(
|
|
71
|
+
userConfig: NonNullable<AimuxUserConfig['gitPane']>
|
|
72
|
+
): 'top' | 'bottom' | undefined {
|
|
73
|
+
if (userConfig.initialPosition === 'top' || userConfig.initialPosition === 'bottom') {
|
|
74
|
+
return userConfig.initialPosition
|
|
75
|
+
}
|
|
76
|
+
if (userConfig.position === 'top' || userConfig.position === 'bottom') {
|
|
77
|
+
return userConfig.position
|
|
78
|
+
}
|
|
79
|
+
return undefined
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function resolveGitPane(userConfig: AimuxUserConfig['gitPane']): ResolvedConfig['gitPane'] {
|
|
83
|
+
if (!userConfig) return {}
|
|
84
|
+
|
|
85
|
+
const initialMode = userConfig.initialMode ?? userConfig.mode
|
|
86
|
+
const paneInitialPosition = resolvePaneInitialPosition(userConfig)
|
|
87
|
+
const embeddedInitialPosition = resolveEmbeddedInitialPosition(userConfig)
|
|
88
|
+
const shared = {
|
|
89
|
+
diffCount: userConfig.diffCount,
|
|
90
|
+
initialDiffModeRatio: userConfig.initialDiffModeRatio ?? userConfig.diffModeRatio,
|
|
91
|
+
initialFileListMode: userConfig.initialFileListMode ?? userConfig.fileListMode,
|
|
92
|
+
initialRatio: userConfig.initialRatio ?? userConfig.ratio,
|
|
93
|
+
initialTreeCompaction: userConfig.initialTreeCompaction ?? userConfig.treeCompaction,
|
|
94
|
+
initialVisible: userConfig.initialVisible ?? userConfig.visible,
|
|
95
|
+
path: userConfig.path,
|
|
96
|
+
prefetchRadius: userConfig.prefetchRadius,
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (initialMode === 'pane') {
|
|
100
|
+
return {
|
|
101
|
+
...shared,
|
|
102
|
+
initialMode: 'pane',
|
|
103
|
+
initialPosition: paneInitialPosition,
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
return {
|
|
108
|
+
...shared,
|
|
109
|
+
...(initialMode === 'embedded' ? { initialMode: 'embedded' as const } : {}),
|
|
110
|
+
initialPosition: embeddedInitialPosition,
|
|
28
111
|
}
|
|
29
112
|
}
|
|
30
113
|
|
package/src/types.ts
CHANGED
|
@@ -24,6 +24,8 @@ export type ModeId =
|
|
|
24
24
|
| 'modal.help.filtering'
|
|
25
25
|
| 'modal.split-picker'
|
|
26
26
|
| 'modal.git-commit'
|
|
27
|
+
| 'modal.git-commit.confirm'
|
|
28
|
+
| 'modal.git-commit.generating'
|
|
27
29
|
| 'modal.update-available'
|
|
28
30
|
|
|
29
31
|
// ─── Primitive app types ──────────────────────────────────────────────────────
|
|
@@ -63,6 +65,7 @@ export interface TerminalLine {
|
|
|
63
65
|
|
|
64
66
|
export interface TerminalSnapshot {
|
|
65
67
|
lines: TerminalLine[]
|
|
68
|
+
tailLines?: TerminalLine[]
|
|
66
69
|
viewportY: number
|
|
67
70
|
baseY: number
|
|
68
71
|
cursorVisible: boolean
|
|
@@ -291,6 +294,7 @@ export interface ModalGitCommit extends ModalBase {
|
|
|
291
294
|
type: 'git-commit'
|
|
292
295
|
activeField: 'title' | 'body'
|
|
293
296
|
contentBuffer: string
|
|
297
|
+
stage: 'edit' | 'generating' | 'confirm'
|
|
294
298
|
}
|
|
295
299
|
export interface ModalSnippetEditor extends ModalBase {
|
|
296
300
|
type: 'snippet-editor'
|
|
@@ -331,6 +335,28 @@ export interface SessionBarState {
|
|
|
331
335
|
position: SessionBarPosition
|
|
332
336
|
}
|
|
333
337
|
|
|
338
|
+
export type AutoCommitSuggestion =
|
|
339
|
+
| { kind: 'idle' }
|
|
340
|
+
| {
|
|
341
|
+
kind: 'generating'
|
|
342
|
+
tabId: string
|
|
343
|
+
workingTreeHash: string
|
|
344
|
+
abortController: AbortController
|
|
345
|
+
startedAt: number
|
|
346
|
+
}
|
|
347
|
+
| {
|
|
348
|
+
kind: 'ready'
|
|
349
|
+
tabId: string
|
|
350
|
+
workingTreeHash: string
|
|
351
|
+
title: string
|
|
352
|
+
body: string
|
|
353
|
+
generatedAt: number
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
export interface AutoCommitState {
|
|
357
|
+
bySession: Record<string, AutoCommitSuggestion>
|
|
358
|
+
}
|
|
359
|
+
|
|
334
360
|
export interface AppState {
|
|
335
361
|
tabs: TabSession[]
|
|
336
362
|
activeTabId: string | null
|
|
@@ -349,6 +375,7 @@ export interface AppState {
|
|
|
349
375
|
customCommands: Record<AssistantId, string>
|
|
350
376
|
gitPanel: GitPanelState
|
|
351
377
|
gitMode: GitModeState
|
|
378
|
+
autoCommit: AutoCommitState
|
|
352
379
|
pendingChords: string[] | null
|
|
353
380
|
}
|
|
354
381
|
|
|
@@ -386,7 +413,7 @@ export type SessionAction =
|
|
|
386
413
|
| { type: 'set-sessions'; sessions: SessionRecord[] }
|
|
387
414
|
| { type: 'create-session-record'; session: SessionRecord }
|
|
388
415
|
| { type: 'rename-session-record'; sessionId: string; name: string }
|
|
389
|
-
| { type: 'delete-session-record'; sessionId: string }
|
|
416
|
+
| { type: 'delete-session-record'; sessionId: string; openSessionPicker?: boolean }
|
|
390
417
|
| { type: 'reorder-sessions'; orderedIds: string[] }
|
|
391
418
|
| { type: 'set-session-status'; sessionId: string; status: SessionStatus }
|
|
392
419
|
|
|
@@ -503,12 +530,35 @@ export type GitModeAction =
|
|
|
503
530
|
fromSection: GitFileSection
|
|
504
531
|
toSection: GitFileSection | null
|
|
505
532
|
}
|
|
506
|
-
| { type: 'open-git-commit-modal' }
|
|
533
|
+
| { type: 'open-git-commit-modal'; sessionId?: string }
|
|
534
|
+
| { type: 'git-commit-enter-confirm' }
|
|
535
|
+
| { type: 'git-commit-leave-confirm' }
|
|
536
|
+
| { type: 'git-commit-enter-generating'; sessionId: string }
|
|
537
|
+
| { type: 'git-commit-leave-generating' }
|
|
507
538
|
|
|
508
539
|
export type DataAction =
|
|
509
540
|
| { type: 'set-snippets'; snippets: SnippetRecord[] }
|
|
510
541
|
| { type: 'delete-snippet'; snippetId: string }
|
|
511
542
|
|
|
543
|
+
export type AutoCommitAction =
|
|
544
|
+
| {
|
|
545
|
+
type: 'auto-commit-generation-started'
|
|
546
|
+
sessionId: string
|
|
547
|
+
tabId: string
|
|
548
|
+
workingTreeHash: string
|
|
549
|
+
abortController: AbortController
|
|
550
|
+
startedAt: number
|
|
551
|
+
}
|
|
552
|
+
| {
|
|
553
|
+
type: 'auto-commit-generation-ready'
|
|
554
|
+
sessionId: string
|
|
555
|
+
workingTreeHash: string
|
|
556
|
+
title: string
|
|
557
|
+
body: string
|
|
558
|
+
generatedAt: number
|
|
559
|
+
}
|
|
560
|
+
| { type: 'auto-commit-clear'; sessionId: string }
|
|
561
|
+
|
|
512
562
|
export type AppAction =
|
|
513
563
|
| ModalAction
|
|
514
564
|
| SessionAction
|
|
@@ -518,6 +568,7 @@ export type AppAction =
|
|
|
518
568
|
| DataAction
|
|
519
569
|
| GitPanelAction
|
|
520
570
|
| GitModeAction
|
|
571
|
+
| AutoCommitAction
|
|
521
572
|
|
|
522
573
|
// ─── Side effects ─────────────────────────────────────────────────────────────
|
|
523
574
|
|
|
@@ -552,6 +603,8 @@ export type SideEffect =
|
|
|
552
603
|
| { type: 'git-restore'; path: string }
|
|
553
604
|
| { type: 'git-rm'; path: string }
|
|
554
605
|
| { type: 'git-commit'; title: string; body: string }
|
|
606
|
+
| { type: 'git-commit-auto'; title: string; body: string }
|
|
607
|
+
| { type: 'generate-auto-commit-now'; sessionId: string }
|
|
555
608
|
| { type: 'git-push' }
|
|
556
609
|
| { type: 'confirm-update-selection' }
|
|
557
610
|
| { type: 'switch-session-by-index'; index: number }
|
|
@@ -692,7 +745,13 @@ export interface KeymapBuilderApi {
|
|
|
692
745
|
// ─── Top-level user config ────────────────────────────────────────────────────
|
|
693
746
|
|
|
694
747
|
export interface SessionBarConfig {
|
|
748
|
+
/** Startup override for the session bar visibility. Reapplied on each launch. */
|
|
749
|
+
initialVisible?: boolean
|
|
750
|
+
/** Startup override for the session bar position. Reapplied on each launch. */
|
|
751
|
+
initialPosition?: SessionBarPosition
|
|
752
|
+
/** @deprecated Use `initialVisible` instead. */
|
|
695
753
|
visible?: boolean
|
|
754
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
696
755
|
position?: SessionBarPosition
|
|
697
756
|
}
|
|
698
757
|
|
|
@@ -705,32 +764,78 @@ export type GitPanePathConfig =
|
|
|
705
764
|
export type GitPaneDiffCountConfig = { enabled: boolean }
|
|
706
765
|
|
|
707
766
|
interface GitPaneBaseConfig {
|
|
767
|
+
/** Startup override for git pane visibility. Reapplied on each launch. */
|
|
768
|
+
initialVisible?: boolean
|
|
769
|
+
/** Startup override for the pane split ratio. Reapplied on each launch. */
|
|
770
|
+
initialRatio?: number
|
|
771
|
+
/** Startup override for fullscreen diff ratio. Reapplied on each launch. */
|
|
772
|
+
initialDiffModeRatio?: number
|
|
773
|
+
/** Startup override for tree/flat file list mode. Reapplied on each launch. */
|
|
774
|
+
initialFileListMode?: GitFileListMode
|
|
775
|
+
/** Startup override for tree compaction. Reapplied on each launch. */
|
|
776
|
+
initialTreeCompaction?: boolean
|
|
777
|
+
path?: GitPanePathConfig
|
|
778
|
+
diffCount?: GitPaneDiffCountConfig
|
|
779
|
+
/** Prefetch N neighbouring diffs around the cursor; 0 disables. */
|
|
780
|
+
prefetchRadius?: number
|
|
781
|
+
/** @deprecated Use `initialVisible` instead. */
|
|
708
782
|
visible?: boolean
|
|
783
|
+
/** @deprecated Use `initialRatio` instead. */
|
|
709
784
|
ratio?: number
|
|
785
|
+
/** @deprecated Use `initialDiffModeRatio` instead. */
|
|
710
786
|
diffModeRatio?: number
|
|
787
|
+
/** @deprecated Use `initialFileListMode` instead. */
|
|
711
788
|
fileListMode?: GitFileListMode
|
|
789
|
+
/** @deprecated Use `initialTreeCompaction` instead. */
|
|
712
790
|
treeCompaction?: boolean
|
|
713
|
-
path?: GitPanePathConfig
|
|
714
|
-
diffCount?: GitPaneDiffCountConfig
|
|
715
|
-
/** Prefetch N neighbouring diffs around the cursor; 0 disables. */
|
|
716
|
-
prefetchRadius?: number
|
|
717
791
|
}
|
|
718
792
|
|
|
719
793
|
export interface GitPaneEmbeddedConfig extends GitPaneBaseConfig {
|
|
794
|
+
initialMode?: 'embedded'
|
|
795
|
+
initialPosition?: 'top' | 'bottom'
|
|
796
|
+
/** @deprecated Use `initialMode` instead. */
|
|
720
797
|
mode?: 'embedded'
|
|
798
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
721
799
|
position?: 'top' | 'bottom'
|
|
722
800
|
}
|
|
723
801
|
|
|
724
802
|
export interface GitPanePaneConfig extends GitPaneBaseConfig {
|
|
803
|
+
initialMode: 'pane'
|
|
804
|
+
initialPosition?: 'left' | 'right'
|
|
805
|
+
/** @deprecated Use `initialMode` instead. */
|
|
725
806
|
mode: 'pane'
|
|
807
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
726
808
|
position?: 'left' | 'right'
|
|
727
809
|
}
|
|
728
810
|
|
|
729
811
|
export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
|
|
730
812
|
|
|
813
|
+
export interface AutoCommitConfig {
|
|
814
|
+
enabled: boolean
|
|
815
|
+
timeoutMs: number
|
|
816
|
+
models: Partial<Record<string, string>>
|
|
817
|
+
}
|
|
818
|
+
|
|
731
819
|
export interface AimuxThemeConfig {
|
|
732
|
-
|
|
820
|
+
/** Startup override for light/dark theme family. Reapplied on each launch. */
|
|
821
|
+
initialMode?: ThemeMode
|
|
733
822
|
paletteOverrides?: Partial<AimuxPalette>
|
|
823
|
+
/** @deprecated Use `initialMode` instead. */
|
|
824
|
+
mode?: ThemeMode
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
export type AIUsageTool = 'claude' | 'codex'
|
|
828
|
+
|
|
829
|
+
export interface AIUsageToolConfig {
|
|
830
|
+
enabled?: boolean
|
|
831
|
+
pollSeconds?: number
|
|
832
|
+
claudePlan?: 'auto' | 'pro' | 'max5' | 'max20'
|
|
833
|
+
codexWeeklyLimit?: number
|
|
834
|
+
tools?: AIUsageTool[]
|
|
835
|
+
}
|
|
836
|
+
|
|
837
|
+
export interface StatusBarConfig {
|
|
838
|
+
aiUsage?: AIUsageToolConfig
|
|
734
839
|
}
|
|
735
840
|
|
|
736
841
|
export interface AimuxUserConfig {
|
|
@@ -742,6 +847,8 @@ export interface AimuxUserConfig {
|
|
|
742
847
|
gitPane?: GitPaneConfig
|
|
743
848
|
hooks?: HooksConfig
|
|
744
849
|
snippets?: SnippetDef[]
|
|
850
|
+
autoCommit?: Partial<AutoCommitConfig>
|
|
851
|
+
statusBar?: StatusBarConfig
|
|
745
852
|
}
|
|
746
853
|
|
|
747
854
|
// ─── Resolved config (internal) ───────────────────────────────────────────────
|
|
@@ -767,12 +874,46 @@ export interface ResolvedKeymapConfig {
|
|
|
767
874
|
}
|
|
768
875
|
|
|
769
876
|
export interface ResolvedConfig {
|
|
770
|
-
theme:
|
|
877
|
+
theme:
|
|
878
|
+
| {
|
|
879
|
+
initialMode?: ThemeMode
|
|
880
|
+
paletteOverrides?: Partial<AimuxPalette>
|
|
881
|
+
}
|
|
882
|
+
| undefined
|
|
771
883
|
keymaps: ResolvedKeymapConfig
|
|
772
884
|
backends: Record<string, BackendConfig>
|
|
773
885
|
sidebar: SidebarConfig
|
|
774
|
-
sessionBar:
|
|
775
|
-
|
|
886
|
+
sessionBar: {
|
|
887
|
+
initialVisible?: boolean
|
|
888
|
+
initialPosition?: SessionBarPosition
|
|
889
|
+
}
|
|
890
|
+
gitPane:
|
|
891
|
+
| {
|
|
892
|
+
initialMode?: 'embedded'
|
|
893
|
+
initialPosition?: 'top' | 'bottom'
|
|
894
|
+
initialVisible?: boolean
|
|
895
|
+
initialRatio?: number
|
|
896
|
+
initialDiffModeRatio?: number
|
|
897
|
+
initialFileListMode?: GitFileListMode
|
|
898
|
+
initialTreeCompaction?: boolean
|
|
899
|
+
path?: GitPanePathConfig
|
|
900
|
+
diffCount?: GitPaneDiffCountConfig
|
|
901
|
+
prefetchRadius?: number
|
|
902
|
+
}
|
|
903
|
+
| {
|
|
904
|
+
initialMode: 'pane'
|
|
905
|
+
initialPosition?: 'left' | 'right'
|
|
906
|
+
initialVisible?: boolean
|
|
907
|
+
initialRatio?: number
|
|
908
|
+
initialDiffModeRatio?: number
|
|
909
|
+
initialFileListMode?: GitFileListMode
|
|
910
|
+
initialTreeCompaction?: boolean
|
|
911
|
+
path?: GitPanePathConfig
|
|
912
|
+
diffCount?: GitPaneDiffCountConfig
|
|
913
|
+
prefetchRadius?: number
|
|
914
|
+
}
|
|
776
915
|
hooks: HooksConfig
|
|
777
916
|
snippets: SnippetDef[]
|
|
917
|
+
autoCommit: AutoCommitConfig
|
|
918
|
+
statusBar: StatusBarConfig
|
|
778
919
|
}
|