feat(web): open the monitoring column on connect (#1616)

Auto-open the monitoring column when a connection is established. Gated on the
disconnected → connected transition (a ref-tracked edge), so it fires on an
actual connect rather than on every render while connected or on a mount that
starts already-connected (which would fight a user who closed it). The column
still only appears when wide and a monitor tab is available.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-07-08 22:26:06 -04:00
commit 20e08e2b1d
2 changed files with 58 additions and 1 deletions

View file

@ -1343,6 +1343,39 @@ describe("InspectorView", () => {
).toBeInTheDocument();
});
it("opens the monitoring column when a connection is established", async () => {
monitorWide.value = true;
const { rerender } = renderWithMantine(
<StatefulInspectorViewHost
{...makeProps({
servers: [httpServer],
activeServer: undefined,
connectionStatus: "disconnected",
})}
/>,
);
// Disconnected: the column is closed.
expect(
screen.queryByRole("button", { name: "Close monitoring column" }),
).toBeNull();
// Connecting opens it (the disconnected → connected transition).
rerender(<StatefulInspectorViewHost {...connectedHttp()} />);
expect(
await screen.findByRole("button", { name: "Close monitoring column" }),
).toBeInTheDocument();
});
it("does not auto-open on a mount that starts already connected", () => {
// No disconnected → connected transition, and no stored preference, so the
// column stays closed (a user who closed it isn't fought on remount).
monitorWide.value = true;
renderWithMantine(<StatefulInspectorViewHost {...connectedHttp()} />);
expect(
screen.queryByRole("button", { name: "Close monitoring column" }),
).toBeNull();
});
it("pins the monitor group into the column and removes it from the header", async () => {
monitorWide.value = true;
renderWithMantine(<StatefulInspectorViewHost {...connectedHttp()} />);

View file

@ -1,4 +1,11 @@
import { useMemo, useState, type ReactNode, type Ref } from "react";
import {
useEffect,
useMemo,
useRef,
useState,
type ReactNode,
type Ref,
} from "react";
import {
AppShell,
Box,
@ -647,6 +654,23 @@ export function InspectorView({
getInitialValueInEffect: false,
});
// Open the monitoring column when a connection is established (#1616). Gated on
// the disconnected → connected *transition* (via the ref) rather than the
// "connected" state itself, so it fires on an actual connect — not on every
// render while connected, and not on a mount that starts already-connected
// (which would fight a user who closed it). The column still only *appears*
// when wide + a monitor tab is available (`effectivePinned`); this just sets
// the preference. Closing it stays closed until the next connect, since this
// effect only re-runs when `connectionStatus` changes.
const wasConnectedRef = useRef(connectionStatus === "connected");
useEffect(() => {
const isConnected = connectionStatus === "connected";
if (isConnected && !wasConnectedRef.current) {
setMonitorPinned(true);
}
wasConnectedRef.current = isConnected;
}, [connectionStatus, setMonitorPinned]);
const appTools = useMemo<Tool[]>(() => {
return tools.filter((tool) => {
try {