diff --git a/package-lock.json b/package-lock.json index 6d755e4..bb1da6d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "drover", - "version": "0.1.18", + "version": "0.1.19", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "drover", - "version": "0.1.18", + "version": "0.1.19", "license": "MIT", "dependencies": { "@radix-ui/react-alert-dialog": "1.1.23", @@ -23,7 +23,6 @@ "@xterm/addon-fit": "0.11.0", "@xterm/addon-search": "0.16.0", "@xterm/addon-web-links": "0.12.0", - "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", @@ -5359,12 +5358,6 @@ "integrity": "sha512-4Smom3RPyVp7ZMYOYDoC/9eGJJJqYhnPLGGqJ6wOBfB8VxPViJNSKdgRYb8NpaM6YSelEKbA2SStD7lGyqaobw==", "license": "MIT" }, - "node_modules/@xterm/addon-webgl": { - "version": "0.19.0", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0.tgz", - "integrity": "sha512-b3fMOsyLVuCeNJWxolACEUED0vm7qC0cy4wRvf3oURSzDTYVQiGPhTnhWZwIHdvC48Y+oLhvYXnY4XDXPoJo6A==", - "license": "MIT" - }, "node_modules/@xterm/xterm": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", diff --git a/package.json b/package.json index d0c37d6..f23b1cc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "drover", "productName": "Drover", - "version": "0.1.18", + "version": "0.1.19", "description": "A native workspace for Herdr-powered agents, terminals, worktrees, and live sessions.", "main": ".vite/build/main.js", "private": true, @@ -79,7 +79,6 @@ "@xterm/addon-fit": "0.11.0", "@xterm/addon-search": "0.16.0", "@xterm/addon-web-links": "0.12.0", - "@xterm/addon-webgl": "0.19.0", "@xterm/xterm": "6.0.0", "class-variance-authority": "0.7.1", "clsx": "2.1.1", diff --git a/src/renderer/App.test.tsx b/src/renderer/App.test.tsx index ad6287c..701b73a 100644 --- a/src/renderer/App.test.tsx +++ b/src/renderer/App.test.tsx @@ -1121,6 +1121,30 @@ describe('App', () => { expect(screen.getByTestId('terminal-w1:p1')).not.toBeVisible(); }); + it('constrains the persistent terminal while switching pane views', async () => { + const user = userEvent.setup(); + const terminalFirstSnapshot: SessionSnapshot = { + ...snapshot, + panes: snapshot.panes.map((pane) => + pane.pane_id === 'w1:p1' ? { ...pane, agent_has_arguments: true } : pane, + ), + }; + vi.mocked(window.herdr.bootstrap).mockResolvedValue({ + ...connected, + snapshot: terminalFirstSnapshot, + }); + render(); + + const terminal = await screen.findByTestId('terminal-w1:p1'); + const surface = terminal.parentElement; + expect(surface).toHaveClass('flex', 'min-h-0', 'min-w-0', 'flex-1', 'overflow-hidden'); + expect(surface).not.toHaveClass('hidden'); + + await user.click(screen.getByRole('button', { name: 'Chat view' })); + expect(terminal.parentElement).toBe(surface); + expect(surface).toHaveClass('hidden', 'min-h-0', 'min-w-0', 'flex-1', 'overflow-hidden'); + }); + it('opens complete pane controls and routes graphical actions to Herdr', async () => { const user = userEvent.setup(); render(); @@ -1740,6 +1764,32 @@ describe('App', () => { expect(screen.getByRole('heading', { name: 'drover' })).toBeInTheDocument(); }); + it('keeps the canonical session when a background bootstrap transiently fails', async () => { + let sessionEvent: + | ((event: { event: string; data: Record }) => void) + | undefined; + window.herdr.onSessionEvent = vi.fn((listener) => { + sessionEvent = listener; + return () => undefined; + }); + vi.mocked(window.herdr.bootstrap).mockResolvedValueOnce(connected).mockResolvedValue({ + state: 'error', + message: 'Herdr could not be reached.', + details: 'transient socket error', + }); + render(); + await screen.findByRole('heading', { name: 'drover' }); + + act(() => sessionEvent?.({ event: 'layout.updated', data: {} })); + await waitFor(() => expect(window.herdr.bootstrap).toHaveBeenCalledTimes(2), { + timeout: 2_000, + }); + + expect(screen.getByRole('heading', { name: 'drover' })).toBeInTheDocument(); + expect(screen.queryByText('Herdr could not be reached')).not.toBeInTheDocument(); + expect(screen.getByText('Engine disconnected')).toBeInTheDocument(); + }); + it('coalesces background refreshes while session events stream', async () => { let sessionEvent: | ((event: { event: string; data: Record }) => void) diff --git a/src/renderer/App.tsx b/src/renderer/App.tsx index 30e881d..eef5e9d 100644 --- a/src/renderer/App.tsx +++ b/src/renderer/App.tsx @@ -752,7 +752,11 @@ function PersistentTerminalSurface({ pane, visible }: { pane: PaneInfo; visible: return null; } return ( -