* feat(apps): add AppListItem and AppDetailPanel groups (#1261)
Two presentational groups for the upcoming AppsScreen.
AppListItem (clients/web/src/components/groups/AppListItem/):
- Props { tool, selected, onClick }
- Renders tool.icons[0] (sized sm) when present, label (title ?? name),
two-line clamped description, and a trailing chevron affordance.
- Reuses ToolListItem's selected-state highlight (UnstyledButton bg via
--mantine-primary-color-light).
AppDetailPanel (clients/web/src/components/groups/AppDetailPanel/):
- Props { tool, formValues, isOpening, onFormChange, onOpenApp }
- Layout: optional icon + title row, optional description, divider,
SchemaForm bound to tool.inputSchema, full-width "Open App" button.
- "Open App" is disabled while isOpening or while any required input
field is empty (validation derived from tool.inputSchema.required).
- No annotations, progress, or cancel — execution is delegated to the
embedded app, not run as an MCP tool call from the panel.
Convention notes:
- Issue text references @tabler IconChevronRight / IconPlayerPlay; the
v2 web client already uses react-icons (md/ri namespaces), so this PR
uses MdChevronRight and MdPlayArrow for consistency rather than
introducing a second icon library.
Stories: AppListItem (Default, Selected, WithIcon, NoDescription, LongName);
AppDetailPanel (NoFields, SimpleStringParam, MultipleParams, WithIcon,
Opening, ComplexSchema).
Tests: 22 cases covering rendering branches, callback wiring, and the
disabled-button derivation. Per-file coverage 100% across lines,
statements, functions, and branches for both new components.
Closes#1261
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(stories): inline SVG for WithIcon stories so they render reliably
The Wikipedia-hosted PNG used in the AppListItem and AppDetailPanel
WithIcon stories returned a broken image in Storybook (rate-limited /
hot-link blocked). Replace it with an inline SVG sun icon as a data
URI so the stories render offline, in Chromatic, and on any network.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(apps): address PR #1273 review feedback
- Extract resolveDisplayLabel(name, title?) to src/utils/toolUtils.ts and
migrate all four call sites (AppListItem, AppDetailPanel, ToolListItem,
ToolDetailPanel) to consume it. Avoids the fourth copy when the next
list/detail screen arrives. Includes a small unit suite.
- AppDetailPanel: only render the <Divider/> when the schema has at least
one property — previously a no-input app showed an orphaned separator
between the description and the Open App button.
- AppDetailPanel: align PanelTitle truncate to `true` (matches the
ItemLabel form used in AppListItem / ToolListItem); behaviorally
identical, just less surprising for future readers.
- AppDetailPanel: add an inline comment explaining that SchemaForm is
disabled only while isOpening, not while validation fails — users
should be able to keep filling required fields. The validation gate
lives on the Open App button.
- Extract the inline SUN_ICON_SVG data URI used by the WithIcon stories
to src/test/fixtures/storyIcons.ts so the next story that needs it
doesn't grow a third copy.
- Tests: add the AppDetailPanel divider-conditional cases (rendered
with fields, omitted without). Per-file coverage stays at 100% for
AppListItem, AppDetailPanel, ToolListItem, ToolDetailPanel, and the
new toolUtils. Suite: 70 files / 618 tests passing.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(toolUtils): rename empty-string test for readability
Drops the doubled "is ... is preserved" phrase in the test name.
Behavior unchanged.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>