refactor(web): unify message-direction arrows and rename Pinned section (#1623)

- Direction labels now both point right so the arrow reads clearly at
  badge size: incoming "client ← server" becomes "server → client"
  (from → to), in MessageDirectionBadge and MessageDirectionFilter
  (shared by the Protocol and Network views).
- The Protocol list's pinned section header now reads "Pinned Messages"
  (was "Pinned Requests"), matching the "Messages" panel title.

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-09 19:45:52 -04:00
commit 9824055425
10 changed files with 21 additions and 21 deletions

View file

@ -22,6 +22,6 @@ export const Incoming: Story = {
args: { direction: "incoming" },
play: async ({ canvasElement }) => {
const canvas = within(canvasElement);
expect(canvas.getByText("client ← server")).toBeInTheDocument();
expect(canvas.getByText("server → client")).toBeInTheDocument();
},
};

View file

@ -8,8 +8,8 @@ describe("MessageDirectionBadge", () => {
expect(screen.getByText("client → server")).toBeInTheDocument();
});
it("renders client ← server for incoming", () => {
it("renders server → client for incoming", () => {
renderWithMantine(<MessageDirectionBadge direction="incoming" />);
expect(screen.getByText("client ← server")).toBeInTheDocument();
expect(screen.getByText("server → client")).toBeInTheDocument();
});
});

View file

@ -4,19 +4,19 @@ export interface MessageDirectionBadgeProps {
/**
* Direction of travel for the entry: "outgoing" = the inspector sent it to
* the server (client → server); "incoming" = the server sent it to the
* inspector (client ← server).
* inspector (server → client).
*/
direction: "outgoing" | "incoming";
}
const LABEL: Record<MessageDirectionBadgeProps["direction"], string> = {
outgoing: "client → server",
incoming: "client ← server",
incoming: "server → client",
};
/**
* Dual-state badge showing which way a Protocol/Network entry traveled. Outgoing
* (client → server) is green; incoming (client ← server) is purple — not yellow,
* (client → server) is green; incoming (server → client) is purple — not yellow,
* which (paired with green) reads as caution/ok status rather than direction.
* Shared by `ProtocolEntry` and `NetworkEntry`.
*/

View file

@ -17,7 +17,7 @@ describe("MessageDirectionFilter", () => {
screen.getByRole("button", { name: "client → server" }),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "client ← server" }),
screen.getByRole("button", { name: "server → client" }),
).toBeInTheDocument();
});
@ -30,7 +30,7 @@ describe("MessageDirectionFilter", () => {
onToggleDirection={onToggleDirection}
/>,
);
await user.click(screen.getByRole("button", { name: "client ← server" }));
await user.click(screen.getByRole("button", { name: "server → client" }));
expect(onToggleDirection).toHaveBeenCalledWith("server", false);
});

View file

@ -9,14 +9,14 @@ const SubtleButton = Button.withProps({
// The two message directions, in display order. Label + color mirror the
// MessageDirectionBadge: outgoing (client → server) is green, incoming
// (client ← server) is violet.
// (server → client) is violet.
const MESSAGE_DIRECTIONS: {
origin: MessageOrigin;
label: string;
color: string;
}[] = [
{ origin: "client", label: "client → server", color: "green" },
{ origin: "server", label: "client ← server", color: "violet" },
{ origin: "server", label: "server → client", color: "violet" },
];
export interface MessageDirectionFilterProps {
@ -27,7 +27,7 @@ export interface MessageDirectionFilterProps {
/**
* "Filter by Message Direction" section — a Select/Deselect All control plus a
* FilterToggleButton per direction (client → server / client ← server). Used by
* FilterToggleButton per direction (client → server / server → client). Used by
* the Protocol controls. (Kept as its own component so the section is testable in
* isolation and reusable if another screen ever needs a direction filter.)
*/

View file

@ -91,7 +91,7 @@ describe("ProtocolControls", () => {
screen.getByRole("button", { name: "client → server" }),
).toBeInTheDocument();
expect(
screen.getByRole("button", { name: "client ← server" }),
screen.getByRole("button", { name: "server → client" }),
).toBeInTheDocument();
});
@ -102,7 +102,7 @@ describe("ProtocolControls", () => {
<ProtocolControls {...baseProps} onToggleDirection={onToggleDirection} />,
);
// Currently visible → clicking turns it off.
await user.click(screen.getByRole("button", { name: "client ← server" }));
await user.click(screen.getByRole("button", { name: "server → client" }));
expect(onToggleDirection).toHaveBeenCalledWith("server", false);
});

View file

@ -158,11 +158,11 @@ describe("ProtocolEntry", () => {
expect(screen.getByText("client → server")).toBeInTheDocument();
});
it("shows client ← server for a server-originated entry", () => {
it("shows server → client for a server-originated entry", () => {
renderWithMantine(
<ProtocolEntry {...baseProps} entry={notificationEntry} />,
);
expect(screen.getByText("client ← server")).toBeInTheDocument();
expect(screen.getByText("server → client")).toBeInTheDocument();
});
it("renders Pin label when not pinned", () => {

View file

@ -116,7 +116,7 @@ describe("ProtocolListPanel", () => {
pinnedIds={new Set(["req-1"])}
/>,
);
expect(screen.getByText("Pinned Requests (1)")).toBeInTheDocument();
expect(screen.getByText("Pinned Messages (1)")).toBeInTheDocument();
expect(screen.getByText("History (2)")).toBeInTheDocument();
});
@ -186,7 +186,7 @@ describe("ProtocolListPanel", () => {
/>,
);
const pinned = screen.getByRole("button", {
name: "Pinned Requests (1)",
name: "Pinned Messages (1)",
});
const history = screen.getByRole("button", { name: "History (2)" });
await user.click(pinned);
@ -346,7 +346,7 @@ describe("ProtocolListPanel", () => {
pinnedIds={new Set(["req-1"])}
/>,
);
expect(screen.getByText("Pinned Requests (1)")).toBeInTheDocument();
expect(screen.getByText("Pinned Messages (1)")).toBeInTheDocument();
expect(screen.queryByText(/^History \(/)).not.toBeInTheDocument();
});

View file

@ -98,7 +98,7 @@ const SectionLinkButton = Button.withProps({
});
function formatPinnedTitle(count: number): string {
return `Pinned Requests (${count})`;
return `Pinned Messages (${count})`;
}
function formatHistoryTitle(count: number): string {

View file

@ -78,7 +78,7 @@ describe("ProtocolScreen", () => {
renderWithMantine(
<ProtocolScreen {...baseProps} onUiChange={onUiChange} />,
);
await user.click(screen.getByRole("button", { name: "client ← server" }));
await user.click(screen.getByRole("button", { name: "server → client" }));
expect(onUiChange).toHaveBeenCalledWith(
expect.objectContaining({
visibleDirections: { client: true, server: false },
@ -130,7 +130,7 @@ describe("ProtocolScreen", () => {
it("drops the filter sidebar when embedded, keeping the request list", () => {
renderWithMantine(<ProtocolScreen {...baseProps} embedded />);
expect(screen.getByText("Requests")).toBeInTheDocument();
expect(screen.getByText("Messages")).toBeInTheDocument();
// The sidebar (ProtocolControls, with its Search box) is not rendered.
expect(screen.queryByPlaceholderText("Search...")).toBeNull();
});