@brimveyn/aimux-config 0.6.9 → 0.6.11

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.6.9",
3
+ "version": "0.6.11",
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
@@ -195,7 +195,7 @@ export const launchSelectedAssistant: ActionFn = (ctx: ModeContext) => {
195
195
  ctx.state.modal.step === 'worktree' &&
196
196
  ctx.state.modal.createWorktree
197
197
  ) {
198
- return r([{ type: 'enter-new-tab-worktree-create' }])
198
+ return r([{ type: 'enter-new-tab-worktree-create' }], [{ type: 'load-new-tab-base-branches' }])
199
199
  }
200
200
  return r([], [{ type: 'launch-selected-assistant' }])
201
201
  }
@@ -210,19 +210,49 @@ export const deleteSelectedWorktree: ActionFn = (ctx: ModeContext) => {
210
210
  const session = ctx.state.sessions.find((entry) => entry.id === sessionId)
211
211
  const worktree = session?.worktrees?.[modal.selectedIndex]
212
212
  if (!worktree) return null
213
+ // First attempt is never forced — a recoverable failure opens the confirm
214
+ // dialog (see set-new-tab-worktree-delete-prompt), where confirmDeleteWorktree
215
+ // retries with force.
213
216
  return r(
214
- [
215
- { index: modal.selectedIndex, type: 'set-modal-selection-index' },
216
- { message: null, type: 'set-new-tab-worktree-delete-state' },
217
- ],
217
+ [{ index: modal.selectedIndex, type: 'set-modal-selection-index' }],
218
+ [{ force: false, sessionId, type: 'delete-worktree', worktreeId: worktree.id }]
219
+ )
220
+ }
221
+
222
+ export const confirmDeleteWorktree: ActionFn = (ctx: ModeContext) => {
223
+ const modal = ctx.state.modal
224
+ const sessionId = ctx.state.currentSessionId
225
+ if (modal.type !== 'new-tab' || modal.worktreeDeletePrompt == null) return null
226
+ if (!(sessionId != null && sessionId !== '')) return null
227
+ const { worktreeId } = modal.worktreeDeletePrompt
228
+ return r(
229
+ [{ prompt: null, type: 'set-new-tab-worktree-delete-prompt' }],
230
+ [{ force: true, sessionId, type: 'delete-worktree', worktreeId }],
231
+ 'modal.new-tab.command-edit'
232
+ )
233
+ }
234
+
235
+ export const cancelDeleteWorktree: KeyResult = r(
236
+ [{ prompt: null, type: 'set-new-tab-worktree-delete-prompt' }],
237
+ [],
238
+ 'modal.new-tab.command-edit'
239
+ )
240
+
241
+ export const confirmWorktreeDeleteModal: ActionFn = (ctx: ModeContext) => {
242
+ const modal = ctx.state.modal
243
+ if (modal.type !== 'worktree-delete-confirm') return null
244
+ return r(
245
+ [{ type: 'close-modal' }],
218
246
  [
219
247
  {
220
- force: modal.worktreeDeleteConfirmId === worktree.id,
221
- sessionId,
248
+ closeTabs: modal.closeTabs,
249
+ force: modal.force,
250
+ sessionId: modal.sessionId,
222
251
  type: 'delete-worktree',
223
- worktreeId: worktree.id,
252
+ worktreeId: modal.worktreeId,
224
253
  },
225
- ]
254
+ ],
255
+ 'navigation'
226
256
  )
227
257
  }
228
258
 
@@ -373,7 +403,10 @@ export const openWorktreeMove: ActionFn = (ctx: ModeContext) => {
373
403
  }
374
404
  // Overlay: no mode transition — deriveModeId routes input to the picker and
375
405
  // focusMode stays 'git' so the git view remains mounted underneath.
376
- return r([{ sourceWorktreeId: source.id, type: 'open-worktree-move-modal' }])
406
+ return r(
407
+ [{ sourceWorktreeId: source.id, type: 'open-worktree-move-modal' }],
408
+ [{ type: 'load-worktree-move-stats' }]
409
+ )
377
410
  }
378
411
 
379
412
  export const toggleWorktreeMoveDelete: KeyResult = r([{ type: 'toggle-worktree-move-delete' }])
@@ -406,6 +439,27 @@ export const confirmWorktreeMove: ActionFn = (ctx: ModeContext) => {
406
439
  )
407
440
  }
408
441
 
442
+ // Confirms the dialog opened after a recoverable move failure: re-runs the
443
+ // move with the flag matching the failure (stash the target / keep conflicts).
444
+ export const confirmWorktreeMoveRetry: ActionFn = (ctx: ModeContext) => {
445
+ const modal = ctx.state.modal
446
+ if (modal.type !== 'worktree-move-confirm') return null
447
+ return r(
448
+ [{ type: 'close-modal' }],
449
+ [
450
+ {
451
+ deleteSource: modal.deleteSource,
452
+ sessionId: modal.sessionId,
453
+ sourceWorktreeId: modal.sourceWorktreeId,
454
+ targetWorktreeId: modal.targetWorktreeId,
455
+ type: 'move-worktree',
456
+ ...(modal.variant === 'stash-target' ? { stashTarget: true } : { keepConflicts: true }),
457
+ },
458
+ ],
459
+ 'navigation'
460
+ )
461
+ }
462
+
409
463
  export const closePane: ActionFn = (ctx: ModeContext) => {
410
464
  const tabId = ctx.state.activeTabId
411
465
  if (!(tabId != null && tabId !== '')) return null
package/src/defaults.ts CHANGED
@@ -204,6 +204,28 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
204
204
  .map('<CR>', actions.confirmWorktreeMove, 'Move')
205
205
  )
206
206
 
207
+ // -----------------------------------------------------------------------
208
+ // Modal: worktree-move recoverable-failure confirmation (stash / conflicts)
209
+ // -----------------------------------------------------------------------
210
+ .mode('modal.worktree-move-confirm', (m) =>
211
+ m
212
+ .map('<Esc>', actions.closeModal, 'Cancel')
213
+ .map('n', actions.closeModal, 'Cancel')
214
+ .map('<CR>', actions.confirmWorktreeMoveRetry, 'Confirm')
215
+ .map('y', actions.confirmWorktreeMoveRetry, 'Confirm')
216
+ )
217
+
218
+ // -----------------------------------------------------------------------
219
+ // Modal: standalone worktree delete confirmation (sidebar "Remove worktree")
220
+ // -----------------------------------------------------------------------
221
+ .mode('modal.worktree-delete-confirm', (m) =>
222
+ m
223
+ .map('<Esc>', actions.closeModal, 'Cancel')
224
+ .map('n', actions.closeModal, 'Cancel')
225
+ .map('<CR>', actions.confirmWorktreeDeleteModal, 'Delete worktree')
226
+ .map('y', actions.confirmWorktreeDeleteModal, 'Delete worktree')
227
+ )
228
+
207
229
  // -----------------------------------------------------------------------
208
230
  // Modal: rename-tab
209
231
  // -----------------------------------------------------------------------
@@ -239,6 +261,17 @@ export function getDefaultKeymapConfig(): ResolvedKeymapConfig {
239
261
  .passthrough()
240
262
  )
241
263
 
264
+ // -----------------------------------------------------------------------
265
+ // Modal: new-tab worktree delete confirmation dialog
266
+ // -----------------------------------------------------------------------
267
+ .mode('modal.new-tab.worktree-delete-confirm', (m) =>
268
+ m
269
+ .map('<Esc>', actions.cancelDeleteWorktree, 'Cancel')
270
+ .map('n', actions.cancelDeleteWorktree, 'Cancel')
271
+ .map('<CR>', actions.confirmDeleteWorktree, 'Delete worktree')
272
+ .map('y', actions.confirmDeleteWorktree, 'Delete worktree')
273
+ )
274
+
242
275
  // -----------------------------------------------------------------------
243
276
  // Modal: session-picker (always in filter mode via Picker)
244
277
  // -----------------------------------------------------------------------
package/src/types.ts CHANGED
@@ -14,6 +14,8 @@ export type ModeId =
14
14
  | 'git-mode'
15
15
  | 'modal.new-tab.command-edit'
16
16
  | 'modal.new-tab.editing-command'
17
+ | 'modal.new-tab.worktree-delete-confirm'
18
+ | 'modal.worktree-delete-confirm'
17
19
  | 'modal.session-picker.filtering'
18
20
  | 'modal.session-name'
19
21
  | 'modal.create-session'
@@ -28,6 +30,7 @@ export type ModeId =
28
30
  | 'modal.git-commit.generating'
29
31
  | 'modal.update-available'
30
32
  | 'modal.worktree-move'
33
+ | 'modal.worktree-move-confirm'
31
34
  | 'modal.ai-usage'
32
35
 
33
36
  // ─── Primitive app types ──────────────────────────────────────────────────────
@@ -296,16 +299,21 @@ export interface ModalClosed extends ModalBase {
296
299
  export interface ModalNewTab extends ModalBase {
297
300
  type: 'new-tab'
298
301
  editingCommand: AssistantId | null
299
- activeField: 'assistant' | 'branch-name' | 'target-worktree' | 'worktree-name'
302
+ activeField: 'assistant' | 'branch-name' | 'target-worktree' | 'worktree-name' | 'base'
300
303
  branchError: string | null
301
304
  branchName: string
302
305
  createWorktree: boolean
303
306
  selectedAssistantId: AssistantId | null
304
307
  step: 'assistant' | 'worktree' | 'worktree-create' | 'template'
305
308
  targetWorktreeIndex: number
306
- worktreeDeleteConfirmId: string | null
307
- worktreeDeleteMessage: string | null
309
+ worktreeDeletePrompt: { worktreeId: string; reason: string } | null
308
310
  worktreeName: string
311
+ /** Filter text typed into the "Base" picker on the worktree-create step. */
312
+ baseQuery: string
313
+ /** Resolved base ref the new worktree is forked from (branch of a worktree or a local branch). */
314
+ baseRef: string
315
+ /** Local branches available as base refs, loaded when the create step opens. */
316
+ baseBranches: string[]
309
317
  }
310
318
  export interface ModalSessionPicker extends ModalBase {
311
319
  type: 'session-picker'
@@ -371,6 +379,42 @@ export interface ModalWorktreeMove extends ModalBase {
371
379
  /** The worktree being moved (may differ from the active one, e.g. a tab menu). */
372
380
  sourceWorktreeId: string
373
381
  deleteSource: boolean
382
+ /** Per-worktree dirty file counts, loaded async when the modal opens. */
383
+ stats: { kind: 'loading' } | { kind: 'ready'; dirtyFiles: Record<string, number> }
384
+ }
385
+
386
+ /**
387
+ * Confirmation for a recoverable move failure: the target's dirty files
388
+ * overlap the incoming changes (stash-target) or the squash conflicts
389
+ * (keep-conflicts). Both worktrees are already restored; confirming re-runs
390
+ * move-worktree with the matching flag.
391
+ */
392
+ export interface ModalWorktreeMoveConfirm extends ModalBase {
393
+ type: 'worktree-move-confirm'
394
+ variant: 'stash-target' | 'keep-conflicts'
395
+ files: string[]
396
+ sessionId: string
397
+ sourceWorktreeId: string
398
+ targetWorktreeId: string
399
+ deleteSource: boolean
400
+ sourceLabel: string
401
+ targetLabel: string
402
+ }
403
+
404
+ /**
405
+ * Standalone confirmation for a recoverable worktree delete failure triggered
406
+ * outside the new-tab picker (e.g. the sidebar's "Remove worktree"). Carries the
407
+ * params needed to re-run the delete with force once confirmed.
408
+ */
409
+ export interface ModalWorktreeDeleteConfirm extends ModalBase {
410
+ type: 'worktree-delete-confirm'
411
+ sessionId: string
412
+ worktreeId: string
413
+ worktreeLabel: string
414
+ reason: string
415
+ closeTabs: boolean
416
+ /** Whether confirming force-deletes — true only after a recoverable failure. */
417
+ force: boolean
374
418
  }
375
419
 
376
420
  export type ModalState =
@@ -389,6 +433,8 @@ export type ModalState =
389
433
  | ModalUpdateAvailable
390
434
  | ModalAIUsage
391
435
  | ModalWorktreeMove
436
+ | ModalWorktreeMoveConfirm
437
+ | ModalWorktreeDeleteConfirm
392
438
 
393
439
  export interface LayoutState {
394
440
  terminalCols: number
@@ -464,10 +510,10 @@ export type ModalAction =
464
510
  | { type: 'open-new-tab-modal' }
465
511
  | { type: 'set-new-tab-branch-error'; message: string | null }
466
512
  | {
467
- type: 'set-new-tab-worktree-delete-state'
468
- confirmWorktreeId?: string | null
469
- message: string | null
513
+ type: 'set-new-tab-worktree-delete-prompt'
514
+ prompt: { worktreeId: string; reason: string } | null
470
515
  }
516
+ | { type: 'set-new-tab-base-branches'; branches: string[] }
471
517
  | { type: 'enter-new-tab-worktree-create' }
472
518
  | { type: 'enter-new-tab-template-pick' }
473
519
  | { type: 'enter-new-tab-template-shortcut' }
@@ -502,6 +548,27 @@ export type ModalAction =
502
548
  | { type: 'open-edit-custom-command'; assistantId: AssistantId }
503
549
  | { type: 'open-worktree-move-modal'; sourceWorktreeId: string }
504
550
  | { type: 'toggle-worktree-move-delete' }
551
+ | { type: 'set-worktree-move-stats'; dirtyFiles: Record<string, number> }
552
+ | {
553
+ type: 'open-worktree-move-confirm'
554
+ variant: 'stash-target' | 'keep-conflicts'
555
+ files: string[]
556
+ sessionId: string
557
+ sourceWorktreeId: string
558
+ targetWorktreeId: string
559
+ deleteSource: boolean
560
+ sourceLabel: string
561
+ targetLabel: string
562
+ }
563
+ | {
564
+ type: 'open-worktree-delete-confirm'
565
+ sessionId: string
566
+ worktreeId: string
567
+ worktreeLabel: string
568
+ reason: string
569
+ closeTabs: boolean
570
+ force: boolean
571
+ }
505
572
 
506
573
  export type SessionAction =
507
574
  | { type: 'load-session'; sessionId: string; workspaceSnapshot?: WorkspaceSnapshotV1 }
@@ -513,7 +580,6 @@ export type SessionAction =
513
580
  | { type: 'reorder-active-session'; delta: number }
514
581
  | { type: 'set-session-status'; sessionId: string; status: SessionStatus }
515
582
  | { type: 'add-worktree-record'; sessionId: string; worktree: WorktreeRecord; activate?: boolean }
516
- | { type: 'remove-worktree-record'; sessionId: string; worktreeId: string }
517
583
  | { type: 'set-active-worktree'; sessionId: string; worktreeId: string }
518
584
  | {
519
585
  type: 'update-worktree-record'
@@ -709,6 +775,7 @@ export type AppAction =
709
775
  export type SideEffect =
710
776
  | { type: 'quit'; state: AppState }
711
777
  | { type: 'launch-selected-assistant' }
778
+ | { type: 'load-new-tab-base-branches' }
712
779
  | { type: 'edit-selected-assistant' }
713
780
  | { type: 'confirm-selected-session' }
714
781
  | { type: 'delete-selected-session' }
@@ -748,14 +815,29 @@ export type SideEffect =
748
815
  | { type: 'cycle-sidebar-item'; direction: 1 | -1 }
749
816
  | { type: 'switch-tab-by-index'; index: number }
750
817
  | { type: 'delete-session'; sessionId: string }
751
- | { type: 'delete-worktree'; sessionId: string; worktreeId: string; force?: boolean }
818
+ | {
819
+ type: 'delete-worktree'
820
+ sessionId: string
821
+ worktreeId: string
822
+ // Force the git worktree removal (discards uncommitted changes in the
823
+ // worktree). Also implies closing the worktree's tabs.
824
+ force?: boolean
825
+ // Close the worktree's tabs without forcing the git removal. Lets the
826
+ // sidebar "Remove worktree" clean up tabs (avoiding orphans) while still
827
+ // refusing to discard uncommitted work in a temp worktree.
828
+ closeTabs?: boolean
829
+ }
752
830
  | {
753
831
  type: 'move-worktree'
754
832
  sessionId: string
755
833
  sourceWorktreeId: string
756
834
  targetWorktreeId: string
757
835
  deleteSource?: boolean
836
+ // Retry flags set by the worktree-move-confirm dialog.
837
+ stashTarget?: boolean
838
+ keepConflicts?: boolean
758
839
  }
840
+ | { type: 'load-worktree-move-stats' }
759
841
  | { type: 'toggle-transparent' }
760
842
  | { type: 'toggle-mode' }
761
843
  | { type: 'open-file-in-editor'; path: string }