* 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>
This commit is contained in:
parent
a7178280f0
commit
dd0f5de820
15 changed files with 812 additions and 117 deletions
|
|
@ -13,6 +13,10 @@ const meta: Meta<typeof HistoryScreen> = {
|
|||
onExport: fn(),
|
||||
onReplay: fn(),
|
||||
onTogglePin: fn(),
|
||||
sortDirection: "newest-first",
|
||||
onSortChange: fn(),
|
||||
compact: true,
|
||||
onToggleCompact: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,10 @@ const baseProps = {
|
|||
onExport: vi.fn(),
|
||||
onReplay: vi.fn(),
|
||||
onTogglePin: vi.fn(),
|
||||
sortDirection: "newest-first" as const,
|
||||
onSortChange: vi.fn(),
|
||||
compact: true,
|
||||
onToggleCompact: vi.fn(),
|
||||
};
|
||||
|
||||
describe("HistoryScreen", () => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { MessageEntry, MessageMethod } from "@inspector/core/mcp/types.js";
|
|||
import { HistoryControls } from "../../groups/HistoryControls/HistoryControls";
|
||||
import { HistoryListPanel } from "../../groups/HistoryListPanel/HistoryListPanel.js";
|
||||
import { extractMethod } from "../../groups/historyUtils.js";
|
||||
import type { SortDirection } from "../../elements/SortToggle/SortToggle";
|
||||
|
||||
export interface HistoryScreenProps {
|
||||
entries: MessageEntry[];
|
||||
|
|
@ -12,6 +13,10 @@ export interface HistoryScreenProps {
|
|||
onExport: () => void;
|
||||
onReplay: (id: string) => void;
|
||||
onTogglePin: (id: string) => void;
|
||||
sortDirection: SortDirection;
|
||||
onSortChange: (next: SortDirection) => void;
|
||||
compact: boolean;
|
||||
onToggleCompact: () => void;
|
||||
}
|
||||
|
||||
const ScreenLayout = Flex.withProps({
|
||||
|
|
@ -38,6 +43,10 @@ export function HistoryScreen({
|
|||
onExport,
|
||||
onReplay,
|
||||
onTogglePin,
|
||||
sortDirection,
|
||||
onSortChange,
|
||||
compact,
|
||||
onToggleCompact,
|
||||
}: HistoryScreenProps) {
|
||||
const [searchText, setSearchText] = useState("");
|
||||
const [methodFilter, setMethodFilter] = useState<MessageMethod | undefined>();
|
||||
|
|
@ -74,6 +83,10 @@ export function HistoryScreen({
|
|||
onExport={onExport}
|
||||
onReplay={onReplay}
|
||||
onTogglePin={onTogglePin}
|
||||
sortDirection={sortDirection}
|
||||
onSortChange={onSortChange}
|
||||
compact={compact}
|
||||
onToggleCompact={onToggleCompact}
|
||||
/>
|
||||
</ScreenLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -13,8 +13,8 @@ const meta: Meta<typeof LoggingScreen> = {
|
|||
onSetLevel: fn(),
|
||||
onClear: fn(),
|
||||
onExport: fn(),
|
||||
autoScroll: true,
|
||||
onToggleAutoScroll: fn(),
|
||||
sortDirection: "newest-first",
|
||||
onSortChange: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,8 +9,8 @@ const baseProps = {
|
|||
onSetLevel: vi.fn(),
|
||||
onClear: vi.fn(),
|
||||
onExport: vi.fn(),
|
||||
autoScroll: true,
|
||||
onToggleAutoScroll: vi.fn(),
|
||||
sortDirection: "newest-first" as const,
|
||||
onSortChange: vi.fn(),
|
||||
};
|
||||
|
||||
describe("LoggingScreen", () => {
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
|
|||
import { LogControls } from "../../groups/LogControls/LogControls";
|
||||
import { LogStreamPanel } from "../../groups/LogStreamPanel/LogStreamPanel";
|
||||
import type { LogEntryData } from "../../elements/LogEntry/LogEntry";
|
||||
import type { SortDirection } from "../../elements/SortToggle/SortToggle";
|
||||
|
||||
export interface LoggingScreenProps {
|
||||
entries: LogEntryData[];
|
||||
|
|
@ -11,8 +12,8 @@ export interface LoggingScreenProps {
|
|||
onSetLevel: (level: LoggingLevel) => void;
|
||||
onClear: () => void;
|
||||
onExport: () => void;
|
||||
autoScroll: boolean;
|
||||
onToggleAutoScroll: () => void;
|
||||
sortDirection: SortDirection;
|
||||
onSortChange: (next: SortDirection) => void;
|
||||
}
|
||||
|
||||
const ALL_LEVELS_VISIBLE: Record<LoggingLevel, boolean> = {
|
||||
|
|
@ -60,8 +61,8 @@ export function LoggingScreen({
|
|||
onSetLevel,
|
||||
onClear,
|
||||
onExport,
|
||||
autoScroll,
|
||||
onToggleAutoScroll,
|
||||
sortDirection,
|
||||
onSortChange,
|
||||
}: LoggingScreenProps) {
|
||||
const [filterText, setFilterText] = useState("");
|
||||
const [visibleLevels, setVisibleLevels] =
|
||||
|
|
@ -95,10 +96,10 @@ export function LoggingScreen({
|
|||
entries={entries}
|
||||
filterText={filterText}
|
||||
visibleLevels={visibleLevels}
|
||||
autoScroll={autoScroll}
|
||||
onToggleAutoScroll={onToggleAutoScroll}
|
||||
onClear={onClear}
|
||||
onExport={onExport}
|
||||
sortDirection={sortDirection}
|
||||
onSortChange={onSortChange}
|
||||
/>
|
||||
</ScreenLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -10,6 +10,10 @@ const meta: Meta<typeof NetworkScreen> = {
|
|||
args: {
|
||||
onClear: fn(),
|
||||
onExport: fn(),
|
||||
sortDirection: "newest-first",
|
||||
onSortChange: fn(),
|
||||
compact: true,
|
||||
onToggleCompact: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -43,6 +43,10 @@ const baseProps = {
|
|||
entries: [transportEntry, authEntry, errorEntry],
|
||||
onClear: vi.fn(),
|
||||
onExport: vi.fn(),
|
||||
sortDirection: "newest-first" as const,
|
||||
onSortChange: vi.fn(),
|
||||
compact: true,
|
||||
onToggleCompact: vi.fn(),
|
||||
};
|
||||
|
||||
describe("NetworkScreen", () => {
|
||||
|
|
|
|||
|
|
@ -6,11 +6,16 @@ import type {
|
|||
} from "@inspector/core/mcp/types.js";
|
||||
import { NetworkControls } from "../../groups/NetworkControls/NetworkControls";
|
||||
import { NetworkStreamPanel } from "../../groups/NetworkStreamPanel/NetworkStreamPanel";
|
||||
import type { SortDirection } from "../../elements/SortToggle/SortToggle";
|
||||
|
||||
export interface NetworkScreenProps {
|
||||
entries: FetchRequestEntry[];
|
||||
onClear: () => void;
|
||||
onExport: () => void;
|
||||
sortDirection: SortDirection;
|
||||
onSortChange: (next: SortDirection) => void;
|
||||
compact: boolean;
|
||||
onToggleCompact: () => void;
|
||||
}
|
||||
|
||||
const ALL_CATEGORIES_VISIBLE: Record<FetchRequestCategory, boolean> = {
|
||||
|
|
@ -44,6 +49,10 @@ export function NetworkScreen({
|
|||
entries,
|
||||
onClear,
|
||||
onExport,
|
||||
sortDirection,
|
||||
onSortChange,
|
||||
compact,
|
||||
onToggleCompact,
|
||||
}: NetworkScreenProps) {
|
||||
const [filterText, setFilterText] = useState("");
|
||||
const [visibleCategories, setVisibleCategories] = useState<
|
||||
|
|
@ -83,6 +92,10 @@ export function NetworkScreen({
|
|||
visibleCategories={visibleCategories}
|
||||
onClear={onClear}
|
||||
onExport={onExport}
|
||||
sortDirection={sortDirection}
|
||||
onSortChange={onSortChange}
|
||||
compact={compact}
|
||||
onToggleCompact={onToggleCompact}
|
||||
/>
|
||||
</ScreenLayout>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -20,6 +20,8 @@ const meta: Meta<typeof ResourcesScreen> = {
|
|||
listChanged: false,
|
||||
subscriptions: [],
|
||||
templates: [],
|
||||
compact: false,
|
||||
onCompactChange: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -26,6 +26,8 @@ const baseProps = {
|
|||
onReadResource: vi.fn(),
|
||||
onSubscribeResource: vi.fn(),
|
||||
onUnsubscribeResource: vi.fn(),
|
||||
compact: false,
|
||||
onCompactChange: vi.fn(),
|
||||
};
|
||||
|
||||
const okResult: ReadResourceResult = {
|
||||
|
|
|
|||
|
|
@ -47,6 +47,8 @@ export interface ResourcesScreenProps {
|
|||
argumentValue: string,
|
||||
context: Record<string, string>,
|
||||
) => Promise<string[]>;
|
||||
compact: boolean;
|
||||
onCompactChange: (next: boolean) => void;
|
||||
}
|
||||
|
||||
const ScreenLayout = Flex.withProps({
|
||||
|
|
@ -112,6 +114,8 @@ export function ResourcesScreen({
|
|||
onSubscribeResource,
|
||||
onUnsubscribeResource,
|
||||
onCompleteArgument,
|
||||
compact,
|
||||
onCompactChange,
|
||||
}: ResourcesScreenProps) {
|
||||
const [selectedResourceUri, setSelectedResourceUri] = useState<
|
||||
string | undefined
|
||||
|
|
@ -252,6 +256,8 @@ export function ResourcesScreen({
|
|||
onSelectUri={handleSelectResource}
|
||||
onSelectTemplate={handleSelectTemplate}
|
||||
onUnsubscribeResource={onUnsubscribeResource}
|
||||
compact={compact}
|
||||
onCompactChange={onCompactChange}
|
||||
/>
|
||||
</SidebarCard>
|
||||
</Sidebar>
|
||||
|
|
|
|||
|
|
@ -18,6 +18,8 @@ const meta: Meta<typeof ServerListScreen> = {
|
|||
onEdit: fn(),
|
||||
onClone: fn(),
|
||||
onRemove: fn(),
|
||||
compact: false,
|
||||
onToggleCompact: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -25,6 +25,8 @@ const baseProps = {
|
|||
onEdit: vi.fn(),
|
||||
onClone: vi.fn(),
|
||||
onRemove: vi.fn(),
|
||||
compact: false,
|
||||
onToggleCompact: vi.fn(),
|
||||
};
|
||||
|
||||
describe("ServerListScreen", () => {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
import { useState } from "react";
|
||||
import { ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core";
|
||||
import type { ServerEntry } from "@inspector/core/mcp/types.js";
|
||||
import { ServerCard } from "../../groups/ServerCard/ServerCard";
|
||||
|
|
@ -19,6 +18,8 @@ export interface ServerListScreenProps {
|
|||
onEdit: (id: string) => void;
|
||||
onClone: (id: string) => void;
|
||||
onRemove: (id: string) => void;
|
||||
compact: boolean;
|
||||
onToggleCompact: () => void;
|
||||
}
|
||||
|
||||
const PageContainer = Stack.withProps({
|
||||
|
|
@ -44,19 +45,15 @@ export function ServerListScreen({
|
|||
onEdit,
|
||||
onClone,
|
||||
onRemove,
|
||||
compact,
|
||||
onToggleCompact,
|
||||
}: ServerListScreenProps) {
|
||||
const [compact, setCompact] = useState<boolean>(false);
|
||||
|
||||
function handleToggleList() {
|
||||
setCompact((prev) => !prev);
|
||||
}
|
||||
|
||||
return (
|
||||
<PageContainer>
|
||||
<ServerListControls
|
||||
serverCount={servers.length}
|
||||
compact={compact}
|
||||
onToggleList={handleToggleList}
|
||||
onToggleList={onToggleCompact}
|
||||
onAddManually={onAddManually}
|
||||
onImportConfig={onImportConfig}
|
||||
onImportServerJson={onImportServerJson}
|
||||
|
|
|
|||
Loading…
Reference in a new issue