Animate InspectorView screen transitions and refactor App.tsx
- InspectorView: wrap each screen in a Mantine `Transition` (`fade-up`, 350ms). Outgoing screens fade out and slide down 30px; incoming screens fade in and slide up to meet the ViewHeader. A `pos: "relative"` Stack container holds the screens so the absolute-positioned `Transition` wrappers stack in the same area without pushing layout. - InspectorView: reorder ALL_TABS so Logs sits beside History at the end: Servers · Tools · Prompts · Resources · Tasks · Logs · History. - App.tsx: refactor to AGENTS.md guidelines — extract `PageContainer`, `PageStack`, `ThemeToggle`, `SubtitleText` as `.withProps()` subcomponents; replace inline unicode glyphs with `MdLightMode` / `MdDarkMode` icons (matches the ViewHeader pattern); convert the click handler to a `function` declaration. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
473bd95826
commit
d3c2645e38
2 changed files with 157 additions and 103 deletions
|
|
@ -1,41 +1,59 @@
|
||||||
import {
|
import {
|
||||||
Title,
|
|
||||||
Text,
|
|
||||||
Container,
|
|
||||||
Stack,
|
|
||||||
useMantineColorScheme,
|
|
||||||
useComputedColorScheme,
|
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
|
Container,
|
||||||
Group,
|
Group,
|
||||||
|
Stack,
|
||||||
|
Text,
|
||||||
|
Title,
|
||||||
|
useComputedColorScheme,
|
||||||
|
useMantineColorScheme,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { MdDarkMode, MdLightMode } from "react-icons/md";
|
||||||
|
|
||||||
|
const PageContainer = Container.withProps({
|
||||||
|
size: "sm",
|
||||||
|
py: "xl",
|
||||||
|
});
|
||||||
|
|
||||||
|
const PageStack = Stack.withProps({
|
||||||
|
align: "center",
|
||||||
|
gap: "md",
|
||||||
|
});
|
||||||
|
|
||||||
|
const ThemeToggle = ActionIcon.withProps({
|
||||||
|
variant: "default",
|
||||||
|
size: "lg",
|
||||||
|
"aria-label": "Toggle color scheme",
|
||||||
|
});
|
||||||
|
|
||||||
|
const SubtitleText = Text.withProps({
|
||||||
|
c: "var(--inspector-text-secondary)",
|
||||||
|
});
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const { setColorScheme } = useMantineColorScheme();
|
const { setColorScheme } = useMantineColorScheme();
|
||||||
const computedColorScheme = useComputedColorScheme("light");
|
const computedColorScheme = useComputedColorScheme("light");
|
||||||
|
const isDark = computedColorScheme === "dark";
|
||||||
|
const ThemeIcon = isDark ? MdLightMode : MdDarkMode;
|
||||||
|
|
||||||
const toggleColorScheme = () => {
|
function toggleColorScheme() {
|
||||||
setColorScheme(computedColorScheme === "dark" ? "light" : "dark");
|
setColorScheme(isDark ? "light" : "dark");
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Container size="sm" py="xl">
|
<PageContainer>
|
||||||
<Stack align="center" gap="md">
|
<PageStack>
|
||||||
<Group>
|
<Group>
|
||||||
<Title order={1}>MCP Inspector</Title>
|
<Title order={1}>MCP Inspector</Title>
|
||||||
<ActionIcon
|
<ThemeToggle onClick={toggleColorScheme}>
|
||||||
onClick={toggleColorScheme}
|
<ThemeIcon size={20} />
|
||||||
variant="default"
|
</ThemeToggle>
|
||||||
size="lg"
|
|
||||||
aria-label="Toggle color scheme"
|
|
||||||
>
|
|
||||||
{computedColorScheme === "dark" ? "\u2600" : "\u263E"}
|
|
||||||
</ActionIcon>
|
|
||||||
</Group>
|
</Group>
|
||||||
<Text c="var(--inspector-text-secondary)">
|
<SubtitleText>
|
||||||
Web client for the Model Context Protocol Inspector
|
Web client for the Model Context Protocol Inspector
|
||||||
</Text>
|
</SubtitleText>
|
||||||
</Stack>
|
</PageStack>
|
||||||
</Container>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useMemo, useState } from "react";
|
import { useMemo, useState, type ReactNode } from "react";
|
||||||
import { AppShell } from "@mantine/core";
|
import { AppShell, Box, Stack, Transition } from "@mantine/core";
|
||||||
import type {
|
import type {
|
||||||
InitializeResult,
|
InitializeResult,
|
||||||
LoggingLevel,
|
LoggingLevel,
|
||||||
|
|
@ -35,11 +35,45 @@ const ALL_TABS: string[] = [
|
||||||
"Tools",
|
"Tools",
|
||||||
"Prompts",
|
"Prompts",
|
||||||
"Resources",
|
"Resources",
|
||||||
"Logs",
|
|
||||||
"Tasks",
|
"Tasks",
|
||||||
|
"Logs",
|
||||||
"History",
|
"History",
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const SCREEN_TRANSITION_MS = 350;
|
||||||
|
|
||||||
|
const ScreenStageContainer = Stack.withProps({
|
||||||
|
pos: "relative",
|
||||||
|
gap: 0,
|
||||||
|
flex: 1,
|
||||||
|
mih: "100%",
|
||||||
|
});
|
||||||
|
|
||||||
|
function ScreenStage({
|
||||||
|
active,
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
active: boolean;
|
||||||
|
children: ReactNode;
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<Transition
|
||||||
|
mounted={active}
|
||||||
|
transition="fade-up"
|
||||||
|
duration={SCREEN_TRANSITION_MS}
|
||||||
|
timingFunction="ease"
|
||||||
|
>
|
||||||
|
{(styles) => (
|
||||||
|
// `style={styles}` is the runtime transition state from Mantine's
|
||||||
|
// Transition API — these are interpolated values, not static styling.
|
||||||
|
<Box style={styles} pos="absolute" top={0} left={0} right={0}>
|
||||||
|
{children}
|
||||||
|
</Box>
|
||||||
|
)}
|
||||||
|
</Transition>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// Demo stub: every screen-action callback below resolves to noop. Phase 3
|
// Demo stub: every screen-action callback below resolves to noop. Phase 3
|
||||||
// wiring will replace each with its real `useManaged*` / `useConnection`
|
// wiring will replace each with its real `useManaged*` / `useConnection`
|
||||||
// hook call. Anything still pointing here in Phase 3 is unfinished.
|
// hook call. Anything still pointing here in Phase 3 is unfinished.
|
||||||
|
|
@ -204,84 +238,86 @@ export function InspectorView({
|
||||||
)}
|
)}
|
||||||
</AppShell.Header>
|
</AppShell.Header>
|
||||||
<AppShell.Main>
|
<AppShell.Main>
|
||||||
{activeTab === SERVERS_TAB && (
|
<ScreenStageContainer>
|
||||||
<ServerListScreen
|
<ScreenStage active={activeTab === SERVERS_TAB}>
|
||||||
servers={servers}
|
<ServerListScreen
|
||||||
activeServer={activeServer}
|
servers={servers}
|
||||||
onAddManually={noop}
|
activeServer={activeServer}
|
||||||
onImportConfig={noop}
|
onAddManually={noop}
|
||||||
onImportServerJson={noop}
|
onImportConfig={noop}
|
||||||
onToggleConnection={handleToggleConnection}
|
onImportServerJson={noop}
|
||||||
onServerInfo={noop}
|
onToggleConnection={handleToggleConnection}
|
||||||
onSettings={noop}
|
onServerInfo={noop}
|
||||||
onEdit={noop}
|
onSettings={noop}
|
||||||
onClone={noop}
|
onEdit={noop}
|
||||||
onRemove={noop}
|
onClone={noop}
|
||||||
/>
|
onRemove={noop}
|
||||||
)}
|
/>
|
||||||
{activeTab === "Tools" && (
|
</ScreenStage>
|
||||||
<ToolsScreen
|
<ScreenStage active={activeTab === "Tools"}>
|
||||||
tools={tools}
|
<ToolsScreen
|
||||||
listChanged={false}
|
tools={tools}
|
||||||
onRefreshList={noop}
|
listChanged={false}
|
||||||
onSelectTool={noop}
|
onRefreshList={noop}
|
||||||
onCallTool={noop}
|
onSelectTool={noop}
|
||||||
/>
|
onCallTool={noop}
|
||||||
)}
|
/>
|
||||||
{activeTab === "Prompts" && (
|
</ScreenStage>
|
||||||
<PromptsScreen
|
<ScreenStage active={activeTab === "Prompts"}>
|
||||||
prompts={prompts}
|
<PromptsScreen
|
||||||
listChanged={false}
|
prompts={prompts}
|
||||||
onRefreshList={noop}
|
listChanged={false}
|
||||||
onSelectPrompt={noop}
|
onRefreshList={noop}
|
||||||
onGetPrompt={noop}
|
onSelectPrompt={noop}
|
||||||
/>
|
onGetPrompt={noop}
|
||||||
)}
|
/>
|
||||||
{activeTab === "Resources" && (
|
</ScreenStage>
|
||||||
<ResourcesScreen
|
<ScreenStage active={activeTab === "Resources"}>
|
||||||
resources={resources}
|
<ResourcesScreen
|
||||||
templates={resourceTemplates}
|
resources={resources}
|
||||||
subscriptions={subscriptions}
|
templates={resourceTemplates}
|
||||||
listChanged={false}
|
subscriptions={subscriptions}
|
||||||
onRefreshList={noop}
|
listChanged={false}
|
||||||
onSelectUri={noop}
|
onRefreshList={noop}
|
||||||
onSelectTemplate={noop}
|
onSelectUri={noop}
|
||||||
onReadResource={noop}
|
onSelectTemplate={noop}
|
||||||
onSubscribeResource={noop}
|
onReadResource={noop}
|
||||||
onUnsubscribeResource={noop}
|
onSubscribeResource={noop}
|
||||||
/>
|
onUnsubscribeResource={noop}
|
||||||
)}
|
/>
|
||||||
{activeTab === "Logs" && (
|
</ScreenStage>
|
||||||
<LoggingScreen
|
<ScreenStage active={activeTab === "Tasks"}>
|
||||||
entries={logs}
|
<TasksScreen
|
||||||
currentLevel={logLevel}
|
tasks={tasks}
|
||||||
onSetLevel={setLogLevel}
|
progressByTaskId={progressByTaskId}
|
||||||
onClear={noop}
|
onRefresh={noop}
|
||||||
onExport={noop}
|
onClearCompleted={noop}
|
||||||
autoScroll={autoScroll}
|
onCancel={noop}
|
||||||
onToggleAutoScroll={() => setAutoScroll((prev) => !prev)}
|
/>
|
||||||
onCopyAll={noop}
|
</ScreenStage>
|
||||||
/>
|
<ScreenStage active={activeTab === "Logs"}>
|
||||||
)}
|
<LoggingScreen
|
||||||
{activeTab === "Tasks" && (
|
entries={logs}
|
||||||
<TasksScreen
|
currentLevel={logLevel}
|
||||||
tasks={tasks}
|
onSetLevel={setLogLevel}
|
||||||
progressByTaskId={progressByTaskId}
|
onClear={noop}
|
||||||
onRefresh={noop}
|
onExport={noop}
|
||||||
onClearCompleted={noop}
|
autoScroll={autoScroll}
|
||||||
onCancel={noop}
|
onToggleAutoScroll={() => setAutoScroll((prev) => !prev)}
|
||||||
/>
|
onCopyAll={noop}
|
||||||
)}
|
/>
|
||||||
{activeTab === "History" && (
|
</ScreenStage>
|
||||||
<HistoryScreen
|
<ScreenStage active={activeTab === "History"}>
|
||||||
entries={history}
|
<HistoryScreen
|
||||||
pinnedIds={new Set()}
|
entries={history}
|
||||||
onClearAll={noop}
|
pinnedIds={new Set()}
|
||||||
onExport={noop}
|
onClearAll={noop}
|
||||||
onReplay={noop}
|
onExport={noop}
|
||||||
onTogglePin={noop}
|
onReplay={noop}
|
||||||
/>
|
onTogglePin={noop}
|
||||||
)}
|
/>
|
||||||
|
</ScreenStage>
|
||||||
|
</ScreenStageContainer>
|
||||||
</AppShell.Main>
|
</AppShell.Main>
|
||||||
</AppShell>
|
</AppShell>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue