feat(apps): render tool.icons on ToolListItem and ToolDetailPanel (#1264) (#1293)

* feat(apps): render tool.icons on ToolListItem (#1264)

Add icon rendering to ToolListItem so the Tools sidebar matches the
Apps sidebar treatment. Falls back gracefully when tool.icons is absent.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(stories): use #228be6 in ToolListItem WithIcon data URL

The source SVG had %23228be6 pre-encoded, but encodeURIComponent then
re-escaped the % to %25, leaving fill="%23228be6" in the decoded SVG
and making the icon render black instead of Mantine blue.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(apps): render tool.icons on ToolDetailPanel (#1264)

Show the tool icon next to the title in the detail panel, matching the
header icon size used in AppsScreen (24x24, fit: contain). Adds a
WithIcon story and tests for icon-present/icon-absent states.

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:
Cliff Hall 2026-05-09 15:45:53 -04:00 • committed by GitHub
commit fc41fdc10a
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -27,6 +27,18 @@ const titledTool: Tool = {
},
};
const ICON_SRC = "data:image/svg+xml,%3Csvg/%3E";
const iconedTool: Tool = {
name: "send_message",
title: "Send Message",
icons: [{ src: ICON_SRC }],
inputSchema: {
type: "object",
properties: {},
},
};
const annotatedTool: Tool = {
name: "delete_records",
description: "Deletes records",
@ -66,6 +78,17 @@ describe("ToolDetailPanel", () => {
).toBeInTheDocument();
});
it("renders the first icon when tool.icons is present", () => {
renderWithMantine(<ToolDetailPanel {...baseProps} tool={iconedTool} />);
const img = screen.getByRole("presentation");
expect(img).toHaveAttribute("src", ICON_SRC);
});
it("does not render an icon when tool.icons is missing", () => {
renderWithMantine(<ToolDetailPanel {...baseProps} tool={simpleTool} />);
expect(screen.queryByRole("presentation")).not.toBeInTheDocument();
});
it("does not render the description when none is provided", () => {
renderWithMantine(<ToolDetailPanel {...baseProps} tool={simpleTool} />);
expect(