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:
cliffhall 2026-07-10 20:11:52 -04:00
commit 5d024ab54f
4 changed files with 88 additions and 18 deletions

View file

@ -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();
},
};

View file

@ -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();
});
});

View file

@ -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>;
}

View file

@ -5,12 +5,12 @@ export interface VersionBadgeProps {
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.
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
* `GET /api/config`; renders nothing until the version is known (or on a legacy
* backend that omits it).