* feat(sort): add SortToggle for Logs, History, and Network (#1371) Introduces a reusable SortToggle element and wires it into the Logs, History, and Network screens so users can flip chronological order. Selection is persisted per-screen to localStorage under the inspector.sortDirection.* namespace, with invalid stored values falling back to the newest-first default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(logs): move auto-scroll checkbox left of the sort selector Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(logs): remove auto-scroll checkbox; ListToggle left of SortToggle - Drops the Logs auto-scroll checkbox and its plumbing through LoggingScreen / InspectorView. - Moves the ListToggle to the left of the SortToggle on the History and Network panel toolbars. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(sort): address review on #1372 - Hydrate sort direction synchronously (getInitialValueInEffect: false) to remove the one-frame newest-first flicker on first paint. - Drop the redundant `[...matched].sort()` copy across the three panels; `.filter()` already returns a fresh array. - Document why serializeSortDirection bypasses Mantine's default JSON stringify (raw enum value in storage). - MemoryStorage.getItem: collapse `has` + `!` to `?? null`. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(history): default the request list to collapsed for parity with Network The History list opened expanded while Network opened collapsed; align History with Network so both panels start in the same compact view. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(history,network): persist list compact state to localStorage Lifts the per-panel `compact` flag out of HistoryListPanel / NetworkStreamPanel and into InspectorView's localStorage hooks, mirroring the sort-direction shape: inspector.listCompact.history inspector.listCompact.network Defaults to collapsed (compact=true) on both, with a corrupted-value deserializer that clamps unknown strings back to the default. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * feat(servers,resources): persist ListToggle state to localStorage Lifts the per-screen compact preference out of ServerListScreen and ResourceControls into InspectorView's localStorage hooks, matching the History/Network shape: inspector.listCompact.servers inspector.listCompact.resources Servers and Resources both default to expanded (compact=false). For ResourceControls the displayed compact is still derived from openSections (so per-section accordion clicks still work locally), and only explicit ListToggle clicks update the persisted preference — the handler computes the next compact value from the click outcome rather than blindly flipping, so a half-open accordion still records the right post-state. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(sort-toggle): switch to icon button matching ListToggle style Replaces the Mantine Select with a subtle Button (or ActionIcon for the subtle variant), using TbSortDescending2 for newest-first and TbSortAscending2 for oldest-first. Clicking the button flips between the two values. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(sort-toggle): restore Select with sort icon in rightSection Reverts SortToggle to a Mantine Select with simplified labels ("Newest First" / "Oldest First") and the corresponding TbSortDescending2 / TbSortAscending2 icon in the rightSection, replacing the default chevron. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sort-toggle): swap icons so they match label direction Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sort-toggle): use down arrow for newest, up arrow for oldest Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * refactor(inspector-view): extract useSortDirection / useListCompact Collapses seven near-identical useLocalStorage call sites in InspectorView into two scoped helpers. Each helper carries the shared namespace, serialize/deserialize adapters, and getInitialValueInEffect: false; the call sites now read as "this screen has sort persisted to this scope" rather than re-stating the storage shape every time. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * fix(sort-toggle): apply prettier formatting Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
103 lines
2.3 KiB
TypeScript
103 lines
2.3 KiB
TypeScript
import type { Meta, StoryObj } from "@storybook/react-vite";
|
|
import { fn } from "storybook/test";
|
|
import { LoggingScreen } from "./LoggingScreen";
|
|
import { mixedEntries } from "./LoggingScreen.fixtures";
|
|
import type { LogEntryData } from "../../elements/LogEntry/LogEntry";
|
|
|
|
const meta: Meta<typeof LoggingScreen> = {
|
|
title: "Screens/LoggingScreen",
|
|
component: LoggingScreen,
|
|
parameters: { layout: "fullscreen" },
|
|
args: {
|
|
currentLevel: "info",
|
|
onSetLevel: fn(),
|
|
onClear: fn(),
|
|
onExport: fn(),
|
|
sortDirection: "newest-first",
|
|
onSortChange: fn(),
|
|
},
|
|
};
|
|
|
|
export default meta;
|
|
type Story = StoryObj<typeof LoggingScreen>;
|
|
|
|
export const Empty: Story = {
|
|
args: {
|
|
entries: [],
|
|
},
|
|
};
|
|
|
|
export const WithEntries: Story = {
|
|
args: {
|
|
entries: mixedEntries,
|
|
},
|
|
};
|
|
|
|
const allLevelEntries: LogEntryData[] = [
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:01Z"),
|
|
params: {
|
|
level: "debug",
|
|
data: "Resolving transport handler for stdio connection",
|
|
logger: "transport",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:02Z"),
|
|
params: { level: "info", data: "MCP session initialized successfully" },
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:03Z"),
|
|
params: {
|
|
level: "notice",
|
|
data: "Server capabilities negotiated: tools, resources, prompts",
|
|
logger: "session",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:04Z"),
|
|
params: {
|
|
level: "warning",
|
|
data: "Rate limit approaching: 85% of quota used",
|
|
logger: "ratelimit",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:05Z"),
|
|
params: {
|
|
level: "error",
|
|
data: "Tool execution failed: timeout after 30s",
|
|
logger: "tools",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:06Z"),
|
|
params: {
|
|
level: "critical",
|
|
data: "Database connection pool exhausted",
|
|
logger: "db",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:07Z"),
|
|
params: {
|
|
level: "alert",
|
|
data: "Memory usage exceeds 95% threshold",
|
|
logger: "system",
|
|
},
|
|
},
|
|
{
|
|
receivedAt: new Date("2026-03-17T10:00:08Z"),
|
|
params: {
|
|
level: "emergency",
|
|
data: "System unresponsive - initiating graceful shutdown",
|
|
logger: "system",
|
|
},
|
|
},
|
|
];
|
|
|
|
export const MixedLevels: Story = {
|
|
args: {
|
|
entries: allLevelEntries,
|
|
},
|
|
};
|