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
2 changed files with 520 additions and 200 deletions

View file

@ -0,0 +1,27 @@
import type { Meta, StoryObj } from "@storybook/react-vite";
import { fn } from "storybook/test";
import { ResourceSubscribedItem } from "./ResourceSubscribedItem";
const meta: Meta<typeof ResourceSubscribedItem> = {
title: "Groups/ResourceSubscribedItem",
component: ResourceSubscribedItem,
args: {
onUnsubscribe: fn(),
},
};
export default meta;
type Story = StoryObj<typeof ResourceSubscribedItem>;
export const WithTimestamp: Story = {
args: {
name: "config.json",
lastUpdated: "2026-03-17T10:30:00Z",
},
};
export const WithoutTimestamp: Story = {
args: {
name: "schema.sql",
},
};

View file

@ -0,0 +1,43 @@
import { Button, Group, Stack, Text } from "@mantine/core";
export interface ResourceSubscribedItemProps {
name: string;
lastUpdated?: string;
onUnsubscribe: () => void;
}
const NameText = Text.withProps({
size: "sm",
fw: 500,
});
const TimestampText = Text.withProps({
size: "xs",
c: "dimmed",
});
const SubtleButton = Button.withProps({
variant: "subtle",
size: "xs",
});
const ItemRow = Group.withProps({
justify: "space-between",
wrap: "nowrap",
});
export function ResourceSubscribedItem({
name,
lastUpdated,
onUnsubscribe,
}: ResourceSubscribedItemProps) {
return (
<ItemRow>
<Stack gap={2}>
<NameText>{name}</NameText>
{lastUpdated && <TimestampText>{lastUpdated}</TimestampText>}
</Stack>
<SubtleButton onClick={onUnsubscribe}>Unsubscribe</SubtleButton>
</ItemRow>
);
}