Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/preview/PreviewView.tsx — The new profile Badge in the preview chrome row uses max-w-28 … truncate, but Badge is inline-flex justify-center whitespace-nowrap, so text-overflow never applies to the anonymous flex item holding the name. A long profile name is hard-clipped with no ellipsis, and justify-center clips it at both ends. Suggested the repo's pill convention: max-w-* on the badge with truncate on an inner span (plus title for the full name).
No other in-scope findings: the MenuSubTrigger leading-icon alignment now matches MenuItem and is scoped away from the chevron (:not(:last-child)), so the icon-less Appearance and Turn sub-triggers keep the chevron's ms-auto; the Clear cookies/cache heading is correctly wrapped in MenuGroup; the browser-profile settings rows reuse SettingsRow, ITEM_ROW_INNER_CLASSNAME, DraftInput, Button, Badge, Tooltip, and gate deletion behind the same AlertDialog pattern used elsewhere in settings; the default-profile control uses the shared Select with a SettingResetButton.
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 examined beyond the diff for context: apps/web/src/components/ui/menu.tsx (MenuItem/MenuSubTrigger/MenuPopup contracts), apps/web/src/components/ui/badge.tsx, apps/web/src/components/ui/input.tsx, apps/web/src/components/ui/draft-input.tsx, apps/web/src/hooks/useCommitOnBlur.ts, apps/web/src/components/settings/settingsLayout.tsx, apps/web/src/components/settings/itemRows.ts, apps/web/src/components/settings/settingsSearch.ts, apps/web/src/components/preview/PreviewChromeRow.tsx, apps/web/src/components/preview/PreviewMoreMenu.tsx, apps/web/src/browser/browserDefaults.ts, and the other two MenuSubTrigger call sites (DiffPanel.tsx, PreviewMoreMenu.tsx).
Checks performed: sub-trigger selector scoping against both existing icon-less sub-triggers; MenuGroupLabel context requirement; required-prop propagation for onAddBrowserInProfile across all RightPanelTabs consumers (ChatView, pull-requests route, test); nativeInput precedent for DraftInput; empty-name rename behaviour via useCommitOnBlur (draft resets, no invalid commit); settings-search catalog entries added for both new rows.
Prior findings from earlier runs (icon-less sub-trigger chevron regression, unbounded badge width, unconfirmed destructive profile deletion) are resolved in this head commit and were not re-reported.
🔍 Agent activity — 1 tool call
💭 I'm checking the current state of the PR to see what's been fixed since the last run—the menu.tsx selector and badge capping issues appear to be resolved. Now I need to examine the changed files in the web app, particularly focusing on menu.tsx, RightPanelTabs, IntegrationsSettings, and Preview to catch any remaining issues. Let me pull the actual file contents for MoreMenu, PreviewChromeRow, and PreviewView to continue the review. Now I'm checking MenuItem to see how it handles icons and whether it has cursor-pointer styling, then I'll look at MenuPopup for comparison. I'm noticing the selector differences between MenuSubTrigger and MenuItem—one uses a descendant combinator for SVGs while the other uses a child combinator. The new selector with
:last-childshould work reliably since the chevron is always positioned last, so leading icons will get the negative margin adjustment correctly. Looking at the other components in scope, RightPanelTabs handles browser profiles from the defaults, and SurfaceMenuItem shows a disabled tooltip when unavailable. But there's a potential keyboard accessibility issue: if the MenuSubTrigger's onClick handler both opens the submenu and closes the menu, pressing Enter on the trigger could trigger both actions simultaneously, which might confuse keyboard users trying to navigate the submenu. Now I need to check PreviewMoreMenu for consistency in how it uses MenuGroup and MenuGroupLabel, and then review IntegrationsSettings to see how the new BrowserProfilesSetting component uses DraftInput, Button, AlertDialog, and other elements compared to similar patterns elsewhere. I need to verify whetherSettingsRowexpects atitleprop and check ifsearchableSettingis providing it automatically when it wrapsBrowserProfilesSetting. Let me trace through both function signatures to see how the title gets passed down. I need to check the SettingsRow component's signature to understand how children are positioned and whether there's a similar list pattern used elsewhere in the ConnectionsSettings component. The row layout checks out, so now I'm looking at PreviewMoreMenu and PreviewChromeRow to verify the badge styling. I need to examine how MenuGroupLabel is being used in PreviewMoreMenu and whether there are other sections that don't use groups. I'm noticing a UI consistency issue where the profile name gets repeated — it shows in the group label like "Profile: Work" and then again in each menu item as "Clear cookies (Work)". This redundancy becomes problematic because profile names can be up to 48 characters long, and since the menu items don't truncate text, long names would cause the popup to expand beyond a reasonable width. The MenuItem uses flexbox without text wrapping constraints, so the text will expand the container rather than wrap. The popup itself has no max-width constraint, only a max-height, so a doubled 48-character name could stretch it quite wide. It's a mild issue — menus with long labels aren't uncommon — but the redundancy is more of an aesthetic concern than a functional violation. Now I'm checking the PreviewView badge and PreviewChromeRow leadingActions placement. I'm thinking through how the Badge component with outline variant handles text truncation — checking whether the default classes use inline-flex with proper gap and whitespace-nowrap, and whether the truncate utility (overflow-hidden, text-overflow ellipsis, whitespace-nowrap) works correctly on a flex container when the text is wrapped in an anonymous flex item. The Badge component usesinline-flexwithjustify-center, and whentruncateis applied at the call site, the text becomes an anonymous flex item within that flex container. Sincetext-overflow: ellipsisonly works on block containers, it doesn't apply here—instead the flex item's overflow gets clipped without the ellipsis effect, which is the typical behavior when truncating text inside a flex parent. I'm realizing thattext-overflowisn't inherited, so even thoughwhite-space: nowrapwould apply to the text, the ellipsis won't show up. This means long profile names in the badge get hard-clipped mid-character rather than properly ellipsized. Let me search the codebase to see how other badges handle this—checking if there are patterns where an inner span appliestruncateinstead. I'm looking at how other Badge components handle text truncation with inline-flex layouts, checking for patterns where truncate classes are applied to inner spans alongside Badge usage. I'm working through how the truncate utilities interact with the badge's flex layout—overflow hidden clips the content, but I need to verify whether text-overflow ellipsis actually applies when the badge itself is a flex container with a text child. Looking at the CSS spec and browser behavior: text-overflow only works on block containers, and while the anonymous flex item is technically a block container, it doesn't inherit the text-overflow property since it's non-inherited. So in practice, the text gets hard-clipped without an ellipsis showing. I'm also realizing that justify-center on the badge compounds this—when content overflows, centering clips from both sides rather than just one. This is actually a regression from my earlier suggestion of that exact code pattern. The proper Tailwind convention for this is wrapping withmin-w-0 max-w-* overflow-hiddenon the flex container and applyingtruncateto an inner span instead. It's a real issue, though I'm weighing whether it's significant enough to flag given that I'm the one who suggested the problematic approach in the first place. Writing the Badge component fix... Now I'm checking other potential issues in the codebase. The submenu items in RightPanelTabs show profile names without truncation, which could be a problem if names get long, but that's minor. I'm also noticing there's no "Default" indicator on submenu items, which is more of an aesthetic concern. The onAddBrowser handler is still properly connected to SurfaceMenuItem, and the disabled state is handled correctly. There's a consistency issue I need to look at with how the Browser row is structured in the available case. The MenuGroupLabel styling looks consistent with the rest of the component. There's some redundancy in the "Profile: X" labels paired with "(X)" in the items, which adds visual noise, but that's more of an aesthetic concern than a functional issue. Now I need to check the SurfaceMenuItem definition to see if MenuSubTrigger loses any important behavior like coarse-pointer or keyboard handling that SurfaceMenuItem provides. I'm checking how keyboard interactions work with the new MenuSub component in RightPanelTabs, specifically whether Base UI's SubmenuTrigger properly handles Enter and Space keys to open submenus through its synthetic click behavior. The real concern is that clicking the submenu trigger both fires the onClick handler (which adds a tab and closes the menu) and toggles the submenu open — so I need to verify if there's an existing pattern in the codebase where MenuSubTrigger has an onClick action to understand how this should be handled. The DiffPanel and PreviewMoreMenu sub-triggers both lack leading icons, so the chevron is the only SVG element, and the:not(:last-child)selector keeps them properly scoped. For the Menu component, I'm making it fully controlled by passingopenandonOpenChangeprops to MenuPrimitive.Root, which means the menu can only open when the state change handler fires. Base UI triggers the onOpenChange callback when the trigger is clicked, so that works fine. When a profile MenuItem in the submenu gets clicked, Base UI closes the entire menu and fires the root's onOpenChange with false, which is the expected behavior. RightPanelTabs also renders the empty-state launcher with onAddBrowser, but that doesn't need the profile submenu since it's not required there. I should verify that onAddBrowserInProfile is wired up at all the call sites—ChatView appears twice, the pull-requests route, tests—and check if there are any other consumers of RightPanelTabs by grepping for onAddBrowser assignments to make sure the required prop passes type checking. Now I'm checking whether theDraftInputprimitive supports properties likenativeInput,size,maxLength,onCommit,disabled,aria-label, andvalue. Let me check the input component to see if it supportsnativeInputandsizeprops. I'm also wondering about the behavior whenrenameProfilegets an empty string — the draft would display locally in the input, but the commit might be skipped on blur, which could leave the empty text visible ifuseCommitOnBlurdoesn't reset the draft back to the original prop value. I'm checking whether theclassNameprop passed to Input actually gets applied to the wrapper, and whether thenativeInputvariant properly handles the className from the call site. I also need to verify if the rename function allows empty strings. When the user clears the input and blurs, the draft becomes empty, which triggersonCommit("")— butrenameProfilereturns early without updating, so the draft resets to null and the input re-renders with the original value. Now I'm looking for other uses ofnativeInputDraftInput in the settings to check for consistency. I'm wondering why this DraftInput specifically usesnativeInputwhen other inputs don't—probably to supportmaxLength, though the Base UI Input primitive likely handles that too. The issue is thatnativeInputbypassesonValueChange, which means it's not using the Base UI primitive at all. Let me search for othernativeInputcall sites to see if there's a pattern here. Looking at thenativeInputusage and thepreviewBridge?.clearCookies()call in IntegrationsSettings — that's guarded properly since previewBridge is desktop-only. But inBrowserProfilesSetting, I'm noticing the profile rows are rendered as children of SettingsRow, and thedisabledprop is being handled through opacity styling inDesktopOnlyBrowserDefaults. The issue is that the wrapper dims h3 and p elements, but the profile rows themselves are just plain text spans and badges that don't get dimmed — though the controls are disabled. On non-desktop, the built-in profiles show at full contrast inside what's supposed to be a disabled block, which creates a visual inconsistency. It's a pre-existing pattern though, and the actual controls (Select/Switch) do get proper disabled styling, so it's marginal. On web, the "Add profile" button correctly shows as disabled. Now I'm looking atPreviewViewto see ifbrowserDefaultscomes fromuseBrowserDefaults()and whether there's a missing tooltip for the truncated badge. The author did follow my earlier suggestion to cap the badge withmax-w-28 shrink-0 truncate, but that creates a subtle issue — the text gets center-clipped without an ellipsis when it overflows. Thejustify-centeron the inline-flex badge combined with overflow hidden causes clipping from both sides, which isn't ideal for readability. Looking at the flex behavior more carefully: the anonymous flex item inside hasflex-shrink: 1by default, but withwhite-space: nowrapthe min-content width becomes the full text width, so it can't actually shrink below that. This forces overflow, and withjustify-content: centerthe text gets clipped symmetrically rather than from one side. The inner-span approach fixes this by applyingtruncateto the span itself, which setsoverflow: hiddenand resets the automatic minimum size to 0, allowing it to shrink and ellipsize properly. This is a real visual defect for longer names, and since the codebase already has this convention established, it's worth flagging as a single comment. Now I'm weighing whether to mark this as a failure. The check defaults to failure when findings exist, but the guidance says not to fail for optional aesthetic preferences. This feels like a genuine consistency issue rather than just a style preference, so I'm leaning toward reporting it as a failure. Let me scan the other changed lines to see if there's anything more substantive before I decide. I'm noticing thatuseClientSettingswith thetoBrowserDefaultsselector creates a fresh object on every call, which could trigger unnecessary re-renders if it's usinguseSyncExternalStore. The selector also builds a newprofilesarray each time viaresolveBrowserProfiles, following the same pre-existing pattern that was already there. Looking at the rest of the changes:RightPanelTabscallsuseBrowserDefaults()unconditionally even in the web component, which is fine. The submenu lists all profiles including "Default" and "Incognito" consistently. There's a minor inconsistency where thePreviewViewbadge only shows when the profile differs from default, butPreviewMoreMenualways displays "Profile: X" when a profile is selected — including for Default — but that's not a real defect. The test file looks good, so I'll focus on posting an inline comment about the Badge truncation issue. Actually, let me verify my understanding of thetruncatebehavior oninline-flexcontainers. The key issue is thattext-overflowrequires a block container, and flex containers don't create block containers for their contents — the contents become flex items. So<div class="inline-flex truncate">text</div>won't render ellipsis. Combined withjustify-centerpotentially clipping both ends, the solution is to wrap the text in an inner span wheretruncatecan actually work. I'm confident enough to comment on this. The fix wraps the name in a span withtruncate, addsmax-w-28andshrink-0to the Badge itself (following the repo's pattern in ThreadSyncStatusPill and PullRequestSummaryTab), and includes atitleattribute for hover reveal since Badge accepts span props. I'm also weighing whether to flag the MenuSubTrigger keyboard interaction issue — where Enter might trigger both the submenu open and a click, causing unexpected navigation — but without being able to verify Base UI's internals, I'm hesitant to raise it as noise rather than a concrete concern. On the mouse interaction side, Base UI's default hover-to-open behavior with click-to-toggle should work fine here even if the submenu's already visible. For the final assessment, I'm seeing one minor visual finding around the badge width constraint, so I'll mark this as a failure with a clear title rather than an all-clear.
1. modify_pr — One finding on the new profile badge in the preview chrome row: the truncate cap doesn't actually ellipsize because Badge is a flex container. Everything else in scope (the MenuSubTrigger icon-column fix, the MenuGroup/MenuGroupLabel wrapping in PreviewMoreMenu, the profile list rows reusing SettingsRow/ITEM_ROW_INNER_CLASSNAME/DraftInput/Button/AlertDialog, and the Select for the default profile) follows the existing primitives and settings conventions.
Agent Credits: 424 credits