A floated promise is invisible at review time — the call reads like an
awaited one minus four characters — and the unhandled rejection it
produces surfaces in a different test, in a different file, as a stack
pointing at SDK internals. In #1947 two un-held `callTool` calls made
`npm run ci` unpassable and took a full investigation to attribute; the
fix was two lines. `@typescript-eslint/no-floating-promises` catches the
class outright, so enable it at `error` everywhere.
The rule is type-aware and `tseslint.configs.recommended` is not, so each
scope's config gains a parser project. The four clients name their own
tsconfig projects (both of them — `src` and `__tests__`/`server` live in
different ones). The root scope has no tsconfig at all, so add
`tsconfig.lint.json`: a lint-only project covering `core/**`,
`test-servers/src/**`, and `vitest.shared.mts`, emitting nothing and
gating nothing. It sets `moduleResolution: bundler` deliberately — under
NodeNext `core/`'s extensionless imports fail to resolve, every import
degrades to `any`, and the rule silently stops seeing promises at all.
Measured surface, and the fixes:
- core (2) — `stream.close()` in two Hono `onAbort` listeners. Hono
invokes subscribers with a bare `subscriber()`, so a promise returned
from the listener is floated one layer up; `closeAbortedStream` owns
the rejection where it can actually be owned.
- tui (5) — `handleDisconnect` had no catch of its own and its only
caller is a key handler that cannot await, so a failing disconnect was
a live unhandled rejection; it now surfaces the message the way
`handleConnect` does. The rest are `void` at callers that genuinely
cannot await, over callees that already own their failures.
- web (92) — 83 are Storybook `expect(...)`, which is instrumented and
returns a promise, now awaited (`scrollAreaStoryAssertions` is async
for the same reason). Two tests were asserting against a provider call
they never awaited. `open(url)` in both web servers now warns instead
of escaping a synchronous callback.
- cli, launcher — already clean.
Type-aware linting costs time: `clients/web`'s `eslint .` goes from ~8s
to ~19s, inside `validate`. Narrowing the loaded projects is the lever if
that needs reducing later; dropping the rule is not.
Closes#1959
Signed-off-by: cliffhall <cliff@futurescale.com>
Codebase-wide sweep bringing the web client into strict compliance with the AGENTS.md TypeScript and Mantine/React conventions: ~170 inline 2+-static-prop elements extracted to named .withProps() constants across ~77 files, the AGENTS.md rule wording tightened to match (static-prop boundary + Accordion/Transition/data-*/Box exemptions), a real ScreenStage Box->Flex layout regression fixed (direction: column) with a Storybook regression guard, and an App.tsx double-cast tightened. Closes#1762.
* web: Group tool-result resource_links in a scrollable "Resource Links" box with Protocol colors
Closes#1685
- ToolResultPanel: coalesce consecutive `resource_link` blocks into a
single bordered, internally-scrollable "Resource Links" box (mirroring
the "Messages" box in the Protocol monitoring sidebar). Non-link blocks
and non-adjacent runs are preserved in order via `segmentContent`.
- ResourceLink: render each link card on the recessed `inset` surface
(`Card variant="inset"`), matching ProtocolEntry's colors in light and
dark modes; nested read-result Code blocks read as raised on the grey
card via the inset cascade variable.
- Drop redundant metadata: the resource `description` line
(ResourceLinkInfo, and thus ContentViewer/ResourceLink) and the green
"Resource:" label above the expanded read result.
- Update tests + stories for the removed props/label and the new grouping
box; add multi-link and split-run coverage.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: keep the resource_link description; only drop the "Resource:" label
Reverts the description removal from the previous commit per review — the
`description` line is retained on ResourceLinkInfo (and its ContentViewer /
ResourceLink callers). Only the redundant green "Resource:" label above the
expanded read result stays removed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: drop the resource_link description from the link cards
The `description` is generated by the server and can be misleading — e.g. a
blob resource served with a `text/plain` mimeType gets described as a
"plaintext resource" even though the payload is a blob. Rather than surface
that nonsense text, drop the description line from ResourceLinkInfo (and its
ContentViewer / ResourceLink callers) entirely.
Reverts the description-retention from the prior commit; the grouped
"Resource Links" box and the "Resource:" label removal are unaffected.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: refine resource-link cards — URI-below-name + copy, ProtocolEntry toggle, full-height box
Follow-up refinements to the Resource Links display:
- ResourceLinkInfo: move the URI below the name and add a CopyButton beside
it so the URI can be copied. The name + MIME badge (and the optional
expand control) sit on the header row; the URI + copy sit on the row below.
- ResourceLink: replace the bespoke chevron with the same tooltip'd
ExpandToggle used by ProtocolEntry, and animate the read-result panel with
Mantine's Collapse (same as ProtocolEntry). The toggle is a sibling of the
URI copy button (no card-wide wrapping button) so the two real buttons
never nest (axe `nested-interactive`).
- ToolResultPanel: when a result contains a Resource Links box, fill the
card's height so the box grows into the available space and scrolls
internally (pinned "Resource Links" heading), instead of capping at a fixed
content height. Plain text/image results keep the scroll-within-card body.
- ToolsScreen: give the result card `flex: 1` so it fills the pane, providing
the definite height the Resource Links box grows into.
- Tests/stories updated for the new layout, the Expand/Collapse toggle labels,
the mounted-through-collapse read result, and the full-height box (with a
height-bounded decorator on the ResourceLinks story).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: align resource-link card with ProtocolEntry — toggle row, badge, URI style
Bring ResourceLinkInfo closer to the ProtocolEntry message card:
- Move the expand/collapse toggle to the second (URI) row, opposite the copy
button — mirroring ProtocolEntry, whose toggle sits on the row below the
badges. The MIME badge stays on the header row's right, beside the name.
- MIME badge: bump to `size="md"` so the lowercase MIME text matches the point
size of ProtocolEntry's uppercase method/status badges, and make it a solid
dark-blue fill with white text in dark mode (keeping the blue-light tint in
light mode) instead of a washed-out translucent chip.
- Render the URI like ProtocolEntry/NetworkEntry render URLs — `sm` / `fw: 500`
in the default sans-serif face and text color — rather than a blue monospace
"link". `monoBreak` is kept only for long-URI wrapping.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: scope result-card fill to resource-link results (review follow-up)
Code-review finding: ToolsScreen gave the result card `flex={1}`
unconditionally, so plain text/image/error results also filled the full pane
height — leaving a tall empty card and a height jump versus the input-form
state. Fill is only needed so a "Resource Links" box can grow and scroll.
Gate the fill on a new `resultHasResourceLinks(result)` helper (extracted to
`toolResultUtils.ts` so the component module keeps exporting only components,
per react-refresh/only-export-components). Non-link results keep their
content-sized card; link results still fill and scroll internally.
Tests: unit tests for the helper (link / text-only / empty / error) and a
ToolsScreen test that a resource-link result renders the Resource Links box
(covering the `flex={1}` branch).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: prettier-format the resultHasResourceLinks error-case test fixture
The error-case fixture in ToolResultPanel.test.tsx wasn't re-formatted after
adding the required `name` field, failing CI's `format:check`. No logic change.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: cap a non-link block above the Resource Links box at 50% height
Addresses the mixed-content case from the review: a long text block preceding
the links would take its full natural height and push the Resource Links box
almost entirely off-screen (only the first card peeking at the bottom).
In the links-containing result, wrap each non-link block in a ScrollArea.Autosize
capped at `mah: 50%` (flex "0 1 auto"), so it scrolls within at most half the
card and the links box keeps the rest. A short block still takes only its
content height; a result with no links renders non-link blocks uncapped in the
outer scroll body as before.
Adds a `ResourceLinksWithLongText` story (full-height decorator) exercising the
cap. Verified visually: the text scrolls within ~50% and the links box fills
the remainder.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: address review — expand-toggle a11y, stale JSDoc, stronger fill test
Review follow-ups on PR #1686:
- ExpandToggle: add an optional `ariaLabel` (defaults to the tooltip verb, so
ProtocolEntry/NetworkEntry/Task are unchanged) and expose `aria-expanded`.
ResourceLink passes a per-resource label ("Expand/Collapse resource <uri>")
so multiple link toggles are distinguishable to assistive tech and announce
their state — restoring the a11y the card-wide button previously provided,
while keeping the visible tooltip a plain "Expand"/"Collapse".
- ResourceLinkInfo: fix the stale JSDoc that still described the URI as
"monospace, link-styled" — it now renders sans-serif sm/fw:500 default color.
- ToolsScreen test: assert the result card actually gets `flex: 1` for a
links result and NOT for a plain text result, so the test verifies the fill
branch it names rather than only that the heading renders.
Tests: ExpandToggle gains ariaLabel + aria-expanded cases; ResourceLink /
ToolResultPanel / stories updated for the per-resource toggle name.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
* web: restore cli/tui/launcher package-locks accidentally committed
These lock files were touched by a local install in the worktree (dropping the
`version` field and adding optional @emnapi platform deps); they are unrelated
to this PR. Restore them to their v2/main state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01RwZGbocZ64yN9bgb2mj1qE
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): single header MonitoringToggle + Prompts-style Tools results (#1661)
Consolidate the monitoring column's open/close affordances into one header
toggle, and align the Tools/Prompts result panes and the sidebar log layout.
- Add a single `MonitoringToggle` in the header, to the right of the theme
icon. It opens/closes the monitoring column and only shows when a server is
connected OR a connect attempt failed (wide viewport + a monitor tab
available) — the same availability rule the column uses.
- Remove the scattered pin/close icons: delete `PinColumnButton`; drop `onPin`
from the Logs/Protocol/Network/Console screens and their stream panels; drop
`onOpenMonitor` from the server list; remove the close button from
`MonitoringControls`/`MonitoringScreen`.
- Tools screen: remove the "Results will appear here" placeholder pane. Results
now replace the input form in a single content pane (the Prompts pattern),
with a top-left X (ToolResultPanel) that dismisses them back to the form;
form values are preserved for a re-run.
- Prompts screen: the argument form fills the content pane width (drop the 40%
cap), matching the Tools input.
- LogEntry: add a `compact` two-line layout (time/level/logger on line one,
message wrapping below) used by the embedded monitoring-column Logs.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sMW1YZXkM7fDkdPtHpE95
* polish(web): tooltips on icon buttons + rename "monitoring column" → "monitoring sidebar" (#1661)
- Add Mantine `Tooltip`s to the icon-only controls: Client settings, Theme
(dynamic light/dark label), the monitoring-sidebar toggle, the list-level and
per-entry Expand/Collapse toggles, the Pin/Unpin toggle, and the Replay
button. Labels mirror each control's existing aria-label.
- Rename the feature term "monitoring column" → "monitoring sidebar" across the
user-facing labels (Open/Close/Resize), comments, and tests, so the UI calls
it a sidebar consistently.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sMW1YZXkM7fDkdPtHpE95
* review: consistent sidebar wording + reuse MonitoringToggleProps (#1661)
Address the two optional notes from the PR #1662 review:
- Make the MonitoringToggle JSDoc voice consistent ("sidebar" throughout,
no lingering "column").
- Drop ViewHeader's duplicate `MonitorToggleProps` and reuse the element's
exported `MonitoringToggleProps` as the single source of truth.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sMW1YZXkM7fDkdPtHpE95
* feat(web): slide-in/out animation for the monitoring-sidebar toggle (#1661)
The header MonitoringToggle now animates in/out via a Mantine Transition whose
in/out styles drive width + margin-left (plus opacity/translateX): as its slot
grows from 0 → the 36px icon, the flex-end header row reflows and pushes the
theme + client-settings buttons one slot to the left; the exit reverses and they
slide back over. A negative margin-left while collapsed cancels the row gap so
the theme icon stays flush-right when the toggle is absent. Only appearance/
disappearance (connect / disconnect / narrow viewport) animates — opening or
closing the sidebar leaves the toggle in place.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015sMW1YZXkM7fDkdPtHpE95
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rename the web client's History tab and its supporting code to Protocol
so the feature reads as the MCP "Protocol" view.
- Tab renders as "Protocol"; INSPECTOR_TAB_IDS, InspectorView tab
constants/MonitorTab, TAB_UI_REGISTRY key, and navigation.ts tab id all
updated to Protocol.
- Rename components + folders: HistoryScreen -> ProtocolScreen,
HistoryListPanel -> ProtocolListPanel, HistoryEntry -> ProtocolEntry,
HistoryControls -> ProtocolControls, historyUtils -> protocolUtils
(plus their tests and stories, and exported types).
- Rename App/InspectorView plumbing: historyUi, pinnedHistoryIds,
on*History* handlers/props, replayHistoryRequest,
isReplayableHistoryMethod, sort/compact scopes, etc. -> Protocol names.
- Export filenames use the "protocol" slug (buildExportFilename +
ExportKind).
Preserved (per issue #1623 decisions):
- The in-panel section discriminator keeps its "pinned" | "history"
values, and the sub-sections stay labeled Pinned / History.
- window.history and core message-log/replay concepts are untouched.
npm run ci passes (format, lint, build, unit tests, >=90 per-file
coverage gate, smoke, Storybook).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
* fix(web): don't show a permanent PENDING badge on history notifications (#1439)
`extractStatus` derived the history status badge purely from whether a
`response` was attached, so a fire-and-forget notification (no id, no
response ever) — and any unmatched standalone response — was labelled
"Pending" forever. The pending → OK/Error lifecycle only applies to
request entries (messageLogState attaches the response by JSON-RPC id),
so scope the status to `direction === "request"` and render no
request-style badge for notifications/responses. The method badge already
labels them.
Part of the History-screen work on #1438.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(core): track outgoing responses so server→client requests resolve in history (#1440)
MessageTrackingTransport.send only tracked outgoing requests, so the
client's response to a server→client request (roots/list, sampling,
elicitation) was never recorded. The inbound request entry never got its
response folded in, leaving it stuck "Pending" with no response body.
Make send symmetric with onmessage: track outgoing responses (by id +
result/error) as well as outgoing requests. The response now correlates
to its request entry, which resolves to OK/Error with the body. Outgoing
notifications remain untracked (separate gap).
Part of the History-screen work on #1438.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): wire up History Pin and Replay (#1438)
Both HistoryEntry actions were rendered but stubbed to todoNoop in App.
- Pin: App now owns a session-scoped Set<string> of pinned entry ids,
toggled by onTogglePinHistory and passed down as pinnedHistoryIds.
HistoryListPanel already sorts pinned entries to the top. The set resets
with the rest of the per-screen state on disconnect/server-switch.
- Replay: onReplayHistory re-issues the entry's original request by method
(tools/call → callTool, prompts/get → getPrompt, resources/read →
readResource). The call flows through InspectorClient → tracked
transport → message log, so the fresh request+response surface as a new
History entry (history-local) without touching the Tools/Prompts/
Resources panels. Unsupported methods / a removed tool surface a toast.
Tests: pin toggle propagation, replay dispatch for each supported method,
unsupported-method and missing-tool toasts.
Closes#1438.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): make history Replay support list/discovery requests
Replaying a tools/list entry reported "Replay isn't supported". Extend
replayHistoryRequest beyond the call-style requests to the read/discovery
methods, re-issuing each via its InspectorClient method (preserving the
pagination cursor):
- tools/list → listTools, prompts/list → listPrompts,
resources/list → listResources,
resources/templates/list → listResourceTemplates, ping → ping.
Tests: tools/list replay (cursor preserved) and an unsupported-method
toast (logging/setLevel).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): replay tasks/list; reorder + conditionally show History buttons
- Add tasks/list to the replayable set (re-issued via listRequestorTasks),
completing the client-issued */list methods.
- Reverse the HistoryEntry actions to Pin then Replay, and hide Replay for
entries whose method can't be replayed.
- Introduce a single source of truth — REPLAYABLE_HISTORY_METHODS /
isReplayableHistoryMethod in historyUtils — used by HistoryEntry to
show/hide the button and by App's replayHistoryRequest to gate dispatch,
so the two can't drift.
Tests: tasks/list replay, Replay hidden for a non-replayable method, and
Pin-before-Replay ordering.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): add client↔server direction badge to History and Network (#1437)
A dual-state badge at the top-left of each entry shows which way it
traveled: "client → server" (green) for inspector-originated, "client ←
server" (yellow) for server-originated.
- New shared element MessageDirectionBadge (Elements).
- History: MessageEntry gains an `origin` ("client" | "server") set at
tracking time — MessageTrackingTransport tags `send` as client and
`onmessage` as server, threaded through the track callbacks. HistoryEntry
maps origin → badge. (direction stays the request/response/notification
message-type used for response correlation; origin is the flow.)
- Network: fetches are always inspector-originated, so NetworkEntry renders
the badge as outgoing.
Tests + stories for the badge; HistoryEntry direction tests; transport
test asserts the origin argument.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(web): use purple (not yellow) for the client ← server direction badge
Yellow + green read as caution/ok status; purple vs green reads as
direction. Incoming (server → client) is now violet; outgoing stays green.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): make History Pinned/History sections collapsible toggles
The "Pinned Requests (N)" and "History (N)" section headers are now
clickable toggle buttons that expand/collapse their entries — styled like
the LogControls level toggles (UnstyledButton listItem variant with the
active background) and wrapping the entries in a Collapse. Both default
open and collapse independently.
Tests assert the per-section aria-expanded toggling and independence.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): replace Expand/Collapse text button with an icon toggle
History, Network, and Task cards now use a shared ExpandToggle ActionIcon
instead of the "Expand"/"Collapse" text button: LuArrowDownToLine when
collapsed (expand), LuArrowUpFromLine when expanded (collapse). The
aria-label stays "Expand"/"Collapse" so the control reads the same.
- New shared element ExpandToggle (+ test + stories).
- HistoryEntry: actions split into a left group (Pin/Replay) with the
toggle pushed right via space-between.
- NetworkEntry / TaskCard: drop their now-unused SubtleButton const.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): pin icon toggle on HistoryEntry, moved to the right
Replace the Pin/Unpin text button with a shared PinToggle ActionIcon
(TiPinOutline when unpinned, TiPin when pinned; aria-label stays
Pin/Unpin). Move it to the right action group, just left of the
expand/collapse toggle — Replay stays on the left.
- New shared element PinToggle (+ test + stories).
- HistoryEntry action row: Replay left; Pin + expand toggle right.
- Drop the now-unused formatPinLabel helper.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): per-section Clear/Export on the Pinned and History headers
Each section header now shows Clear and Export links on the right, but
only when both sections are present (a single section is already covered
by the panel-level Clear/Export). They act on just that section's entries:
- Clear: removes that section's entries by pin membership (clearing the
Pinned section also drops the now-stale pinned id set).
- Export: downloads just that section's entries
(inspector-history-pinned/-unpinned-...json).
Threaded onClearSection/onExportSection App → InspectorView → HistoryScreen
→ HistoryListPanel; App computes each section from messages + pinned ids.
Extracted a CollapsibleSection subcomponent and a SectionActions links
group. Extended ExportKind with history-pinned/history-unpinned.
Tests cover both-sections visibility gating and per-section dispatch.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): Filter by Message Direction on the History controls
Add a "Filter by Message Direction" section to HistoryControls, modeled on
LogControls' "Filter by Level": a Select/Deselect All control plus two
listItem toggles — "client → server" and "client ← server" — that show or
hide entries by origin.
- HistoryUiState gains visibleDirections (Record<MessageOrigin, boolean>),
defaulting to both visible; persists with the other per-screen state.
- HistoryScreen wires the toggles (single + all) through onUiChange and
passes the filter to HistoryListPanel, where matchesFilters drops entries
whose direction is off (entries without a recorded origin are never
filtered out).
Tests cover the controls (render, single toggle, select/deselect all), the
HistoryScreen onUiChange wiring, and the list-level direction filtering.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(web): section Clear/Export as subtle buttons, not link anchors
Match the Select/Deselect All control in HistoryControls: the per-section
Clear/Export on the History/Pinned headers now use a subtle Button
(variant subtle, size xs) instead of an Anchor link.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(web): color the direction filter labels to match the badge
The "client → server" / "client ← server" toggles in HistoryControls now
color their labels green / violet, matching the MessageDirectionBadge.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): track outgoing client notifications in history too
Review follow-up (#1441): MessageTrackingTransport.send now also tracks
the client's own notifications (initialized, progress,
roots/list_changed) as origin "client", making send fully symmetric with
onmessage. Previously only id-bearing outgoing messages (requests,
responses) were tracked, so client notifications never reached History
while incoming server notifications did.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(web): Filter by Message Direction on the Network screen
Add the same "Filter by Message Direction" section to NetworkControls as
on HistoryControls. Extracted the shared section into a MessageDirectionFilter
group component (used by both HistoryControls and NetworkControls) so they
stay identical — a Select/Deselect All control plus green/violet
client → server / client ← server toggles.
- NetworkUiState gains visibleDirections (default both visible), persisted
with the rest of the per-screen state; NetworkScreen wires the toggles and
NetworkStreamPanel filters on them.
- Network fetches are always inspector-originated, so all entries are
client → server: the client toggle gates them all and the server toggle
matches nothing (documented in NetworkStreamPanel). The section mirrors
History for parity.
Tests cover the shared filter, both controls' direction sections, the
Network list filtering, and the NetworkScreen wiring; ambiguous "Deselect
All" selectors (now two sections) disambiguated by order.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* revert(web): drop message direction from the Network tab
Network traffic is always client → server — even SSE is a client-initiated
stream tagged outgoing — so a direction badge/filter there is meaningless.
Remove the MessageDirectionBadge from NetworkEntry and the Filter by Message
Direction section (and its NetworkUiState.visibleDirections wiring) from the
Network screen.
History keeps its direction badge + filter; the shared MessageDirectionBadge
and MessageDirectionFilter components stay (still used by History).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(web): a lone History section isn't a collapsible accordion
When only one of Pinned / History is present, its header is now a plain
title and its entries always show — instead of a `listItem` accordion
toggle that could hide them based on a stale collapsed state from when
both sections were on screen. The accordion (and per-section Clear/Export)
only applies when both sections are present.
Example fixed: collapse History while pins exist, then unpin everything —
History becomes the sole section and shows its entries under a plain
header rather than staying collapsed.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(web): put section Clear/Export on the header pleat itself
The per-section Clear/Export sat to the right of the collapsible header
bar, on the panel background. Make the whole header row the "pleat":
a new Group `sectionHeader` theme variant (rounded + hover, active bg
passed per instance) wraps a flex-1 toggle button (the title) on the left
and the Clear/Export buttons on the right — so the actions live on the
pleat. The toggle is a separate button since the action buttons can't nest
inside it.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* style(web): entry expand toggle uses the same icons as ListToggle
Swap ExpandToggle (History / Network / Task cards) from the LuArrow icons
to RiExpandVerticalLine / RiCollapseVerticalLine — the same expand/collapse
icons the list-level ListToggle uses. Same ActionIcon size, so it matches
the other small buttons on the cards; aria-labels unchanged.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* fix(web): panel Clear keeps pinned entries (history-only)
Review follow-up (#1441): the panel Clear button is gated on the unpinned
count (implying history-only), but onClearHistory wiped everything
(pinned included) and left pinnedHistoryIds stale. Make Clear history-only
— clearMessages((m) => !pinnedHistoryIds.has(m.id)) — so pinning protects
an entry, matching the gating and the per-section model; the pin set stays
valid since its entries survive.
Also drop the stale "and Network" mention from the MessageDirectionFilter
docstring (Network direction filter was reverted).
Test: panel Clear's predicate keeps pinned ids and removes unpinned.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>