Refactor renamed the component groups to something less obtuse:

Elements -> Groups -> Screens -> Views
This commit is contained in:
cliffhall 2026-03-28 12:58:47 -04:00
commit dd46acba87
2 changed files with 151 additions and 144 deletions

View file

@ -0,0 +1,99 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { ToolDetailPanel } from "./ToolDetailPanel";
const meta: Meta<typeof ToolDetailPanel> = {
title: "Groups/ToolDetailPanel",
component: ToolDetailPanel,
args: {
onFormChange: fn(),
onExecute: fn(),
onCancel: fn(),
formValues: {},
isExecuting: false,
},
};
export default meta;
type Story = StoryObj<typeof ToolDetailPanel>;
export const SimpleStringParam: Story = {
args: {
name: "send_message",
schema: {
type: "object",
properties: {
message: { type: "string", description: "The message to send" },
},
required: ["message"],
},
},
};
export const MultipleParams: Story = {
args: {
name: "create_record",
description: "Creates a new record with the given parameters",
schema: {
type: "object",
properties: {
title: { type: "string", description: "Record title" },
count: { type: "number", description: "Number of items" },
enabled: {
type: "boolean",
description: "Whether the record is active",
},
},
required: ["title"],
},
},
};
export const WithAnnotations: Story = {
args: {
name: "delete_records",
description: "Deletes records matching the given criteria",
annotations: {
audience: "admin",
readOnly: true,
destructive: true,
},
schema: {
type: "object",
properties: {
pattern: { type: "string", description: "Pattern to match records" },
},
},
},
};
export const Executing: Story = {
args: {
name: "long_query",
description: "Runs a long database query",
isExecuting: true,
schema: {
type: "object",
properties: {
query: { type: "string", description: "SQL query to execute" },
},
},
formValues: { query: "SELECT * FROM users" },
},
};
export const WithProgress: Story = {
args: {
name: "batch_process",
description: "Processes items in batch",
isExecuting: true,
progress: { percent: 60, description: "Processing step 3 of 5" },
schema: {
type: "object",
properties: {
batchSize: { type: "number", description: "Number of items per batch" },
},
},
formValues: { batchSize: 100 },
},
};

View file

@ -0,0 +1,131 @@
import { Button, Divider, Group, Stack, Text } from "@mantine/core";
import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge";
import { ProgressDisplay } from "../../elements/ProgressDisplay/ProgressDisplay";
import { SchemaForm } from "../SchemaForm/SchemaForm";
import type { JsonSchema } from "../SchemaForm/SchemaForm";
export interface ToolAnnotations {
audience?: string;
readOnly?: boolean;
destructive?: boolean;
longRunning?: boolean;
hints?: string;
}
export interface ToolDetailPanelProps {
name: string;
title?: string;
description?: string;
annotations?: ToolAnnotations;
schema: JsonSchema;
formValues: Record<string, unknown>;
isExecuting: boolean;
progress?: { percent: number; description?: string };
onFormChange: (values: Record<string, unknown>) => void;
onExecute: () => void;
onCancel: () => void;
}
const ToolTitle = Text.withProps({
fw: 700,
size: "lg",
truncate: "end",
});
const HintsText = Text.withProps({
size: "xs",
c: "dimmed",
fs: "italic",
});
const DescriptionText = Text.withProps({
size: "sm",
c: "dimmed",
});
const CancelButton = Button.withProps({
variant: "light",
color: "red",
});
function resolveTitle(name: string, title?: string): string {
return title ?? name;
}
function hasAnyAnnotation(annotations?: ToolAnnotations): boolean {
return !!(
annotations &&
(annotations.audience ||
annotations.readOnly ||
annotations.destructive ||
annotations.longRunning ||
annotations.hints)
);
}
export function ToolDetailPanel({
name,
title,
description,
annotations,
schema,
formValues,
isExecuting,
progress,
onFormChange,
onExecute,
onCancel,
}: ToolDetailPanelProps) {
return (
<Stack gap="md" miw={0}>
<ToolTitle>{resolveTitle(name, title)}</ToolTitle>
{hasAnyAnnotation(annotations) && annotations && (
<Group gap="xs">
{annotations.audience && (
<AnnotationBadge label={annotations.audience} variant="audience" />
)}
{annotations.readOnly && (
<AnnotationBadge label="read-only" variant="readOnly" />
)}
{annotations.destructive && (
<AnnotationBadge label="destructive" variant="destructive" />
)}
{annotations.longRunning && (
<AnnotationBadge label="long-run" variant="longRun" />
)}
{annotations.hints && <HintsText>{annotations.hints}</HintsText>}
</Group>
)}
{description && <DescriptionText>{description}</DescriptionText>}
<Divider />
<SchemaForm
schema={schema}
values={formValues}
onChange={onFormChange}
disabled={isExecuting}
/>
{progress && (
<ProgressDisplay
progress={progress.percent}
description={progress.description}
/>
)}
<Group>
<Button
fullWidth
onClick={onExecute}
disabled={isExecuting}
loading={isExecuting}
>
Execute Tool
</Button>
{isExecuting && <CancelButton onClick={onCancel}>Cancel</CancelButton>}
</Group>
</Stack>
);
}