feat(web): add copyright notice + footer/toolbar polish (#1639)
- Add a grey CopyrightBadge ("Copyright © Model Context Protocol a Series of LF
Projects, LLC.") to the footer. Version (bottom-left) and copyright
(bottom-right) share the bottom `xl` margin band: vertically centered in it
and inset by `xl` to line up with the content's left/right margins.
- Move the list expand/collapse toggle to the right of the "Add Servers" button
in ServerListControls.
- Make the monitoring column's close-sidebar control match the open-sidebar
PinColumnButton theme (Button size sm / variant subtle) instead of a gray
ActionIcon, so the open/close affordances read as a pair.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
This commit is contained in:
parent
650e5bd020
commit
5d024ab54f
9 changed files with 88 additions and 18 deletions
|
|
@ -102,6 +102,7 @@ import { useInspectorVersion } from "@inspector/core/react/useInspectorVersion.j
|
||||||
import { usePendingClientRequests } from "@inspector/core/react/usePendingClientRequests.js";
|
import { usePendingClientRequests } from "@inspector/core/react/usePendingClientRequests.js";
|
||||||
import { InspectorView } from "./components/views/InspectorView/InspectorView";
|
import { InspectorView } from "./components/views/InspectorView/InspectorView";
|
||||||
import { VersionBadge } from "./components/elements/VersionBadge/VersionBadge";
|
import { VersionBadge } from "./components/elements/VersionBadge/VersionBadge";
|
||||||
|
import { CopyrightBadge } from "./components/elements/CopyrightBadge/CopyrightBadge";
|
||||||
import type {
|
import type {
|
||||||
ToolCallState,
|
ToolCallState,
|
||||||
ToolsUiState,
|
ToolsUiState,
|
||||||
|
|
@ -3824,6 +3825,7 @@ function App() {
|
||||||
onRefreshApps={onRefreshTools}
|
onRefreshApps={onRefreshTools}
|
||||||
/>
|
/>
|
||||||
</Box>
|
</Box>
|
||||||
|
<CopyrightBadge />
|
||||||
<VersionBadge version={inspectorVersion} />
|
<VersionBadge version={inspectorVersion} />
|
||||||
<ServerConfigModal
|
<ServerConfigModal
|
||||||
opened={configModal !== null}
|
opened={configModal !== null}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { expect, within } from "storybook/test";
|
||||||
|
import { CopyrightBadge, COPYRIGHT_NOTICE } from "./CopyrightBadge";
|
||||||
|
|
||||||
|
const meta: Meta<typeof CopyrightBadge> = {
|
||||||
|
title: "Elements/CopyrightBadge",
|
||||||
|
component: CopyrightBadge,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof CopyrightBadge>;
|
||||||
|
|
||||||
|
// The grey copyright notice pinned to the lower-left corner of the viewport.
|
||||||
|
export const Default: Story = {
|
||||||
|
play: async ({ canvasElement }) => {
|
||||||
|
const canvas = within(canvasElement);
|
||||||
|
await expect(canvas.getByText(COPYRIGHT_NOTICE)).toBeInTheDocument();
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,17 @@
|
||||||
|
import { describe, it, expect } from "vitest";
|
||||||
|
import { renderWithMantine, screen } from "../../../test/renderWithMantine";
|
||||||
|
import { CopyrightBadge, COPYRIGHT_NOTICE } from "./CopyrightBadge";
|
||||||
|
|
||||||
|
describe("CopyrightBadge", () => {
|
||||||
|
it("renders the project copyright notice", () => {
|
||||||
|
renderWithMantine(<CopyrightBadge />);
|
||||||
|
expect(screen.getByText(COPYRIGHT_NOTICE)).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
|
||||||
|
it("names the Model Context Protocol and LF Projects", () => {
|
||||||
|
renderWithMantine(<CopyrightBadge />);
|
||||||
|
expect(
|
||||||
|
screen.getByText(/Model Context Protocol.*Series of LF Projects, LLC\./),
|
||||||
|
).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
import { Text } from "@mantine/core";
|
||||||
|
|
||||||
|
/** The project's copyright notice. */
|
||||||
|
export const COPYRIGHT_NOTICE =
|
||||||
|
"Copyright © Model Context Protocol a Series of LF Projects, LLC.";
|
||||||
|
|
||||||
|
// The `copyrightBadge` variant (src/theme/Text.ts) pins it to the lower-right
|
||||||
|
// corner in grey, on the same row as the version badge, and non-interactive.
|
||||||
|
const CopyrightText = Text.withProps({ variant: "copyrightBadge" });
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The project copyright notice, fixed to the lower-right corner of the screen
|
||||||
|
* (#1639) — the grey, non-interactive twin of the lower-left version badge,
|
||||||
|
* sharing its row.
|
||||||
|
*/
|
||||||
|
export function CopyrightBadge() {
|
||||||
|
return <CopyrightText>{COPYRIGHT_NOTICE}</CopyrightText>;
|
||||||
|
}
|
||||||
|
|
@ -5,12 +5,12 @@ export interface VersionBadgeProps {
|
||||||
version?: string;
|
version?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The `versionBadge` variant (src/theme/Text.ts) pins it to the lower-right
|
// The `versionBadge` variant (src/theme/Text.ts) pins it to the lower-left
|
||||||
// corner in grey and makes it non-interactive.
|
// corner in grey and makes it non-interactive.
|
||||||
const VersionText = Text.withProps({ variant: "versionBadge" });
|
const VersionText = Text.withProps({ variant: "versionBadge" });
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A small, unobtrusive build-version label fixed to the lower-right corner of
|
* A small, unobtrusive build-version label fixed to the lower-left corner of
|
||||||
* the screen (#1639). Sourced from the root `package.json` via the backend's
|
* the screen (#1639). Sourced from the root `package.json` via the backend's
|
||||||
* `GET /api/config`; renders nothing until the version is known (or on a legacy
|
* `GET /api/config`; renders nothing until the version is known (or on a legacy
|
||||||
* backend that omits it).
|
* backend that omits it).
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { ActionIcon, Group, SegmentedControl, TextInput } from "@mantine/core";
|
import { Button, Group, SegmentedControl, TextInput } from "@mantine/core";
|
||||||
import { TbLayoutSidebarRightCollapse } from "react-icons/tb";
|
import { TbLayoutSidebarRightCollapse } from "react-icons/tb";
|
||||||
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
import { ClearButton } from "../../elements/ClearButton/ClearButton";
|
||||||
|
|
||||||
|
|
@ -69,15 +69,14 @@ export function MonitoringControls({
|
||||||
) : null
|
) : null
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
<ActionIcon
|
<Button
|
||||||
|
size="sm"
|
||||||
variant="subtle"
|
variant="subtle"
|
||||||
color="gray"
|
|
||||||
size="lg"
|
|
||||||
aria-label="Close monitoring column"
|
aria-label="Close monitoring column"
|
||||||
onClick={onClose}
|
onClick={onClose}
|
||||||
>
|
>
|
||||||
<TbLayoutSidebarRightCollapse size={20} />
|
<TbLayoutSidebarRightCollapse size={20} />
|
||||||
</ActionIcon>
|
</Button>
|
||||||
</ControlsBar>
|
</ControlsBar>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -39,8 +39,9 @@ describe("ServerListControls", () => {
|
||||||
onToggleList={onToggleList}
|
onToggleList={onToggleList}
|
||||||
/>,
|
/>,
|
||||||
);
|
);
|
||||||
const buttons = screen.getAllByRole("button");
|
await user.click(
|
||||||
await user.click(buttons[0]);
|
screen.getByRole("button", { name: /Expand all|Collapse all/ }),
|
||||||
|
);
|
||||||
expect(onToggleList).toHaveBeenCalledTimes(1);
|
expect(onToggleList).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,9 +27,6 @@ export function ServerListControls({
|
||||||
}: ServerListControlsProps) {
|
}: ServerListControlsProps) {
|
||||||
return (
|
return (
|
||||||
<Group justify="flex-end">
|
<Group justify="flex-end">
|
||||||
{serverCount > 0 && (
|
|
||||||
<ListToggle compact={compact} onToggle={onToggleList} />
|
|
||||||
)}
|
|
||||||
<Button variant="default" onClick={onExport} disabled={serverCount === 0}>
|
<Button variant="default" onClick={onExport} disabled={serverCount === 0}>
|
||||||
Export
|
Export
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -40,6 +37,9 @@ export function ServerListControls({
|
||||||
onImportServerJson={onImportServerJson}
|
onImportServerJson={onImportServerJson}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
{serverCount > 0 && (
|
||||||
|
<ListToggle compact={compact} onToggle={onToggleList} />
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -37,15 +37,29 @@ export const ThemeText = Text.extend({
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
// A small, unobtrusive build-version label pinned to the lower-right corner
|
// Two small, unobtrusive labels pinned to the bottom corners of the screen
|
||||||
// of the screen (#1639): grey, non-interactive (clicks pass through), and
|
// on the same row (#1639): the build version (bottom-left) and the
|
||||||
// out of the tab/selection flow so it never interferes with the UI beneath.
|
// copyright notice (bottom-right). Each occupies the full-height `xl` bottom
|
||||||
if (props.variant === "versionBadge") {
|
// margin band (`height: xl` + flex centering) so the text sits vertically
|
||||||
|
// centered in it, and insets by `xl` horizontally to line up with the
|
||||||
|
// content's left/right margins. Both are grey, non-interactive (clicks pass
|
||||||
|
// through), and out of the tab/selection flow.
|
||||||
|
if (
|
||||||
|
props.variant === "versionBadge" ||
|
||||||
|
props.variant === "copyrightBadge"
|
||||||
|
) {
|
||||||
|
const horizontal =
|
||||||
|
props.variant === "versionBadge"
|
||||||
|
? { left: "var(--mantine-spacing-xl)" }
|
||||||
|
: { right: "var(--mantine-spacing-xl)" };
|
||||||
return {
|
return {
|
||||||
root: {
|
root: {
|
||||||
position: "fixed",
|
position: "fixed",
|
||||||
bottom: "0.35rem",
|
bottom: 0,
|
||||||
right: "0.6rem",
|
height: "var(--mantine-spacing-xl)",
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
...horizontal,
|
||||||
zIndex: 100,
|
zIndex: 100,
|
||||||
color: "var(--inspector-text-secondary)",
|
color: "var(--inspector-text-secondary)",
|
||||||
fontSize: "var(--mantine-font-size-xs)",
|
fontSize: "var(--mantine-font-size-xs)",
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue