inspector/clients/web
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Cliff Hall 8d6dea0af2
Port resource preview UX improvements to PromptsScreen (#1328) (#1331)
* Port resource preview UX improvements to PromptsScreen (#1328)

Brings the prompt-fetching flow up to parity with the resource preview
work from #1326:

- Cap the argument-form pane at maw=40% so a bare input + button doesn't
  stretch across wide displays.
- Auto-fetch no-argument prompts the moment they're picked from the
  sidebar (handleSelectPrompt routes them straight to onGetPrompt({})),
  and hide the form once the user clicks Get Prompt — the result panel
  takes the same fixed-height column the resource preview uses.
- Apply the PreviewCard variant=preview pattern: card sizes to content
  but caps at viewport, PromptMessagesDisplay pins its header (flex 0 0
  auto) and lets the inner ScrollArea (flex 0 1 auto, mih 0) absorb
  overflow.
- Tag getPromptState with the prompt name in App.tsx so the screen can
  ignore a stale result whose name no longer matches the selection.
- MessageBubble now routes each content block through ContentViewer
  (markdown for text via mimeType="text/markdown", image/audio/resource
  via existing ContentViewer branches). Non-renderable block types
  (tool_use, tool_result) are filtered out; the role-label header keeps
  the bubble visible regardless.
- PromptArgumentsForm gains onCompleteArgument + completionsSupported;
  when both are set, each input becomes Mantine Autocomplete with the
  same per-arg AbortController + per-arg debounce timer pattern as
  ResourceTemplatePanel. PromptsScreen + InspectorView + App.tsx wire
  the callback to inspectorClient.getCompletions with a
  ref/prompt envelope.

Closes #1328.

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

* Add close (X) buttons to ResourcePreviewPanel and PromptMessagesDisplay

A top-left CloseButton dismisses the preview panel. The host screen
decides what to fall back to:

- ResourcesScreen remembers the originating template URI when the user
  reads from a template form (originatingTemplateUri); closing the
  preview restores the template form. Plain-resource reads (sidebar
  selection) just empty the selection and return to the empty state.
  Picking a different template / resource from the sidebar clears the
  back-trail.
- PromptsScreen flips submittedFor back to undefined when the closed
  prompt has arguments — the argument form re-renders with the
  user's values preserved so they can edit and re-submit. No-arg
  prompts have nothing to fall back to, so the selection is dropped
  and the empty state appears.

The X button is hidden when the host omits onClose, so callers that
don't want a dismiss control keep their existing rendering.

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

* Add close button to pending and error preview states

The OK-state preview already had its X button (inside the panel
component itself). Pending and error states were inline cards in
renderReadState / renderPreview with no way to dismiss them, so a
user who submitted bad input to a resource template or prompt was
stuck staring at the error until they picked a different sidebar
item. Adds a top-left CloseButton row to both states on both screens,
wired to the same handleClosePreview handler — so the template form
or argument form re-appears on close just like it does from the OK
state.

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

* Fire prompt completions on focus and send every sibling in context

Two fixes to the PromptArgumentsForm autocomplete:

- Focusing an argument input now fires completion/complete immediately
  (handleFocus) — the dropdown is populated as soon as the user clicks
  in, not only after they start typing. Any in-flight debounce timer
  for the same arg is cancelled so a stale keystroke request can't
  overwrite the fresh focus response.
- The completion context now includes every declared prompt argument
  (with "" for ones the user hasn't typed into yet), minus the one
  being completed. Previously the context only carried args the user
  had touched, so servers that disambiguate based on co-arguments
  couldn't see the full picture on the first keystroke.

Tests cover both: a focus-only path that fires before any keystroke,
and a typing path that asserts the empty sibling is sent through.

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

* Fire resource-template completions on focus

Mirrors the prompt-side change: focusing a template variable input
now fires completion/complete immediately so the dropdown populates
the moment the user clicks in, rather than waiting for the first
keystroke + 300ms debounce. Any pending debounce timer for the same
variable is cancelled first so a stale keystroke response can't
overwrite the focus response.

The sibling-context coverage was already correct here — `variables`
is seeded with empty strings for every declared template variable at
mount and on template switch, so the context payload always carries
the full variable set minus the one being completed.

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

* Style Autocomplete dropdowns to match the rest of the app

Two fixes so the completion dropdowns on prompt-argument and
resource-template inputs look like every other dropdown in the app:

- App.css: the dark-mode dropdown / option-hover rules used to target
  .mantine-Select-* only, so the Autocomplete dropdowns shipped with
  Mantine's default surface color and a different hover background.
  Re-scoped to .mantine-Combobox-* so every Combobox-built input
  (Select, Autocomplete, MultiSelect, …) inherits the same styling.
- theme/Autocomplete.ts: new theme module setting `radius: "md"` so
  Autocomplete matches the default Select / TextInput corner radius.
  Wired through theme/index.ts and theme/theme.ts.

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

* Fix dropdown styling — Select and Autocomplete need separate selectors

The previous commit re-scoped the dark-mode dropdown rules from
`.mantine-Select-*` to `.mantine-Combobox-*` on the assumption that
both Select and Autocomplete share a Combobox class on the dropdown
root. They don't — Mantine's Combobox factory takes
`__staticSelector` from the parent component, so Select emits
`mantine-Select-dropdown` and Autocomplete emits
`mantine-Autocomplete-dropdown` with no shared `Combobox` class on
that element. The `.mantine-Combobox-*` selector matched nothing,
so the TaskControls filter (and every other Select) lost its gray
surface and reverted to Mantine's default brown-ish dark background.

List both selectors so Select dropdowns keep their styling and
Autocomplete dropdowns pick it up.

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

* Address review feedback: canSubmit, fire-time context, re-click guard

- PromptArgumentsForm: add a canSubmit guard that disables Get Prompt
  until every required argument has a value, matching the resource
  template form's symmetry. Optional args may stay blank.
- Both forms: capture sibling context inside the debounce callback,
  not at schedule time. Hold the latest argumentValues / variables
  in a ref + sync via useEffect, and call buildContext() at fire
  time. Without this, typing in arg A then arg B within the 300ms
  window would ship A's request with B's pre-keystroke value.
- PromptsScreen.handleSelectPrompt: early-return when the user
  re-clicks the already-selected prompt — sidebar is for navigation,
  ✕ is for dismiss. Previously a re-click wiped form values and
  re-fired the auto-fetch for no-arg prompts.
- MessageBubble: expand the comment on effectiveMimeForBlock to flag
  the unconditional markdown promotion as a known asymmetry with
  the resource side (which only promotes when the server signals it),
  pending a per-block mimeType in the spec.
- New tests: canSubmit disabled / enabled transitions, fire-time
  context capture across siblings, abort-path verification (a stale
  in-flight response must not overwrite the fresh one), and the
  no-op re-click guard on PromptsScreen.

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

* Address follow-up review nits: ghost suggestions, dead previewActive branch

- Clear `completions[name]` at the top of handleChange in both
  PromptArgumentsForm and ResourceTemplatePanel so the dropdown
  doesn't show stale options from the previous prefix during the
  300ms debounce + network window.
- Simplify previewActive in PromptsScreen to drop the unreachable
  `!getPromptState?.promptName` fallback. App.tsx tags every prompt
  state transition with `promptName`, so the fallback was dead code
  that obscured the actual runtime invariant.
- Add a focused test for each form that asserts stale dropdown
  options vanish the instant a new keystroke arrives.

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-23 09:20:20 -04:00
..
.storybook Phase 1: Style refinements and component tweaks from Storybook review 2026-04-17 19:26:21 -04:00
public * Added base project to clients/web 2026-03-16 22:47:03 -04:00
server Port v1.5 web dev backend (vite-hono-plugin + remote-server bootstrap) (#1321) 2026-05-18 19:22:14 -04:00
src Port resource preview UX improvements to PromptsScreen (#1328) (#1331) 2026-05-23 09:20:20 -04:00
static Port v1.5 web dev backend (vite-hono-plugin + remote-server bootstrap) (#1321) 2026-05-18 19:22:14 -04:00
.gitignore test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
chromatic.config.json add chromatic.config.json 2026-03-22 15:42:09 -04:00
eslint.config.js test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
index.html Add new components, typography, badge contrast, and screen layout refactors 2026-03-28 20:38:04 -04:00
package-lock.json Expose live resource subscriptions via a hook (#1325) (#1326) 2026-05-19 20:15:51 -04:00
package.json Expose live resource subscriptions via a hook (#1325) (#1326) 2026-05-19 20:15:51 -04:00
README.md * Added base project to clients/web 2026-03-16 22:47:03 -04:00
tsconfig.app.json refactor(core): apply v1.5-port cleanups deferred from #1306 (#1316) 2026-05-17 19:15:48 -04:00
tsconfig.json test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269) 2026-05-01 15:15:32 -04:00
tsconfig.node.json Port v1.5 web dev backend (vite-hono-plugin + remote-server bootstrap) (#1321) 2026-05-18 19:22:14 -04:00
tsconfig.storybook.json fix dark mode, storybook config, and minor UI issues 2026-03-23 13:12:37 -04:00
tsconfig.test.json refactor(core): apply v1.5-port cleanups deferred from #1306 (#1316) 2026-05-17 19:15:48 -04:00
vite.config.ts Wire App.tsx to v2 core/ hook layer; remove demo stub from InspectorView (#1322) 2026-05-19 15:41:55 -04:00
vitest.shims.d.ts * Added base project to clients/web 2026-03-16 22:47:03 -04:00

React + TypeScript + Vite

This template provides a minimal setup to get React working in Vite with HMR and some ESLint rules.

Currently, two official plugins are available:

React Compiler

The React Compiler is not enabled on this template because of its impact on dev & build performances. To add it, see this documentation.

Expanding the ESLint configuration

If you are developing a production application, we recommend updating the configuration to enable type-aware lint rules:

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...

      // Remove tseslint.configs.recommended and replace with this
      tseslint.configs.recommendedTypeChecked,
      // Alternatively, use this for stricter rules
      tseslint.configs.strictTypeChecked,
      // Optionally, add this for stylistic rules
      tseslint.configs.stylisticTypeChecked,

      // Other configs...
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])

You can also install eslint-plugin-react-x and eslint-plugin-react-dom for React-specific lint rules:

// eslint.config.js
import reactX from 'eslint-plugin-react-x'
import reactDom from 'eslint-plugin-react-dom'

export default defineConfig([
  globalIgnores(['dist']),
  {
    files: ['**/*.{ts,tsx}'],
    extends: [
      // Other configs...
      // Enable lint rules for React
      reactX.configs['recommended-typescript'],
      // Enable lint rules for React DOM
      reactDom.configs.recommended,
    ],
    languageOptions: {
      parserOptions: {
        project: ['./tsconfig.node.json', './tsconfig.app.json'],
        tsconfigRootDir: import.meta.dirname,
      },
      // other options...
    },
  },
])