feat: improve terminal navigation and UI behavior
- refine tree and history navigation behavior - improve scrolling continuity in the terminal UI - keep AI worklog and prompt log updated - validate changes with project quality checks
This commit is contained in:
@@ -26,3 +26,18 @@ Store only the user prompt and a short result summary.
|
|||||||
|
|
||||||
- prompt summary: Introduce a standard AI log entry format in `instructions-agent.md` and record the change in the repository AI logs.
|
- prompt summary: Introduce a standard AI log entry format in `instructions-agent.md` and record the change in the repository AI logs.
|
||||||
- result summary: Added a standardized `AI BUILD ENTRY` and `AI PLAN ENTRY` format, improved consistency expectations for AI logs, and updated the log files to reflect the new format.
|
- result summary: Added a standardized `AI BUILD ENTRY` and `AI PLAN ENTRY` format, improved consistency expectations for AI logs, and updated the log files to reflect the new format.
|
||||||
|
|
||||||
|
## 2026-03-15
|
||||||
|
|
||||||
|
- prompt summary: Improve keyboard navigation in `src/app/ui.py` so Right expands or enters, Left collapses or goes to the parent, Enter confirms the selected destination, and ESC cancels back to the starting directory.
|
||||||
|
- result summary: Updated the tree-mode key handling in the terminal UI, preserved history mode behavior, and added focused UI tests for the new navigation rules.
|
||||||
|
|
||||||
|
## 2026-03-15
|
||||||
|
|
||||||
|
- prompt summary: Improve vertical scrolling in `src/app/ui.py` so the selected entry always remains visible in tree mode and history mode when the list is taller than the screen.
|
||||||
|
- result summary: Added UI scroll offset handling for tree and history views, kept existing navigation behavior unchanged, and added focused tests for the scroll helper logic.
|
||||||
|
|
||||||
|
## 2026-03-16 00:27
|
||||||
|
|
||||||
|
- prompt summary: Improve terminal UI navigation by refining tree and history keyboard behavior, adding scrolling continuity so selection stays visible, and preserving confirmation, cancellation, and history mode behavior.
|
||||||
|
- result summary: Updated `src/app/ui.py` and `tests/test_ui.py` to support dedicated history selection, improved Right/Left/Enter/ESC behavior, and vertical scrolling for both tree and history views while keeping the AI logs current.
|
||||||
|
|||||||
@@ -14,23 +14,80 @@ Each entry should include:
|
|||||||
|
|
||||||
Entries must be concise and chronological.
|
Entries must be concise and chronological.
|
||||||
|
|
||||||
## 2026-03-15 - build
|
### AI BUILD ENTRY
|
||||||
|
Date: 2026-03-15
|
||||||
|
Task: build
|
||||||
|
Objective: Replace the temporary history debug overlay with a dedicated history mode in the terminal UI.
|
||||||
|
|
||||||
- objective: Replace the temporary history debug overlay with a dedicated history mode in the terminal UI.
|
Files Modified:
|
||||||
- files modified: `src/app/ui.py`, `tests/test_ui.py`
|
- `src/app/ui.py`
|
||||||
- key decisions: Keep `b` and `f` unchanged, add separate history-mode selection state inside the UI, hide the tree while history mode is active, and use Enter to select a history entry and return to the tree view.
|
- `tests/test_ui.py`
|
||||||
- validation: `make fix`, `make quality`
|
|
||||||
- result: History mode now shows only the history list, supports Up/Down selection, allows Enter to jump to a prior directory, and exits cleanly back to normal navigation.
|
|
||||||
- open issues: None.
|
|
||||||
|
|
||||||
## 2026-03-15 - build
|
Key Decisions:
|
||||||
|
- Keep `b` and `f` unchanged.
|
||||||
|
- Add separate history-mode selection state inside the UI.
|
||||||
|
- Hide the tree while history mode is active.
|
||||||
|
- Use Enter to select a history entry and return to the tree view.
|
||||||
|
|
||||||
- objective: Introduce mandatory AI logging rules and document the repository logging workflow.
|
Validation:
|
||||||
- files modified: `instructions-agent.md`, `docs/ai-worklog.md`, `docs/ai-prompts.md`
|
- make fix
|
||||||
- key decisions: Require AI agents to log significant plan and build tasks, preserve separate logs for work performed and prompts received, and keep entries concise and user-facing.
|
- make quality
|
||||||
- validation: not run
|
|
||||||
- result: The repository now defines mandatory AI logging in `instructions-agent.md` and includes dedicated log files for AI work history and prompt summaries.
|
Result:
|
||||||
- open issues: None.
|
- History mode now shows only the history list, supports Up/Down selection, allows Enter to jump to a prior directory, and exits cleanly back to normal navigation.
|
||||||
|
|
||||||
|
Open Issues:
|
||||||
|
- None.
|
||||||
|
|
||||||
|
### AI BUILD ENTRY
|
||||||
|
Date: 2026-03-16 00:27
|
||||||
|
Task: build
|
||||||
|
Objective: Consolidate recent terminal UI navigation improvements and document the updated interaction behavior.
|
||||||
|
|
||||||
|
Files Modified:
|
||||||
|
- `src/app/ui.py`
|
||||||
|
- `tests/test_ui.py`
|
||||||
|
- `docs/ai-worklog.md`
|
||||||
|
- `docs/ai-prompts.md`
|
||||||
|
|
||||||
|
Key Decisions:
|
||||||
|
- Keep dedicated history mode while refining tree-mode keyboard behavior for Right, Left, Enter, and ESC.
|
||||||
|
- Add scroll continuity for both tree and history views so the selected row remains visible on long lists.
|
||||||
|
- Cover the UI-focused behavior with helper-level tests instead of changing navigator or filesystem logic.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- make fix (passed)
|
||||||
|
- make quality (passed)
|
||||||
|
|
||||||
|
Result:
|
||||||
|
- The terminal UI now combines dedicated history mode, clearer confirmation and cancellation behavior, improved tree navigation semantics, and vertical scrolling that keeps the current selection visible in both tree and history views.
|
||||||
|
|
||||||
|
Open Issues:
|
||||||
|
- None.
|
||||||
|
|
||||||
|
### AI BUILD ENTRY
|
||||||
|
Date: 2026-03-15
|
||||||
|
Task: build
|
||||||
|
Objective: Introduce mandatory AI logging rules and document the repository logging workflow.
|
||||||
|
|
||||||
|
Files Modified:
|
||||||
|
- `instructions-agent.md`
|
||||||
|
- `docs/ai-worklog.md`
|
||||||
|
- `docs/ai-prompts.md`
|
||||||
|
|
||||||
|
Key Decisions:
|
||||||
|
- Require AI agents to log significant plan and build tasks.
|
||||||
|
- Preserve separate logs for work performed and prompts received.
|
||||||
|
- Keep entries concise and user-facing.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- not run
|
||||||
|
|
||||||
|
Result:
|
||||||
|
- The repository now defines mandatory AI logging in `instructions-agent.md` and includes dedicated log files for AI work history and prompt summaries.
|
||||||
|
|
||||||
|
Open Issues:
|
||||||
|
- None.
|
||||||
|
|
||||||
### AI BUILD ENTRY
|
### AI BUILD ENTRY
|
||||||
Date: 2026-03-15
|
Date: 2026-03-15
|
||||||
@@ -55,3 +112,53 @@ Result:
|
|||||||
|
|
||||||
Open Issues:
|
Open Issues:
|
||||||
- None.
|
- None.
|
||||||
|
|
||||||
|
### AI BUILD ENTRY
|
||||||
|
Date: 2026-03-15
|
||||||
|
Task: build
|
||||||
|
Objective: Improve vertical scrolling in the terminal UI so the selected row remains visible in tree mode and history mode.
|
||||||
|
|
||||||
|
Files Modified:
|
||||||
|
- `src/app/ui.py`
|
||||||
|
- `tests/test_ui.py`
|
||||||
|
- `docs/ai-worklog.md`
|
||||||
|
- `docs/ai-prompts.md`
|
||||||
|
|
||||||
|
Key Decisions:
|
||||||
|
- Add separate scroll offsets for tree mode and history mode inside the UI state.
|
||||||
|
- Introduce a small helper to clamp scroll offsets so the selected row always stays within the visible window.
|
||||||
|
- Keep all existing navigation semantics unchanged while limiting the change to UI rendering behavior.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- make fix (passed)
|
||||||
|
- make quality (passed)
|
||||||
|
|
||||||
|
Result:
|
||||||
|
- The terminal UI now scrolls vertically in both tree mode and history mode to keep the current selection visible when the list is taller than the screen.
|
||||||
|
|
||||||
|
Open Issues:
|
||||||
|
- None.
|
||||||
|
|
||||||
|
### AI BUILD ENTRY
|
||||||
|
Date: 2026-03-15
|
||||||
|
Task: build
|
||||||
|
Objective: Improve keyboard navigation behavior in the terminal UI for tree mode.
|
||||||
|
|
||||||
|
Files Modified:
|
||||||
|
- `src/app/ui.py`
|
||||||
|
- `tests/test_ui.py`
|
||||||
|
|
||||||
|
Key Decisions:
|
||||||
|
- Make Right expand collapsed directories and enter already expanded directories.
|
||||||
|
- Make Left collapse expanded directories and otherwise navigate to the parent directory.
|
||||||
|
- Make Enter confirm the selected path as the final destination and make ESC return the original starting directory.
|
||||||
|
|
||||||
|
Validation:
|
||||||
|
- make fix (passed)
|
||||||
|
- make quality (passed)
|
||||||
|
|
||||||
|
Result:
|
||||||
|
- Tree-mode keyboard navigation now supports expand-or-enter on Right, collapse-or-parent on Left, explicit selection confirmation on Enter, and cancel-to-start behavior on ESC.
|
||||||
|
|
||||||
|
Open Issues:
|
||||||
|
- None.
|
||||||
|
|||||||
154
src/app/ui.py
154
src/app/ui.py
@@ -76,12 +76,34 @@ def build_history_lines(
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def clamp_scroll_offset(
|
||||||
|
selected_index: int, scroll_offset: int, visible_count: int, total_count: int
|
||||||
|
) -> int:
|
||||||
|
"""Keep the selected row within the visible scrolling window."""
|
||||||
|
|
||||||
|
if visible_count <= 0 or total_count <= visible_count:
|
||||||
|
return 0
|
||||||
|
|
||||||
|
max_offset = total_count - visible_count
|
||||||
|
offset = max(0, min(scroll_offset, max_offset))
|
||||||
|
|
||||||
|
if selected_index < offset:
|
||||||
|
return selected_index
|
||||||
|
|
||||||
|
if selected_index >= offset + visible_count:
|
||||||
|
return selected_index - visible_count + 1
|
||||||
|
|
||||||
|
return offset
|
||||||
|
|
||||||
|
|
||||||
class TerminalUI:
|
class TerminalUI:
|
||||||
"""Minimal curses UI for the directory navigator MVP."""
|
"""Minimal curses UI for the directory navigator MVP."""
|
||||||
|
|
||||||
def __init__(self) -> None:
|
def __init__(self) -> None:
|
||||||
self._history_mode = False
|
self._history_mode = False
|
||||||
self._history_selected_index = 0
|
self._history_selected_index = 0
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
self._history_scroll_offset = 0
|
||||||
|
|
||||||
def run(self, navigator: Navigator) -> Path:
|
def run(self, navigator: Navigator) -> Path:
|
||||||
"""Start the interactive session and return the final directory path."""
|
"""Start the interactive session and return the final directory path."""
|
||||||
@@ -93,13 +115,14 @@ class TerminalUI:
|
|||||||
def _run_session(self, stdscr: curses.window, navigator: Navigator) -> Path:
|
def _run_session(self, stdscr: curses.window, navigator: Navigator) -> Path:
|
||||||
curses.curs_set(0)
|
curses.curs_set(0)
|
||||||
stdscr.keypad(True)
|
stdscr.keypad(True)
|
||||||
|
original_path = navigator.current_path
|
||||||
|
|
||||||
while True:
|
while True:
|
||||||
self._render(stdscr, navigator)
|
self._render(stdscr, navigator)
|
||||||
key = stdscr.getch()
|
key = stdscr.getch()
|
||||||
|
|
||||||
if key == ESCAPE_KEY:
|
if key == ESCAPE_KEY:
|
||||||
return navigator.current_path
|
return original_path
|
||||||
|
|
||||||
if key == ord("h"):
|
if key == ord("h"):
|
||||||
self._toggle_history_mode(navigator)
|
self._toggle_history_mode(navigator)
|
||||||
@@ -109,44 +132,9 @@ class TerminalUI:
|
|||||||
self._handle_history_key(key, navigator)
|
self._handle_history_key(key, navigator)
|
||||||
continue
|
continue
|
||||||
|
|
||||||
if key == curses.KEY_UP:
|
tree_result = self._handle_tree_key(key, navigator)
|
||||||
navigator.set_selected_index(
|
if tree_result is not None:
|
||||||
move_selection(
|
return tree_result
|
||||||
navigator.selected_index,
|
|
||||||
step=-1,
|
|
||||||
entry_count=len(navigator.visible_entries),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key == curses.KEY_DOWN:
|
|
||||||
navigator.set_selected_index(
|
|
||||||
move_selection(
|
|
||||||
navigator.selected_index,
|
|
||||||
step=1,
|
|
||||||
entry_count=len(navigator.visible_entries),
|
|
||||||
)
|
|
||||||
)
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key == curses.KEY_RIGHT:
|
|
||||||
navigator.expand_selected_directory()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key == curses.KEY_LEFT:
|
|
||||||
navigator.collapse_selected_directory()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key == ord("b"):
|
|
||||||
navigator.go_back()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key == ord("f"):
|
|
||||||
navigator.go_forward()
|
|
||||||
continue
|
|
||||||
|
|
||||||
if key in (curses.KEY_ENTER, 10, 13):
|
|
||||||
navigator.enter_selected_directory()
|
|
||||||
|
|
||||||
def _render(self, stdscr: curses.window, navigator: Navigator) -> None:
|
def _render(self, stdscr: curses.window, navigator: Navigator) -> None:
|
||||||
stdscr.erase()
|
stdscr.erase()
|
||||||
@@ -160,7 +148,19 @@ class TerminalUI:
|
|||||||
path_text = str(navigator.current_path)
|
path_text = str(navigator.current_path)
|
||||||
stdscr.addnstr(0, 0, path_text, width - 1)
|
stdscr.addnstr(0, 0, path_text, width - 1)
|
||||||
|
|
||||||
for row, line in enumerate(build_render_lines(navigator), start=2):
|
lines = build_render_lines(navigator)
|
||||||
|
visible_count = max(0, height - 2)
|
||||||
|
self._tree_scroll_offset = clamp_scroll_offset(
|
||||||
|
navigator.selected_index,
|
||||||
|
self._tree_scroll_offset,
|
||||||
|
visible_count,
|
||||||
|
len(lines),
|
||||||
|
)
|
||||||
|
|
||||||
|
for row, line in enumerate(
|
||||||
|
lines[self._tree_scroll_offset : self._tree_scroll_offset + visible_count],
|
||||||
|
start=2,
|
||||||
|
):
|
||||||
if row >= height:
|
if row >= height:
|
||||||
break
|
break
|
||||||
|
|
||||||
@@ -176,10 +176,23 @@ class TerminalUI:
|
|||||||
navigator.history.entries(), self._history_selected_index
|
navigator.history.entries(), self._history_selected_index
|
||||||
)
|
)
|
||||||
start_row = 1
|
start_row = 1
|
||||||
|
visible_count = max(0, height - start_row - 1)
|
||||||
|
self._history_scroll_offset = clamp_scroll_offset(
|
||||||
|
self._history_selected_index,
|
||||||
|
self._history_scroll_offset,
|
||||||
|
visible_count,
|
||||||
|
len(history_lines),
|
||||||
|
)
|
||||||
|
|
||||||
stdscr.addnstr(start_row, 0, "History:", width - 1, curses.A_BOLD)
|
stdscr.addnstr(start_row, 0, "History:", width - 1, curses.A_BOLD)
|
||||||
|
|
||||||
for index, line in enumerate(history_lines, start=1):
|
for index, line in enumerate(
|
||||||
|
history_lines[
|
||||||
|
self._history_scroll_offset : self._history_scroll_offset
|
||||||
|
+ visible_count
|
||||||
|
],
|
||||||
|
start=1,
|
||||||
|
):
|
||||||
row = start_row + index
|
row = start_row + index
|
||||||
if row >= height:
|
if row >= height:
|
||||||
break
|
break
|
||||||
@@ -191,6 +204,7 @@ class TerminalUI:
|
|||||||
self._history_mode = not self._history_mode
|
self._history_mode = not self._history_mode
|
||||||
if self._history_mode:
|
if self._history_mode:
|
||||||
self._history_selected_index = self._get_current_history_index(navigator)
|
self._history_selected_index = self._get_current_history_index(navigator)
|
||||||
|
self._history_scroll_offset = 0
|
||||||
|
|
||||||
def _handle_history_key(self, key: int, navigator: Navigator) -> None:
|
def _handle_history_key(self, key: int, navigator: Navigator) -> None:
|
||||||
history_entries = navigator.history.entries()
|
history_entries = navigator.history.entries()
|
||||||
@@ -214,6 +228,64 @@ class TerminalUI:
|
|||||||
if key in (curses.KEY_ENTER, 10, 13) and history_entries:
|
if key in (curses.KEY_ENTER, 10, 13) and history_entries:
|
||||||
navigator.select_history_entry(self._history_selected_index)
|
navigator.select_history_entry(self._history_selected_index)
|
||||||
self._history_mode = False
|
self._history_mode = False
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
|
||||||
|
def _handle_tree_key(self, key: int, navigator: Navigator) -> Path | None:
|
||||||
|
if key == curses.KEY_UP:
|
||||||
|
navigator.set_selected_index(
|
||||||
|
move_selection(
|
||||||
|
navigator.selected_index,
|
||||||
|
step=-1,
|
||||||
|
entry_count=len(navigator.visible_entries),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key == curses.KEY_DOWN:
|
||||||
|
navigator.set_selected_index(
|
||||||
|
move_selection(
|
||||||
|
navigator.selected_index,
|
||||||
|
step=1,
|
||||||
|
entry_count=len(navigator.visible_entries),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key == curses.KEY_RIGHT:
|
||||||
|
was_expanded = navigator.selected_entry.is_expanded
|
||||||
|
|
||||||
|
if not navigator.expand_selected_directory():
|
||||||
|
navigator.enter_selected_directory()
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
return None
|
||||||
|
|
||||||
|
if was_expanded:
|
||||||
|
navigator.enter_selected_directory()
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key == curses.KEY_LEFT:
|
||||||
|
if not navigator.collapse_selected_directory():
|
||||||
|
navigator.go_to_parent_directory()
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key == ord("b"):
|
||||||
|
navigator.go_back()
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key == ord("f"):
|
||||||
|
navigator.go_forward()
|
||||||
|
self._tree_scroll_offset = 0
|
||||||
|
return None
|
||||||
|
|
||||||
|
if key in (curses.KEY_ENTER, 10, 13):
|
||||||
|
return navigator.selected_entry.path
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
def _get_current_history_index(self, navigator: Navigator) -> int:
|
def _get_current_history_index(self, navigator: Navigator) -> int:
|
||||||
history_entries = navigator.history.entries()
|
history_entries = navigator.history.entries()
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ from app.ui import (
|
|||||||
TerminalUI,
|
TerminalUI,
|
||||||
build_history_lines,
|
build_history_lines,
|
||||||
build_render_lines,
|
build_render_lines,
|
||||||
|
clamp_scroll_offset,
|
||||||
format_entry,
|
format_entry,
|
||||||
move_selection,
|
move_selection,
|
||||||
open_terminal_streams,
|
open_terminal_streams,
|
||||||
@@ -79,6 +80,18 @@ def test_build_history_lines_marks_selected_history_entry(tmp_path: Path) -> Non
|
|||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_clamp_scroll_offset_keeps_selection_visible_when_moving_down() -> None:
|
||||||
|
assert clamp_scroll_offset(5, scroll_offset=0, visible_count=3, total_count=10) == 3
|
||||||
|
|
||||||
|
|
||||||
|
def test_clamp_scroll_offset_keeps_selection_visible_when_moving_up() -> None:
|
||||||
|
assert clamp_scroll_offset(1, scroll_offset=4, visible_count=3, total_count=10) == 1
|
||||||
|
|
||||||
|
|
||||||
|
def test_clamp_scroll_offset_returns_zero_when_all_items_fit() -> None:
|
||||||
|
assert clamp_scroll_offset(2, scroll_offset=1, visible_count=5, total_count=3) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_toggle_history_mode_selects_current_history_entry(tmp_path: Path) -> None:
|
def test_toggle_history_mode_selects_current_history_entry(tmp_path: Path) -> None:
|
||||||
root = tmp_path / "root"
|
root = tmp_path / "root"
|
||||||
child = root / "child"
|
child = root / "child"
|
||||||
@@ -134,6 +147,80 @@ def test_history_mode_toggle_off_keeps_current_directory(tmp_path: Path) -> None
|
|||||||
assert navigator.current_path == root.resolve()
|
assert navigator.current_path == root.resolve()
|
||||||
|
|
||||||
|
|
||||||
|
def test_right_arrow_expands_collapsed_directory(tmp_path: Path) -> None:
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
scripts = workspace / "scripts"
|
||||||
|
build = scripts / "build"
|
||||||
|
workspace.mkdir()
|
||||||
|
scripts.mkdir()
|
||||||
|
build.mkdir()
|
||||||
|
|
||||||
|
navigator = Navigator(workspace)
|
||||||
|
navigator.set_selected_index(1)
|
||||||
|
ui = TerminalUI()
|
||||||
|
|
||||||
|
assert ui._handle_tree_key(curses.KEY_RIGHT, navigator) is None
|
||||||
|
assert navigator.current_path == workspace.resolve()
|
||||||
|
assert [entry.name for entry in navigator.visible_entries] == [
|
||||||
|
"..",
|
||||||
|
"scripts",
|
||||||
|
"build",
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def test_right_arrow_enters_already_expanded_directory(tmp_path: Path) -> None:
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
scripts = workspace / "scripts"
|
||||||
|
build = scripts / "build"
|
||||||
|
workspace.mkdir()
|
||||||
|
scripts.mkdir()
|
||||||
|
build.mkdir()
|
||||||
|
|
||||||
|
navigator = Navigator(workspace)
|
||||||
|
navigator.set_selected_index(1)
|
||||||
|
navigator.expand_selected_directory()
|
||||||
|
ui = TerminalUI()
|
||||||
|
|
||||||
|
assert ui._handle_tree_key(curses.KEY_RIGHT, navigator) is None
|
||||||
|
assert navigator.current_path == scripts.resolve()
|
||||||
|
assert navigator.selected_index == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_left_arrow_goes_to_parent_when_selected_entry_is_collapsed(
|
||||||
|
tmp_path: Path,
|
||||||
|
) -> None:
|
||||||
|
parent = tmp_path / "parent"
|
||||||
|
current = parent / "current"
|
||||||
|
child = current / "child"
|
||||||
|
parent.mkdir()
|
||||||
|
current.mkdir()
|
||||||
|
child.mkdir()
|
||||||
|
|
||||||
|
navigator = Navigator(current)
|
||||||
|
navigator.set_selected_index(1)
|
||||||
|
ui = TerminalUI()
|
||||||
|
|
||||||
|
assert ui._handle_tree_key(curses.KEY_LEFT, navigator) is None
|
||||||
|
assert navigator.current_path == parent.resolve()
|
||||||
|
assert navigator.selected_index == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_enter_returns_selected_path_without_navigating(tmp_path: Path) -> None:
|
||||||
|
workspace = tmp_path / "workspace"
|
||||||
|
child = workspace / "child"
|
||||||
|
workspace.mkdir()
|
||||||
|
child.mkdir()
|
||||||
|
|
||||||
|
navigator = Navigator(workspace)
|
||||||
|
navigator.set_selected_index(1)
|
||||||
|
ui = TerminalUI()
|
||||||
|
|
||||||
|
result = ui._handle_tree_key(10, navigator)
|
||||||
|
|
||||||
|
assert result == child.resolve()
|
||||||
|
assert navigator.current_path == workspace.resolve()
|
||||||
|
|
||||||
|
|
||||||
def test_open_terminal_streams_raises_without_terminal(
|
def test_open_terminal_streams_raises_without_terminal(
|
||||||
monkeypatch: pytest.MonkeyPatch,
|
monkeypatch: pytest.MonkeyPatch,
|
||||||
) -> None:
|
) -> None:
|
||||||
|
|||||||
Reference in New Issue
Block a user