@brimveyn/aimux-config 0.4.6 → 0.5.1
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 +95 -9
- package/src/auto-commit-runtime.ts +18 -0
- package/src/defaults.ts +38 -1
- package/src/index.ts +5 -0
- package/src/resolver.ts +87 -4
- package/src/types.ts +158 -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'] = [],
|
|
@@ -43,6 +48,12 @@ export function helpModal(scope?: ModeId): KeyResult {
|
|
|
43
48
|
export const toggleSidebar: KeyResult = r([{ type: 'toggle-sidebar' }])
|
|
44
49
|
export const toggleGitPane: KeyResult = r([{ type: 'toggle-git-pane' }])
|
|
45
50
|
export const toggleSessionBar: KeyResult = r([{ type: 'toggle-session-bar' }])
|
|
51
|
+
export const toggleAIUsage: ActionFn = (ctx: ModeContext) => {
|
|
52
|
+
if (ctx.state.modal.type === 'ai-usage') {
|
|
53
|
+
return r([{ type: 'close-modal' }], [], 'navigation')
|
|
54
|
+
}
|
|
55
|
+
return r([{ type: 'open-ai-usage-modal' }], [], 'modal.ai-usage')
|
|
56
|
+
}
|
|
46
57
|
|
|
47
58
|
export function setGitPaneMode(mode: 'embedded' | 'pane'): KeyResult {
|
|
48
59
|
return r([{ mode, type: 'set-git-pane-mode' }])
|
|
@@ -545,7 +556,12 @@ export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
|
|
|
545
556
|
},
|
|
546
557
|
])
|
|
547
558
|
}
|
|
548
|
-
|
|
559
|
+
const sessionId = ctx.state.currentSessionId ?? undefined
|
|
560
|
+
return r(
|
|
561
|
+
[...clearPendingDelete(ctx), { sessionId, type: 'open-git-commit-modal' }],
|
|
562
|
+
[],
|
|
563
|
+
'modal.git-commit'
|
|
564
|
+
)
|
|
549
565
|
}
|
|
550
566
|
|
|
551
567
|
export const gitPush: ActionFn = (ctx: ModeContext) => {
|
|
@@ -564,18 +580,88 @@ export const gitPush: ActionFn = (ctx: ModeContext) => {
|
|
|
564
580
|
// Modal: git-commit
|
|
565
581
|
// ---------------------------------------------------------------------------
|
|
566
582
|
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
if (modal.type !== 'git-commit')
|
|
572
|
-
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
573
|
-
}
|
|
583
|
+
function extractGitCommitFields(modal: ModeContext['state']['modal']): {
|
|
584
|
+
title: string
|
|
585
|
+
body: string
|
|
586
|
+
} | null {
|
|
587
|
+
if (modal.type !== 'git-commit') return null
|
|
574
588
|
const editBuffer = modal.editBuffer ?? ''
|
|
575
589
|
const activeIsTitle = modal.activeField === 'title'
|
|
576
590
|
const title = (activeIsTitle ? editBuffer : modal.contentBuffer).trim()
|
|
577
591
|
const body = (activeIsTitle ? modal.contentBuffer : editBuffer).trim()
|
|
578
|
-
return
|
|
592
|
+
return { body, title }
|
|
593
|
+
}
|
|
594
|
+
|
|
595
|
+
export const gitCommitCancel: ActionFn = (ctx: ModeContext) => {
|
|
596
|
+
const modal = ctx.state.modal
|
|
597
|
+
if (modal.type === 'git-commit' && modal.stage === 'confirm') {
|
|
598
|
+
return r([{ type: 'git-commit-leave-confirm' }], [], 'modal.git-commit')
|
|
599
|
+
}
|
|
600
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export const gitCommitEnterConfirm: ActionFn = (ctx: ModeContext) => {
|
|
604
|
+
const modal = ctx.state.modal
|
|
605
|
+
if (modal.type !== 'git-commit') return null
|
|
606
|
+
if (!isAutoCommitEnabled()) {
|
|
607
|
+
return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
|
|
608
|
+
}
|
|
609
|
+
const fields = extractGitCommitFields(modal)
|
|
610
|
+
const hasTitle = !!fields && fields.title.length > 0
|
|
611
|
+
if (hasTitle) {
|
|
612
|
+
return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
|
|
613
|
+
}
|
|
614
|
+
const sessionId = ctx.state.currentSessionId
|
|
615
|
+
if (!sessionId) {
|
|
616
|
+
return r([{ type: 'git-commit-enter-confirm' }], [], 'modal.git-commit.confirm')
|
|
617
|
+
}
|
|
618
|
+
return r(
|
|
619
|
+
[{ sessionId, type: 'git-commit-enter-generating' }],
|
|
620
|
+
[{ sessionId, type: 'generate-auto-commit-now' }],
|
|
621
|
+
'modal.git-commit.generating'
|
|
622
|
+
)
|
|
623
|
+
}
|
|
624
|
+
|
|
625
|
+
export const gitCommitLeaveConfirm: KeyResult = r(
|
|
626
|
+
[{ type: 'git-commit-leave-confirm' }],
|
|
627
|
+
[],
|
|
628
|
+
'modal.git-commit'
|
|
629
|
+
)
|
|
630
|
+
|
|
631
|
+
export const gitCommitLeaveGenerating: ActionFn = (ctx: ModeContext) => {
|
|
632
|
+
const sessionId = ctx.state.currentSessionId
|
|
633
|
+
const actionsList: AppAction[] = [{ type: 'git-commit-leave-generating' }]
|
|
634
|
+
if (sessionId) {
|
|
635
|
+
actionsList.push({ sessionId, type: 'auto-commit-clear' })
|
|
636
|
+
}
|
|
637
|
+
return r(actionsList, [], 'modal.git-commit')
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
export const gitCommitAutoAccept: ActionFn = (ctx: ModeContext) => {
|
|
641
|
+
if (!isAutoCommitEnabled()) {
|
|
642
|
+
return r([{ message: AUTO_COMMIT_DISABLED_MESSAGE, type: 'git-mode-set-message' }])
|
|
643
|
+
}
|
|
644
|
+
const fields = extractGitCommitFields(ctx.state.modal)
|
|
645
|
+
if (!fields) {
|
|
646
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
647
|
+
}
|
|
648
|
+
return r(
|
|
649
|
+
[{ type: 'close-modal' }],
|
|
650
|
+
[{ body: fields.body, title: fields.title, type: 'git-commit-auto' }],
|
|
651
|
+
'git-mode'
|
|
652
|
+
)
|
|
653
|
+
}
|
|
654
|
+
|
|
655
|
+
export const gitCommitSubmit: ActionFn = (ctx: ModeContext) => {
|
|
656
|
+
const fields = extractGitCommitFields(ctx.state.modal)
|
|
657
|
+
if (!fields) {
|
|
658
|
+
return r([{ type: 'close-modal' }], [], 'git-mode')
|
|
659
|
+
}
|
|
660
|
+
return r(
|
|
661
|
+
[{ type: 'close-modal' }],
|
|
662
|
+
[{ body: fields.body, title: fields.title, type: 'git-commit' }],
|
|
663
|
+
'git-mode'
|
|
664
|
+
)
|
|
579
665
|
}
|
|
580
666
|
|
|
581
667
|
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
|
*/
|
|
@@ -66,6 +75,7 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
66
75
|
.mode(['navigation', 'terminal-input'], (m) =>
|
|
67
76
|
m
|
|
68
77
|
.map('<Leader>b', actions.toggleSessionBar, 'Toggle session bar')
|
|
78
|
+
.map('<Leader>u', actions.toggleAIUsage, 'Toggle AI usage')
|
|
69
79
|
.map('<Leader>1', actions.switchSessionByIndex(1), 'Session 1')
|
|
70
80
|
.map('<Leader>2', actions.switchSessionByIndex(2), 'Session 2')
|
|
71
81
|
.map('<Leader>3', actions.switchSessionByIndex(3), 'Session 3')
|
|
@@ -138,6 +148,13 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
138
148
|
.passthrough()
|
|
139
149
|
)
|
|
140
150
|
|
|
151
|
+
// -----------------------------------------------------------------------
|
|
152
|
+
// Modal: ai-usage (info-only)
|
|
153
|
+
// -----------------------------------------------------------------------
|
|
154
|
+
.mode('modal.ai-usage', (m) =>
|
|
155
|
+
m.map('<Esc>', actions.closeModal, 'Close').map('<Leader>u', actions.toggleAIUsage, 'Close')
|
|
156
|
+
)
|
|
157
|
+
|
|
141
158
|
// -----------------------------------------------------------------------
|
|
142
159
|
// Modal: update-available
|
|
143
160
|
// -----------------------------------------------------------------------
|
|
@@ -276,10 +293,30 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
|
|
|
276
293
|
m
|
|
277
294
|
.map('<Esc>', actions.gitCommitCancel, 'Cancel')
|
|
278
295
|
.map('<C-CR>', actions.gitCommitSubmit, 'Commit')
|
|
296
|
+
.map('<C-a>', actions.gitCommitEnterConfirm, 'Auto-commit (stage all)')
|
|
279
297
|
.map('<Tab>', actions.switchField, 'Next field')
|
|
280
298
|
.map('<CR>', actions.gitCommitReturnKey, 'Newline / confirm')
|
|
281
299
|
.passthrough()
|
|
282
300
|
)
|
|
283
301
|
|
|
302
|
+
// -----------------------------------------------------------------------
|
|
303
|
+
// Modal: git-commit confirm (auto-commit stage all + commit)
|
|
304
|
+
// -----------------------------------------------------------------------
|
|
305
|
+
.mode('modal.git-commit.confirm', (m) =>
|
|
306
|
+
m
|
|
307
|
+
.map('<Esc>', actions.gitCommitLeaveConfirm, 'Cancel auto-commit')
|
|
308
|
+
.map('<CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
|
|
309
|
+
.map('<C-CR>', actions.gitCommitAutoAccept, 'Stage all + commit')
|
|
310
|
+
.map('<Tab>', actions.switchField, 'Next field')
|
|
311
|
+
.passthrough()
|
|
312
|
+
)
|
|
313
|
+
|
|
314
|
+
// -----------------------------------------------------------------------
|
|
315
|
+
// Modal: git-commit generating (waiting for LLM)
|
|
316
|
+
// -----------------------------------------------------------------------
|
|
317
|
+
.mode('modal.git-commit.generating', (m) =>
|
|
318
|
+
m.map('<Esc>', actions.gitCommitLeaveGenerating, 'Cancel generation')
|
|
319
|
+
)
|
|
320
|
+
|
|
284
321
|
return kb._build()
|
|
285
322
|
}
|
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,7 +24,10 @@ 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'
|
|
30
|
+
| 'modal.ai-usage'
|
|
28
31
|
|
|
29
32
|
// ─── Primitive app types ──────────────────────────────────────────────────────
|
|
30
33
|
|
|
@@ -63,6 +66,7 @@ export interface TerminalLine {
|
|
|
63
66
|
|
|
64
67
|
export interface TerminalSnapshot {
|
|
65
68
|
lines: TerminalLine[]
|
|
69
|
+
tailLines?: TerminalLine[]
|
|
66
70
|
viewportY: number
|
|
67
71
|
baseY: number
|
|
68
72
|
cursorVisible: boolean
|
|
@@ -291,6 +295,7 @@ export interface ModalGitCommit extends ModalBase {
|
|
|
291
295
|
type: 'git-commit'
|
|
292
296
|
activeField: 'title' | 'body'
|
|
293
297
|
contentBuffer: string
|
|
298
|
+
stage: 'edit' | 'generating' | 'confirm'
|
|
294
299
|
}
|
|
295
300
|
export interface ModalSnippetEditor extends ModalBase {
|
|
296
301
|
type: 'snippet-editor'
|
|
@@ -304,6 +309,10 @@ export interface ModalUpdateAvailable extends ModalBase {
|
|
|
304
309
|
latestVersion: string
|
|
305
310
|
}
|
|
306
311
|
|
|
312
|
+
export interface ModalAIUsage extends ModalBase {
|
|
313
|
+
type: 'ai-usage'
|
|
314
|
+
}
|
|
315
|
+
|
|
307
316
|
export type ModalState =
|
|
308
317
|
| ModalClosed
|
|
309
318
|
| ModalNewTab
|
|
@@ -318,6 +327,7 @@ export type ModalState =
|
|
|
318
327
|
| ModalSnippetEditor
|
|
319
328
|
| ModalGitCommit
|
|
320
329
|
| ModalUpdateAvailable
|
|
330
|
+
| ModalAIUsage
|
|
321
331
|
|
|
322
332
|
export interface LayoutState {
|
|
323
333
|
terminalCols: number
|
|
@@ -331,6 +341,28 @@ export interface SessionBarState {
|
|
|
331
341
|
position: SessionBarPosition
|
|
332
342
|
}
|
|
333
343
|
|
|
344
|
+
export type AutoCommitSuggestion =
|
|
345
|
+
| { kind: 'idle' }
|
|
346
|
+
| {
|
|
347
|
+
kind: 'generating'
|
|
348
|
+
tabId: string
|
|
349
|
+
workingTreeHash: string
|
|
350
|
+
abortController: AbortController
|
|
351
|
+
startedAt: number
|
|
352
|
+
}
|
|
353
|
+
| {
|
|
354
|
+
kind: 'ready'
|
|
355
|
+
tabId: string
|
|
356
|
+
workingTreeHash: string
|
|
357
|
+
title: string
|
|
358
|
+
body: string
|
|
359
|
+
generatedAt: number
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
export interface AutoCommitState {
|
|
363
|
+
bySession: Record<string, AutoCommitSuggestion>
|
|
364
|
+
}
|
|
365
|
+
|
|
334
366
|
export interface AppState {
|
|
335
367
|
tabs: TabSession[]
|
|
336
368
|
activeTabId: string | null
|
|
@@ -349,6 +381,7 @@ export interface AppState {
|
|
|
349
381
|
customCommands: Record<AssistantId, string>
|
|
350
382
|
gitPanel: GitPanelState
|
|
351
383
|
gitMode: GitModeState
|
|
384
|
+
autoCommit: AutoCommitState
|
|
352
385
|
pendingChords: string[] | null
|
|
353
386
|
}
|
|
354
387
|
|
|
@@ -380,13 +413,14 @@ export type ModalAction =
|
|
|
380
413
|
| { type: 'set-theme-entry-count'; count: number }
|
|
381
414
|
| { type: 'open-theme-picker' }
|
|
382
415
|
| { type: 'open-update-available-modal'; currentVersion: string; latestVersion: string }
|
|
416
|
+
| { type: 'open-ai-usage-modal' }
|
|
383
417
|
|
|
384
418
|
export type SessionAction =
|
|
385
419
|
| { type: 'load-session'; sessionId: string; workspaceSnapshot?: WorkspaceSnapshotV1 }
|
|
386
420
|
| { type: 'set-sessions'; sessions: SessionRecord[] }
|
|
387
421
|
| { type: 'create-session-record'; session: SessionRecord }
|
|
388
422
|
| { type: 'rename-session-record'; sessionId: string; name: string }
|
|
389
|
-
| { type: 'delete-session-record'; sessionId: string }
|
|
423
|
+
| { type: 'delete-session-record'; sessionId: string; openSessionPicker?: boolean }
|
|
390
424
|
| { type: 'reorder-sessions'; orderedIds: string[] }
|
|
391
425
|
| { type: 'set-session-status'; sessionId: string; status: SessionStatus }
|
|
392
426
|
|
|
@@ -503,12 +537,35 @@ export type GitModeAction =
|
|
|
503
537
|
fromSection: GitFileSection
|
|
504
538
|
toSection: GitFileSection | null
|
|
505
539
|
}
|
|
506
|
-
| { type: 'open-git-commit-modal' }
|
|
540
|
+
| { type: 'open-git-commit-modal'; sessionId?: string }
|
|
541
|
+
| { type: 'git-commit-enter-confirm' }
|
|
542
|
+
| { type: 'git-commit-leave-confirm' }
|
|
543
|
+
| { type: 'git-commit-enter-generating'; sessionId: string }
|
|
544
|
+
| { type: 'git-commit-leave-generating' }
|
|
507
545
|
|
|
508
546
|
export type DataAction =
|
|
509
547
|
| { type: 'set-snippets'; snippets: SnippetRecord[] }
|
|
510
548
|
| { type: 'delete-snippet'; snippetId: string }
|
|
511
549
|
|
|
550
|
+
export type AutoCommitAction =
|
|
551
|
+
| {
|
|
552
|
+
type: 'auto-commit-generation-started'
|
|
553
|
+
sessionId: string
|
|
554
|
+
tabId: string
|
|
555
|
+
workingTreeHash: string
|
|
556
|
+
abortController: AbortController
|
|
557
|
+
startedAt: number
|
|
558
|
+
}
|
|
559
|
+
| {
|
|
560
|
+
type: 'auto-commit-generation-ready'
|
|
561
|
+
sessionId: string
|
|
562
|
+
workingTreeHash: string
|
|
563
|
+
title: string
|
|
564
|
+
body: string
|
|
565
|
+
generatedAt: number
|
|
566
|
+
}
|
|
567
|
+
| { type: 'auto-commit-clear'; sessionId: string }
|
|
568
|
+
|
|
512
569
|
export type AppAction =
|
|
513
570
|
| ModalAction
|
|
514
571
|
| SessionAction
|
|
@@ -518,6 +575,7 @@ export type AppAction =
|
|
|
518
575
|
| DataAction
|
|
519
576
|
| GitPanelAction
|
|
520
577
|
| GitModeAction
|
|
578
|
+
| AutoCommitAction
|
|
521
579
|
|
|
522
580
|
// ─── Side effects ─────────────────────────────────────────────────────────────
|
|
523
581
|
|
|
@@ -552,6 +610,8 @@ export type SideEffect =
|
|
|
552
610
|
| { type: 'git-restore'; path: string }
|
|
553
611
|
| { type: 'git-rm'; path: string }
|
|
554
612
|
| { type: 'git-commit'; title: string; body: string }
|
|
613
|
+
| { type: 'git-commit-auto'; title: string; body: string }
|
|
614
|
+
| { type: 'generate-auto-commit-now'; sessionId: string }
|
|
555
615
|
| { type: 'git-push' }
|
|
556
616
|
| { type: 'confirm-update-selection' }
|
|
557
617
|
| { type: 'switch-session-by-index'; index: number }
|
|
@@ -692,7 +752,13 @@ export interface KeymapBuilderApi {
|
|
|
692
752
|
// ─── Top-level user config ────────────────────────────────────────────────────
|
|
693
753
|
|
|
694
754
|
export interface SessionBarConfig {
|
|
755
|
+
/** Startup override for the session bar visibility. Reapplied on each launch. */
|
|
756
|
+
initialVisible?: boolean
|
|
757
|
+
/** Startup override for the session bar position. Reapplied on each launch. */
|
|
758
|
+
initialPosition?: SessionBarPosition
|
|
759
|
+
/** @deprecated Use `initialVisible` instead. */
|
|
695
760
|
visible?: boolean
|
|
761
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
696
762
|
position?: SessionBarPosition
|
|
697
763
|
}
|
|
698
764
|
|
|
@@ -705,32 +771,78 @@ export type GitPanePathConfig =
|
|
|
705
771
|
export type GitPaneDiffCountConfig = { enabled: boolean }
|
|
706
772
|
|
|
707
773
|
interface GitPaneBaseConfig {
|
|
774
|
+
/** Startup override for git pane visibility. Reapplied on each launch. */
|
|
775
|
+
initialVisible?: boolean
|
|
776
|
+
/** Startup override for the pane split ratio. Reapplied on each launch. */
|
|
777
|
+
initialRatio?: number
|
|
778
|
+
/** Startup override for fullscreen diff ratio. Reapplied on each launch. */
|
|
779
|
+
initialDiffModeRatio?: number
|
|
780
|
+
/** Startup override for tree/flat file list mode. Reapplied on each launch. */
|
|
781
|
+
initialFileListMode?: GitFileListMode
|
|
782
|
+
/** Startup override for tree compaction. Reapplied on each launch. */
|
|
783
|
+
initialTreeCompaction?: boolean
|
|
784
|
+
path?: GitPanePathConfig
|
|
785
|
+
diffCount?: GitPaneDiffCountConfig
|
|
786
|
+
/** Prefetch N neighbouring diffs around the cursor; 0 disables. */
|
|
787
|
+
prefetchRadius?: number
|
|
788
|
+
/** @deprecated Use `initialVisible` instead. */
|
|
708
789
|
visible?: boolean
|
|
790
|
+
/** @deprecated Use `initialRatio` instead. */
|
|
709
791
|
ratio?: number
|
|
792
|
+
/** @deprecated Use `initialDiffModeRatio` instead. */
|
|
710
793
|
diffModeRatio?: number
|
|
794
|
+
/** @deprecated Use `initialFileListMode` instead. */
|
|
711
795
|
fileListMode?: GitFileListMode
|
|
796
|
+
/** @deprecated Use `initialTreeCompaction` instead. */
|
|
712
797
|
treeCompaction?: boolean
|
|
713
|
-
path?: GitPanePathConfig
|
|
714
|
-
diffCount?: GitPaneDiffCountConfig
|
|
715
|
-
/** Prefetch N neighbouring diffs around the cursor; 0 disables. */
|
|
716
|
-
prefetchRadius?: number
|
|
717
798
|
}
|
|
718
799
|
|
|
719
800
|
export interface GitPaneEmbeddedConfig extends GitPaneBaseConfig {
|
|
801
|
+
initialMode?: 'embedded'
|
|
802
|
+
initialPosition?: 'top' | 'bottom'
|
|
803
|
+
/** @deprecated Use `initialMode` instead. */
|
|
720
804
|
mode?: 'embedded'
|
|
805
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
721
806
|
position?: 'top' | 'bottom'
|
|
722
807
|
}
|
|
723
808
|
|
|
724
809
|
export interface GitPanePaneConfig extends GitPaneBaseConfig {
|
|
810
|
+
initialMode: 'pane'
|
|
811
|
+
initialPosition?: 'left' | 'right'
|
|
812
|
+
/** @deprecated Use `initialMode` instead. */
|
|
725
813
|
mode: 'pane'
|
|
814
|
+
/** @deprecated Use `initialPosition` instead. */
|
|
726
815
|
position?: 'left' | 'right'
|
|
727
816
|
}
|
|
728
817
|
|
|
729
818
|
export type GitPaneConfig = GitPaneEmbeddedConfig | GitPanePaneConfig
|
|
730
819
|
|
|
820
|
+
export interface AutoCommitConfig {
|
|
821
|
+
enabled: boolean
|
|
822
|
+
timeoutMs: number
|
|
823
|
+
models: Partial<Record<string, string>>
|
|
824
|
+
}
|
|
825
|
+
|
|
731
826
|
export interface AimuxThemeConfig {
|
|
732
|
-
|
|
827
|
+
/** Startup override for light/dark theme family. Reapplied on each launch. */
|
|
828
|
+
initialMode?: ThemeMode
|
|
733
829
|
paletteOverrides?: Partial<AimuxPalette>
|
|
830
|
+
/** @deprecated Use `initialMode` instead. */
|
|
831
|
+
mode?: ThemeMode
|
|
832
|
+
}
|
|
833
|
+
|
|
834
|
+
export type AIUsageTool = 'claude' | 'codex'
|
|
835
|
+
|
|
836
|
+
export interface AIUsageToolConfig {
|
|
837
|
+
enabled?: boolean
|
|
838
|
+
pollSeconds?: number
|
|
839
|
+
claudePlan?: 'auto' | 'pro' | 'max5' | 'max20'
|
|
840
|
+
codexWeeklyLimit?: number
|
|
841
|
+
tools?: AIUsageTool[]
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
export interface StatusBarConfig {
|
|
845
|
+
aiUsage?: AIUsageToolConfig
|
|
734
846
|
}
|
|
735
847
|
|
|
736
848
|
export interface AimuxUserConfig {
|
|
@@ -742,6 +854,8 @@ export interface AimuxUserConfig {
|
|
|
742
854
|
gitPane?: GitPaneConfig
|
|
743
855
|
hooks?: HooksConfig
|
|
744
856
|
snippets?: SnippetDef[]
|
|
857
|
+
autoCommit?: Partial<AutoCommitConfig>
|
|
858
|
+
statusBar?: StatusBarConfig
|
|
745
859
|
}
|
|
746
860
|
|
|
747
861
|
// ─── Resolved config (internal) ───────────────────────────────────────────────
|
|
@@ -767,12 +881,46 @@ export interface ResolvedKeymapConfig {
|
|
|
767
881
|
}
|
|
768
882
|
|
|
769
883
|
export interface ResolvedConfig {
|
|
770
|
-
theme:
|
|
884
|
+
theme:
|
|
885
|
+
| {
|
|
886
|
+
initialMode?: ThemeMode
|
|
887
|
+
paletteOverrides?: Partial<AimuxPalette>
|
|
888
|
+
}
|
|
889
|
+
| undefined
|
|
771
890
|
keymaps: ResolvedKeymapConfig
|
|
772
891
|
backends: Record<string, BackendConfig>
|
|
773
892
|
sidebar: SidebarConfig
|
|
774
|
-
sessionBar:
|
|
775
|
-
|
|
893
|
+
sessionBar: {
|
|
894
|
+
initialVisible?: boolean
|
|
895
|
+
initialPosition?: SessionBarPosition
|
|
896
|
+
}
|
|
897
|
+
gitPane:
|
|
898
|
+
| {
|
|
899
|
+
initialMode?: 'embedded'
|
|
900
|
+
initialPosition?: 'top' | 'bottom'
|
|
901
|
+
initialVisible?: boolean
|
|
902
|
+
initialRatio?: number
|
|
903
|
+
initialDiffModeRatio?: number
|
|
904
|
+
initialFileListMode?: GitFileListMode
|
|
905
|
+
initialTreeCompaction?: boolean
|
|
906
|
+
path?: GitPanePathConfig
|
|
907
|
+
diffCount?: GitPaneDiffCountConfig
|
|
908
|
+
prefetchRadius?: number
|
|
909
|
+
}
|
|
910
|
+
| {
|
|
911
|
+
initialMode: 'pane'
|
|
912
|
+
initialPosition?: 'left' | 'right'
|
|
913
|
+
initialVisible?: boolean
|
|
914
|
+
initialRatio?: number
|
|
915
|
+
initialDiffModeRatio?: number
|
|
916
|
+
initialFileListMode?: GitFileListMode
|
|
917
|
+
initialTreeCompaction?: boolean
|
|
918
|
+
path?: GitPanePathConfig
|
|
919
|
+
diffCount?: GitPaneDiffCountConfig
|
|
920
|
+
prefetchRadius?: number
|
|
921
|
+
}
|
|
776
922
|
hooks: HooksConfig
|
|
777
923
|
snippets: SnippetDef[]
|
|
924
|
+
autoCommit: AutoCommitConfig
|
|
925
|
+
statusBar: StatusBarConfig
|
|
778
926
|
}
|