Skip to content

fix(web): render persisted bash tool output in expanded turns

MacroscopeApp / Macroscope - UI Consistency failed Aug 16, 2026 in 2m 23s

UI Consistency: 3 issues found

Three findings in apps/web/src/components/chat/MessagesTimeline.tsx, all introduced by the new expanded Command-run output UI:

  • Keyboard interaction regression (line ~2431): the expanded body container stops only click and pointerdown from reaching the row toggle. The new "Show more" and copy buttons are the first focusable controls inside it, so Enter/Space bubbles to the row's onKeyDown, which calls preventDefault() (blocking button activation) and collapses the row. Suggested fix: add onKeyDown={stopRowToggle} to the same container.
  • Primitive contract override (lines 2152-2154): the toggle Button overrides the primitive's core height, padding and base text color (h-5 px-1.5 text-[11px] text-muted-foreground on size="xs" variant="ghost"). The muted ghost treatment is already a named variant (ghost-muted) which also restores text-foreground on hover; the override leaves the label static on hover.
  • Lost block separation (lines 2437-2439): splitting the previously \n\n-joined body into sibling <pre> blocks left the files block with no top margin, so it renders flush against the preamble/output block above.

Inline comments with suggested fixes have been posted on the PR.

Details

Note

Your check run agent prompt is: .macroscope/check-run-agents/ui-consistency.md
More information about how Check Run Agents work can be found in our Help Center.

Scope reviewed: apps/web/src/components/chat/MessagesTimeline.tsx, apps/web/src/components/chat/MessagesTimeline.logic.ts, apps/web/src/session-logic.ts and their tests (web include patterns only; mobile/server changes used as context).

Checks performed:

  • Compared the new ToolCallOutputBlock control against apps/web/src/components/ui/button.tsx variants/sizes (xs, ghost, ghost-muted, icon-xs) and MessageCopyButton's prop contract; size="icon-xs" variant="ghost" is a supported combination and was not flagged.
  • Traced the row-level toggle handlers in PlainWorkEntryRow (rowToggleProps, stopRowToggle) to confirm keydown is not guarded on the expanded container; before this PR the container held only non-focusable <pre> content, so the gap was not reachable.
  • Compared the previous buildToolCallExpandedBody string-joining behavior (blocks.join("\n\n")) with the new three-section render to identify the lost inter-block spacing.
  • presentExpandedToolOutput logic, memoization, focus ring, cursor and coarse-pointer hit-target behavior (pointer-coarse:after:min-h-11 on the Button base) were reviewed and found acceptable; no CSS/global-stylesheet or theme-variant changes are present in this PR.

🔍 Agent activity — 1 tool call

💭 I'm examining the new ToolCallOutputBlock component in MessagesTimeline.tsx and noticing some potential issues with the Button component API — specifically whether it actually supports size="xs" and variant="ghost", and whether MessageCopyButton's size="icon-xs" is valid. There's also a concern about class string overrides that might be conflicting with the primitive styling for height, padding, and text size, plus a memoized component that appears to be using state in a way that could be problematic. I'm checking the available button size options in the component file to see if "xs" is actually defined alongside the other variants. I'm creating a button component that overrides the primitive's default sizing—specifically the height, padding, and text size—which violates the design system rules about call-site overrides of core properties. The better approach would be to use the ghost-muted variant that already exists, which handles the muted text color with proper hover states, paired with an existing size option instead of these inline overrides. Before committing to this fix, I should search the codebase for other "Show more"/"Show less" toggle patterns to ensure consistency across the app. I'm noticing that the h-5 override on this xs-sized Button is actually following an established pattern in the codebase—there are similar examples like the one in ConnectionsSettings.tsx that combine height overrides with padding and text sizing. The linting rule flags large call-site class strings that override core properties like height, radius, padding, focus ring, cursor, hit target, or base state colors, and this case does override both height and padding, so it technically fits the criteria. But the more significant issue is that text-muted-foreground on a variant="ghost" button breaks the hover behavior—the ghost variant sets up text color changes on hover, but overriding to muted without restoring that hover state means the button won't change color when you interact with it. The codebase already has a ghost-muted variant designed exactly for this use case: muted text that properly restores to foreground color on hover. This is a clear case where a named primitive variant should be used instead of manually overriding with utility classes. Now I'm checking the MessagesTimeline component around those line ranges for other issues—specifically looking at whether the "Show more" button has proper accessibility attributes like aria-expanded, and whether there's event propagation handling since the button sits inside a clickable row that might toggle expansion. I should also verify that the MessageCopyButton inside the row doesn't trigger unintended row collapse when clicked. I'm noticing a potential issue with event propagation: the expanded container stops click events so the "Show more" button works properly, but the row wrapper listens for Enter and Space keys to toggle collapse. Since keydown events bubble up from the button inside the expanded body to the row's handler, pressing Enter or Space on the button would trigger the row's preventDefault and collapse the entire row instead of just closing the expanded section. The fix is to add a keydown handler to the expanded body container that stops propagation, preventing keyboard events from bubbling up to the row's toggle logic. This would be a targeted change to the container element that's already in the diff. On the button styling front, the ghost variant with custom classes overriding height, padding, and text size is worth examining. The ghost-muted variant exists for exactly this use case—muted ghost buttons with hover states that restore foreground color. The h-5 override does reduce the hit target below the primitive's standard, though the base includes pointer-coarse handling that preserves the target for touch devices. So the hit target concern might not be as critical, but the class override pattern itself seems like it's violating the principle of using the primitive's built-in variants rather than layering custom classes on top. The real issue though is the keyboard interaction bug. The row div has role="button", tabIndex={0}, and an onKeyDown handler, but inside the expanded body there's a native <button> element. When Space is pressed on a focused button, the keydown event bubbles up to the div's handler, which calls preventDefault() and toggles the expanded state—collapsing the row and losing focus. Same problem with Enter. That's a genuine regression risk that should fail the check. The Tooltip on the copy button would have the same keyboard activation issue. I should also flag that the "Show more" Button is missing aria-expanded, though that's minor and I can mention it alongside the variant comment to keep it to two findings max. When the row collapses and expands again, outputExpanded persists correctly. The onToggleExpanded arrow function is recreated each render, making the memoized ToolCallOutputBlock less effective, but that's just a minor performance thing. Looking at the spacing between the preamble, output, and files sections — they're now separate <pre> blocks with only mt-1 on the output block while the files block has no margin, so I need to check if the spacing is adequate. I'm noticing the files block lacks proper spacing from the preceding content—when it follows the output block or preamble, there's no margin separating them, which creates a visual regression. I should add some top margin to the files block to restore that visual separation, and I'm also flagging that each pre element has a max-height with overflow, which means there are now multiple scrollable sections. For the review comments on the diff, I need to ensure the line numbers fall within the actual diff hunk that GitHub requires. The changes around lines 2427-2439 are added lines, so those are safe anchors. For the keyboard issue comment, I'm considering anchoring it around line 2430-2436 where the ToolCallOutputBlock is used, though I need to verify those lines are actually included in the diff hunk's context. For the button styling on line 2154, the issue is that it's overriding the primitive's base styling with inline classes. The better approach would be to use the ghost-muted variant which handles the muted appearance through the component's design rather than call-site overrides, while keeping the compact sizing that was intentionally chosen. I'll suggest switching to that variant and keeping the minimal contextual sizing, then craft an inline suggestion that targets the exact line range. Now I'm checking the hover behavior for the ghost-muted variant to confirm it restores the foreground color on interaction, and verifying whether the keyboard handling for row toggling is managed elsewhere in the component. I'm reviewing the code and found that there's no keydown guard in place. I'm posting feedback with three comments, and the review is failing. For the third comment about spacing, I'm suggesting a wrapper class around line 2437 that applies the expanded body styling with a top margin to the pre element, using the cn utility function that's already imported.

1. modify_pr — Three findings in the new expanded Command-run output UI in apps/web/src/components/chat/MessagesTimeline.tsx. The keyboard one is a real interaction regression: the expanded body now contains focusable controls but only stops pointer events from reaching the row toggle.


Agent Credits: 91 credits