fix(directory-picker-browse): keep the typed level in the last pane

Skipping the draft-following scan whenever ANY pane happened to list the
directory was the cheaper rule and the wrong one: erasing a segment left the
level being typed on the LEFT, with its own child pane still standing to its
right, so the two panes stopped reading as "where I am, and where I came
from".

The pane arity is now the invariant the editor maintains — the last pane lists
the level the path names, its parent sits beside it, and only a display root
lists alone. Only that last pane's own tail costs no scan; every other
directory part re-lands.
This commit is contained in:
creatixchu
2026-08-03 13:27:45 +08:00
parent 233ea50104
commit 2ceed380dd
9 changed files with 59 additions and 30 deletions

View File

@@ -707,11 +707,13 @@ describe('DirectoryBrowser', () => {
fireEvent.change(input, { target: { value: `${DOCS}/zzz` } })
expect(within(columns()[1]!).getByText('harness')).toBeTruthy()
expect(within(columns()[0]!).getByText('Documents')).toBeTruthy()
// Erasing back into the parent's own path moves the filter to the LEFT
// pane and releases the right one — no scan, both levels are on screen.
// Erasing back into the parent's own path re-lands on it rather than
// filtering the LEFT pane: the level being typed is always the last pane,
// never a pane with a deeper level standing to its right. Home is the
// display root, so it lands alone.
fireEvent.change(input, { target: { value: `${HOME}/zz` } })
expect(within(columns()[0]!).getAllByRole('listitem').map(item => item.textContent)).toEqual(['Documents'])
expect(within(columns()[1]!).getByText('harness')).toBeTruthy()
await waitFor(() => { expect(columns()).toHaveLength(1) })
expect(screen.getAllByRole('listitem').map(item => item.textContent)).toEqual(['Documents'])
})
it('follows the draft into a directory no pane lists, landing the two-pane Miller view', async () => {
@@ -740,6 +742,26 @@ describe('DirectoryBrowser', () => {
expect(columns()).toHaveLength(2)
})
it('keeps the typed level in the last pane, its parent beside it, as the draft walks', async () => {
const b = mount()
await waitFor(() => { expect(screen.getByRole('listitem')).toBeTruthy() })
fireEvent.click(screen.getByRole('button', { name: 'browser.editPath' }))
const input = screen.getByLabelText<HTMLInputElement>('browser.editPath')
// Two levels down: the typed level on the right, its parent on the left.
fireEvent.change(input, { target: { value: `${HARNESS}/` } })
await waitFor(() => { expect(within(columns()[0]!).getByText('harness')).toBeTruthy() })
expect(columns()).toHaveLength(2)
expect(within(columns()[1]!).queryAllByRole('listitem')).toHaveLength(0)
// Erasing back to the parent's own path re-lands on it: the level being
// typed moves BACK into the last pane instead of staying on the left with
// its own child pane still to the right.
fireEvent.change(input, { target: { value: `${DOCS}/ha` } })
await waitFor(() => { expect(within(columns()[0]!).getByText('Documents')).toBeTruthy() })
expect(columns()).toHaveLength(2)
expect(within(columns()[1]!).getAllByRole('listitem').map(item => item.textContent)).toEqual(['harness'])
expect(b.listDirectory).toHaveBeenCalledWith(`${DOCS}/`, expect.anything())
})
it('walks the panes back up when erased segments leave the listed levels', async () => {
const b = mount()
await waitFor(() => { expect(screen.getByRole('listitem')).toBeTruthy() })