Normalize screen layouts: extract control groups, unify sidebar widths, and refactor PromptsScreen

- Extract ToolControls group from ToolsScreen (search, list, list-changed indicator)
- Extract PromptControls and PromptListItem groups for PromptsScreen sidebar
- Refactor PromptsScreen to sidebar + detail layout (matching ResourcesScreen pattern)
- Refactor PromptArgumentsForm to accept a single prompt instead of a dropdown
- Normalize all screen sidebar widths to 340px (Tools, Resources, Prompts, Logs, Tasks, History)
- Convert ToolsScreen from Grid-only to sidebar + Grid layout
- Replace LogControls checkboxes with full-width toggle buttons
- Move Clear/Export buttons from LogControls to LogStreamPanel toolbar
- Make PromptArgumentsForm "Get Prompt" button right-aligned and compact
- Update all affected stories and ConnectedView

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-03-31 17:54:20 -04:00
commit a6724626b7
2 changed files with 627 additions and 233 deletions

View file

@ -0,0 +1,76 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { PromptControls } from "./PromptControls";
const meta: Meta<typeof PromptControls> = {
title: "Groups/PromptControls",
component: PromptControls,
args: {
searchText: "",
onSearchChange: fn(),
onRefreshList: fn(),
onSelectPrompt: fn(),
listChanged: false,
},
};
export default meta;
type Story = StoryObj<typeof PromptControls>;
const samplePrompts = [
{
name: "summarize",
description: "Summarize the given text into key points",
selected: false,
},
{
name: "translate",
description: "Translate text from one language to another",
selected: false,
},
{
name: "analyze",
description: "Analyze sentiment and tone of the text",
selected: false,
},
{
name: "code-review",
description: "Review code for issues",
selected: false,
},
{ name: "refactor", selected: false },
];
export const Default: Story = {
args: {
prompts: samplePrompts,
},
};
export const WithSelection: Story = {
args: {
prompts: samplePrompts.map((p) =>
p.name === "translate" ? { ...p, selected: true } : p,
),
},
};
export const WithSearch: Story = {
args: {
prompts: samplePrompts,
searchText: "sum",
},
};
export const ListChanged: Story = {
args: {
prompts: samplePrompts,
listChanged: true,
},
};
export const Empty: Story = {
args: {
prompts: [],
},
};

View file

@ -0,0 +1,55 @@
import { ScrollArea, Stack, TextInput, Title } from "@mantine/core";
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
import { PromptListItem } from "../PromptListItem/PromptListItem";
import type { PromptItem } from "../../screens/PromptsScreen/PromptsScreen";
export interface PromptControlsProps {
prompts: PromptItem[];
listChanged: boolean;
searchText: string;
onSearchChange: (text: string) => void;
onRefreshList: () => void;
onSelectPrompt: (name: string) => void;
}
function listMaxHeight(): string {
return "calc(100vh - var(--app-shell-header-height, 0px) - var(--mantine-spacing-xl) * 2 - 160px)";
}
export function PromptControls({
prompts,
listChanged,
searchText,
onSearchChange,
onRefreshList,
onSelectPrompt,
}: PromptControlsProps) {
const filteredPrompts = prompts.filter((p) =>
p.name.toLowerCase().includes(searchText.toLowerCase()),
);
return (
<Stack gap="sm">
<Title order={4}>Prompts</Title>
<ListChangedIndicator visible={listChanged} onRefresh={onRefreshList} />
<TextInput
placeholder="Search prompts..."
value={searchText}
onChange={(e) => onSearchChange(e.currentTarget.value)}
/>
<ScrollArea.Autosize mah={listMaxHeight()}>
<Stack gap="xs">
{filteredPrompts.map((prompt) => (
<PromptListItem
key={prompt.name}
name={prompt.name}
description={prompt.description}
selected={prompt.selected}
onClick={() => onSelectPrompt(prompt.name)}
/>
))}
</Stack>
</ScrollArea.Autosize>
</Stack>
);
}