Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/ChatMarkdown.tsx — CHAT_MARKDOWN_IMAGE_SIZE_CLASS_NAME includes h-auto w-auto, which is applied to every markdown image (remote and workspace). Author-origin utilities outrank the width/height presentational hints that CHAT_MARKDOWN_SANITIZE_SCHEMA still allows on img, so images with explicit dimensions (e.g. <img src="https://…" width="120">) now render at intrinsic size up to 30rem instead of their authored size. h-auto is additionally redundant with the preflight img { height: auto }. Suggested fix: keep only max-h-[30rem] max-w-[min(100%,30rem)] object-contain, which still clamps oversized images while preserving explicit dimensions. An inline comment with a suggestion was posted.
The file://localhost classification issue flagged on the previous run is resolved by the uncHostname handling in apps/web/src/markdown-links.ts plus the new tests, so it was not re-reported.
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.
Files reviewed in scope: apps/web/src/components/ChatMarkdown.tsx, apps/web/src/components/ChatMarkdown.workspace-images.test.tsx, apps/web/src/markdown-links.ts, apps/web/src/markdown-links.test.ts (mobile and packages/client-runtime changes used only as context).
Checks performed:
- Verified
rehypeNormalizeWindowsImageSrcruns beforerehypeSanitizeinCHAT_MARKDOWN_REHYPE_PLUGINS, so drive-absolutesrcsurvives the protocol allowlist. - Confirmed the
block!override is a deliberate, commented interaction with the unlayered.chat-markdown img { display: inline-block }rule inindex.css; the remote-image path intentionally keeps inline layout, so that asymmetry was not flagged. - Loading placeholder uses a raw
<span>rather than the sharedSkeletonprimitive, butSkeletonrenders a<div>and markdown images live inside<p>, and the static (non-shimmer) placeholder was an explicit earlier decision (fix(web): keep image loading placeholder static) — not reported. - Fallback chip and placeholder use existing tokens (
border-border/40,bg-muted/40,text-muted-foreground) consistent with surrounding markdown chips. - Cascade reasoning for the reported finding: presentational hints from
width/heightsit before all author declarations in the cascade, so a Tailwindwidth: autoutility wins; preflight already handlesheight: auto.
🔍 Agent activity — 10 tool calls
💭 Checking the diff between the flagged commit and current head, it looks like the localhost handling issue in markdown-links.ts was already fixed with proper hostname normalization and accompanying tests. Now I'm shifting focus to reviewing ChatMarkdown.tsx for UI consistency, starting with how the image component handler normalizes URLs. I need to verify that plugin ordering is correct — specifically that the Windows path normalization for image sources runs before rehype-sanitize, since sanitize could otherwise strip a
D:\...style path by mistaking the drive letter for an unrecognized URL scheme. I also want to confirm there's a global CSS rule making.chat-markdown imgdisplay as inline-block, since the component references that pattern via an important-flagged class. I'm also checking the loading placeholder markup — it uses a static (non-pulsing) status indicator, which the test explicitly expects, so I want to see if that's consistent with a shared skeleton component elsewhere or just a deliberate one-off design choice. Then I want to confirm the necessary imports (useState, TriangleAlertIcon) and props (cwd, threadRef) actually exist in the file, so let me pull it up and read through it.
1. git_tools — CHAT_MARKDOWN_REHYPE_PLUGINS|CHAT_MARKDOWN_SANITIZE_SCHEMA|rehypeNormalizeWindowsImageSrc
2. git_tools — chat-markdown img
💭 I notice the grep results at HEAD show different constant names than what the diff indicated, which seems like a discrepancy I need to look into further. Both calls used the same tree reference, so the mismatch is puzzling — possibly the two grep calls returned results from different points despite my intent to check the same commit. I'll just directly view the file at head to resolve the discrepancy.
3. browse_code — apps/web/src/components/ChatMarkdown.tsx
💭 That earlier grep result must have been stale, mixing in content from a prior commit. Let me move on and check the image renderer around lines 1819 onward.
4. browse_code — apps/web/src/components/ChatMarkdown.tsx
💭 I'm checking consistency around image rendering - direct images only get the size class without the border/rounding styles, and I'm trying to recall what the styling looked like before this PR, since there used to be a global inline-block rule for chat-markdown images. Now I notice an actual inconsistency: an earlier commit applied block! styling to all images uniformly, but the current version treats workspace images differently (block with rounded border) versus remote images (inline-block, no border). I'm also comparing the loading placeholder styles against the failed-load fallback chip, since the fallback switches from block layout to inline-flex, which breaks the visual continuity between states. The intentional global rule keeps markdown images inline-block since markdown assumes images can lay out in a row like buttons, but forcing workspace images to block! undermines that for local images — two local badges side by side would stack instead of flowing inline. This feels like a genuine design tension worth flagging, though it's arguable whether the placeholder itself could've stayed inline-block instead. I'm also noticing the loading placeholder reserves a fixed aspect-video box while the loaded image uses auto height/width, which could cause layout shift when the image finally renders — not itself a consistency violation, but worth checking whether other loading placeholders in the codebase (like a shared Skeleton component) handle this differently rather than using a raw styled span. Beyond that, I'm looking at smaller details: the className merge order that lets size classes override consumer classes contrary to the pattern used elsewhere, and whether the fallback chip's styling matches how other "unavailable" states are rendered in the codebase.
5. browse_code — apps/web/src/components/ui/*.tsx
6. git_tools — animate-pulse|Skeleton
💭 I notice there's a shared Skeleton component using a shimmer animation class, but the new loading placeholder is instead a locally reconstructed span with a muted background instead of reusing that shared primitive. I'm also noticing the codebase has static placeholders elsewhere labeled explicitly as having no shimmer, and the test checks for absence of animate-pulse, which doesn't quite align with the shared skeleton's actual animate-skeleton class—so I need to reconcile what the test is really verifying. Since Skeleton renders as a div and markdown images sit inside paragraph tags, using Skeleton there would create invalid div-in-p nesting, which is likely why a plain span was used instead—so this raw element seems intentionally justified rather than something to flag, even though it lacks a shimmer that everything else has. Now I'm noticing the loading placeholder sizes to a fixed 30rem aspect-video box before swapping to the actual image's intrinsic dimensions, which would cause a layout shift, and it also lacks the border that the loaded image has—raising a question about whether this counts as a genuine regression versus a minor aesthetic mismatch. I'm also checking whether adding
h-auto w-autoclasses to direct images actually overrides the HTML width/height attributes or just affects intrinsic CSS sizing. CSS presentational hints have lower priority than author styles, so the Tailwindw-auto h-autoclasses would indeed defeat awidth="600"attribute — confirming this is a real regression. I recall the earlier version usedmax-h-96 max-w-fullinstead, which wouldn't have this problem since max-width doesn't override an explicit width attribute; addingw-auto h-autonow breaks sizing for images like<img width="200">in markdown. I'm confirming this class is applied consistently to both direct and workspace images, which reinforces that this is worth flagging as a genuine violation of the "preserve responsive geometry" rule. I'm less sure aboutw-autothough — maybe it counters some other rule. Let me grep index.css for any img-specific width rules within chat-markdown to be sure.
7. git_tools — img
8. git_tools — 7107a98..25ba6d7
💭 I'm checking the latest commit that fixes markdown image sizing to understand why it uses clamped auto height and width.
9. git_tools — 25ba6d7
💭 I realize this introduces a regression: remote or HTML images with explicit width/height attributes—like badge images common in GitHub comments—used to render at their specified size, but now the added
w-auto h-autoclasses override those attributes since author CSS beats presentational hints, forcing intrinsic sizing and potentially blowing up small badges to full size. But then I reconsider whetherh-autois even necessary here, since Tailwind's own preflight already setsimg { max-width: 100%; height: auto }as base-layer author CSS, which already overrides theheightattribute regardless of specificity within presentational hints—so the explicitly addedh-auto w-autoclasses might be redundant with what preflight already enforces. Butw-autois different: preflight doesn't touch width, sow-autoactually overrides an author-setwidthattribute, meaning an image explicitly sized small viawidth="120"could now render up to its full clamped max instead of its intended size—that's a real, checkable regression for markdown content containing sized<img>tags like those in PR descriptions. TheclassNameattribute is likely restricted to code/span elements for syntax highlighting, not a broad concern. I'm also noting that remote images intentionally skip the border/rounded treatment applied to workspace images, which creates a visual inconsistency between otherwise similar content, though it seems to be a deliberate design choice rather than a bug worth flagging strongly. There is a real behavioral asymmetry worth noting: a workspace image becomes block-level, breaking the inline row layout, while a remote image stays inline, so mixed badge rows would render differently — but this seems explicitly intentional, so I'll skip it. The class merging order means raw HTML className can't override the size clamp, which seems minor and possibly desirable, and the loading placeholder being static rather than shimmering also appears to be an intentional choice based on a specific commit. Moving on, the fallback chip for workspace images without a threadRef looks fine, and mobile is out of scope here. The accessible status span has no visible content but its fixed aspect-video placeholder versus the loaded image's intrinsic size could cause a layout shift, though that's minor, and object-contain on an auto-width image is a harmless no-op. I want to verify that the img handler's props actually include width and height — checking how react-markdown passes hast properties confirms that spreading props does include these values. I'm also confirming that combining a max-width utility with auto width on an inline-block image inside a paragraph still behaves correctly, and that tailwind-merge won't create conflicts with the width utility in this case. So it seems like there's one solid finding worth reporting, and I'm weighing whether it's significant enough to fail the review — given the guidance that a clear consistency or regression risk warrants failing, I'll report it with a single inline comment. Before finalizing, I want to double-check the core CSS cascade assumption underpinning this finding, since everything hinges on how presentational hints like width/height attributes are prioritized relative to author-level style declarations. Presentational hints do lose to author style sheet rules — they're treated as author-origin declarations placed at the very start of the origin, effectively in a prelude layer before all other layers, so the Tailwind utility for auto width takes precedence. I'm also noting that the HTML width/height attributes influence aspect-ratio through the user-agent stylesheet, but with auto width and height applied, the browser falls back to the image's intrinsic dimensions instead. This means an image with an explicit width attribute set in markdown would now render at its natural size rather than the specified pixel width, and the fix would be to remove the auto width and height utilities from the markdown image styling configuration. Checking whether removing the height-auto utility would break other cases—since preflight already sets height to auto by default, images without a width attribute should still scale proportionally, and the existing test checking for max-width and max-height classes should still pass. I'm now working out where to place the inline review comment, since the size class definition falls within the added lines of the diff, and planning a self-contained suggestion limited to just that line. I'll write the comment body describing the issue and fix, keeping in mind that the earlier localhost markdown-links flag was already resolved so I shouldn't bring it up again, then post the review marking it as a failing state.
10. modify_pr — One finding on the new markdown image clamp. The earlier file://localhost classification concern is resolved by the added uncHostname handling and tests, so no further comment there.
Agent Credits: 102 credits