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
+
+ )}
);
}