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

@ -1,4 +1,4 @@
import { Button, Divider, Group, Stack, Text } from "@mantine/core";
import { Button, Divider, Group, Image, Stack, Text } from "@mantine/core";
import type {
ProgressNotification,
Tool,
@ -24,6 +24,19 @@ export interface ToolDetailPanelProps {
onCancel: () => void;
}
const TitleRow = Group.withProps({
gap: "sm",
wrap: "nowrap",
align: "center",
miw: 0,
});
const ToolIcon = Image.withProps({
w: 24,
h: 24,
fit: "contain",
});
const ToolTitle = Text.withProps({
fw: 700,
size: "lg",
@ -59,11 +72,15 @@ export function ToolDetailPanel({
onExecute,
onCancel,
}: ToolDetailPanelProps) {
const { name, title, description, annotations, inputSchema } = tool;
const { name, title, description, icons, annotations, inputSchema } = tool;
const iconSrc = icons?.[0]?.src;
return (
<Stack gap="md" miw={0}>
<ToolTitle>{resolveDisplayLabel(name, title)}</ToolTitle>
<TitleRow>
{iconSrc && <ToolIcon src={iconSrc} alt="" />}
<ToolTitle>{resolveDisplayLabel(name, title)}</ToolTitle>
</TitleRow>
{hasAnyAnnotation(annotations) && annotations && (
<Group gap="xs">
{annotations.readOnlyHint && (