diff --git a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx index 3515ab25..1d940599 100644 --- a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx +++ b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx @@ -1,4 +1,5 @@ -import { Image, Paper, Stack, Text } from "@mantine/core"; +import { Group, Image, Paper, Stack, Text } from "@mantine/core"; +import { CopyButton } from "../CopyButton/CopyButton"; export interface MessageBubbleProps { index: number; @@ -18,9 +19,12 @@ export function MessageBubble({ return ( - - [{index}] role: {role} - + + + [{index}] role: {role} + + + "{content}" {imageContent && ( + ); diff --git a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx index c86161b9..625fd118 100644 --- a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx +++ b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx @@ -5,9 +5,57 @@ import { ConnectedLayout } from "./ConnectedLayout"; import { ToolsScreen } from "../../organisms/ToolsScreen/ToolsScreen"; import { ResourcesScreen } from "../../organisms/ResourcesScreen/ResourcesScreen"; import { PromptsScreen } from "../../organisms/PromptsScreen/PromptsScreen"; +import { LoggingScreen } from "../../organisms/LoggingScreen/LoggingScreen"; +import { TasksScreen } from "../../organisms/TasksScreen/TasksScreen"; +import { HistoryScreen } from "../../organisms/HistoryScreen/HistoryScreen"; + +import type { LogEntryProps } from "../../atoms/LogEntry/LogEntry"; +import type { LogLevel } from "../../atoms/LogEntry/LogEntry"; const allTabs = ["Tools", "Resources", "Prompts", "Logs", "Tasks", "History"]; +const logMessages: { level: LogLevel; message: string; logger?: string }[] = [ + { level: "info", message: "Server started on port 3000" }, + { level: "debug", message: "Loading configuration from /etc/mcp/config.json", logger: "config" }, + { level: "warning", message: "Deprecated API endpoint called: /v1/tools", logger: "http" }, + { level: "info", message: "Client connected: inspector-web-ui" }, + { level: "error", message: "Failed to read resource: file not found at /data/missing.txt", logger: "resources" }, + { level: "info", message: "Tool execution completed: search_files (245ms)", logger: "tools" }, + { level: "debug", message: "Parsing JSON-RPC request body", logger: "transport" }, + { level: "info", message: "Listing available tools for client session", logger: "tools" }, + { level: "warning", message: "Slow query detected: 1200ms for resource lookup", logger: "db" }, + { level: "info", message: "Resource subscription added: file:///config.json", logger: "resources" }, + { level: "debug", message: "Heartbeat ping received from client", logger: "session" }, + { level: "error", message: "Permission denied reading /etc/secrets.env", logger: "resources" }, + { level: "info", message: "Prompt 'summarize' resolved with 2 messages", logger: "prompts" }, + { level: "debug", message: "Cache hit for resource: db://users/42", logger: "cache" }, + { level: "info", message: "Tool 'create_record' executed successfully in 89ms", logger: "tools" }, + { level: "warning", message: "Client reconnection attempt #3", logger: "session" }, + { level: "info", message: "New resource detected: file:///data/output.csv", logger: "resources" }, + { level: "debug", message: "Serializing response payload (2.4KB)", logger: "transport" }, + { level: "error", message: "Timeout waiting for tool response after 30s", logger: "tools" }, + { level: "info", message: "Session initialized with capabilities: tools, resources, prompts" }, + { level: "debug", message: "Validating input schema for tool 'batch_process'", logger: "tools" }, + { level: "info", message: "Resource template resolved: db://tables/users/rows/15", logger: "resources" }, + { level: "warning", message: "Memory usage at 78% — consider increasing limits", logger: "system" }, + { level: "info", message: "Client disconnected gracefully", logger: "session" }, + { level: "debug", message: "Flushing log buffer to disk (128 entries)", logger: "logging" }, +]; + +function pad(n: number): string { + return n.toString().padStart(2, "0"); +} + +const sampleLogEntries: LogEntryProps[] = Array.from({ length: 50 }, (_, i) => { + const src = logMessages[i % logMessages.length]; + return { + timestamp: `2026-03-17T10:00:${pad(i + 1)}Z`, + level: src.level, + message: src.message, + logger: src.logger, + }; +}); + const meta: Meta = { title: "Layouts/ConnectedLayout", component: ConnectedLayout, @@ -168,6 +216,7 @@ export const PromptsActive: Story = { onGetPrompt: fn(), }} messages={{ + onCopyAll: fn(), messages: [ { role: "user", content: 'Translate the following text to Spanish: "Hello, how are you?"' }, { role: "assistant", content: "Hola, como estas?" }, @@ -180,6 +229,199 @@ export const PromptsActive: Story = { }, }; +export const LoggingActive: Story = { + args: { + activeTab: "Logs", + children: ( + + ), + }, +}; + +export const TasksActive: Story = { + args: { + activeTab: "Tasks", + children: ( + + ), + }, +}; + +export const HistoryActive: Story = { + args: { + activeTab: "History", + children: ( + + ), + }, +}; + export const LimitedTabs: Story = { args: { availableTabs: ["Tools", "Resources", "Prompts"], diff --git a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx index 0194f920..48205cce 100644 --- a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx +++ b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx @@ -52,7 +52,6 @@ export function PromptArgumentsForm({ )} {promptArguments.length > 0 && ( <> - Arguments {promptArguments.map((arg) => ( void; } export function PromptMessagesDisplay({ messages, + onCopyAll, }: PromptMessagesDisplayProps) { return ( - Messages + + Messages + {onCopyAll && messages.length > 0 && ( + + )} + {messages.length === 0 ? ( No messages to display ) : ( diff --git a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx index bab21dd5..e7c46eb8 100644 --- a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx +++ b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx @@ -28,38 +28,3 @@ export const Selected: Story = { selected: true, }, }; - -export const WithAnnotations: Story = { - args: { - name: "app-settings.json", - uri: "file:///app-settings.json", - selected: false, - annotations: { - audience: "application", - priority: 0.9, - }, - }, -}; - -export const WithHighPriority: Story = { - args: { - name: "critical-data.json", - uri: "file:///critical-data.json", - selected: false, - annotations: { - audience: "user", - priority: 0.8, - }, - }, -}; - -export const NoPriority: Story = { - args: { - name: "readme.md", - uri: "file:///readme.md", - selected: false, - annotations: { - audience: "user", - }, - }, -}; diff --git a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx index ebaa0dd0..14ce5b83 100644 --- a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx +++ b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx @@ -1,5 +1,6 @@ import { Button, + Container, Divider, Group, Paper, @@ -51,6 +52,7 @@ export function HistoryScreen({ onExport, }: HistoryScreenProps) { return ( + @@ -119,5 +121,6 @@ export function HistoryScreen({ + ); } diff --git a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx index 8a98cc84..6b8f6a74 100644 --- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx +++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx @@ -46,43 +46,49 @@ export const Empty: Story = { }, }; -const mixedEntries: LogEntryProps[] = [ - { - timestamp: "2026-03-17T10:00:01Z", - level: "info", - message: "Server started on port 3000", - }, - { - timestamp: "2026-03-17T10:00:02Z", - level: "debug", - message: "Loading configuration from /etc/mcp/config.json", - logger: "config", - }, - { - timestamp: "2026-03-17T10:00:03Z", - level: "warning", - message: "Deprecated API endpoint called: /v1/tools", - logger: "http", - }, - { - timestamp: "2026-03-17T10:00:04Z", - level: "info", - message: "Client connected: inspector-web-ui", - }, - { - timestamp: "2026-03-17T10:00:05Z", - level: "error", - message: "Failed to read resource: file not found at /data/missing.txt", - logger: "resources", - }, - { - timestamp: "2026-03-17T10:00:06Z", - level: "info", - message: "Tool execution completed: search_files (245ms)", - logger: "tools", - }, +const logMessages: { level: string; message: string; logger?: string }[] = [ + { level: "info", message: "Server started on port 3000" }, + { level: "debug", message: "Loading configuration from /etc/mcp/config.json", logger: "config" }, + { level: "warning", message: "Deprecated API endpoint called: /v1/tools", logger: "http" }, + { level: "info", message: "Client connected: inspector-web-ui" }, + { level: "error", message: "Failed to read resource: file not found at /data/missing.txt", logger: "resources" }, + { level: "info", message: "Tool execution completed: search_files (245ms)", logger: "tools" }, + { level: "debug", message: "Parsing JSON-RPC request body", logger: "transport" }, + { level: "info", message: "Listing available tools for client session", logger: "tools" }, + { level: "warning", message: "Slow query detected: 1200ms for resource lookup", logger: "db" }, + { level: "info", message: "Resource subscription added: file:///config.json", logger: "resources" }, + { level: "debug", message: "Heartbeat ping received from client", logger: "session" }, + { level: "error", message: "Permission denied reading /etc/secrets.env", logger: "resources" }, + { level: "info", message: "Prompt 'summarize' resolved with 2 messages", logger: "prompts" }, + { level: "debug", message: "Cache hit for resource: db://users/42", logger: "cache" }, + { level: "info", message: "Tool 'create_record' executed successfully in 89ms", logger: "tools" }, + { level: "warning", message: "Client reconnection attempt #3", logger: "session" }, + { level: "info", message: "New resource detected: file:///data/output.csv", logger: "resources" }, + { level: "debug", message: "Serializing response payload (2.4KB)", logger: "transport" }, + { level: "error", message: "Timeout waiting for tool response after 30s", logger: "tools" }, + { level: "info", message: "Session initialized with capabilities: tools, resources, prompts" }, + { level: "debug", message: "Validating input schema for tool 'batch_process'", logger: "tools" }, + { level: "info", message: "Resource template resolved: db://tables/users/rows/15", logger: "resources" }, + { level: "warning", message: "Memory usage at 78% — consider increasing limits", logger: "system" }, + { level: "info", message: "Client disconnected gracefully", logger: "session" }, + { level: "debug", message: "Flushing log buffer to disk (128 entries)", logger: "logging" }, ]; +function pad(n: number): string { + return n.toString().padStart(2, "0"); +} + +const mixedEntries: LogEntryProps[] = Array.from({ length: 50 }, (_, i) => { + const src = logMessages[i % logMessages.length]; + const seconds = pad(i + 1); + return { + timestamp: `2026-03-17T10:00:${seconds}Z`, + level: src.level, + message: src.message, + logger: src.logger, + }; +}); + export const WithEntries: Story = { args: { controls: makeControls(), diff --git a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx index 9d32d31b..9a146e4e 100644 --- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx +++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx @@ -1,10 +1,11 @@ import { Button, + Card, Checkbox, Container, Grid, Group, - Paper, + ScrollArea, Stack, Text, @@ -34,12 +35,12 @@ export function LoggingScreen({ - + - + - + Log Stream @@ -68,7 +69,7 @@ export function LoggingScreen({ )} - + diff --git a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx index 6628082f..24db4d58 100644 --- a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx +++ b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx @@ -65,6 +65,7 @@ export const WithResult: Story = { onGetPrompt: fn(), }, messages: { + onCopyAll: fn(), messages: [ { role: "user", diff --git a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx index 27273599..2e90baf9 100644 --- a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx +++ b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx @@ -1,4 +1,4 @@ -import { Button, Container, Group, Stack, Text, Title } from "@mantine/core"; +import { Button, Container, Group, Paper, Stack, Text, Title } from "@mantine/core"; import { TaskCard } from "../../molecules/TaskCard/TaskCard"; import type { TaskCardProps } from "../../molecules/TaskCard/TaskCard"; @@ -17,14 +17,14 @@ export function TasksScreen({ }: TasksScreenProps) { return ( + - - - - Active Tasks ({activeTasks.length}) {activeTasks.length === 0 ? ( No active tasks ) : ( @@ -53,6 +53,7 @@ export function TasksScreen({ )} + ); }