mirror of
https://github.com/deepseek-ai/deepseek-harness
synced 2026-08-15 21:04:50 +00:00
fix(host): restart home on early-edit cancel; clamp the dialog to short viewports
Escape canceling a path edit opened before any level listed relaunches the home listing instead of stranding a blank picker (the editor had superseded the initial request while parent was still null). The card's height clamps to the viewport (min(420px, 100dvh - 32px)); header and footer are flex-none and the columns scroll, so Open/Cancel stay reachable on landscape phones and short embedded windows.
This commit is contained in:
@@ -320,13 +320,13 @@ describe('createFixtureApi', () => {
|
||||
const created = await api.host.createDirectory(req({ path: '/', name: 'srv' }))
|
||||
if (!created.result.ok) throw new Error('create failed')
|
||||
expect(created.result.value.path).toBe('/srv')
|
||||
const listed = await api.host.listDirectory(req({ path: '/srv' }))
|
||||
const listed = await api.host.listDirectory(req({ path: '/srv' }), new AbortController().signal)
|
||||
if (!listed.result.ok) throw new Error('list failed')
|
||||
expect(listed.result.value.crumbs).toEqual([
|
||||
{ name: '/', path: '/', hidden: false },
|
||||
{ name: 'srv', path: '/srv', hidden: false },
|
||||
])
|
||||
const root = await api.host.listDirectory(req({ path: '/' }))
|
||||
const root = await api.host.listDirectory(req({ path: '/' }), new AbortController().signal)
|
||||
if (!root.result.ok) throw new Error('root list failed')
|
||||
expect(root.result.value.entries).toContainEqual({ name: 'srv', path: '/srv', hidden: false })
|
||||
})
|
||||
|
||||
@@ -1,12 +1,15 @@
|
||||
/* Directory-browser dialog (figma 813-23126 family). The shared Modal renders
|
||||
* headless here — mask, card, Escape only — and this module owns the figma
|
||||
* frame exactly: fixed 600×420 card, header (title + crumbs, l3 separator),
|
||||
* frame: 600×420 card (viewport-clamped), header (title + crumbs, l3 separator),
|
||||
* the one-or-two-column Miller content, and the bordered footer. */
|
||||
|
||||
/* Doubled class beats Modal's own .dialog regardless of stylesheet order. */
|
||||
/* Short viewports clamp the card: header/footer are flex-none and the
|
||||
* columns scroll, so shrinking the height keeps Open/Cancel reachable
|
||||
* instead of clipping them below a fixed overlay. */
|
||||
.dialog.dialog {
|
||||
width: min(600px, 100%);
|
||||
height: 420px;
|
||||
height: min(420px, calc(100dvh - 32px));
|
||||
padding: 0;
|
||||
gap: 0;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
/**
|
||||
* The in-app workspace-directory browser (figma Harness 813-23126 family): a
|
||||
* fixed 600×420 dialog whose header carries the title, the selection-path
|
||||
* 600×420 dialog (clamped to short/narrow viewports — the Miller row scrolls
|
||||
* sideways, the columns scroll down) whose header carries the title, the selection-path
|
||||
* breadcrumb, and a click-to-edit path zone; below it a Miller view — one
|
||||
* full-width level until a row is selected, then two 256px columns (level |
|
||||
* selected folder's children) around a hairline divider. Selecting in the
|
||||
@@ -364,6 +365,10 @@ export function DirectoryBrowser({ open, listDirectory, createDirectory, onOpen,
|
||||
// half-empty two-pane view, so cancel falls back to the
|
||||
// single-pane level.
|
||||
if (child === null) setSelected(null)
|
||||
// With no level listed yet (the editor superseded the
|
||||
// initial home listing), plain cancellation would leave a
|
||||
// permanently blank picker: restart the home listing.
|
||||
if (parent === null) navigate()
|
||||
}
|
||||
}}
|
||||
/>
|
||||
|
||||
@@ -177,6 +177,27 @@ describe('DirectoryBrowser', () => {
|
||||
expect(screen.queryByLabelText('browser.editPath', { selector: 'input' })).toBeNull()
|
||||
})
|
||||
|
||||
it('restarts the home listing when Escape cancels an edit opened before any level listed', async () => {
|
||||
// The initial home listing hangs; Edit Path supersedes it while parent
|
||||
// is still null, and Escape must not strand a blank picker.
|
||||
let settled = false
|
||||
const gate = new Promise<never>(() => {})
|
||||
const listDirectory = vi.fn(async (path?: string) => {
|
||||
if (!settled) { settled = true; return gate }
|
||||
return listingFor(path)
|
||||
})
|
||||
mount({ listDirectory })
|
||||
fireEvent.click(screen.getByRole('button', { name: 'browser.editPath' }))
|
||||
const input = screen.getByLabelText<HTMLInputElement>('browser.editPath')
|
||||
expect(input.value).toBe('')
|
||||
fireEvent.keyDown(input, { key: 'Escape' })
|
||||
// Cancellation relaunched the home listing instead of leaving neither
|
||||
// rows nor status behind.
|
||||
await waitFor(() => { expect(screen.getByRole('listitem').textContent).toBe('Documents') })
|
||||
expect(listDirectory).toHaveBeenCalledTimes(2)
|
||||
expect(listDirectory).toHaveBeenLastCalledWith(undefined)
|
||||
})
|
||||
|
||||
it('surfaces an unreadable target as an alert and keeps the edit open for correction', async () => {
|
||||
mount()
|
||||
await waitFor(() => { expect(screen.getByRole('listitem')).toBeTruthy() })
|
||||
|
||||
Reference in New Issue
Block a user