Redesign Resources screen: template panel, subscriptions, hover states, and callback architecture

- Rename ResourceTemplateInput to ResourceTemplatePanel with panel layout
  (title header, URI with live variable preview, labeled inputs, Read Resource button)
- ResourceTemplatePanel manages its own variable state via useState;
  single onReadResource(uri) callback returns fully resolved URI
- Extract ResourceSubscribedItem group component with name, timestamp, and
  subtle Unsubscribe button
- Add hover state to listItem variant via theme classNames + App.css pseudo-selector
- Add Refresh button to ResourcePreviewPanel
- Refactor ResourcesScreen callbacks: onSelectUri, onSelectTemplate,
  onReadResource, onSubscribeResource, onUnsubscribeResource replace
  per-item onClick/onUnsubscribe props; screen wires callbacks to children
- Define data-only types (ResourceItem, TemplateListItem, SubscriptionItem,
  SelectedResource, SelectedTemplate) separating data from callbacks
- Extract named .withProps() subcomponents (FooterRow, AnnotationGroup,
  ActionGroup, Spacer) for AGENTS.md compliance
- Update all stories (ResourceTemplatePanel, ResourcesScreen, ConnectedView)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-03-29 20:04:25 -04:00
commit a0e763a315

View file

@ -249,57 +249,66 @@ export const ResourcesActive: Story = {
name: "config.json",
uri: "file:///config.json",
annotations: { audience: "developer", priority: 0.8 },
selected: true,
onClick: fn(),
selected: false,
},
{
name: "README.md",
uri: "file:///README.md",
selected: false,
onClick: fn(),
},
{
name: "schema.sql",
uri: "file:///schema.sql",
annotations: { priority: 0.5 },
selected: false,
onClick: fn(),
},
]}
templates={[
{
template: "file:///users/{userId}/profile",
variables: { userId: "" },
onVariableChange: fn(),
onSubmit: fn(),
name: "User Profile",
uriTemplate: "file:///users/{userId}/profile",
selected: true,
},
{
name: "Table Row",
title: "Database Table Row",
uriTemplate: "db://tables/{tableName}/rows/{rowId}",
selected: false,
},
]}
subscriptions={[
{ name: "config.json", lastUpdated: "2026-03-17T10:30:00Z" },
{
name: "config.json",
uri: "file:///config.json",
lastUpdated: "2026-03-17T10:30:00Z",
},
]}
selectedTemplate={{
name: "User Profile",
uriTemplate: "file:///users/{userId}/profile",
description: "Fetch a user profile by their unique identifier.",
}}
selectedResource={{
uri: "file:///config.json",
uri: "file:///users/42/profile",
mimeType: "application/json",
annotations: { audience: "developer", priority: 0.8 },
content: JSON.stringify(
{
name: "my-project",
version: "1.0.0",
settings: { debug: true, logLevel: "info" },
},
{ id: 42, name: "Alice", email: "alice@example.com" },
null,
2,
),
lastUpdated: "2026-03-17T10:30:00Z",
isSubscribed: true,
onSubscribe: fn(),
onUnsubscribe: fn(),
lastUpdated: "2026-03-17T11:15:00Z",
isSubscribed: false,
}}
listChanged={false}
searchText=""
onSearchChange={fn()}
onRefreshList={fn()}
onSelectResource={fn()}
onSelectUri={fn()}
onSelectTemplate={fn()}
onReadResource={fn()}
onSubscribeResource={fn()}
onUnsubscribeResource={fn()}
/>
),
},