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() {