fix(web-ui): menu placement and scrolling, tool-row and settings polish

Menus: keep 12px viewport clearance with internal scroll, pin workspace
create actions in a footer, and pre-render portal lists hidden so the
first painted frame is already at its final position (no open jump).
Tool rows: 14px icons, secondary titles, no hover fill, and a hover
chevron preview on in-place expandable rows. Settings: 800x600 layer-2
panel over a blurred mask, hover states, and wrapping selector cubes;
ModelSelect surface tokens now match the Menu primitive.
This commit is contained in:
Yif
2026-07-28 04:25:18 +08:00
parent fed1b23eac
commit 1f79b78045
29 changed files with 367 additions and 175 deletions

View File

@@ -263,25 +263,21 @@ describe('WorkspaceBrowser', () => {
}
})
it('rail create-workspace expands the shell and opens the picker; wide toggles in place', () => {
it('rail create-workspace toggles the create-only picker in place, without expanding', () => {
const expandSidebar = vi.fn()
const b = mount({ wide: false, expandSidebar, useWorkspaces: hook(workspaceState([workspace('alpha', [])])) })
mount({ wide: false, expandSidebar, useWorkspaces: hook(workspaceState([workspace('alpha', [])])) })
fireEvent.click(screen.getByRole('button', { name: 'Create workspace' }))
expect(expandSidebar).toHaveBeenCalledTimes(1)
rerender(b, { wide: true })
// The picker menu is open (anchored on the +); picking starts a session.
fireEvent.click(screen.getByRole('menuitem', { name: 'alpha' }))
expect(b.props.startSession).toHaveBeenCalledWith(wid('alpha'))
expect(screen.queryByRole('menu')).toBeNull()
// Wide toggle: open and close without expand requests.
fireEvent.click(screen.getByRole('button', { name: 'Create workspace' }))
expect(screen.getByRole('menu')).toBeTruthy()
expect(expandSidebar).not.toHaveBeenCalled()
// createOnly: existing workspaces are not listed, only the create actions.
expect(screen.queryByRole('menuitem', { name: 'alpha' })).toBeNull()
expect(screen.getByRole('menuitem', { name: 'Open local folder…' })).toBeTruthy()
// Toggle: open and close in place.
fireEvent.click(screen.getByRole('button', { name: 'Create workspace' }))
expect(screen.queryByRole('menu')).toBeNull()
expect(expandSidebar).toHaveBeenCalledTimes(1)
// Escape closes the picker through its own onClose.
fireEvent.click(screen.getByRole('button', { name: 'Create workspace' }))
expect(screen.getByRole('menu')).toBeTruthy()
fireEvent.keyDown(document, { key: 'Escape' })
expect(screen.queryByRole('menu')).toBeNull()
})

View File

@@ -205,6 +205,8 @@ describe('WorkspacePicker', () => {
it('reports non-Error creation failures', async () => {
const b = mount([], vi.fn(async () => { throw 'permission denied' }))
chooseItem('Create a new workspace')
// The name field starts empty (no prefill); a name is required to submit.
fireEvent.change(screen.getByLabelText('New workspace name'), { target: { value: 'broken' } })
fireEvent.click(screen.getByRole('button', { name: 'Create workspace' }))
await waitFor(() => {
expect(screen.getByRole('alert').textContent).toBe('Workspace creation failed: permission denied')