@brimveyn/aimux-config 0.1.0 → 0.2.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brimveyn/aimux-config",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "TypeScript configuration API for aimux — keymaps, themes, and backends with a fluent builder.",
5
5
  "keywords": [
6
6
  "aimux",
package/src/actions.ts CHANGED
@@ -1,4 +1,4 @@
1
- import type { ActionFn, KeyResult, ModeContext } from './types'
1
+ import type { ActionFn, AppAction, KeyResult, ModeContext } from './types'
2
2
 
3
3
  function r(
4
4
  actions: KeyResult['actions'] = [],
@@ -36,6 +36,7 @@ export const helpModal: KeyResult = r([{ type: 'open-help-modal' }], [], 'modal.
36
36
 
37
37
  export const toggleSidebar: KeyResult = r([{ type: 'toggle-sidebar' }])
38
38
  export const toggleGitPanel: KeyResult = r([{ type: 'toggle-git-panel' }])
39
+ export const enterGitMode: KeyResult = r([{ type: 'enter-git-mode' }], [], 'git-mode')
39
40
 
40
41
  export const splitVertical: KeyResult = r(
41
42
  [{ direction: 'vertical', type: 'open-split-picker' }],
@@ -366,3 +367,113 @@ export const snippetFilterPasteToGroup: KeyResult = r(
366
367
  [{ type: 'paste-snippet-to-group' }],
367
368
  'navigation'
368
369
  )
370
+
371
+ // ---------------------------------------------------------------------------
372
+ // Git mode (src/input/modes/handlers/git-mode.ts replacement)
373
+ // ---------------------------------------------------------------------------
374
+
375
+ function clearPendingDelete(ctx: ModeContext): AppAction[] {
376
+ if (ctx.state.gitMode.pendingDeletePath === null) return []
377
+ return [{ path: null, type: 'git-mode-set-pending-delete' }]
378
+ }
379
+
380
+ export const exitGitMode: KeyResult = r([{ type: 'exit-git-mode' }], [], 'navigation')
381
+
382
+ export function selectGitFile(delta: -1 | 1): ActionFn {
383
+ return (ctx: ModeContext) => {
384
+ const total = ctx.state.gitPanel.files.length
385
+ if (total === 0) return r([])
386
+ const current = ctx.state.gitMode.selectedFileIndex
387
+ const next = (current + delta + total) % total
388
+ if (next === current) return r([])
389
+ return r([{ delta, type: 'git-mode-select-file' }])
390
+ }
391
+ }
392
+
393
+ export function scrollGitDiff(delta: number): KeyResult {
394
+ return r([], [{ delta, type: 'scroll-git-diff' }])
395
+ }
396
+
397
+ export const gitStageSelected: ActionFn = (ctx: ModeContext) => {
398
+ const file = ctx.state.gitPanel.files[ctx.state.gitMode.selectedFileIndex]
399
+ if (!file) return r(clearPendingDelete(ctx))
400
+ const actions = clearPendingDelete(ctx)
401
+ if (file.section === 'staged') return r(actions)
402
+ actions.push({
403
+ fromSection: file.section,
404
+ path: file.path,
405
+ toSection: 'staged',
406
+ type: 'git-mode-optimistic-move',
407
+ })
408
+ return r(actions, [{ path: file.path, type: 'git-stage' }])
409
+ }
410
+
411
+ export const gitDestructiveSelected: ActionFn = (ctx: ModeContext) => {
412
+ const file = ctx.state.gitPanel.files[ctx.state.gitMode.selectedFileIndex]
413
+ if (!file) return r(clearPendingDelete(ctx))
414
+
415
+ if (file.section === 'staged') {
416
+ const actions = clearPendingDelete(ctx)
417
+ const toSection = file.status === 'A' ? 'untracked' : 'unstaged'
418
+ actions.push({
419
+ fromSection: 'staged',
420
+ path: file.path,
421
+ toSection,
422
+ type: 'git-mode-optimistic-move',
423
+ })
424
+ return r(actions, [{ path: file.path, type: 'git-unstage' }])
425
+ }
426
+
427
+ const isUntracked = file.section === 'untracked'
428
+ const pending = ctx.state.gitMode.pendingDeletePath
429
+
430
+ if (pending === file.path) {
431
+ const actions: AppAction[] = [
432
+ { path: null, type: 'git-mode-set-pending-delete' },
433
+ {
434
+ fromSection: file.section,
435
+ path: file.path,
436
+ toSection: null,
437
+ type: 'git-mode-optimistic-move',
438
+ },
439
+ ]
440
+ const effectType = isUntracked ? 'git-rm' : 'git-restore'
441
+ return r(actions, [{ path: file.path, type: effectType }])
442
+ }
443
+
444
+ return r([{ path: file.path, type: 'git-mode-set-pending-delete' }])
445
+ }
446
+
447
+ export const gitCommitOpen: ActionFn = (ctx: ModeContext) => {
448
+ return r([...clearPendingDelete(ctx), { type: 'open-git-commit-modal' }], [], 'modal.git-commit')
449
+ }
450
+
451
+ export const gitPush: ActionFn = (ctx: ModeContext) => {
452
+ return r(clearPendingDelete(ctx), [{ type: 'git-push' }])
453
+ }
454
+
455
+ // ---------------------------------------------------------------------------
456
+ // Modal: git-commit
457
+ // ---------------------------------------------------------------------------
458
+
459
+ export const gitCommitCancel: KeyResult = r([{ type: 'close-modal' }], [], 'git-mode')
460
+
461
+ export const gitCommitSubmit: ActionFn = (ctx: ModeContext) => {
462
+ const modal = ctx.state.modal
463
+ if (modal.type !== 'git-commit') {
464
+ return r([{ type: 'close-modal' }], [], 'git-mode')
465
+ }
466
+ const editBuffer = modal.editBuffer ?? ''
467
+ const activeIsTitle = modal.activeField === 'title'
468
+ const title = (activeIsTitle ? editBuffer : modal.contentBuffer).trim()
469
+ const body = (activeIsTitle ? modal.contentBuffer : editBuffer).trim()
470
+ return r([{ type: 'close-modal' }], [{ body, title, type: 'git-commit' }], 'git-mode')
471
+ }
472
+
473
+ export const gitCommitReturnKey: ActionFn = (ctx: ModeContext) => {
474
+ const modal = ctx.state.modal
475
+ if (modal.type === 'git-commit' && modal.activeField === 'body') {
476
+ return r([{ char: '\n', type: 'update-command-edit' }])
477
+ }
478
+ return r([{ type: 'switch-create-session-field' }])
479
+ }
package/src/defaults.ts CHANGED
@@ -28,6 +28,7 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
28
28
  .map('<C-h>', actions.resizeSidebar(-2))
29
29
  .map('<C-l>', actions.resizeSidebar(2))
30
30
  .map('G', actions.toggleGitPanel)
31
+ .map('<C-d>', actions.enterGitMode)
31
32
  .map('<C-j>', actions.resizeGitPanel(-0.05))
32
33
  .map('<C-k>', actions.resizeGitPanel(0.05))
33
34
  .map('J', actions.reorderTab(1))
@@ -72,6 +73,24 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
72
73
  .map('q', actions.closePane)
73
74
  )
74
75
 
76
+ // -----------------------------------------------------------------------
77
+ // Git mode (src/input/modes/handlers/git-mode.ts)
78
+ // -----------------------------------------------------------------------
79
+ .mode('git-mode', (m) =>
80
+ m
81
+ .map('<Esc>', actions.exitGitMode)
82
+ .map('j', actions.selectGitFile(1))
83
+ .map('k', actions.selectGitFile(-1))
84
+ .map('<C-d>', actions.scrollGitDiff(20))
85
+ .map('<C-u>', actions.scrollGitDiff(-20))
86
+ .map('<Down>', actions.scrollGitDiff(1))
87
+ .map('<Up>', actions.scrollGitDiff(-1))
88
+ .map('a', actions.gitStageSelected)
89
+ .map('d', actions.gitDestructiveSelected)
90
+ .map('c', actions.gitCommitOpen)
91
+ .map('p', actions.gitPush)
92
+ )
93
+
75
94
  // -----------------------------------------------------------------------
76
95
  // Modal: help (src/input/modes/handlers/modal-help.ts)
77
96
  // -----------------------------------------------------------------------
@@ -240,5 +259,17 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
240
259
  .map('<CR>', actions.confirmSplit)
241
260
  )
242
261
 
262
+ // -----------------------------------------------------------------------
263
+ // Modal: git-commit (src/input/modes/handlers/modal-git-commit.ts)
264
+ // -----------------------------------------------------------------------
265
+ .mode('modal.git-commit', (m) =>
266
+ m
267
+ .map('<Esc>', actions.gitCommitCancel)
268
+ .map('<C-CR>', actions.gitCommitSubmit)
269
+ .map('<Tab>', actions.switchField)
270
+ .map('<CR>', actions.gitCommitReturnKey)
271
+ .passthrough()
272
+ )
273
+
243
274
  return kb._build()
244
275
  }
package/src/types.ts CHANGED
@@ -12,6 +12,7 @@ export type ModeId =
12
12
  | 'navigation'
13
13
  | 'terminal-input'
14
14
  | 'layout'
15
+ | 'git-mode'
15
16
  | 'modal.new-tab'
16
17
  | 'modal.new-tab.command-edit'
17
18
  | 'modal.session-picker'
@@ -25,6 +26,7 @@ export type ModeId =
25
26
  | 'modal.theme-picker'
26
27
  | 'modal.help'
27
28
  | 'modal.split-picker'
29
+ | 'modal.git-commit'
28
30
 
29
31
  // ─── Primitive app types ──────────────────────────────────────────────────────
30
32
 
@@ -32,7 +34,13 @@ export type BuiltinAssistantId = 'claude' | 'codex' | 'opencode' | 'terminal'
32
34
  export type AssistantId = BuiltinAssistantId | (string & {})
33
35
  export type TabStatus = 'starting' | 'running' | 'disconnected' | 'exited' | 'error'
34
36
  export type TabActivity = 'busy' | 'idle'
35
- export type FocusMode = 'navigation' | 'terminal-input' | 'modal' | 'command-edit' | 'layout'
37
+ export type FocusMode =
38
+ | 'navigation'
39
+ | 'terminal-input'
40
+ | 'modal'
41
+ | 'command-edit'
42
+ | 'layout'
43
+ | 'git'
36
44
  export type SplitDirection = 'horizontal' | 'vertical'
37
45
 
38
46
  // ─── Terminal data shapes ─────────────────────────────────────────────────────
@@ -179,10 +187,31 @@ export interface GitPanelState {
179
187
  error: GitPanelError | null
180
188
  }
181
189
 
190
+ export type DiffFileStatus = 'modified' | 'new' | 'deleted' | 'binary' | 'renamed'
191
+
192
+ export interface DiffData {
193
+ path: string
194
+ status: DiffFileStatus
195
+ oldPath?: string
196
+ rawDiff: string
197
+ binarySizeBefore?: number
198
+ binarySizeAfter?: number
199
+ errorMessage?: string
200
+ }
201
+
202
+ export interface GitModeState {
203
+ selectedFileIndex: number
204
+ diffs: Record<string, DiffData>
205
+ loading: Record<string, boolean>
206
+ pendingDeletePath: string | null
207
+ actionMessage: string | null
208
+ }
209
+
182
210
  interface ModalBase {
183
211
  selectedIndex: number
184
212
  editBuffer: string | null
185
213
  sessionTargetId: string | null
214
+ cursorPos?: number
186
215
  }
187
216
 
188
217
  export interface ModalClosed extends ModalBase {
@@ -223,6 +252,11 @@ export interface ModalCreateSession extends ModalBase {
223
252
  activeField: 'directory' | 'name'
224
253
  nameBuffer: string
225
254
  }
255
+ export interface ModalGitCommit extends ModalBase {
256
+ type: 'git-commit'
257
+ activeField: 'title' | 'body'
258
+ contentBuffer: string
259
+ }
226
260
  export interface ModalSnippetEditor extends ModalBase {
227
261
  type: 'snippet-editor'
228
262
  activeField: 'name' | 'content'
@@ -241,6 +275,7 @@ export type ModalState =
241
275
  | ModalSplitPicker
242
276
  | ModalCreateSession
243
277
  | ModalSnippetEditor
278
+ | ModalGitCommit
244
279
 
245
280
  export interface LayoutState {
246
281
  terminalCols: number
@@ -261,6 +296,7 @@ export interface AppState {
261
296
  layout: LayoutState
262
297
  customCommands: Record<AssistantId, string>
263
298
  gitPanel: GitPanelState
299
+ gitMode: GitModeState
264
300
  pendingChords: string[] | null
265
301
  }
266
302
 
@@ -369,6 +405,23 @@ export type GitPanelAction =
369
405
  | { type: 'git-refresh-error'; kind: GitPanelError }
370
406
  | { type: 'git-panel-reset' }
371
407
 
408
+ export type GitModeAction =
409
+ | { type: 'enter-git-mode' }
410
+ | { type: 'exit-git-mode' }
411
+ | { type: 'git-mode-select-file'; delta: -1 | 1 }
412
+ | { type: 'git-mode-set-diff'; path: string; diff: DiffData }
413
+ | { type: 'git-mode-set-loading'; path: string; loading: boolean }
414
+ | { type: 'git-mode-set-pending-delete'; path: string | null }
415
+ | { type: 'git-mode-clear-diff-cache'; path: string }
416
+ | { type: 'git-mode-set-message'; message: string | null }
417
+ | {
418
+ type: 'git-mode-optimistic-move'
419
+ path: string
420
+ fromSection: GitFileSection
421
+ toSection: GitFileSection | null
422
+ }
423
+ | { type: 'open-git-commit-modal' }
424
+
372
425
  export type DataAction =
373
426
  | { type: 'set-snippets'; snippets: SnippetRecord[] }
374
427
  | { type: 'delete-snippet'; snippetId: string }
@@ -381,6 +434,7 @@ export type AppAction =
381
434
  | UIAction
382
435
  | DataAction
383
436
  | GitPanelAction
437
+ | GitModeAction
384
438
 
385
439
  // ─── Side effects ─────────────────────────────────────────────────────────────
386
440
 
@@ -406,6 +460,13 @@ export type SideEffect =
406
460
  | { type: 'rename-session'; sessionId: string; name: string }
407
461
  | { type: 'split-pane'; direction: SplitDirection }
408
462
  | { type: 'confirm-split' }
463
+ | { type: 'scroll-git-diff'; delta: number }
464
+ | { type: 'git-stage'; path: string }
465
+ | { type: 'git-unstage'; path: string }
466
+ | { type: 'git-restore'; path: string }
467
+ | { type: 'git-rm'; path: string }
468
+ | { type: 'git-commit'; title: string; body: string }
469
+ | { type: 'git-push' }
409
470
 
410
471
  // ─── Key input / KeyResult / ModeContext ──────────────────────────────────────
411
472