diff --git a/clients/web/src/components/groups/NetworkStreamPanel/NetworkStreamPanel.tsx b/clients/web/src/components/groups/NetworkStreamPanel/NetworkStreamPanel.tsx index 6bd6b44c..c809c553 100644 --- a/clients/web/src/components/groups/NetworkStreamPanel/NetworkStreamPanel.tsx +++ b/clients/web/src/components/groups/NetworkStreamPanel/NetworkStreamPanel.tsx @@ -34,10 +34,17 @@ const PanelContainer = Paper.withProps({ variant: "panel", }); +// Centered in the full-height panel so the empty message sits mid-panel rather +// than clinging to the top of an otherwise-empty box (matches LogStreamPanel). +const EmptyCenter = Stack.withProps({ + flex: 1, + align: "center", + justify: "center", +}); + const EmptyState = Text.withProps({ c: "dimmed", ta: "center", - py: "xl", }); function formatTitle(count: number): string { @@ -133,7 +140,9 @@ export function NetworkStreamPanel({ {!hasResults ? ( - No network requests + + No network requests + ) : ( void; } -const LIST_MAX_HEIGHT = - "calc(100vh - var(--app-shell-header-height, 0px) - var(--mantine-spacing-xl) * 2 - 160px)"; - export function PromptControls({ prompts, selectedName, @@ -39,7 +36,10 @@ export function PromptControls({ ); return ( - + // Fill the full-height `sidebar` Card (a flex column) so the list runs to the + // bottom of the card before it scrolls, instead of being capped short by a + // fixed max-height. `mih: 0` lets the scroll child shrink and scroll. + Prompts @@ -53,7 +53,7 @@ export function PromptControls({ searchText ? onSearchChange("")} /> : null } /> - + {filteredPrompts.map((prompt) => ( ))} - + ); } diff --git a/clients/web/src/components/groups/ProtocolListPanel/ProtocolListPanel.tsx b/clients/web/src/components/groups/ProtocolListPanel/ProtocolListPanel.tsx index 6315f40d..f7d53945 100644 --- a/clients/web/src/components/groups/ProtocolListPanel/ProtocolListPanel.tsx +++ b/clients/web/src/components/groups/ProtocolListPanel/ProtocolListPanel.tsx @@ -54,10 +54,17 @@ const PanelContainer = Paper.withProps({ variant: "panel", }); +// Centered in the full-height panel so the empty message sits mid-panel rather +// than clinging to the top of an otherwise-empty box (matches LogStreamPanel). +const EmptyCenter = Stack.withProps({ + flex: 1, + align: "center", + justify: "center", +}); + const EmptyState = Text.withProps({ c: "dimmed", ta: "center", - py: "xl", }); // The section header is a single "pleat" bar (rounded, with the filter-button @@ -293,7 +300,9 @@ export function ProtocolListPanel({ {!hasResults ? ( - No request history + + No request history + ) : ( diff --git a/clients/web/src/components/groups/ToolControls/ToolControls.tsx b/clients/web/src/components/groups/ToolControls/ToolControls.tsx index 6eb6620a..54978535 100644 --- a/clients/web/src/components/groups/ToolControls/ToolControls.tsx +++ b/clients/web/src/components/groups/ToolControls/ToolControls.tsx @@ -17,9 +17,6 @@ export interface ToolControlsProps { onSelectTool: (name: string) => void; } -const LIST_MAX_HEIGHT = - "calc(100vh - var(--app-shell-header-height, 0px) - var(--mantine-spacing-xl) * 2 - 160px)"; - export function ToolControls({ tools, selectedName, @@ -40,7 +37,12 @@ export function ToolControls({ : tools; return ( - + // Fill the full-height `sidebar` Card (a flex column) so the scroll region + // below claims all the remaining space under the fixed title/search — the + // list runs to the bottom of the card before it scrolls, instead of being + // capped short by a fixed max-height. `mih: 0` lets the scroll child shrink + // and scroll rather than overflow the card. + Tools @@ -54,7 +56,7 @@ export function ToolControls({ searchText ? onSearchChange("")} /> : null } /> - + {filteredTools.map((tool) => ( ))} - + ); } diff --git a/clients/web/src/components/screens/ConsoleScreen/ConsoleScreen.tsx b/clients/web/src/components/screens/ConsoleScreen/ConsoleScreen.tsx index e69b78dd..79c003e2 100644 --- a/clients/web/src/components/screens/ConsoleScreen/ConsoleScreen.tsx +++ b/clients/web/src/components/screens/ConsoleScreen/ConsoleScreen.tsx @@ -124,7 +124,10 @@ export function ConsoleScreen({ }, [entries, filterText, sortDirection]); return ( - + // See LoggingScreen: only override `h` when embedded, so the standalone + // screen keeps ScreenLayout's default full-screen height (a `h={undefined}` + // would clobber it and collapse an empty screen to its controls' height). + {embedded ? null : ( diff --git a/clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx b/clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx index a9c716a8..755fce30 100644 --- a/clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx +++ b/clients/web/src/components/screens/LoggingScreen/LoggingScreen.tsx @@ -78,7 +78,11 @@ export function LoggingScreen({ } return ( - + // Embedded fills the monitoring sidebar column (100%); standalone keeps the + // ScreenLayout's default full-screen height. Passing `h={undefined}` here + // would clobber that default (withProps plain-spreads), collapsing an empty + // screen to its controls' height — so only override `h` when embedded. + {embedded ? null : ( diff --git a/clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx b/clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx index efde1ef1..430022e2 100644 --- a/clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx +++ b/clients/web/src/components/screens/NetworkScreen/NetworkScreen.tsx @@ -84,7 +84,10 @@ export function NetworkScreen({ } return ( - + // See LoggingScreen: only override `h` when embedded, so the standalone + // screen keeps ScreenLayout's default full-screen height (a `h={undefined}` + // would clobber it and collapse an empty screen to its controls' height). + {embedded ? null : ( diff --git a/clients/web/src/components/screens/PromptsScreen/PromptsScreen.tsx b/clients/web/src/components/screens/PromptsScreen/PromptsScreen.tsx index b138da7c..5225386a 100644 --- a/clients/web/src/components/screens/PromptsScreen/PromptsScreen.tsx +++ b/clients/web/src/components/screens/PromptsScreen/PromptsScreen.tsx @@ -69,9 +69,13 @@ const Sidebar = Stack.withProps({ flex: "0 0 auto", }); +// `sidebar` variant makes the card a full-height flex column capped at the +// screen height, so PromptControls' list fills the card and scrolls internally +// once it overflows (matching the Resources sidebar). const SidebarCard = Card.withProps({ withBorder: true, padding: "lg", + variant: "sidebar", }); const DetailCard = Card.withProps({ diff --git a/clients/web/src/components/screens/ProtocolScreen/ProtocolScreen.tsx b/clients/web/src/components/screens/ProtocolScreen/ProtocolScreen.tsx index de396de1..cac9f83d 100644 --- a/clients/web/src/components/screens/ProtocolScreen/ProtocolScreen.tsx +++ b/clients/web/src/components/screens/ProtocolScreen/ProtocolScreen.tsx @@ -104,7 +104,10 @@ export function ProtocolScreen({ }, [ui, visibleDirections, onUiChange]); return ( - + // See LoggingScreen: only override `h` when embedded, so the standalone + // screen keeps ScreenLayout's default full-screen height (a `h={undefined}` + // would clobber it and collapse an empty screen to its controls' height). + {embedded ? null : ( diff --git a/clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx b/clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx index e62129aa..587472e8 100644 --- a/clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx +++ b/clients/web/src/components/screens/ToolsScreen/ToolsScreen.tsx @@ -79,9 +79,13 @@ const Sidebar = Stack.withProps({ flex: "0 0 auto", }); +// `sidebar` variant makes the card a full-height flex column capped at the +// screen height, so ToolControls' list fills the card and scrolls internally +// once it overflows (matching the Resources sidebar). (#1417) const SidebarCard = Card.withProps({ withBorder: true, padding: "lg", + variant: "sidebar", }); // Column wrapper: stretches to the screen's available height (capped by the @@ -102,6 +106,15 @@ const ContentCard = Card.withProps({ variant: "preview", }); +// Full-height card for the empty placeholder (used with `flex={1}`) so it fills +// the screen height like the Prompts/Resources placeholders, rather than +// shrinking to its text. The result/detail states keep the content-sized +// `ContentCard` (their inner ScrollArea handles overflow). +const DetailCard = Card.withProps({ + withBorder: true, + padding: "lg", +}); + const EmptyState = Text.withProps({ c: "dimmed", ta: "center", @@ -160,21 +173,25 @@ export function ToolsScreen({ - - - {callState?.result ? ( - // Results replace the input form while present, and the panel's - // top-left X dismisses them back to the form (#1661) — the Prompts - // screen pattern. `formValues` live in the lifted UI state, so the - // form is restored intact for a re-run. A call in flight sets a - // `pending` state with no `result` (App.tsx), so the executing form - // (progress + cancel) shows until the result lands. + {callState?.result ? ( + // Results replace the input form while present, and the panel's top-left + // X dismisses them back to the form (#1661) — the Prompts screen pattern. + // `formValues` live in the lifted UI state, so the form is restored + // intact for a re-run. A call in flight sets a `pending` state with no + // `result` (App.tsx), so the executing form (progress + cancel) shows + // until the result lands. + + onClearResult?.()} onReadResource={onReadResource} /> - ) : selectedTool ? ( + + + ) : selectedTool ? ( + + onCancelCall?.()} /> - ) : ( - Select a tool to view details - )} - - + + + ) : ( + // Empty placeholder fills the full screen height (like Prompts/Resources) + // rather than shrinking to its text. + + Select a tool to view details + + )} ); }