refactor organism components to use .withProps() subcomponents and extract inline code

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-03-24 15:32:15 -04:00
commit ca6de91a96
7 changed files with 250 additions and 96 deletions

View file

@ -38,6 +38,49 @@ const METHOD_OPTIONS = [
"elicitation/create", "elicitation/create",
]; ];
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const ContentPanel = Paper.withProps({
withBorder: true,
p: "md",
});
const HeaderRow = Group.withProps({
justify: "space-between",
wrap: "wrap",
});
const ToolbarButton = Button.withProps({
variant: "light",
size: "sm",
});
const EmptyState = Text.withProps({
c: "dimmed",
ta: "center",
py: "xl",
});
const CountText = Text.withProps({
size: "sm",
c: "dimmed",
});
function entryKey(entry: HistoryEntryProps): string {
return `${entry.timestamp}-${entry.method}`;
}
function formatPinnedTitle(count: number): string {
return `Pinned Requests (${count})`;
}
function formatPagination(displayed: number, total: number): string {
return `Showing ${displayed} of ${total} entries`;
}
export function HistoryScreen({ export function HistoryScreen({
entries, entries,
pinnedEntries, pinnedEntries,
@ -52,10 +95,10 @@ export function HistoryScreen({
onExport, onExport,
}: HistoryScreenProps) { }: HistoryScreenProps) {
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Paper withBorder p="md"> <ContentPanel>
<Stack gap="md"> <Stack gap="md">
<Group justify="space-between" wrap="wrap"> <HeaderRow>
<Title order={3}>Request History</Title> <Title order={3}>Request History</Title>
<Group> <Group>
<TextInput <TextInput
@ -70,26 +113,17 @@ export function HistoryScreen({
onChange={(value) => onMethodFilterChange(value ?? "")} onChange={(value) => onMethodFilterChange(value ?? "")}
clearable clearable
/> />
<Button variant="light" size="sm" onClick={onExport}> <ToolbarButton onClick={onExport}>Export JSON</ToolbarButton>
Export JSON <ToolbarButton onClick={onClearAll}>Clear All</ToolbarButton>
</Button>
<Button variant="light" size="sm" onClick={onClearAll}>
Clear All
</Button>
</Group> </Group>
</Group> </HeaderRow>
{entries.length === 0 ? ( {entries.length === 0 ? (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>No request history</EmptyState>
No request history
</Text>
) : ( ) : (
<Stack gap="md"> <Stack gap="md">
{entries.map((entry) => ( {entries.map((entry) => (
<HistoryEntry <HistoryEntry key={entryKey(entry)} {...entry} />
key={`${entry.timestamp}-${entry.method}`}
{...entry}
/>
))} ))}
</Stack> </Stack>
)} )}
@ -97,30 +131,25 @@ export function HistoryScreen({
{pinnedEntries.length > 0 && ( {pinnedEntries.length > 0 && (
<> <>
<Divider /> <Divider />
<Title order={4}>Pinned Requests ({pinnedEntries.length})</Title> <Title order={4}>{formatPinnedTitle(pinnedEntries.length)}</Title>
<Stack gap="sm"> <Stack gap="sm">
{pinnedEntries.map((entry) => ( {pinnedEntries.map((entry) => (
<HistoryEntry <HistoryEntry key={entryKey(entry)} {...entry} />
key={`${entry.timestamp}-${entry.method}`}
{...entry}
/>
))} ))}
</Stack> </Stack>
</> </>
)} )}
<Group justify="flex-end"> <Group justify="flex-end">
<Text size="sm" c="dimmed"> <CountText>
Showing {displayedCount} of {totalCount} entries {formatPagination(displayedCount, totalCount)}
</Text> </CountText>
{displayedCount < totalCount && ( {displayedCount < totalCount && (
<Button variant="light" size="sm" onClick={onLoadMore}> <ToolbarButton onClick={onLoadMore}>Load More</ToolbarButton>
Load More
</Button>
)} )}
</Group> </Group>
</Stack> </Stack>
</Paper> </ContentPanel>
</Container> </PageContainer>
); );
} }

View file

@ -23,6 +23,38 @@ export interface LoggingScreenProps {
onCopyAll: () => void; onCopyAll: () => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const SidebarCard = Card.withProps({
withBorder: true,
padding: "lg",
});
const FullHeightCard = Card.withProps({
withBorder: true,
padding: "lg",
h: "100%",
});
const FullHeightStack = Stack.withProps({
gap: "sm",
h: "100%",
});
const ToolbarButton = Button.withProps({
variant: "light",
size: "sm",
});
const EmptyCenter = Stack.withProps({
flex: 1,
align: "center",
justify: "center",
});
export function LoggingScreen({ export function LoggingScreen({
controls, controls,
entries, entries,
@ -31,16 +63,16 @@ export function LoggingScreen({
onCopyAll, onCopyAll,
}: LoggingScreenProps) { }: LoggingScreenProps) {
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Grid align="stretch"> <Grid align="stretch">
<Grid.Col span={3}> <Grid.Col span={3}>
<Card withBorder padding="lg"> <SidebarCard>
<LogControls {...controls} /> <LogControls {...controls} />
</Card> </SidebarCard>
</Grid.Col> </Grid.Col>
<Grid.Col span={9}> <Grid.Col span={9}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
<Stack gap="sm" h="100%"> <FullHeightStack>
<Group justify="space-between"> <Group justify="space-between">
<Title order={4}>Log Stream</Title> <Title order={4}>Log Stream</Title>
<Group> <Group>
@ -49,9 +81,7 @@ export function LoggingScreen({
checked={autoScroll} checked={autoScroll}
onChange={onToggleAutoScroll} onChange={onToggleAutoScroll}
/> />
<Button variant="light" size="sm" onClick={onCopyAll}> <ToolbarButton onClick={onCopyAll}>Copy All</ToolbarButton>
Copy All
</Button>
</Group> </Group>
</Group> </Group>
{entries.length > 0 ? ( {entries.length > 0 ? (
@ -63,14 +93,14 @@ export function LoggingScreen({
</Stack> </Stack>
</ScrollArea> </ScrollArea>
) : ( ) : (
<Stack flex={1} align="center" justify="center"> <EmptyCenter>
<Text c="dimmed">No log entries</Text> <Text c="dimmed">No log entries</Text>
</Stack> </EmptyCenter>
)} )}
</Stack> </FullHeightStack>
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Container> </PageContainer>
); );
} }

View file

@ -12,6 +12,23 @@ export interface PromptsScreenProps {
onRefreshList: () => void; onRefreshList: () => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const FullHeightCard = Card.withProps({
withBorder: true,
padding: "lg",
h: "100%",
});
const EmptyState = Text.withProps({
c: "dimmed",
ta: "center",
py: "xl",
});
export function PromptsScreen({ export function PromptsScreen({
promptForm, promptForm,
messages, messages,
@ -19,10 +36,10 @@ export function PromptsScreen({
onRefreshList, onRefreshList,
}: PromptsScreenProps) { }: PromptsScreenProps) {
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Grid align="stretch"> <Grid align="stretch">
<Grid.Col span={5}> <Grid.Col span={5}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
<Stack gap="md"> <Stack gap="md">
<ListChangedIndicator <ListChangedIndicator
visible={listChanged} visible={listChanged}
@ -30,20 +47,20 @@ export function PromptsScreen({
/> />
<PromptArgumentsForm {...promptForm} /> <PromptArgumentsForm {...promptForm} />
</Stack> </Stack>
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
<Grid.Col span={7}> <Grid.Col span={7}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
{messages ? ( {messages ? (
<PromptMessagesDisplay {...messages} /> <PromptMessagesDisplay {...messages} />
) : ( ) : (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>
Select a prompt and click Get Prompt to see messages Select a prompt and click Get Prompt to see messages
</Text> </EmptyState>
)} )}
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Container> </PageContainer>
); );
} }

View file

@ -33,6 +33,42 @@ export interface ResourcesScreenProps {
onSelectResource: (uri: string) => void; onSelectResource: (uri: string) => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const FullHeightCard = Card.withProps({
withBorder: true,
padding: "lg",
h: "100%",
});
const DetailCard = Card.withProps({
withBorder: true,
padding: "lg",
});
const EmptyState = Text.withProps({
c: "dimmed",
ta: "center",
py: "xl",
});
const SubscriptionMeta = Text.withProps({
span: true,
c: "dimmed",
size: "xs",
});
function formatSectionCount(label: string, count: number): string {
return `${label} (${count})`;
}
function formatLastUpdated(lastUpdated: string): string {
return ` — ${lastUpdated}`;
}
export function ResourcesScreen({ export function ResourcesScreen({
resources, resources,
templates, templates,
@ -54,10 +90,10 @@ export function ResourcesScreen({
]; ];
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Grid align="stretch"> <Grid align="stretch">
<Grid.Col span={4}> <Grid.Col span={4}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
<Stack gap="sm"> <Stack gap="sm">
<Title order={4}>Resources</Title> <Title order={4}>Resources</Title>
<ListChangedIndicator <ListChangedIndicator
@ -72,7 +108,7 @@ export function ResourcesScreen({
<Accordion multiple defaultValue={openSections}> <Accordion multiple defaultValue={openSections}>
<Accordion.Item value="resources"> <Accordion.Item value="resources">
<Accordion.Control disabled={filteredResources.length === 0}> <Accordion.Control disabled={filteredResources.length === 0}>
URIs ({filteredResources.length}) {formatSectionCount("URIs", filteredResources.length)}
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<Stack gap="xs"> <Stack gap="xs">
@ -85,7 +121,7 @@ export function ResourcesScreen({
<Accordion.Item value="templates"> <Accordion.Item value="templates">
<Accordion.Control disabled={templates.length === 0}> <Accordion.Control disabled={templates.length === 0}>
Templates ({templates.length}) {formatSectionCount("Templates", templates.length)}
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<Stack gap="xs"> <Stack gap="xs">
@ -101,7 +137,7 @@ export function ResourcesScreen({
<Accordion.Item value="subscriptions"> <Accordion.Item value="subscriptions">
<Accordion.Control disabled={subscriptions.length === 0}> <Accordion.Control disabled={subscriptions.length === 0}>
Subscriptions ({subscriptions.length}) {formatSectionCount("Subscriptions", subscriptions.length)}
</Accordion.Control> </Accordion.Control>
<Accordion.Panel> <Accordion.Panel>
<Stack gap="xs"> <Stack gap="xs">
@ -109,10 +145,9 @@ export function ResourcesScreen({
<Text key={sub.name} size="sm"> <Text key={sub.name} size="sm">
{sub.name} {sub.name}
{sub.lastUpdated && ( {sub.lastUpdated && (
<Text span c="dimmed" size="xs"> <SubscriptionMeta>
{" "} {formatLastUpdated(sub.lastUpdated)}
— {sub.lastUpdated} </SubscriptionMeta>
</Text>
)} )}
</Text> </Text>
))} ))}
@ -121,21 +156,19 @@ export function ResourcesScreen({
</Accordion.Item> </Accordion.Item>
</Accordion> </Accordion>
</Stack> </Stack>
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
<Grid.Col span={8}> <Grid.Col span={8}>
<Card withBorder padding="lg"> <DetailCard>
{selectedResource ? ( {selectedResource ? (
<ResourcePreviewPanel {...selectedResource} /> <ResourcePreviewPanel {...selectedResource} />
) : ( ) : (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>Select a resource to preview</EmptyState>
Select a resource to preview
</Text>
)} )}
</Card> </DetailCard>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Container> </PageContainer>
); );
} }

View file

@ -10,6 +10,17 @@ export interface ServerListScreenProps {
onImportServerJson: () => void; onImportServerJson: () => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const EmptyState = Text.withProps({
c: "dimmed",
ta: "center",
py: "xl",
});
export function ServerListScreen({ export function ServerListScreen({
servers, servers,
onAddManually, onAddManually,
@ -17,7 +28,7 @@ export function ServerListScreen({
onImportServerJson, onImportServerJson,
}: ServerListScreenProps) { }: ServerListScreenProps) {
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Stack gap="lg"> <Stack gap="lg">
<Group justify="flex-end"> <Group justify="flex-end">
<AddServerMenu <AddServerMenu
@ -28,9 +39,9 @@ export function ServerListScreen({
</Group> </Group>
{servers.length === 0 ? ( {servers.length === 0 ? (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>
No servers configured. Add a server to get started. No servers configured. Add a server to get started.
</Text> </EmptyState>
) : ( ) : (
<SimpleGrid <SimpleGrid
cols={{ base: 1, md: 2 }} cols={{ base: 1, md: 2 }}
@ -43,6 +54,6 @@ export function ServerListScreen({
</SimpleGrid> </SimpleGrid>
)} )}
</Stack> </Stack>
</Container> </PageContainer>
); );
} }

View file

@ -17,6 +17,25 @@ export interface TasksScreenProps {
onClearHistory: () => void; onClearHistory: () => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const ContentPanel = Paper.withProps({
withBorder: true,
p: "md",
});
const CompactButton = Button.withProps({
variant: "light",
size: "xs",
});
function formatSectionTitle(label: string, count: number): string {
return `${label} (${count})`;
}
export function TasksScreen({ export function TasksScreen({
activeTasks, activeTasks,
completedTasks, completedTasks,
@ -24,14 +43,14 @@ export function TasksScreen({
onClearHistory, onClearHistory,
}: TasksScreenProps) { }: TasksScreenProps) {
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Paper withBorder p="md"> <ContentPanel>
<Stack gap="lg"> <Stack gap="lg">
<Group justify="space-between"> <Group justify="space-between">
<Title order={4}>Active Tasks ({activeTasks.length})</Title> <Title order={4}>
<Button variant="light" size="xs" onClick={onRefresh}> {formatSectionTitle("Active Tasks", activeTasks.length)}
Refresh Tasks </Title>
</Button> <CompactButton onClick={onRefresh}>Refresh Tasks</CompactButton>
</Group> </Group>
{activeTasks.length === 0 ? ( {activeTasks.length === 0 ? (
<Text c="dimmed">No active tasks</Text> <Text c="dimmed">No active tasks</Text>
@ -44,11 +63,13 @@ export function TasksScreen({
)} )}
<Group justify="space-between"> <Group justify="space-between">
<Title order={4}>Completed Tasks ({completedTasks.length})</Title> <Title order={4}>
{formatSectionTitle("Completed Tasks", completedTasks.length)}
</Title>
{completedTasks.length > 0 && ( {completedTasks.length > 0 && (
<Button variant="light" size="xs" onClick={onClearHistory}> <CompactButton onClick={onClearHistory}>
Clear History Clear History
</Button> </CompactButton>
)} )}
</Group> </Group>
{completedTasks.length === 0 ? ( {completedTasks.length === 0 ? (
@ -61,7 +82,7 @@ export function TasksScreen({
</Stack> </Stack>
)} )}
</Stack> </Stack>
</Paper> </ContentPanel>
</Container> </PageContainer>
); );
} }

View file

@ -26,6 +26,23 @@ export interface ToolsScreenProps {
onSelectTool: (name: string) => void; onSelectTool: (name: string) => void;
} }
const PageContainer = Container.withProps({
size: "xl",
py: "xl",
});
const FullHeightCard = Card.withProps({
withBorder: true,
padding: "lg",
h: "100%",
});
const EmptyState = Text.withProps({
c: "dimmed",
ta: "center",
py: "xl",
});
export function ToolsScreen({ export function ToolsScreen({
tools, tools,
selectedTool, selectedTool,
@ -43,10 +60,10 @@ export function ToolsScreen({
: tools; : tools;
return ( return (
<Container size="xl" py="xl"> <PageContainer>
<Grid align="stretch"> <Grid align="stretch">
<Grid.Col span={3}> <Grid.Col span={3}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
<Stack gap="sm"> <Stack gap="sm">
<Title order={4}>Tools</Title> <Title order={4}>Tools</Title>
<ListChangedIndicator <ListChangedIndicator
@ -66,33 +83,29 @@ export function ToolsScreen({
/> />
))} ))}
</Stack> </Stack>
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
<Grid.Col span={5}> <Grid.Col span={5}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
{selectedTool ? ( {selectedTool ? (
<ToolDetailPanel {...selectedTool} /> <ToolDetailPanel {...selectedTool} />
) : ( ) : (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>Select a tool to view details</EmptyState>
Select a tool to view details
</Text>
)} )}
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
<Grid.Col span={4}> <Grid.Col span={4}>
<Card withBorder padding="lg" h="100%"> <FullHeightCard>
{result ? ( {result ? (
<ResultPanel {...result} /> <ResultPanel {...result} />
) : ( ) : (
<Text c="dimmed" ta="center" py="xl"> <EmptyState>Results will appear here</EmptyState>
Results will appear here
</Text>
)} )}
</Card> </FullHeightCard>
</Grid.Col> </Grid.Col>
</Grid> </Grid>
</Container> </PageContainer>
); );
} }