* 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>
* test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245)
Establishes the unit-test runtime AGENTS.md mandates: a vitest "unit"
project alongside the existing storybook project, RTL + jest-dom +
happy-dom, a Mantine-aware render helper, and a 90%-line per-file
coverage gate enforced by CI. Adds 66 test files covering all 62
components under elements/groups/screens/views, plus core/mcp and
clients/web/src/utils. Total: 577 tests, 98.7% line coverage globally.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* test(coverage): address PR #1269 review feedback
- AGENTS.md: correct coverage gate description (lines ≥ 90, statements ≥ 85,
functions ≥ 80, branches ≥ 50) with rationale for the relaxed branch
threshold
- InspectorView.test.tsx: document what the Math.random() mock values
(0.1 / 0.99) control vs. STUB_SUCCESS_RATE in InspectorView.tsx
- vite.config.ts / tsconfig.test.json: drop unused `globals: true` and the
`vitest/globals` type now that every test file imports describe/it/expect/vi
explicitly; manual `afterEach(cleanup)` in setup.ts remains required because
testing-library auto-cleanup relies on globalThis.afterEach
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Fix hasAnyAnnotation checking annotations.title which is never rendered
- Reset formValues when switching tools via onSelectTool handler
- Extract ToolProgress type from ToolDetailPanel, reuse in ToolsScreen
- Fix contradictory readOnlyHint + destructiveHint in story fixture
- Replace listMaxHeight function with LIST_MAX_HEIGHT constant
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace ad hoc local types with MCP SDK schema types across the Tools
group components and ToolsScreen, per the v2 UX interfaces plan.
- ToolListItem: accept `tool: Tool` instead of flat `name`/`title`
- ToolControls: accept `Tool[]` + `selectedName` instead of `ToolListItemProps[]`
- ToolDetailPanel: accept `tool: Tool` instead of flat props; use SDK
`ToolAnnotations` (readOnlyHint/destructiveHint/idempotentHint/openWorldHint);
use `ProgressNotification['params']` for progress
- ToolResultPanel: accept `CallToolResult` instead of `ResultContentItem[]`
- ToolsScreen: accept `Tool[]` + `selectedToolName` + `ToolCallState`;
derive selected tool via `tools.find()`; own `formValues` state locally
- Delete local `ToolAnnotations`, `ResultContentItem` types
- Update all stories with real SDK `Tool` fixtures
- Update ConnectedView ToolsActive story
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. ServerCard: import ConnectionStatus and ServerType from core instead
of inline union types
2. ResourcePreviewPanel: move ContentBlock import to top of file
3. ContentViewer: remove dead annotation mimeType check in isJsonText
4. ProgressDisplay: use Pick to exclude progressToken from params type;
remove fake tokens from callers and stories
5. AnnotationBadge: restore human-readable priority labels (high/medium/low)
and remove unused --inspector-annotation-* CSS vars from App.css
6. MessageBubble: add fallback rendering for resource, resource_link,
tool_use, and tool_result content block types
7. TaskControls: import TaskStatus directly from SDK instead of via TaskCard
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Rename StatusIndicator to ServerStatusIndicator across all files
- ServerCard: add activeServer/onSetActiveServer props for coordinated
connection flow — when a card's toggle is switched on, it sets itself
as the active server, shows "Connecting" status, and dims all other
cards (opacity + pointerEvents none + inert for full keyboard blocking)
- ServerListScreen: manage activeServer state internally, pass to cards;
switch to full-width layout with 3-column grid and scrollable card area
- ServerCard: move "Test Client Features" dropdown above ContentViewer
with min-height row for consistent card heights
- ToolResultPanel: move Clear button to top-right header row
- ToolDetailPanel: right-align Execute Tool button, size md, not full-width
- ListChangedIndicator: bump Refresh button to size sm
- ToolsActive story: expand to 30 tools with titles
- UnconnectedView stories: all servers disconnected, remove Connecting story
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>