From d2b16380d150a94e029a70b5bcadf47c2d2abbdf Mon Sep 17 00:00:00 2001 From: creatixchu Date: Wed, 29 Jul 2026 00:04:06 +0800 Subject: [PATCH] fix(host): keep path entry available when the home listing fails With no listed level (an unreadable or missing home directory), the path-edit zone previously disabled forever, stranding the operator on the alert with only Cancel; it now opens with an empty draft so an absolute path remains the way forward. Covered by a recovery test. --- .../src/client/DirectoryBrowser.tsx | 8 +++++--- .../tests/directory-browser.spec.tsx | 15 +++++++++++++++ 2 files changed, 20 insertions(+), 3 deletions(-) diff --git a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx index 19e7a6f799..5318ad0ea0 100644 --- a/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx +++ b/packages/host/directory-picker-browse/src/client/DirectoryBrowser.tsx @@ -282,9 +282,11 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen, type="button" className={css.crumbEditZone} aria-label={t('browser.editPath')} - disabled={parent === null || parentInert} - /* v8 ignore next -- narrowing guard: the zone disables while the level is null. */ - onClick={() => { if (parent !== null) setPathDraft(selected?.path ?? parent.path) }} + // Stays available with no listed level: when the home + // listing itself fails, typing an absolute path is the one + // remaining way forward. + disabled={parentInert} + onClick={() => { setPathDraft(selected?.path ?? parent?.path ?? '') }} /> ) diff --git a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx index 4022d12754..daf795ddbf 100644 --- a/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx +++ b/packages/host/directory-picker-browse/tests/directory-browser.spec.tsx @@ -269,6 +269,21 @@ describe('DirectoryBrowser', () => { expect(screen.getByRole('button', { name: 'browser.newFolder' }).disabled).toBe(false) }) + it('keeps path entry available when the home listing fails', async () => { + const listDirectory = vi.fn(async (): Promise => { + throw new DirectoryBrowseError({ code: 'directory-unreadable', message: 'home unreadable', details: { path: HOME } }) + }) + mount({ listDirectory }) + await waitFor(() => { expect(screen.getByRole('alert').textContent).toBe('home unreadable') }) + // With no listed level, typing an absolute path is the one way forward. + fireEvent.click(screen.getByRole('button', { name: 'browser.editPath' })) + const input = screen.getByLabelText('browser.editPath') + fireEvent.change(input, { target: { value: DOCS } }) + listDirectory.mockImplementation(async (path?: string) => listingFor(path)) + fireEvent.keyDown(input, { key: 'Enter' }) + await waitFor(() => { expect(screen.getByText('harness')).toBeTruthy() }) + }) + it('ignores dismissal while adoption is busy', async () => { const b = mount({ busy: true }) await waitFor(() => { expect(screen.getByRole('dialog')).toBeTruthy() })