Skip to content

Commit f1a8cae

Browse files
johnlindquistclaude
andcommitted
feat: add navigation history to path selector
Implement navigation history that remembers directory selections when navigating up and down multiple levels. The path selector now tracks which items were selected in each directory and restores focus when returning to previously visited directories. - Add Map to track path -> selection name pairs - Save selections when navigating up or down - Restore focus to saved selections when revisiting directories - Fix event handlers to pass full choice object instead of just value - Add comprehensive tests for navigation history feature 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 03e74e5 commit f1a8cae

File tree

2 files changed

+60
-8
lines changed

2 files changed

+60
-8
lines changed

src/target/path/path.test.ts

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -453,3 +453,33 @@ ava("Path utility normalizes all directory inputs", async t => {
453453
global.isDir = originalIsDir;
454454
global.pathExists = originalPathExists;
455455
});
456+
457+
// Test navigation history feature
458+
ava("Navigation history remembers selections when going up and down", async t => {
459+
// Test the navigation history Map behavior
460+
const navigationHistory = new Map<string, string>();
461+
462+
// Simulate navigation: /Users/ -> /Users/john/ -> /Users/john/Documents/
463+
const paths = process.platform === "win32"
464+
? ["C:\\Users\\", "C:\\Users\\john\\", "C:\\Users\\john\\Documents\\"]
465+
: ["/Users/", "/Users/john/", "/Users/john/Documents/"];
466+
467+
// Going down, saving selections
468+
navigationHistory.set(paths[0], "john");
469+
navigationHistory.set(paths[1], "Documents");
470+
471+
// Verify saved selections
472+
t.is(navigationHistory.get(paths[0]), "john", "Should remember 'john' was selected in /Users/");
473+
t.is(navigationHistory.get(paths[1]), "Documents", "Should remember 'Documents' was selected in /Users/john/");
474+
475+
// Going up should retrieve the saved selections
476+
const selection1 = navigationHistory.get(paths[1]);
477+
t.is(selection1, "Documents", "Going back to /Users/john/ should focus on 'Documents'");
478+
479+
const selection2 = navigationHistory.get(paths[0]);
480+
t.is(selection2, "john", "Going back to /Users/ should focus on 'john'");
481+
482+
// Test overwriting: if we go down a different path from the same directory
483+
navigationHistory.set(paths[0], "jane"); // Different selection
484+
t.is(navigationHistory.get(paths[0]), "jane", "Navigation history should update with new selection");
485+
});

src/target/path/path.ts

Lines changed: 30 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ let __pathSelector = async (config: string | PathConfig = home(), actions?: Acti
166166
let onlyDirs = false
167167
let missingChoices: Choice[] = defaultPathMissingChoices
168168

169+
// Navigation history: Map of path -> last selected item name
170+
const navigationHistory = new Map<string, string>()
171+
169172
// Handle string config (direct path)
170173
if (typeof config === 'string') {
171174
startPath = config
@@ -279,6 +282,11 @@ ${error}
279282
// This ensures we're always working with the most current value
280283
const currentPath = global.__kitPromptState?.input || currentInput || startPath
281284

285+
// Save the current selection in navigation history before going up
286+
if (dir?.name && currentPath) {
287+
navigationHistory.set(currentPath, dir.name)
288+
}
289+
282290
// Remove trailing separator before getting parent, but not for root paths
283291
let cleanPath
284292
const rootPath = isWin ? ogPath.parse(currentPath).root : '/'
@@ -314,7 +322,12 @@ ${error}
314322
// Force refresh the directory listing with the new path
315323
await lsCurrentDir(newPath)
316324

317-
if (dir) {
325+
// Check if we have a saved selection for the parent directory
326+
const savedSelection = navigationHistory.get(newPath)
327+
if (savedSelection) {
328+
focusOn = savedSelection
329+
} else if (dir) {
330+
// Default behavior: focus on the directory we came from
318331
focusOn = path.basename(cleanPath)
319332
}
320333
}
@@ -327,7 +340,7 @@ ${error}
327340
// Get the current path from the actual input state, not startPath
328341
const currentPath = global.__kitPromptState?.input || currentInput || startPath
329342

330-
let targetPath = ogPath.resolve(currentPath, dir)
343+
let targetPath = typeof dir === 'string' ? ogPath.resolve(currentPath, dir) : dir.value
331344
let allowed = true
332345
let needsPermission =
333346
targetPath === home('Downloads') || targetPath === home('Documents') || targetPath === home('Desktop')
@@ -346,13 +359,24 @@ ${error}
346359
if (await isDir(targetPath)) {
347360
const newPath = targetPath + path.sep
348361

362+
// Save current directory selection before navigating down
363+
if (dir?.name && currentPath) {
364+
navigationHistory.set(currentPath, dir.name)
365+
}
366+
349367
// Update startPath immediately to prevent race conditions
350368
startPath = newPath
351369

352370
await setInput(newPath)
353371

354372
// Force refresh the directory listing
355373
await lsCurrentDir(newPath)
374+
375+
// Check if we have a saved selection for this directory
376+
const savedSelection = navigationHistory.get(newPath)
377+
if (savedSelection) {
378+
focusOn = savedSelection
379+
}
356380
}
357381
} else {
358382
let html = md(`
@@ -458,24 +482,22 @@ Please grant permission in System Preferences > Security & Privacy > Privacy > F
458482
}
459483

460484
let onTab = (input, state) => {
461-
let dir = state.focused.value
462-
463485
if (state.modifiers.includes('shift')) {
464-
upDir(dir)
486+
upDir(state.focused)
465487
} else {
466-
downDir(dir)
488+
downDir(state.focused)
467489
}
468490
}
469491

470492
let onRight = (input, state: AppState) => {
471493
if (!state.flaggedValue) {
472-
downDir(state.focused.value)
494+
downDir(state.focused)
473495
}
474496
}
475497

476498
let onLeft = (input, state) => {
477499
if (!state.flaggedValue) {
478-
upDir(state.focused.value)
500+
upDir(state.focused)
479501
}
480502
}
481503

0 commit comments

Comments
 (0)