* fix(web): section-header hover outline + clear buttons out of tab order (#1487) Two button-behavior adjustments: 1. History section headers (Pinned / History) now use the filter-toggle style — a thin outline on hover instead of a background fill — by pointing the `sectionHeader` Group variant at the shared `.filter-toggle` class. The open section keeps its filled background (passed per-instance via `bg`). 2. Every input clear button added in #1448 is removed from the keyboard tab order via `tabIndex={-1}` (CloseButton clear buttons app-wide, plus the Autocomplete clear button through `theme/Autocomplete.ts` clearButtonProps), so tabbing through a form moves to the next field, not onto the clear button. The buttons stay mouse-clickable. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * fix(web): apply filter-toggle hover outline to Resources accordion headers (#1487) The Resources screen `disclosure` accordion controls now share the same outline-on-hover treatment as the filter toggle buttons and History section headers: a thin border on hover instead of a background fill, plus a filled background when the section is open (keyed off `aria-expanded`). The `.filter- toggle` class is added to the disclosure control slot; the default-variant accordion (Server Settings) keeps its background-fill hover via a `:not(.filter- toggle)` guard. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> * refactor(web): extract ClearButton element, replace 34 inline clear buttons Addresses the code-review suggestion on PR #1488: the same five-line `CloseButton` clear-affordance block (aria-label="Clear" + tabIndex={-1} + onClick) was hand-copied across 17 files. Extract a single `elements/ClearButton` (Mantine `CloseButton.withProps` with the aria-label and tabIndex baked in) and replace all 34 call sites, so the clear-button contract lives in one place. A global CloseButton theme default would be wrong here — it would also catch tabbable modal-close X buttons and the Select clear buttons — so a dedicated element is the right altitude. Net -151 lines. Adds element unit tests + a Storybook play test covering the aria-label / tabIndex / clickable contract for all sites at once. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
2488ca5e38
commit
bb32d1db5f
26 changed files with 180 additions and 196 deletions
|
|
@ -283,14 +283,18 @@
|
||||||
|
|
||||||
/* ── Filter toggle buttons (Logging / History / Network) ─────────
|
/* ── Filter toggle buttons (Logging / History / Network) ─────────
|
||||||
*
|
*
|
||||||
* Shared by the FilterToggleButton element. Unlike `.list-item`, hover is a
|
* Shared by the FilterToggleButton element, the History list's collapsible
|
||||||
* thin border rather than a background fill, and the active (on) state is the
|
* section headers (the `sectionHeader` Group variant), and the Resources
|
||||||
* filled background. Keeping the two states distinct means toggling a button
|
* disclosure accordion controls (the `disclosure` Accordion variant). The
|
||||||
* off while the cursor is still over it is immediately visible, instead of the
|
* active (on / open) state is keyed off `aria-pressed` for the toggle buttons
|
||||||
* hover background masking the removal of the active background (issue #1460).
|
* and `aria-expanded` for the accordion controls. Unlike `.list-item`,
|
||||||
* The 1px transparent border is reserved here (not as an inline style in the
|
* hover is a thin border rather than a background fill, and the active (on)
|
||||||
* theme variant — inline styles outrank stylesheet `:hover` rules) so the hover
|
* state is the filled background. Keeping the two states distinct means
|
||||||
* border doesn't shift layout. */
|
* toggling a button off while the cursor is still over it is immediately
|
||||||
|
* visible, instead of the hover background masking the removal of the active
|
||||||
|
* background (issue #1460). The 1px transparent border is reserved here (not as
|
||||||
|
* an inline style in the theme variant — inline styles outrank stylesheet
|
||||||
|
* `:hover` rules) so the hover border doesn't shift layout. */
|
||||||
|
|
||||||
.filter-toggle {
|
.filter-toggle {
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
|
|
@ -301,7 +305,8 @@
|
||||||
border-color: var(--inspector-brand-primary);
|
border-color: var(--inspector-brand-primary);
|
||||||
}
|
}
|
||||||
|
|
||||||
.filter-toggle[aria-pressed="true"] {
|
.filter-toggle[aria-pressed="true"],
|
||||||
|
.filter-toggle[aria-expanded="true"] {
|
||||||
background-color: var(--mantine-primary-color-light);
|
background-color: var(--mantine-primary-color-light);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -342,7 +347,11 @@
|
||||||
|
|
||||||
/* ── Accordion button hover ─────────────────────────────── */
|
/* ── Accordion button hover ─────────────────────────────── */
|
||||||
|
|
||||||
.mantine-Accordion-control:hover {
|
/* Default accordions (e.g. Server Settings) keep the background-fill hover.
|
||||||
|
* The Resources `disclosure` accordion opts out via `.filter-toggle` (added to
|
||||||
|
* its control slot in theme/Accordion.ts) so it gets the outline-on-hover
|
||||||
|
* treatment instead — see the `.filter-toggle` rules above. */
|
||||||
|
.mantine-Accordion-control:not(.filter-toggle):hover {
|
||||||
background-color: var(--mantine-primary-color-light-hover);
|
background-color: var(--mantine-primary-color-light-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,21 @@
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { expect, userEvent, within } from "storybook/test";
|
||||||
|
import { ClearButton } from "./ClearButton";
|
||||||
|
|
||||||
|
const meta: Meta<typeof ClearButton> = {
|
||||||
|
title: "Elements/ClearButton",
|
||||||
|
component: ClearButton,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ClearButton>;
|
||||||
|
|
||||||
|
// Default: labelled "Clear", out of the tab order, but still mouse-clickable.
|
||||||
|
export const Default: Story = {
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
const button = await canvas.findByRole("button", { name: "Clear" });
|
||||||
|
await expect(button).toHaveAttribute("tabindex", "-1");
|
||||||
|
await userEvent.click(button);
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { describe, it, expect, vi } from "vitest";
|
||||||
|
import userEvent from "@testing-library/user-event";
|
||||||
|
import { renderWithMantine, screen } from "../../../test/renderWithMantine";
|
||||||
|
import { ClearButton } from "./ClearButton";
|
||||||
|
|
||||||
|
describe("ClearButton", () => {
|
||||||
|
it('renders with the "Clear" accessible name', () => {
|
||||||
|
renderWithMantine(<ClearButton />);
|
||||||
|
expect(screen.getByRole("button", { name: "Clear" })).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("is removed from the keyboard tab order", () => {
|
||||||
|
renderWithMantine(<ClearButton />);
|
||||||
|
expect(screen.getByRole("button", { name: "Clear" })).toHaveAttribute(
|
||||||
|
"tabindex",
|
||||||
|
"-1",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("invokes onClick when clicked", async () => {
|
||||||
|
const user = userEvent.setup();
|
||||||
|
const onClick = vi.fn();
|
||||||
|
renderWithMantine(<ClearButton onClick={onClick} />);
|
||||||
|
await user.click(screen.getByRole("button", { name: "Clear" }));
|
||||||
|
expect(onClick).toHaveBeenCalledTimes(1);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("allows the accessible name to be overridden", () => {
|
||||||
|
renderWithMantine(<ClearButton aria-label="Clear search" />);
|
||||||
|
expect(
|
||||||
|
screen.getByRole("button", { name: "Clear search" }),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,14 @@
|
||||||
|
import { CloseButton } from "@mantine/core";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The clear (×) affordance shown in a populated text input's right section
|
||||||
|
* (`rightSection`). Wraps Mantine's `CloseButton` with a fixed
|
||||||
|
* `aria-label="Clear"` and `tabIndex={-1}`, so the button stays mouse-clickable
|
||||||
|
* but is skipped during keyboard tab navigation — tabbing through a form lands
|
||||||
|
* on the next field, not on the clear button (see #1487). Pass `onClick` to
|
||||||
|
* reset the field's value to "". Both presets can still be overridden per-site.
|
||||||
|
*/
|
||||||
|
export const ClearButton = CloseButton.withProps({
|
||||||
|
"aria-label": "Clear",
|
||||||
|
tabIndex: -1,
|
||||||
|
});
|
||||||
|
|
@ -1,5 +1,4 @@
|
||||||
import {
|
import {
|
||||||
CloseButton,
|
|
||||||
Group,
|
Group,
|
||||||
ScrollArea,
|
ScrollArea,
|
||||||
Stack,
|
Stack,
|
||||||
|
|
@ -7,6 +6,7 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
import type { Tool } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||||
import { AppListItem } from "../AppListItem/AppListItem";
|
import { AppListItem } from "../AppListItem/AppListItem";
|
||||||
|
|
@ -64,12 +64,7 @@ export function AppControls({
|
||||||
onChange={(e) => onSearchChange(e.currentTarget.value)}
|
onChange={(e) => onSearchChange(e.currentTarget.value)}
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchText ? (
|
searchText ? <ClearButton onClick={() => onSearchChange("")} /> : null
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onSearchChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
|
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
|
||||||
|
|
|
||||||
|
|
@ -5,7 +5,6 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CloseButton,
|
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
|
|
@ -15,6 +14,7 @@ import {
|
||||||
Textarea,
|
Textarea,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type {
|
import type {
|
||||||
ClientCapabilities,
|
ClientCapabilities,
|
||||||
JSONRPCErrorResponse,
|
JSONRPCErrorResponse,
|
||||||
|
|
@ -248,8 +248,7 @@ export function ExperimentalFeaturesPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
header.key ? (
|
header.key ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onHeaderChange(index, "", header.value)}
|
onClick={() => onHeaderChange(index, "", header.value)}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
@ -264,8 +263,7 @@ export function ExperimentalFeaturesPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
header.value ? (
|
header.value ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onHeaderChange(index, header.key, "")}
|
onClick={() => onHeaderChange(index, header.key, "")}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
@ -291,10 +289,7 @@ export function ExperimentalFeaturesPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
requestDraft ? (
|
requestDraft ? (
|
||||||
<CloseButton
|
<ClearButton onClick={() => onRequestChange("")} />
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onRequestChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -45,6 +45,14 @@ describe("HistoryControls", () => {
|
||||||
expect(onSearchChange).toHaveBeenCalledWith("");
|
expect(onSearchChange).toHaveBeenCalledWith("");
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it("keeps the search Clear button out of the keyboard tab order", () => {
|
||||||
|
renderWithMantine(<HistoryControls {...baseProps} searchText="abc" />);
|
||||||
|
expect(screen.getByRole("button", { name: "Clear" })).toHaveAttribute(
|
||||||
|
"tabindex",
|
||||||
|
"-1",
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
it("renders the method filter placeholder", () => {
|
it("renders the method filter placeholder", () => {
|
||||||
renderWithMantine(<HistoryControls {...baseProps} />);
|
renderWithMantine(<HistoryControls {...baseProps} />);
|
||||||
expect(screen.getByPlaceholderText("All methods")).toBeInTheDocument();
|
expect(screen.getByPlaceholderText("All methods")).toBeInTheDocument();
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,5 @@
|
||||||
import { CloseButton, Select, Stack, TextInput, Title } from "@mantine/core";
|
import { Select, Stack, TextInput, Title } from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type {
|
import type {
|
||||||
MessageMethod,
|
MessageMethod,
|
||||||
MessageOrigin,
|
MessageOrigin,
|
||||||
|
|
@ -35,12 +36,7 @@ export function HistoryControls({
|
||||||
onChange={(event) => onSearchChange(event.currentTarget.value)}
|
onChange={(event) => onSearchChange(event.currentTarget.value)}
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchText ? (
|
searchText ? <ClearButton onClick={() => onSearchChange("")} /> : null
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onSearchChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -58,8 +58,9 @@ const EmptyState = Text.withProps({
|
||||||
py: "xl",
|
py: "xl",
|
||||||
});
|
});
|
||||||
|
|
||||||
// The section header is a single "pleat" bar (rounded, hover-highlighted, with
|
// The section header is a single "pleat" bar (rounded, with the filter-button
|
||||||
// the active background passed per instance via `bg`). Inside it sit the
|
// outline-on-hover treatment and the active background passed per instance via
|
||||||
|
// `bg`). Inside it sit the
|
||||||
// clickable toggle area (the title, filling the left) and the optional
|
// clickable toggle area (the title, filling the left) and the optional
|
||||||
// Clear/Export actions on the right — so the actions live on the pleat itself,
|
// Clear/Export actions on the right — so the actions live on the pleat itself,
|
||||||
// not beside it. The toggle is its own button (the actions can't nest inside a
|
// not beside it. The toggle is its own button (the actions can't nest inside a
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Radio,
|
Radio,
|
||||||
|
|
@ -10,6 +9,7 @@ import {
|
||||||
Textarea,
|
Textarea,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { InspectorServerJsonDraft } from "@inspector/core/mcp/types.js";
|
import type { InspectorServerJsonDraft } from "@inspector/core/mcp/types.js";
|
||||||
|
|
||||||
export interface ValidationResult {
|
export interface ValidationResult {
|
||||||
|
|
@ -92,7 +92,7 @@ export function ImportServerJsonPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
draft.rawText ? (
|
draft.rawText ? (
|
||||||
<CloseButton aria-label="Clear" onClick={() => onJsonChange("")} />
|
<ClearButton onClick={() => onJsonChange("")} />
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
@ -149,8 +149,7 @@ export function ImportServerJsonPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
envVar.value ? (
|
envVar.value ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onEnvVarChange(envVar.name, "")}
|
onClick={() => onEnvVarChange(envVar.name, "")}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
@ -169,10 +168,7 @@ export function ImportServerJsonPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
draft.nameOverride ? (
|
draft.nameOverride ? (
|
||||||
<CloseButton
|
<ClearButton onClick={() => onServerNameChange("")} />
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onServerNameChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,5 @@
|
||||||
import {
|
import { Button, Group, Select, Stack, TextInput, Title } from "@mantine/core";
|
||||||
Button,
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
CloseButton,
|
|
||||||
Group,
|
|
||||||
Select,
|
|
||||||
Stack,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
|
import type { LoggingLevel } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton";
|
import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton";
|
||||||
|
|
||||||
|
|
@ -66,12 +59,7 @@ export function LogControls({
|
||||||
onChange={(e) => onFilterChange(e.currentTarget.value)}
|
onChange={(e) => onFilterChange(e.currentTarget.value)}
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
filterText ? (
|
filterText ? <ClearButton onClick={() => onFilterChange("")} /> : null
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onFilterChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { Button, Group, Stack, TextInput, Title } from "@mantine/core";
|
||||||
Button,
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
CloseButton,
|
|
||||||
Group,
|
|
||||||
Stack,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import type { FetchRequestCategory } from "@inspector/core/mcp/types.js";
|
import type { FetchRequestCategory } from "@inspector/core/mcp/types.js";
|
||||||
import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton";
|
import { FilterToggleButton } from "../../elements/FilterToggleButton/FilterToggleButton";
|
||||||
|
|
||||||
|
|
@ -47,12 +41,7 @@ export function NetworkControls({
|
||||||
onChange={(e) => onFilterChange(e.currentTarget.value)}
|
onChange={(e) => onFilterChange(e.currentTarget.value)}
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
filterText ? (
|
filterText ? <ClearButton onClick={() => onFilterChange("")} /> : null
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onFilterChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import { useCallback, useEffect, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { Prompt } from "@modelcontextprotocol/sdk/types.js";
|
import type { Prompt } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
export interface PromptArgumentsFormProps {
|
export interface PromptArgumentsFormProps {
|
||||||
|
|
@ -225,10 +225,7 @@ export function PromptArgumentsForm({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
argumentValues[arg.name] ? (
|
argumentValues[arg.name] ? (
|
||||||
<CloseButton
|
<ClearButton onClick={() => handleChange(arg.name, "")} />
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => handleChange(arg.name, "")}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { Group, ScrollArea, Stack, TextInput, Title } from "@mantine/core";
|
||||||
CloseButton,
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
Group,
|
|
||||||
ScrollArea,
|
|
||||||
Stack,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import type { Prompt } from "@modelcontextprotocol/sdk/types.js";
|
import type { Prompt } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
import { ListChangedIndicator } from "../../elements/ListChangedIndicator/ListChangedIndicator";
|
||||||
import { PromptListItem } from "../PromptListItem/PromptListItem";
|
import { PromptListItem } from "../PromptListItem/PromptListItem";
|
||||||
|
|
@ -56,12 +50,7 @@ export function PromptControls({
|
||||||
onChange={(e) => onSearchChange(e.currentTarget.value)}
|
onChange={(e) => onSearchChange(e.currentTarget.value)}
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchText ? (
|
searchText ? <ClearButton onClick={() => onSearchChange("")} /> : null
|
||||||
<CloseButton
|
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onSearchChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
|
<ScrollArea.Autosize viewportRef={viewportRef} mah={LIST_MAX_HEIGHT}>
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,5 @@
|
||||||
import {
|
import { Accordion, Group, Stack, TextInput, Title } from "@mantine/core";
|
||||||
Accordion,
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
CloseButton,
|
|
||||||
Group,
|
|
||||||
Stack,
|
|
||||||
TextInput,
|
|
||||||
Title,
|
|
||||||
} from "@mantine/core";
|
|
||||||
import { RiArrowRightSLine } from "react-icons/ri";
|
import { RiArrowRightSLine } from "react-icons/ri";
|
||||||
import type {
|
import type {
|
||||||
Resource,
|
Resource,
|
||||||
|
|
@ -186,10 +180,7 @@ export function ResourceControls({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
searchText ? (
|
searchText ? (
|
||||||
<CloseButton
|
<ClearButton onClick={() => onSearchChange("")} />
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onSearchChange("")}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -2,13 +2,13 @@ import { useCallback, useEffect, useMemo, useRef, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Autocomplete,
|
Autocomplete,
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";
|
import type { ResourceTemplate } from "@modelcontextprotocol/sdk/types.js";
|
||||||
import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge";
|
import { AnnotationBadge } from "../../elements/AnnotationBadge/AnnotationBadge";
|
||||||
import { CopyButton } from "../../elements/CopyButton/CopyButton";
|
import { CopyButton } from "../../elements/CopyButton/CopyButton";
|
||||||
|
|
@ -265,8 +265,7 @@ export function ResourceTemplatePanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
variables[varName] ? (
|
variables[varName] ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => handleVariableChange(varName, "")}
|
onClick={() => handleVariableChange(varName, "")}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
|
||||||
|
|
@ -2,7 +2,6 @@ import {
|
||||||
ActionIcon,
|
ActionIcon,
|
||||||
Alert,
|
Alert,
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Stack,
|
Stack,
|
||||||
|
|
@ -11,6 +10,7 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { Root } from "@modelcontextprotocol/sdk/types.js";
|
import type { Root } from "@modelcontextprotocol/sdk/types.js";
|
||||||
|
|
||||||
export type RootDraft = { name: string; uri: string };
|
export type RootDraft = { name: string; uri: string };
|
||||||
|
|
@ -94,8 +94,7 @@ export function RootsTable({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
newRootDraft.name ? (
|
newRootDraft.name ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onNewRootDraftChange({ ...newRootDraft, name: "" })
|
onNewRootDraftChange({ ...newRootDraft, name: "" })
|
||||||
}
|
}
|
||||||
|
|
@ -115,8 +114,7 @@ export function RootsTable({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
newRootDraft.uri ? (
|
newRootDraft.uri ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onNewRootDraftChange({ ...newRootDraft, uri: "" })}
|
onClick={() => onNewRootDraftChange({ ...newRootDraft, uri: "" })}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import {
|
import {
|
||||||
Badge,
|
Badge,
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Divider,
|
Divider,
|
||||||
Group,
|
Group,
|
||||||
Paper,
|
Paper,
|
||||||
|
|
@ -12,6 +11,7 @@ import {
|
||||||
Textarea,
|
Textarea,
|
||||||
Title,
|
Title,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type {
|
import type {
|
||||||
CreateMessageRequestParams,
|
CreateMessageRequestParams,
|
||||||
CreateMessageResult,
|
CreateMessageResult,
|
||||||
|
|
@ -159,8 +159,7 @@ export function SamplingRequestPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
draftResult.content.type === "text" && draftResult.content.text ? (
|
draftResult.content.type === "text" && draftResult.content.text ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
onResultChange({
|
onResultChange({
|
||||||
...draftResult,
|
...draftResult,
|
||||||
|
|
@ -181,8 +180,7 @@ export function SamplingRequestPanel({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
draftResult.model ? (
|
draftResult.model ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => onResultChange({ ...draftResult, model: "" })}
|
onClick={() => onResultChange({ ...draftResult, model: "" })}
|
||||||
/>
|
/>
|
||||||
) : null
|
) : null
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
Checkbox,
|
Checkbox,
|
||||||
CloseButton,
|
|
||||||
JsonInput,
|
JsonInput,
|
||||||
MultiSelect,
|
MultiSelect,
|
||||||
NumberInput,
|
NumberInput,
|
||||||
|
|
@ -9,6 +8,7 @@ import {
|
||||||
Text,
|
Text,
|
||||||
TextInput,
|
TextInput,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type { JsonSchemaType } from "../../../utils/jsonUtils";
|
import type { JsonSchemaType } from "../../../utils/jsonUtils";
|
||||||
|
|
||||||
const FieldLabel = Text.withProps({
|
const FieldLabel = Text.withProps({
|
||||||
|
|
@ -119,10 +119,7 @@ export function SchemaForm({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
rawValue ? (
|
rawValue ? (
|
||||||
<CloseButton
|
<ClearButton onClick={() => handleFieldChange(fieldName, "")} />
|
||||||
aria-label="Clear"
|
|
||||||
onClick={() => handleFieldChange(fieldName, "")}
|
|
||||||
/>
|
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,6 @@
|
||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
CloseButton,
|
|
||||||
Group,
|
Group,
|
||||||
Modal,
|
Modal,
|
||||||
Select,
|
Select,
|
||||||
|
|
@ -10,6 +9,7 @@ import {
|
||||||
TextInput,
|
TextInput,
|
||||||
Textarea,
|
Textarea,
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
import type {
|
import type {
|
||||||
MCPServerConfig,
|
MCPServerConfig,
|
||||||
StdioServerConfig,
|
StdioServerConfig,
|
||||||
|
|
@ -279,8 +279,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.id ? (
|
form.id ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, id: "" }))}
|
onClick={() => setForm((f) => ({ ...f, id: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
@ -321,8 +320,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.command ? (
|
form.command ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, command: "" }))}
|
onClick={() => setForm((f) => ({ ...f, command: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
@ -344,8 +342,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.argsText ? (
|
form.argsText ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, argsText: "" }))}
|
onClick={() => setForm((f) => ({ ...f, argsText: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
@ -367,8 +364,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.envText ? (
|
form.envText ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, envText: "" }))}
|
onClick={() => setForm((f) => ({ ...f, envText: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
@ -387,8 +383,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.cwd ? (
|
form.cwd ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, cwd: "" }))}
|
onClick={() => setForm((f) => ({ ...f, cwd: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
@ -410,8 +405,7 @@ export function ServerConfigModal({
|
||||||
rightSectionPointerEvents="auto"
|
rightSectionPointerEvents="auto"
|
||||||
rightSection={
|
rightSection={
|
||||||
form.url ? (
|
form.url ? (
|
||||||
<CloseButton
|
<ClearButton
|
||||||
aria-label="Clear"
|
|
||||||
disabled={submitting}
|
disabled={submitting}
|
||||||
onClick={() => setForm((f) => ({ ...f, url: "" }))}
|
onClick={() => setForm((f) => ({ ...f, url: "" }))}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue