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

@ -112,3 +112,29 @@ export const HttpDirect: Story = {
canTestClientFeatures: false,
},
};
export const CompactConnected: Story = {
args: {
name: "My MCP Server",
version: "1.2.0",
transport: "stdio",
connectionMode: "Subprocess",
command: "npx -y @modelcontextprotocol/server-everything",
status: "connected",
canTestClientFeatures: false,
compact: true,
},
};
export const CompactDisconnected: Story = {
args: {
name: "My MCP Server",
version: "1.2.0",
transport: "stdio",
connectionMode: "Subprocess",
command: "npx -y @modelcontextprotocol/server-everything",
status: "disconnected",
canTestClientFeatures: false,
compact: true,
},
};

View file

@ -25,6 +25,7 @@ export interface ServerCardProps {
onTestElicitationForm?: () => void;
onTestElicitationUrl?: () => void;
onConfigureRoots?: () => void;
compact?: boolean;
}
const HeaderLeft = Group.withProps({
@ -80,6 +81,7 @@ export function ServerCard({
onTestElicitationForm,
onTestElicitationUrl,
onConfigureRoots,
compact = false,
}: ServerCardProps) {
const isConnected = status === "connected";
const isConnecting = status === "connecting";
@ -103,53 +105,57 @@ export function ServerCard({
</HeaderRight>
</Group>
<Group gap="sm">
<TransportBadge transport={transport} />
<ModeText>{connectionMode}</ModeText>
</Group>
{!compact && (
<>
<Group gap="sm">
<TransportBadge transport={transport} />
<ModeText>{connectionMode}</ModeText>
</Group>
<ContentViewer type="text" content={command} copyable />
<ContentViewer type="text" content={command} copyable />
{error && (
<InlineError
message={error.message}
details={error.details}
retryCount={retryCount}
/>
)}
<Group justify="space-between">
<ActionsRow>
<SubtleButton onClick={onClone}>Clone</SubtleButton>
<SubtleButton onClick={onEdit}>Edit</SubtleButton>
<RemoveButton onClick={onRemove}>Remove</RemoveButton>
</ActionsRow>
<ActionsRow>
<SubtleButton onClick={onServerInfo}>Server Info</SubtleButton>
<SubtleButton onClick={onSettings}>Settings</SubtleButton>
{canTestClientFeatures && (
<Menu>
<Menu.Target>
<SubtleButton>Test Client Features &#x25BE;</SubtleButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={onTestSampling}>
Simulate Sampling Request
</Menu.Item>
<Menu.Item onClick={onTestElicitationForm}>
Simulate Elicitation (Form)
</Menu.Item>
<Menu.Item onClick={onTestElicitationUrl}>
Simulate Elicitation (URL)
</Menu.Item>
<Menu.Item onClick={onConfigureRoots}>
Configure Roots
</Menu.Item>
</Menu.Dropdown>
</Menu>
{error && (
<InlineError
message={error.message}
details={error.details}
retryCount={retryCount}
/>
)}
</ActionsRow>
</Group>
<Group justify="space-between">
<ActionsRow>
<SubtleButton onClick={onClone}>Clone</SubtleButton>
<SubtleButton onClick={onEdit}>Edit</SubtleButton>
<RemoveButton onClick={onRemove}>Remove</RemoveButton>
</ActionsRow>
<ActionsRow>
<SubtleButton onClick={onServerInfo}>Server Info</SubtleButton>
<SubtleButton onClick={onSettings}>Settings</SubtleButton>
{canTestClientFeatures && (
<Menu>
<Menu.Target>
<SubtleButton>Test Client Features &#x25BE;</SubtleButton>
</Menu.Target>
<Menu.Dropdown>
<Menu.Item onClick={onTestSampling}>
Simulate Sampling Request
</Menu.Item>
<Menu.Item onClick={onTestElicitationForm}>
Simulate Elicitation (Form)
</Menu.Item>
<Menu.Item onClick={onTestElicitationUrl}>
Simulate Elicitation (URL)
</Menu.Item>
<Menu.Item onClick={onConfigureRoots}>
Configure Roots
</Menu.Item>
</Menu.Dropdown>
</Menu>
)}
</ActionsRow>
</Group>
</>
)}
</Stack>
</Card>
);