Add new components, typography, badge contrast, and screen layout refactors

New element components:
- ListToggle: expand/collapse button using react-icons (RiExpandVerticalLine/RiCollapseVerticalLine)
- LogLevelBadge: extracted from LogEntry, handles all 8 log levels with filled variant and dark/light text contrast

New group components:
- ViewHeader: shared header bar for ConnectedView and UnconnectedView with discriminated union on `connected` prop; MCP logo links to modelcontextprotocol.io
- ServerListControls: combines ListToggle and AddServerMenu, hides toggle when no servers
- LogStreamPanel: extracted from LoggingScreen with scroll containment, filtering by visible levels and search text
- HistoryControls: vertical sidebar with search, method filter, export, and clear
- HistoryListPanel: extracted from HistoryScreen with ListToggle compact mode, filtering by method and search text

Badge contrast improvements:
- AnnotationBadge: filled variant for both dark/light modes; black text in dark, white text in light
- LogLevelBadge: same contrast approach; bold weight for alert/emergency levels
- Badge theme default weight reduced from 700 to 600

Typography:
- Added Fredoka (300-700) as body and heading font
- Added Roboto Mono as monospace font
- Google Fonts loaded in index.html and Storybook previewHead
- Heading weight set to 600

Screen layout refactors:
- LoggingScreen: owns filterText and visibleLevels state; passes to LogControls and LogStreamPanel; removed controls prop in favor of flat props
- HistoryScreen: owns searchText and methodFilter state; passes to HistoryControls and HistoryListPanel for filtering
- Both screens use new ScreenLayout (Flex variant="screen") and Sidebar subcomponents
- Added Flex theme with "screen" variant (overflow: hidden)
- Added Paper theme "panel" variant (flex column, overflow hidden)

Component enhancements:
- ServerCard: added compact prop showing only header row
- HistoryEntry: renamed isExpanded to isListExpanded; internal useState syncs via useEffect; individual entries toggle independently
- LogControls: Select All / Deselect All toggle for level checkboxes; nowrap on level selector group
- ConnectionToggle: LoadingOverlay with rounded corners for connecting state
- ConnectedView: ThemeToggle ActionIcon matched to DisconnectButton height (36px) and border color (outline variant)
- Storybook preview: fullscreen layout wrapper with 100vh overflow hidden

New stories:
- UnconnectedView: Connecting and ManyServers (12 servers for scroll testing)
- ServerCard: CompactConnected and CompactDisconnected
- All new components have corresponding stories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-03-28 20:38:04 -04:00
commit 961dc283fc
2 changed files with 1602 additions and 790 deletions

View file

@ -0,0 +1,32 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { HistoryControls } from "./HistoryControls";
const meta: Meta<typeof HistoryControls> = {
title: "Groups/HistoryControls",
component: HistoryControls,
};
export default meta;
type Story = StoryObj<typeof HistoryControls>;
export const Default: Story = {
args: {
searchText: "",
onSearchChange: fn(),
onMethodFilterChange: fn(),
onClearAll: fn(),
onExport: fn(),
},
};
export const WithFilters: Story = {
args: {
searchText: "tools",
methodFilter: "tools/call",
onSearchChange: fn(),
onMethodFilterChange: fn(),
onClearAll: fn(),
onExport: fn(),
},
};

View file

@ -0,0 +1,62 @@
import { Button, Group, Select, Stack, TextInput, Title } from "@mantine/core";
const METHOD_OPTIONS = [
"tools/call",
"tools/list",
"resources/read",
"resources/list",
"prompts/get",
"prompts/list",
"sampling/createMessage",
"elicitation/create",
];
const ToolbarButton = Button.withProps({
variant: "light",
size: "sm",
});
export interface HistoryControlsProps {
searchText: string;
methodFilter?: string;
onSearchChange: (text: string) => void;
onMethodFilterChange: (method: string) => void;
onClearAll: () => void;
onExport: () => void;
}
export function HistoryControls({
searchText,
methodFilter,
onSearchChange,
onMethodFilterChange,
onClearAll,
onExport,
}: HistoryControlsProps) {
return (
<Stack gap="md">
<Title order={4}>History</Title>
<Title order={5}>Search</Title>
<TextInput
placeholder="Search..."
value={searchText}
onChange={(event) => onSearchChange(event.currentTarget.value)}
/>
<Title order={6}>Filter by Method</Title>
<Select
placeholder="All methods"
data={METHOD_OPTIONS}
value={methodFilter}
onChange={(value) => onMethodFilterChange(value ?? "")}
clearable
/>
<Group>
<ToolbarButton onClick={onExport}>Export JSON</ToolbarButton>
<ToolbarButton onClick={onClearAll}>Clear All</ToolbarButton>
</Group>
</Stack>
);
}