diff --git a/desktop/src/components/chat/ChatInput.test.tsx b/desktop/src/components/chat/ChatInput.test.tsx index be175876..6e65185a 100644 --- a/desktop/src/components/chat/ChatInput.test.tsx +++ b/desktop/src/components/chat/ChatInput.test.tsx @@ -748,6 +748,42 @@ describe('ChatInput file mentions', () => { }) }) + it('keeps slash and @ popovers outside the drop target clipping context', async () => { + mocks.search.mockResolvedValueOnce({ + currentPath: '/repo', + parentPath: null, + query: '', + entries: [ + { name: 'README.md', path: '/repo/README.md', isDirectory: false }, + ], + }) + + render() + + const panel = screen.getByTestId('chat-input-panel') + const input = screen.getByRole('textbox') as HTMLTextAreaElement + + fireEvent.change(input, { + target: { + value: '/', + selectionStart: 1, + }, + }) + expect(await screen.findByText('/mcp')).toBeInTheDocument() + expect(panel).toHaveClass('overflow-visible') + expect(panel).not.toHaveClass('overflow-hidden') + + fireEvent.change(input, { + target: { + value: '@readme', + selectionStart: 7, + }, + }) + expect(await screen.findByText('README.md')).toBeInTheDocument() + expect(panel).toHaveClass('overflow-visible') + expect(panel).not.toHaveClass('overflow-hidden') + }) + it('uses larger icon-only mobile action buttons for browser H5 access', async () => { viewportMocks.isMobile = true mocks.search.mockResolvedValueOnce({ diff --git a/desktop/src/components/chat/ChatInput.tsx b/desktop/src/components/chat/ChatInput.tsx index de5d1bfe..e449c83f 100644 --- a/desktop/src/components/chat/ChatInput.tsx +++ b/desktop/src/components/chat/ChatInput.tsx @@ -809,10 +809,10 @@ export function ChatInput({ variant = 'default', compact = false }: ChatInputPro ref={panelRef} data-testid="chat-input-panel" className={isHeroComposer - ? `glass-panel relative flex flex-col gap-3 overflow-hidden ${embedLaunchControlsInHero ? 'rounded-xl' : 'rounded-t-xl rounded-b-none'} p-4 transition-colors ${isDragActive ? 'composer-drop-target-active' : ''}` + ? `glass-panel relative flex flex-col gap-3 overflow-visible ${embedLaunchControlsInHero ? 'rounded-xl' : 'rounded-t-xl rounded-b-none'} p-4 transition-colors ${isDragActive ? 'composer-drop-target-active' : ''}` : compact - ? `glass-panel relative overflow-hidden p-3 transition-colors ${isMobileComposer ? 'rounded-2xl shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl'} ${isDragActive ? 'composer-drop-target-active' : ''}` - : `glass-panel relative overflow-hidden transition-colors ${isMobileComposer ? 'rounded-2xl p-3 shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl p-4'} ${isDragActive ? 'composer-drop-target-active' : ''}`} + ? `glass-panel relative overflow-visible p-3 transition-colors ${isMobileComposer ? 'rounded-2xl shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl'} ${isDragActive ? 'composer-drop-target-active' : ''}` + : `glass-panel relative overflow-visible transition-colors ${isMobileComposer ? 'rounded-2xl p-3 shadow-[0_-12px_36px_rgba(54,35,28,0.12)]' : 'rounded-xl p-4'} ${isDragActive ? 'composer-drop-target-active' : ''}`} {...dragHandlers} > {isDragActive && ( diff --git a/desktop/src/components/workspace/WorkspacePanel.test.tsx b/desktop/src/components/workspace/WorkspacePanel.test.tsx index 334aaa2e..d3a652ec 100644 --- a/desktop/src/components/workspace/WorkspacePanel.test.tsx +++ b/desktop/src/components/workspace/WorkspacePanel.test.tsx @@ -970,7 +970,7 @@ describe('WorkspacePanel', () => { expect(view.getByTestId('workspace-code').textContent).toContain('const line2300 = 2300') }) expect(view.getByRole('button', { name: 'Collapse preview' })).toBeTruthy() - }) + }, 10_000) it('renders image previews from workspace files', async () => { await setWorkspaceState((state) => ({ diff --git a/desktop/src/pages/EmptySession.test.tsx b/desktop/src/pages/EmptySession.test.tsx index ed57a01e..67bb8904 100644 --- a/desktop/src/pages/EmptySession.test.tsx +++ b/desktop/src/pages/EmptySession.test.tsx @@ -9,6 +9,8 @@ const mocks = vi.hoisted(() => ({ getMessages: vi.fn(), getSlashCommands: vi.fn(), listSkills: vi.fn(), + search: vi.fn(), + browse: vi.fn(), getTasksForList: vi.fn(), resetTaskList: vi.fn(), wsClearHandlers: vi.fn(), @@ -39,6 +41,13 @@ vi.mock('../api/skills', () => ({ }, })) +vi.mock('../api/filesystem', () => ({ + filesystemApi: { + search: mocks.search, + browse: mocks.browse, + }, +})) + vi.mock('../api/cliTasks', () => ({ cliTasksApi: { getTasksForList: mocks.getTasksForList, @@ -191,6 +200,12 @@ describe('EmptySession', () => { mocks.getMessages.mockResolvedValue({ messages: [] }) mocks.getSlashCommands.mockResolvedValue({ commands: [] }) mocks.listSkills.mockResolvedValue({ skills: [] }) + mocks.search.mockResolvedValue({ + currentPath: '/workspace/project', + parentPath: null, + query: '', + entries: [], + }) mocks.getTasksForList.mockResolvedValue({ tasks: [] }) mocks.resetTaskList.mockResolvedValue(undefined) }) @@ -489,6 +504,42 @@ describe('EmptySession', () => { }) }) + it('keeps slash and @ popovers visible above the empty-session drop target', async () => { + mocks.search.mockResolvedValueOnce({ + currentPath: '/workspace/project', + parentPath: null, + query: '', + entries: [ + { name: 'README.md', path: '/workspace/project/README.md', isDirectory: false }, + ], + }) + + render() + + const panel = screen.getByTestId('empty-session-composer-panel') + const input = screen.getByRole('textbox') as HTMLTextAreaElement + + fireEvent.change(input, { + target: { + value: '/', + selectionStart: 1, + }, + }) + expect(await screen.findByText('/mcp')).toBeInTheDocument() + expect(panel).toHaveClass('overflow-visible') + expect(panel).not.toHaveClass('overflow-hidden') + + fireEvent.change(input, { + target: { + value: '@readme', + selectionStart: 7, + }, + }) + expect(await screen.findByText('README.md')).toBeInTheDocument() + expect(panel).toHaveClass('overflow-visible') + expect(panel).not.toHaveClass('overflow-hidden') + }) + it('starts in a selected non-Git project without showing a repository warning', async () => { mocks.getRepositoryContext.mockResolvedValueOnce(notGitRepositoryContext()) diff --git a/desktop/src/pages/EmptySession.tsx b/desktop/src/pages/EmptySession.tsx index fdf235d2..82469abb 100644 --- a/desktop/src/pages/EmptySession.tsx +++ b/desktop/src/pages/EmptySession.tsx @@ -566,7 +566,7 @@ export function EmptySession() {