Commit graph inspector/clients/web/src/components/views
Author SHA1 Message Date
Cliff Hall
6678313373
Expose live resource subscriptions via a hook (#1325) (#1326)
* Expose live resource subscriptions via a hook (#1325)

Add ResourceSubscriptionsState that mirrors InspectorClient's subscribed
URIs as InspectorResourceSubscription[], resolving each URI against the
managed resources list (so the subscription tile shows server-supplied
name/title) and stamping lastUpdated on notifications/resources/updated.
Pair it with useResourceSubscriptions and wire App.tsx so the Resources
screen reflects subscribe/unsubscribe actions in real time.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Address review notes on resourceSubscriptionsState

- Soften the onResourceUpdated comment: the client's dispatch is already
  guarded by subscribedResources.has(uri), so the re-check is true
  defense-in-depth rather than guarding a known hazard.
- Use this.getSubscriptions() in the statusChange handler so every emit
  goes through the defensive-copy path.
- Document the deliberate fallback to a synthetic Resource when a
  previously-listed resource is removed from the managed list while
  the user is still subscribed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Reflect subscribed state in preview + compact subscribed tile labels

- Wire `isSubscribed` on the ReadResourceState passed to ResourcesScreen
  by deriving it from the live subscriptions list in App.tsx. The
  ResourcePreviewPanel's SubscribeButton already flips its label to
  "Unsubscribe" when subscribed; without this derivation isSubscribed
  was always false and the button looked stuck on "Subscribe".
- In ResourceSubscribedItem, display only the last URI path segment
  (truncated with ellipsis if it still overflows) and surface the full
  URI via a hover tooltip. Keeps the Subscriptions pleat readable when
  resources have long path-style URIs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Auto-read resource on sidebar click

Clicking a resource in the URIs accordion now triggers onReadResource
in addition to setting the selection, so the preview panel jumps
straight to the pending → ok render path. Removes the unreachable
"Click to read this resource" placeholder, since the loader / preview /
error states always cover the rendered output for a selected resource.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Render markdown resources via react-markdown + fix preview overflow

- Add react-markdown / remark-gfm dependencies.
- ContentViewer accepts an optional mimeType prop; when it matches
  text/markdown (or text/x-markdown), text blocks render via
  react-markdown wrapped in a `.markdown-content` container. Non-markdown
  text now uses the existing Code "wrapping" variant so long lines stay
  inside the panel.
- ResourcePreviewPanel infers `text/markdown` from a `.md` / `.markdown`
  URI suffix when the server didn't supply a mimeType, and threads the
  effective mime through to ContentViewer per content item.
- ResourcesScreen flex children get miw=0 so a long unwrappable line in
  the resource body can no longer push the preview past the viewport's
  right edge.
- App.css gains a `.markdown-content` ruleset constraining nested pre,
  table, code, and img elements to the container's width.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Contain resource preview: sticky header/footer, scrollable body

Restructure ResourcePreviewPanel into a fixed-height column with the
resource title + URI pinned to the top and the timestamp / annotations /
subscribe-refresh actions pinned to the bottom. The content viewer area
in the middle now owns its own ScrollArea, so a long markdown body
scrolls within the panel instead of pushing the subscribe button below
the viewport.

In ResourcesScreen, the selectedResource branch (and the template
branch's right pane) now hosts the panel inside a PreviewPane Flex
column with the screen's max-height, and renderReadState wraps in a
FillDetailCard sized to fill that column. The legacy outer
ScrollArea.Autosize is removed for these panes since scrolling is
internal now.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Auto-hide template after read; size preview to content with cap

- After the user clicks Read Resource on the template form,
  handleReadResource now clears selectedTemplateUri so the screen
  swaps from the template form to the resource preview. Previously
  both panels stayed mounted side-by-side.
- The preview no longer hard-fills the viewport. The Card uses a new
  "preview" theme variant (overflow: hidden) and is content-sized,
  capped at SCROLL_MAX_HEIGHT. ResourcePreviewPanel's flex column
  marks the header / meta / footer rows as `flex: 0 0 auto` and the
  ContentScroll as `flex: 0 1 auto` with `mih: 0`, so:
    - short content → card hugs it, footer sits right under the body
    - long content → card caps at viewport, ContentScroll shrinks
      and scrolls internally, footer stays pinned at the cap
- ScrollArea / Group imports in ResourcesScreen pruned to match the
  simpler single-pane layout.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Cap template form panel at 40% width

A bare text input + Read Resource button stretched across the whole
main content area looks awkward on wide displays. Apply maw=40% to
the PreviewPane in the template branch so the form keeps a reasonable
form-field width regardless of viewport.

The preview branch is unaffected — resource bodies still get the full
main area for content.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* Live completion/complete suggestions on resource template inputs

Wires the server's `completions` capability through to the resource
template form so each variable's input becomes an Autocomplete that
fires `completion/complete` (debounced, 300ms) on every keystroke and
surfaces the returned values as a dropdown — mirroring v1's behavior.

- core/mcp/inspectorClientProtocol.ts: surface getCompletions on the
  protocol so non-runtime callers (state managers, hooks, tests) can
  depend on it. FakeInspectorClient gains a vi.fn-backed stub.
- ResourceTemplatePanel: accepts onCompleteArgument + completionsSupported.
  When both are present it renders Mantine Autocomplete instead of
  TextInput, debounces keystrokes via per-arg timers, aborts in-flight
  requests on the next keystroke, and disables client-side filtering
  (the server already filtered for the typed prefix).
- ResourcesScreen / InspectorView: thread the props through; the
  screen-level callback re-injects the active template's URI as the
  `ref: "ref/resource"` so the panel-level callback stays ref-free.
- App.tsx: wires onCompleteArgument to inspectorClient.getCompletions
  and derives completionsSupported from `capabilities?.completions`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 20:15:51 -04:00
Cliff Hall
4d74b94bed
Wire App.tsx to v2 core/ hook layer; remove demo stub from InspectorView (#1322)
* feat(web): wire App.tsx to v2 core/ hook layer; remove demo stub from InspectorView

App.tsx mounts InspectorView with live data from the v2 hook layer instead
of the title-page placeholder. InspectorView becomes pure prop-driven; the
demo handshake stub and connection state machinery are removed.

Connection lifecycle:
- App.tsx lazily instantiates `InspectorClient` on the connect edge,
  rebuilding it (and its state managers) when the user picks a different
  server. State managers (Managed{Tools,Prompts,Resources,ResourceTemplates,
  RequestorTasks}State + MessageLog/FetchRequestLog/StderrLog) are
  destroyed on switch.
- `useInspectorClient` + the per-primitive `useManaged*` hooks drive the
  view's data. `latencyMs` is captured at the connecting → connected edge
  via a ref so the intermediate rerenders don't lose the start timestamp.
- `errorMessage` is captured from `client.connect()` rejection; status-
  machine-driven errors that don't surface through the promise are a known
  follow-up (no `error` event in the v2 InspectorClientEventMap yet).

Action handlers:
- `onCallTool` / `onGetPrompt` / `onReadResource` await the corresponding
  InspectorClient method and reflect pending → ok/error in `ToolCallState`
  / `GetPromptState` / `ReadResourceState` panel props. Manually verified
  end-to-end against `npx @modelcontextprotocol/server-filesystem` —
  the Tools result panel renders the real "Allowed directories: /private/tmp"
  output rather than a stub.
- `onSetLogLevel` calls `client.setLoggingLevel(level)` and optimistically
  bumps `currentLogLevel` locally (the request has no echo notification).
- `onSubscribeResource` / `onUnsubscribeResource` / `onCancelTask` route to
  their respective client methods. Server CRUD (`onServerAdd`/`onServerEdit`
  /…) and history pinning are intentionally `todoNoop` for now; the seed
  server list is hardcoded per the agreed scope (a `useServers` v2-only
  hook is a separate effort).
- `notifications/message` notifications flowing through `MessageLogState`
  are filtered into `LogEntryData[]` and passed to the Logs screen, so
  `logging/setLevel` and the resulting log stream round-trip.

InspectorView (`InspectorView.tsx`):
- Removed: `STUB_*` constants, `handshakeTimer` ref, `handleToggleConnection`
  / `disconnect` functions, stub `bridgeFactory`/`sandboxPath`, internal
  connection state, `logLevel` state.
- Added 38 props covering connection state, panel states, log level, and
  action callbacks. The view is pure presentational.
- `availableTabs` is derived from `connectionStatus`; `activeTab` is
  clamped to whatever's currently available (avoiding the `set-state-in-effect`
  lint and keeping the previously-selected tab on reconnect).

Stories + tests:
- `InspectorView.stories.tsx` adds spy callbacks for every action prop and
  new `Connected` / `ConnectionError` stories for the connected/error
  narratives.
- `InspectorView.test.tsx` is rewritten around the prop-driven contract
  (10 tests covering the empty list, server cards, toggle dispatch,
  connected header, error banner, tab snap-back on disconnect, app
  filtering, log-level dispatch, autoScroll local toggle).

Dev backend port wiring (`vite.config.ts`):
- Vite's dev server is now pinned to `CLIENT_PORT` (default 6274) with
  `strictPort: true`, matching the dev backend's `allowedOrigins`. Without
  this, Vite would fall back to 5173 while the Hono origin check still
  required 6274 — breaking every browser `/api/*` fetch out of the box.
- Top-level `resolve.alias` now spreads the same bare-module aliases the
  vitest projects use. Rolldown can't reach `pino/browser.js` /
  `zustand/middleware` from `core/`'s parent (no node_modules there); the
  wired App pulls those modules into the browser dep graph for the first
  time, so the aliases need to be visible at build time too.

createWebEnvironment helper (`src/lib/environmentFactory.ts`):
- Mirrors v1.5's `lib/adapters/environmentFactory.ts`: builds
  `InspectorClientEnvironment` from `createRemoteTransport` +
  `createRemoteFetch` + `createRemoteLogger` + `BrowserOAuthStorage` +
  `BrowserNavigation`. Returns the assembled environment plus the logger
  for direct app-level use.

Closes #1244.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(web): wire dev-backend auth token into createWebEnvironment + show full stdio command in ServerCard

Two issues surfaced after the #1244 wiring landed locally:

1. **401 on `/api/mcp/connect`** when running `npm run dev` with the
   default auth (no `DANGEROUSLY_OMIT_AUTH`). The dev backend generates a
   random token and prints `http://localhost:6274?MCP_INSPECTOR_API_TOKEN=…`
   in the banner, but App.tsx was calling `createWebEnvironment(undefined, …)`
   so the browser never sent `x-mcp-remote-auth`. The Hono middleware
   correctly rejected with "Authentication required. Use the
   x-mcp-remote-auth header with Bearer token."

   Adds a small `getAuthToken()` helper in App.tsx that reads
   `MCP_INSPECTOR_API_TOKEN` off `window.location.search`, falls back to
   `sessionStorage`, and persists fresh values into sessionStorage so
   client-side navigation / OAuth round-trips don't drop the token. The
   resolved value is passed to `createWebEnvironment` whenever a new
   InspectorClient is built.

2. **Misleading ServerCard display** — the seed server's `config` is
   `{ command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem",
   "/tmp"] }` but the card only rendered `"npx"`, so it looked like the
   command was incomplete. `getCommandOrUrl` now joins `command + args`
   for stdio configs, so the card shows the same string that gets spawned.
   SSE / streamable-http paths unchanged.

Manual end-to-end re-verified against `npm run dev` (no DANGEROUSLY_OMIT_AUTH):
- Banner prints the auth-token URL.
- Browser navigates to that URL → `getAuthToken()` picks the token off the
  query string, persists to sessionStorage, threads it through to every
  `/api/*` request.
- Server card shows `npx -y @modelcontextprotocol/server-filesystem /tmp`.
- Toggle connect → connection succeeds; ViewHeader shows
  `Connected (~1.3s)`; Tools tab populates with the 14 filesystem tools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(web): use dark-9 instead of blue-9 for dark-mode page background

`main.tsx` overrode `--mantine-color-body` to `--mantine-color-blue-9` in
dark mode, while `.storybook/preview.tsx` used `--mantine-color-dark-9`.
The dev app then rendered with a blue background while every story (and
the design intent visible in storybook) used dark grey.

Switch main.tsx to `--mantine-color-dark-9` so the dev app matches the
storybook reference.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* feat(web): seed the Servers screen with the "everything" reference server

Adds `@modelcontextprotocol/server-everything` to the hardcoded seed list
alongside the filesystem server. The everything server exposes tools,
prompts, resources, sampling, and completion — covering most of the
surface a developer might want to exercise against the wired-up
InspectorView without configuring anything.

Comment updated to explain the two-seed shape.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(web): address #1244 PR review feedback (JsonValue cast, client lifecycle, dead code)

- **#1 — Misleading `Record<string, never>` cast**: Imported `JsonValue`
  from `@inspector/core/mcp/index.js` and changed `args as Record<string,
  never>` to `args as Record<string, JsonValue>` in `onCallTool`. The new
  cast honestly narrows from the screen-level `Record<string, unknown>`
  to what `InspectorClient.callTool` actually requires. Added a comment
  explaining the boundary.

- **#2 + #3 — Previous InspectorClient not disconnected on server switch
  or unmount**: Added a `useEffect` keyed on `inspectorClient` whose
  cleanup calls `inspectorClient.disconnect()`. One effect covers both
  the swap-on-switch leak (prior session's transport stayed open until
  GC) and the unmount-during-connected leak (HMR / tests).
  `setupClientForServer`'s existing state-manager `destroy()` calls
  handle the listener side; the new effect handles the transport side.

- **#4 — Dead `void` block at bottom of file**: Deleted the trailing
  `void FetchRequestLogState; void StderrLogState;` block. The state
  managers are already constructed via `new FetchRequestLogState(client)`
  / `new StderrLogState(client)`, so the imports are referenced by the
  linter's reckoning. The `void` pair was dead code with a comment that
  no longer matched reality.

- **#5 / #7 / #8 — Follow-up TODOs**: Added TODO refs to the new
  follow-up issues:
  - TODO(#1323): `error` event in InspectorClientEventMap so mid-session
    transport failures surface (connect-only catch is incomplete).
  - TODO(#1324): negotiated `protocolVersion` through `useInspectorClient`
    (currently hard-coded `"2025-06-18"`).
  - TODO(#1325): `useResourceSubscriptions` hook so subscribe/unsubscribe
    buttons reflect their server-side effect.

Items #6 (useCallback dep churn), #9 (helper unit tests), and #10
(shared no-op bridge factory fixture) are acknowledged in the PR reply
as deferred — no functional impact and out of scope for a "wire the
hook layer" PR.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-19 15:41:55 -04:00
Cliff Hall
5f296589ea
feat(apps): AppsScreen and InspectorView wiring (#1263) (#1286)
* feat(apps): AppsScreen and InspectorView wiring (#1263)

Adds the Apps tab as a top-level screen composed of `AppListItem`,
`AppDetailPanel`, and `AppRenderer`. The screen is presentational —
selection, form values, running, and maximize state are local; data and
side effects come in via props and callbacks.

InspectorView filters `tools` through `isAppTool` (wrapped in a try/catch
so a malformed `_meta.ui.resourceUri` skips one tool instead of dropping
every following App), exposes the Apps tab in `ALL_TABS` after Tools, and
wires Phase-3 stub callbacks plus a stub bridge factory.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(apps): extract AppControls and rename Refresh button

Pulls the sidebar list, search input, "List updated" indicator, and
Refresh button out of `AppsScreen` into a dedicated `AppControls`
group, mirroring `ToolControls` and `TaskControls`. The Refresh button
moves from a full-width row beneath the list to the top-right of the
title row (single label "Refresh", matching `TaskControls`).

`AppsScreen` keeps the right-pane orchestration (selection, form,
running, maximize, header actions) and now delegates the entire
sidebar to `AppControls`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(stories): seed InspectorView demo tools with MCP Apps

Adds three apps (Cohort Data, Weather Widget, Ops Dashboard) carrying
real `_meta.ui.resourceUri` to the demo tools list so the Apps tab has
content in the Default story alongside the existing tools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* fix(apps): drop AppDetailPanel title row to avoid duplicate name

The parent AppsScreen header already shows the app name, so the panel's
own title/icon row rendered the name twice. Remove the title row (and
its now-unused imports, styled subcomponents, and the four tests that
asserted on title/icon rendering).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* refactor(resources): drop annotation badges from ResourceListItem

The list row now renders just the resource title/name. Audience and
priority badges remain visible in the preview/template panels, so the
data is still surfaced — just not duplicated in the picker. Drops the
now-unused AnnotationBadge import, the badge-only stories, and the
badge-rendering test.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(apps): address review — share hasInputFields, show header icon

- Extracts the duplicated `Object.keys(inputSchema.properties ?? {}).length > 0`
  check from `AppsScreen` (and the equivalent in `AppDetailPanel`'s
  `hasFields`) into a shared `hasInputFields` helper in `toolUtils`,
  with tests.
- Renders the selected tool's first icon next to the right-pane header
  title in `AppsScreen`, restoring the icon visibility that was lost
  when `AppDetailPanel`'s title row was removed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* chore(apps): finish hasInputFields extraction; document isOpening

- AppDetailPanel now actually calls the shared `hasInputFields` helper
  instead of inlining the same `Object.keys(...).length > 0` check.
- AppsScreen comments why `isOpening={false}` is hardcoded: the screen
  flips to AppRenderer synchronously, so the loading state is unreachable
  here — the prop remains for standalone use and Phase 3 wiring.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 14:18:27 -04:00
Cliff Hall
75f74d64c1
test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269)
* 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>
2026-05-01 15:15:32 -04:00
cliffhall
c5d02456e0 Phase 5: Lift ServerEntry into core/mcp/types.ts
ServerEntry was previously defined as `Pick<ServerCardProps, ...>` in
ServerListScreen, inverting the natural dependency direction (data shape
derived from a UI prop type). Phase 5's cleanup pass calls this out
specifically.

Move the canonical ServerEntry into core/mcp/types.ts and have
ServerCardProps extend it. ServerListScreen, InspectorView, and their
stories now import ServerEntry from core directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 19:17:57 -04:00
cliffhall
bb8241c2aa Share long tool fixture across ToolControls and InspectorView stories
Extract a 20-tool fixture (filesystem, database, git, http, misc) into
ToolsScreen.fixtures.ts — large enough that the ToolControls list scrolls
and the Tools tab in InspectorView shows realistic surface area.

Wire it into the Default stories of both ToolControls and InspectorView,
mirroring the LoggingScreen.fixtures.ts pattern.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 11:25:54 -04:00
cliffhall
4c1d2f2c34 Phase 4: Move screen selection state into the screens themselves
ToolsScreen, PromptsScreen, and ResourcesScreen each tracked the user's
current selection via `selectedXxxName` / `onSelectXxx` props, hoisting
screen-local UI state into whatever rendered them. InspectorView never
threaded those callbacks anywhere, so clicks in the controls panel did
not display the matching detail panel — the detail panel only rendered
if a parent fed selection back in.

Move selection into `useState` inside each screen and drop the
`selectedXxxName` / `onSelectXxx` props. MCP-derived data (`callState`,
`getPromptState`, `readState`) keeps flowing in from `InspectorView` as
props. ResourcesScreen wraps `onReadResource` so a template-expanded URI
becomes the selected resource for preview rendering.

Update each screen's stories to drop the deleted props. Stories that
need a pre-selected item use a `play` function to click it — no
component coupling to Storybook.

Add Phase 4.3 to the interfaces plan describing the rule.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-26 09:27:22 -04:00
cliffhall
1ed81c5f0e Address PR #1221 review round 4 feedback
- InspectorView: cancel any in-flight stub handshake timer when a new
  connect request arrives or the user disconnects, via a `useRef`-backed
  timer handle. Prevents a stale handshake resolver from overwriting the
  next attempt's connection state.
- InspectorView: document that screens unmount on tab switch (matches
  prior `{activeTab === X && <Screen />}` behavior; local screen state
  resets on every transition). If Phase 3 needs persistence, add
  `keepMounted` to the Transition.
- InspectorView: document the `mih: "100%"` height assumption on
  ScreenStageContainer — depends on `AppShell.Main` providing a definite
  parent height for the stack itself to fill (absolute children render
  regardless, but nested ScrollArea screens may need this).
- ViewHeader: comment the `Select w={140}` magic number — sized to fit
  the longest current tab label ("Resources").

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:50:58 -04:00
cliffhall
1785a5281c Share LoggingScreen fixture with InspectorView story; tune transition timing
- Extract `mixedEntries` (50 mixed-level log entries) into
  `LoggingScreen.fixtures.ts` so both `LoggingScreen.stories` and
  `InspectorView.stories` can consume it. The InspectorView Default story
  now shows a populated, scrollable Logs tab.
- InspectorView screen transitions: separate enter (350ms) and exit (250ms)
  durations via Mantine Transition's `exitDuration` prop, so screens leave
  faster than they enter.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:46:18 -04:00
cliffhall
d3c2645e38 Animate InspectorView screen transitions and refactor App.tsx
- InspectorView: wrap each screen in a Mantine `Transition` (`fade-up`,
  350ms). Outgoing screens fade out and slide down 30px; incoming screens
  fade in and slide up to meet the ViewHeader. A `pos: "relative"` Stack
  container holds the screens so the absolute-positioned `Transition`
  wrappers stack in the same area without pushing layout.
- InspectorView: reorder ALL_TABS so Logs sits beside History at the end:
  Servers · Tools · Prompts · Resources · Tasks · Logs · History.
- App.tsx: refactor to AGENTS.md guidelines — extract `PageContainer`,
  `PageStack`, `ThemeToggle`, `SubtitleText` as `.withProps()`
  subcomponents; replace inline unicode glyphs with `MdLightMode` /
  `MdDarkMode` icons (matches the ViewHeader pattern); convert the click
  handler to a `function` declaration.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:33:57 -04:00
cliffhall
473bd95826 Address PR #1221 review round 3 feedback
- InspectorView: simulate the connect flow through "connecting" with a
  randomized 50-500ms handshake delay and an 85% success rate. On success,
  measure observed latency from connect start; on failure, set a plausible
  error message. Connection error message flows through to the active
  ServerCard's connection state. Real wiring (Phase 3) replaces this with
  `useConnection`.
- InspectorView: document that the view owns connection state (input
  `serversInput` connection fields are intentionally ignored), and mark
  `noop` callbacks as Phase-3 stubs.
- ServerStatusIndicator: add `showLabel?: boolean` escape hatch so consumers
  in constrained containers can override the default viewport-based
  visibility.
- ServerListScreen.stories: add `WithActiveServer` story exercising the
  dimming + `inert` behaviour in isolation.
- ViewHeader.stories: rename "Logging" → "Logs" to match `ALL_TABS` in
  InspectorView.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 19:05:18 -04:00
cliffhall
0babbfa6a9 Remove ConnectedView and UnconnectedView, superseded by InspectorView
The two prior shell views took `children` and routed connection state by
having two distinct mounts. `InspectorView` collapses both into a single
state-machine view that switches `ViewHeader` modes internally, so the
old shells have no remaining call sites.

Spec section 4 (Views) rewritten to describe `InspectorView` instead.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:38:45 -04:00
cliffhall
9553fb9c8a Add InspectorView state machine driving screens from props
InspectorView replaces the prior pair (ConnectedView/UnconnectedView wrapping
children) with a single state-machine view. Holds activeServer, connectionStatus,
initializeResult, latencyMs, activeTab, and availableTabs internally; renders
the appropriate screen based on activeTab; switches the header between connected
and unconnected modes.

Screen data (servers, tools, prompts, resources, resourceTemplates, subscriptions,
logs, tasks, progressByTaskId, history) flows in via props so stories can populate
the entire view with fixtures. Demo data lives in stories, not in the view.

Stories include one server per transport (stdio, sse, streamable-http) so the
TransportBadge / descriptor variants are all visible.

Reorders ViewHeader.stories tabs to match the new ALL_TABS sequence.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:33:52 -04:00
cliffhall
1149288ea6 Refactor ServerCard stack to use id-based callbacks
Distinguishes server `id` (the unique map key in MCPConfig.mcpServers)
from `name` (display label) so containers can dispatch by id without
relying on display strings. ServerCard, ConnectionToggle, ServerListScreen,
and supporting stories all updated.

- ServerCard: add `id` prop; all action callbacks now `(id: string) => void`
- ConnectionToggle: collapse `onConnect`/`onDisconnect` into single `onToggle`
- ServerListScreen: lift `activeServer` to a prop; combine `onSetActive` +
  `onToggleConnection` into a single `onToggleConnection(id)` callback
- Stories: use UUIDs for server ids so callbacks visibly carry ids, not names

Removes the "Direct"/"Via Proxy" connection mode concept, which is no
longer planned. ServerSettingsForm drops the section, ServerCard drops
the badge, InspectorServerSettings drops the field.

Removes the "Test Client Features" dropdown from ServerCard along with
its four handlers; that surface will live elsewhere.

TransportBadge now collapses `sse` and `streamable-http` into a single
"HTTP" label; ServerCard adds a grey transport descriptor next to the
badge ("Standard I/O", "SSE (Server Sent Events) [deprecated]",
"Streamable HTTP"). The version badge moves from the header row to sit
beside the transport badge.

ServerCard header no longer wraps; long server names truncate with
ellipsis.

Specification docs (`v2_scope.md`, `v2_screenshots.md`,
`v2_ux_interfaces.md`, `v2_ux_storybook.md`) updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-04-25 18:33:38 -04:00
cliffhall
a6fc900271 Add ServerSettingsModal group, revert form to controlled component
Revert ServerSettingsForm to a controlled component with
expandedSections/onExpandedSectionsChange props. The form is a
dumb component — accordion state belongs to its container.

Create ServerSettingsModal group that manages:
- Accordion expand/collapse state with ListToggle expand-all button
- Add/remove/edit header pairs
- Add/remove/edit metadata pairs
- Connection mode, timeout, and OAuth changes
- All changes flow through a single onSettingsChange callback

Update ServerSettingsForm stories with useState render functions
so accordion sections are interactive.

Update UnconnectedView WithSettingsModal story to use the new
ServerSettingsModal component.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 17:13:57 -04:00
cliffhall
adb909daf6 Internalize accordion state in ServerSettingsForm
Move expandedSections/onExpandedSectionsChange from props to internal
useState. The accordion open/close is local display logic, not parent
state. Add optional defaultExpandedSections prop for initial state.

Simplify UnconnectedView WithSettingsModal story — no longer needs
external state management for accordion sections.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 17:00:25 -04:00
cliffhall
771a67ee20 Phase 2: Refactor Server/Settings/ViewShell cluster to MCP SDK types
ServerCard: Replace flat scalar props (name, version, transport, status,
error) with grouped objects (config: MCPServerConfig, info?: Implementation,
connection: ConnectionState). Remove auto-connect useEffect — dumb
components must not self-dispatch side effects. Derive transport and
command/url from config.

ServerInfoContent: Replace flat props with initializeResult: InitializeResult
and clientCapabilities: ClientCapabilities from MCP SDK. Derive capability
lists by iterating the ServerCapabilities record.

ServerSettingsForm: Collapse flat setting props (connectionMode, headers,
metadata, timeouts, oauth*) into settings: InspectorServerSettings. Add
InspectorServerSettings type to core/mcp/types.ts.

ImportServerJsonPanel: Replace scattered scalar props (jsonContent,
selectedPackageIndex, serverName) with draft: InspectorServerJsonDraft.

ViewHeader: Replace serverName: string with serverInfo: Implementation.
Use ConnectionStatus from core types.

ConnectedView: Update to pass serverInfo: Implementation instead of
serverName. Use ConnectionStatus type.

Update all stories for ServerCard, ServerInfoContent, ServerSettingsForm,
ImportServerJsonPanel, ViewHeader, ServerListScreen, ConnectedView, and
UnconnectedView.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-21 15:22:08 -04:00
cliffhall
f104e6e21a Phase 2: Refactor History cluster to use MessageEntry
Replace ad hoc flat props with MessageEntry from core/mcp/types.ts
across the History group components and HistoryScreen.

- HistoryEntry: accept `entry: MessageEntry` instead of flat props
  (timestamp, method, target, status, durationMs, parameters, response);
  derive method/target/status from embedded JSON-RPC objects; fix
  Collapse animation by removing outer {isExpanded && ...} wrapper;
  drop childEntries (not part of MessageEntry)
- HistoryControls: type onMethodFilterChange as (string | undefined)
- HistoryListPanel: accept `MessageEntry[]` + `pinnedIds: Set<string>`
  instead of separate entries/pinnedEntries arrays with pre-bound
  callbacks; pass onReplay/onTogglePin by entry ID
- HistoryScreen: accept `MessageEntry[]` + `pinnedIds` + ID-based
  callbacks; collapse two-array model into single list with pinned
  overlay
- Delete local HistoryEntryProps data shape, HistoryChildEntry type
- Update all stories with real JSON-RPC MessageEntry fixtures
- Update ConnectedView HistoryActive story

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 16:55:23 -04:00
cliffhall
6261a7e270 Use descriptive progress message in task stories
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 13:37:30 -04:00
cliffhall
f9f239c8c7 Thread progressByTaskId through TaskListPanel and TasksScreen
Progress notifications arrive separately from the Task object, so the
parent provides a map of progress data keyed by taskId. TaskListPanel
and TasksScreen accept an optional progressByTaskId prop and pass the
matching entry to each TaskCard.

Updated Mixed story and ConnectedView TasksActive story to demonstrate
the progress bar on the active "working" task.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 11:07:43 -04:00
cliffhall
6abb39a5fe Phase 2: Refactor Tasks cluster to MCP SDK types
Replace ad hoc TaskCardProps data shape with SDK Task type across the
Tasks group components and TasksScreen.

- TaskCard: accept `task: Task` instead of flat props (taskId, status,
  method, target, progress, etc.); derive all display fields from the
  SDK Task object (taskId, status, ttl, createdAt, lastUpdatedAt,
  statusMessage); remove fields not present on SDK Task (method, target,
  progress as number, error, completedAt, elapsed)
- TaskControls: type statusFilter as `TaskStatus | undefined`; add
  runtime validation guard on Select onChange
- TaskListPanel: accept `Task[]` instead of `TaskCardProps[]`; pass
  `onCancel(taskId)` through to each TaskCard; use `task.taskId` as key
- TasksScreen: accept `Task[]` + `onCancel(taskId)` callback; type
  internal statusFilter state as `TaskStatus | undefined`
- Delete re-export of `TaskStatus` from TaskCard (import directly from
  SDK where needed)
- Update all stories with real SDK `Task` fixtures
- Update ConnectedView TasksActive story

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-20 10:28:24 -04:00
cliffhall
d817c46911 Phase 2: Refactor Resources cluster to MCP SDK types
Replace ad hoc local types with MCP SDK schema types across the Resources
group components and ResourcesScreen, per the v2 UX interfaces plan.

- ResourceListItem: accept `resource: Resource | ResourceTemplate` instead
  of flat `name`/`uri`; render `title ?? name`; delete local
  `ResourceAnnotations` type
- ResourceControls: accept `Resource[]`, `ResourceTemplate[]`,
  `InspectorResourceSubscription[]` + `selectedUri`/`selectedTemplate`
  instead of `ResourceItem[]`/`TemplateListItem[]`/`SubscriptionItem[]`;
  no-op on re-selecting
- ResourcePreviewPanel: accept `resource: Resource` +
  `contents: (TextResourceContents | BlobResourceContents)[]` instead of
  flat `content: string`; use SDK `Annotations` (audience as Role[]);
  `lastUpdated` as `Date` not `string`
- ResourceTemplatePanel: accept `template: ResourceTemplate` instead of
  flat props; use SDK `Annotations`
- ResourceSubscribedItem: accept `subscription: InspectorResourceSubscription`
  instead of flat `name`/`lastUpdated`; `lastUpdated` as `Date`
- ResourcesScreen: accept `Resource[]`, `ResourceTemplate[]`,
  `InspectorResourceSubscription[]` + `selectedResourceUri`/
  `selectedTemplateUri` + `ReadResourceState`; derive selections via
  `find()`; add loading/error states
- Delete local `ResourceItem`, `TemplateListItem`, `SubscriptionItem`,
  `SelectedResource`, `SelectedTemplate`, `ResourceAnnotations` types
- Update all stories with real SDK fixtures
- Update ConnectedView ResourcesActive story

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 18:34:32 -04:00
cliffhall
de40b3720e Phase 2: Refactor Prompts cluster to MCP SDK types
Replace ad hoc local types with MCP SDK schema types across the Prompts
group components and PromptsScreen, per the v2 UX interfaces plan.

- PromptListItem: accept `prompt: Prompt` instead of flat `name`/`description`;
  render `title ?? name` as primary label
- PromptControls: accept `Prompt[]` + `selectedName` instead of `PromptItem[]`;
  no-op on clicking already-selected prompt; search includes title field
- PromptArgumentsForm: accept `prompt: Prompt` instead of flat props;
  read `prompt.arguments` directly; display `title ?? name` as heading
- PromptMessagesDisplay: narrow type from `(PromptMessage | SamplingMessage)[]`
  to `PromptMessage[]` per spec
- PromptsScreen: accept `Prompt[]` + `selectedPromptName` + `GetPromptState`;
  derive selected prompt via `prompts.find()`; own `argumentValues` state
  locally; reset arguments on tool switch
- Delete local `PromptItem`, `SelectedPrompt`, `PromptArgument` types
- Update all stories with real SDK `Prompt` fixtures
- Update ConnectedView PromptsActive story

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-18 17:49:27 -04:00
cliffhall
92ecfcbd6c Phase 2: Refactor Tools cluster to use MCP SDK types
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>
2026-04-18 16:57:10 -04:00
cliffhall
658ef86944 Phase 1: Style refinements and component tweaks from Storybook review
Theme & styling:
- Add custom "inspector" color palette with dark navy primary
- Change primaryShade to { light: 7, dark: 8 }
- Add Input theme override to apply --inspector-input-background globally
- Add --inspector-input-background token (white in light, dark-9 in dark)
- Override --mantine-color-body in dark mode via cssVariablesResolver
  in both main.tsx and Storybook preview.tsx
- Add dark-mode CSS overrides for Select dropdown and option hover
- Add dark-mode CSS override for Accordion control hover
- Rename CSS variable --inspector-status-failed → --inspector-status-error

Component changes:
- CopyButton: increase icon font size to 24px, use --inspector-text-primary
  for high-contrast color (black/white per theme)
- TaskControls: remove onClearHistory (moved to TaskListPanel), fix Select
  clear button not disappearing by passing null instead of undefined
- TaskListPanel: add onClearCompleted callback, render "Clear" button
  next to Completed section header
- HistoryListPanel: move "Clear" button from toolbar to History section
  header, rename from "Clear All"

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 19:26:21 -04:00
cliffhall
2d4fca4fa6 Phase 1: Refactor all 16 element component interfaces to use MCP SDK types
Replace ad-hoc local type definitions in element components with real
MCP SDK types and Inspector wrapper types from core/mcp/types.ts:

Schema-typed primitives:
- LogLevelBadge: LogLevel → LoggingLevel (SDK)
- TransportBadge: "stdio"|"http" → ServerType (core), add sse/streamable-http
- TaskStatusBadge: local TaskStatus → SDK TaskStatus (working/input_required/...)
- ServerStatusIndicator: inline union → ConnectionStatus (core), "failed"→"error"

MCP wrapper consumers:
- AnnotationBadge: label+variant → facet+value with SDK Role type
- CapabilityItem: name:string → capability:keyof ServerCapabilities|ClientCapabilities
- LogEntry: flat props → entry:{receivedAt:Date, params:LoggingMessageNotification["params"]}
- MessageBubble: flat props → message:SamplingMessage|PromptMessage
- ProgressDisplay: flat props → params:ProgressNotification["params"]

Composite-input elements:
- ContentViewer: type+content+mimeType → block:ContentBlock (SDK discriminated union)
- InlineError: message+details → error:{message,data?}
- ConnectionToggle: checked+loading+onChange → status:ConnectionStatus+onConnect/onDisconnect

Also updates all group/screen/view consumers to keep the build green,
and renames CSS variable --inspector-status-failed → --inspector-status-error.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-17 11:57:53 -04:00
cliffhall
8483b98c37 Collapse ServerSettingsForm into accordion and add subtle ListToggle variant
Wraps every section of ServerSettingsForm (Connection Mode, Custom
Headers, Request Metadata, Timeouts, OAuth Settings) in a controlled
Mantine Accordion. The accordion is driven by new required
`expandedSections` / `onExpandedSectionsChange` props so the parent
owns expand/collapse state — consistent with the dumb-component
principle.

Adds a `variant?: "default" | "subtle"` prop to ListToggle. In subtle
mode it renders as an ActionIcon with `variant="subtle"`, `color="gray"`,
and a 16px icon so it visually matches Mantine's CloseButton in modal
headers (transparent until hover).

Adds a WithSettingsModal story on UnconnectedView that presents the
form inside a Mantine Modal with a custom three-column header: subtle
ListToggle on the left (expands/collapses all sections at once),
"Server Settings" Title centered, CloseButton on the right.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-05 09:36:14 -04:00
cliffhall
064b807ea4 Move search state into ToolControls and PromptControls, search response in history
- ToolControls and PromptControls now manage searchText as local state
  and filter their lists internally (matches on name + title/description)
- Removed searchText/onSearchChange props from ToolsScreen, PromptsScreen,
  and their associated stories
- HistoryListPanel search now also matches against stringified response
  payloads

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-04 16:45:36 -04:00
cliffhall
bf31ae6cfc Fix inert type to boolean and apply linter formatting
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 18:06:46 -04:00
cliffhall
8e73bc6ffb Refactor ServerCard with active server logic, rename StatusIndicator, and polish UI panels
- 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>
2026-04-02 18:00:18 -04:00
cliffhall
a68933464b Add ListToggle and built-in search filtering to ResourceControls, move Export/Clear buttons to HistoryListPanel
ResourceControls now manages its own search state internally and filters
all three accordion sections (URIs, Templates, Subscriptions) by name
and URI. Added ListToggle to expand/collapse all sections. Moved Export
JSON and Clear All buttons from HistoryControls sidebar to the
HistoryListPanel header for better proximity to the data they act on.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 19:24:11 -04:00
cliffhall
5594e940c9 Inline ListChangedIndicator with titles, normalize screen gaps, and rename ResultPanel to ToolResultPanel
- Place ListChangedIndicator on same line as section titles using Group with space-between in ToolControls, ResourceControls, and PromptControls
- Add ListChanged stories for ResourceControls and show indicators in ConnectedView stories
- Normalize ScreenLayout gap from "xl" to "md" across all screens for consistent spacing
- Rename ResultPanel to ToolResultPanel (component, interface, files, and stories)
- Replace Grid layout in ToolsScreen with equal-width flex children for detail and result panels

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 12:47:41 -04:00
cliffhall
a6724626b7 Normalize screen layouts: extract control groups, unify sidebar widths, and refactor PromptsScreen
- Extract ToolControls group from ToolsScreen (search, list, list-changed indicator)
- Extract PromptControls and PromptListItem groups for PromptsScreen sidebar
- Refactor PromptsScreen to sidebar + detail layout (matching ResourcesScreen pattern)
- Refactor PromptArgumentsForm to accept a single prompt instead of a dropdown
- Normalize all screen sidebar widths to 340px (Tools, Resources, Prompts, Logs, Tasks, History)
- Convert ToolsScreen from Grid-only to sidebar + Grid layout
- Replace LogControls checkboxes with full-width toggle buttons
- Move Clear/Export buttons from LogControls to LogStreamPanel toolbar
- Make PromptArgumentsForm "Get Prompt" button right-aligned and compact
- Update all affected stories and ConnectedView

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 17:54:20 -04:00
cliffhall
eca6cf7665 Extract ResourceControls, refactor ResourcesScreen layout, and add scrollable accordion panels
- Extract ResourceControls group component from ResourcesScreen:
  title, search, list-changed indicator, and three-section accordion
  (URIs, Templates, Subscriptions) with per-panel ScrollArea.Autosize
- Refactor ResourcesScreen to sidebar+panel Flex layout matching
  HistoryScreen/LoggingScreen pattern (ScreenLayout, Sidebar, SidebarCard)
- Each accordion panel dynamically computes max height based on how many
  sections are open, dividing available space evenly
- Accordion uses controlled state (value/onChange) to track open sections
- Sidebar card sizes to content naturally (compact when collapsed,
  grows with open sections, capped by scroll areas)
- Right panel wrapped in ScrollArea.Autosize for long content
- Add AllSections story with 20 URIs, 7 templates, 8 subscriptions
- Add more templates and subscriptions to ConnectedView ResourcesActive
- Add ResourceControls stories (Default, WithSearch, Empty)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-31 14:38:02 -04:00
cliffhall
c494d2feb4 Redesign Tasks screen: expandable TaskCard, sidebar controls, and list panel
- Redesign TaskCard to match v1 Task Details panel layout:
  header with task ID + status badge + cancel button,
  always-visible summary row (Status, Last Updated, Created At, TTL)
  with Expand/Collapse button, expandable section with status message,
  progress bar, error, and Full Task Object JSON viewer
- Add TaskControls group: search input + status filter dropdown +
  Refresh/Clear History buttons (mirrors HistoryControls pattern)
- Add TaskListPanel group: scrollable panel with ListToggle for
  expand/collapse all, splits tasks into Active/Completed sections
  with counts, filters by search text and status
- Refactor TasksScreen to sidebar + panel layout matching HistoryScreen:
  manages search/filter state internally, flat tasks array
- Simplify TaskCard callbacks to just onCancel
- Extract named .withProps() subcomponents (HeaderLeftGroup,
  HeaderRightGroup, SectionTitle, SummaryRow, DetailRow) for
  AGENTS.md compliance
- Update all stories (TaskCard, TaskControls, TaskListPanel,
  TasksScreen, ConnectedView)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-29 22:03:54 -04:00
cliffhall
a0e763a315 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>
2026-03-29 20:04:25 -04:00
cliffhall
961dc283fc Add new components, typography, badge contrast, and screen layout refactors
New element components:
- ListToggle: expand/collapse button using react-icons (RiExpandVerticalLine/RiCollapseVerticalLine)
- LogLevelBadge: extracted from LogEntry, handles all 8 log levels with filled variant and dark/light text contrast

New group components:
- ViewHeader: shared header bar for ConnectedView and UnconnectedView with discriminated union on `connected` prop; MCP logo links to modelcontextprotocol.io
- ServerListControls: combines ListToggle and AddServerMenu, hides toggle when no servers
- LogStreamPanel: extracted from LoggingScreen with scroll containment, filtering by visible levels and search text
- HistoryControls: vertical sidebar with search, method filter, export, and clear
- HistoryListPanel: extracted from HistoryScreen with ListToggle compact mode, filtering by method and search text

Badge contrast improvements:
- AnnotationBadge: filled variant for both dark/light modes; black text in dark, white text in light
- LogLevelBadge: same contrast approach; bold weight for alert/emergency levels
- Badge theme default weight reduced from 700 to 600

Typography:
- Added Fredoka (300-700) as body and heading font
- Added Roboto Mono as monospace font
- Google Fonts loaded in index.html and Storybook previewHead
- Heading weight set to 600

Screen layout refactors:
- LoggingScreen: owns filterText and visibleLevels state; passes to LogControls and LogStreamPanel; removed controls prop in favor of flat props
- HistoryScreen: owns searchText and methodFilter state; passes to HistoryControls and HistoryListPanel for filtering
- Both screens use new ScreenLayout (Flex variant="screen") and Sidebar subcomponents
- Added Flex theme with "screen" variant (overflow: hidden)
- Added Paper theme "panel" variant (flex column, overflow hidden)

Component enhancements:
- ServerCard: added compact prop showing only header row
- HistoryEntry: renamed isExpanded to isListExpanded; internal useState syncs via useEffect; individual entries toggle independently
- LogControls: Select All / Deselect All toggle for level checkboxes; nowrap on level selector group
- ConnectionToggle: LoadingOverlay with rounded corners for connecting state
- ConnectedView: ThemeToggle ActionIcon matched to DisconnectButton height (36px) and border color (outline variant)
- Storybook preview: fullscreen layout wrapper with 100vh overflow hidden

New stories:
- UnconnectedView: Connecting and ManyServers (12 servers for scroll testing)
- ServerCard: CompactConnected and CompactDisconnected
- All new components have corresponding stories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 20:38:04 -04:00
cliffhall
dd46acba87 Refactor renamed the component groups to something less obtuse:
Elements -> Groups -> Screens -> Views
2026-03-28 12:58:47 -04:00