Repository navigation
UI Consistency: 2 issues found
Two interaction-consistency issues were found in the changed web UI and posted as inline review comments.
apps/web/src/components/settings/ComputerHistorySettings.tsx(~L96-101): the exclusion-listInput's Escape handler cancels the inline draft but does notpreventDefault()/stopPropagation(), so Base UI's dialog dismiss also fires and the Computer History privacy dialog closes, discarding both unsaved exclusion lists. The repo convention for this case (SettingsSidebarNav.tsx,ModelPickerContent.tsx,SettingsPanels.tsx) is to stop the event.apps/web/src/components/settings/ComputerHistorySettings.tsx(~L350-354): the "Mirror to Codex skysight"Switchis missingdisabled={!onDesktop}, unlike its siblingenabled/pausedswitches and the PrivacyManagebutton, so its disabled affordance disagrees with the rest of the section on a non-desktop host.
Both findings from the previous run are resolved on this head and were not re-posted: RowTitle is now the shared SettingRowTitle exported from settingsLayout.tsx, and the exclusion chips no longer override Badge's height/padding/typography contract.
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 examined
Files matching the check's include patterns that this PR changes:
apps/web/src/components/settings/ComputerHistorySettings.tsx(new)apps/web/src/components/settings/ComputerUseSettings.tsx(new)apps/web/src/components/settings/ComputerUseMoreBrowsers.tsx(new)apps/web/src/components/settings/browserBrandIcons.tsx(new)apps/web/src/components/settings/settingsLayout.tsx(addsSettingIconAction,SettingRowTitle)apps/web/src/components/settings/settingsSearch.ts,SettingsSidebarNav.tsxapps/web/src/components/chat/ComposerPendingApprovalPanel.tsx,MessagesTimeline.tsxapps/web/src/routes/settings.computer-use.tsx,settings.computer-history.tsx,routeTree.gen.tsapps/web/src/session-logic.tsand tests
Note: the PR head advanced from e6e6dd4 to ad0a0ea during review; findings and line numbers were verified against ad0a0ea.
Checks performed and cleared
- Shared primitives: new settings pages compose
Button,Switch,Input,Badge,Dialog*,Collapsible*,SettingsRow,SettingsSectionrather than raw controls. The<img>brand icons inbrowserBrandIcons.tsxare image targets, not reconstructed primitives. - Primitive extraction:
SettingIconAction(size="icon-micro"+variant="ghost-muted") andSettingRowTitleare correctly owned bysettingsLayout.tsxand reused byPolicyTooltip,SettingResetButton, and the three new panels. - Tokens:
bg-success,bg-warning,text-destructive,text-muted-foregroundall resolve to existing theme variables inindex.cssand match established usage. - CSS/Tailwind ownership: no
apps/web/src/index.csschanges in this PR; all new presentation is owner-local static Tailwind. No theme variants, generated selectors,MenuPopupdefaulting,ScrollArea, or virtualized-fade behavior touched. - Accessibility:
aria-labelpresent on every icon-only and switch control;permissionLabel/ExtensionStatuspair the status dot with text rather than color alone;CollapsibleTriggerrenders throughButtonwithrender, preserving focus ring and cursor. - Change discipline: findings are limited to lines this PR introduces; unrelated legacy settings code was used only as reference for convention.
Not reported (below the bar)
- Minor sibling icon-size variance in Computer Use rows (
size-5vssize-6) reads as intentional given the PNG agent-cursor asset's padding. - Spreading
searchableSetting(...)intoSettingsSectionand then overridingtitle(Privacy / History sections) diverges from the catalog title, but theid-only pattern used elsewhere is equivalent in behavior and this is a naming preference. - ~2px cross-axis mismatch between
Input(default height) andButton size="sm"in the add-entry form is cosmetic.
🔍 Agent activity — 1 tool call
💭 I'm examining the PR diff to understand what changes were made. I need to narrow my focus to just the apps/web/src directory since this PR is massive. Let me check what's changed since the last review at commit e4775dc—there were 2 comments posted then, but the current head is e6e6dd4—so I should look at the diff for that specific path to catch any new issues or things that haven't been flagged yet. I'm checking what's changed since the last commit at e4775dc, where two issues were flagged: a Badge override in ComputerHistorySettings.tsx and a RowTitle duplication in ComputerUseMoreBrowsers.tsx. Looking at the current diff, it seems the Badge styling issue in ComputerHistorySettings.tsx is still present. The prior commit was force-pushed, so the diff is entirely new, but I need to avoid duplicating the two existing comments that still describe the current code. Let me look through the new UI code to identify other issues, particularly checking whether SettingsRow in settingsLayout.tsx supports the
children,status, andresetActionprops that ComputerUseMoreBrowsers is passing to it. I should also verify the available Button variants likeghost-mutedandicon-micro, plus check the Badge component. I need to confirm that SettingIconAction properly importsComponentPropsWithoutRef<typeof Button>and then examine the settingsLayout.tsx file to validate these component APIs. I'm checking how other settings pages handle consistency — ComputerHistorySettings usessearchableSettingwhich returns an object with id and title, and there's a minor inconsistency where RowTitle wraps content in an inline-flex span without much purpose. I'm starting to identify some key UI consistency patterns to address. Now I need to verify whether the color tokens likebg-successandbg-warningused in ExtensionStatus are actually defined in the CSS, since missing tokens would make those status indicators invisible — that's a concrete bug I should search for. Looking at the Collapsible component setup withCollapsibleTriggerusing arenderprop for the Button, and checking what the ui/collapsible exports along with the CollapsiblePanel's id prop. I'm noticing a potential conflict whereComputerUseMoreBrowsersmanually setsaria-controlson the trigger, but Base UI's Collapsible already generates this automatically — so the manual attribute might override what Base UI intended. I've already noted the Badge issue. Now I'm checking the Button component to verify whether theicon-microandghost-mutedvariants exist, and to confirm thesize-5claim from my earlier comment. I'm noticing thatSettingIconActioninherits Button's props, which means consumers could passsizeorvariant, but since those are spread after the defaults, caller values would override them. TheclassNameis destructured separately before the spread, so that's handled correctly. Looking at the other components,PrivacyExclusionListuses the Input and Button primitives straightforwardly. TheSettingIconActioninside Badge passestype="button"explicitly, though Button already defaults to that. Accessibility-wise, having a button inside a span (which Badge renders) is fine. For ComputerHistorySettings, thesearchableSettingspread for "computer-history-enabled" provides theidandtitlecorrectly. Now I need to check howsearchableSettingis actually implemented. I'm noticing a mismatch in how the searchable settings are mapping their IDs to target IDs—some items like "computer-history-privacy" have matching IDs and targetIds, but others like "computer-history-enabled" have a targetId that points to a different ID ("computer-history"), which could cause lookup issues. I see the targetId pattern is working, but there's a conflict with thecomputer-history-privacyitem—the spread operator passes in the catalog title "Computer History privacy", but then the explicittitle="Privacy"attribute overrides it, which breaks the single source of truth principle. The same thing happens withcomputer-history-timelinegetting overridden to just "History", whilecomputer-history-enabledkeeps its catalog title "Enable Computer History". This inconsistency between the search index and what actually renders is worth investigating—let me search for other instances wheresearchableSetting()is used with title overrides to see if this is a pattern across the settings pages. Theid={searchableSetting(...).id}pattern with a separate title is already established in SourceControlSettings, so using spread with a title override isn't a significant finding. Now I'm looking for actual UI-consistency issues in the changed web code, and a strong candidate is theComputerUseSettingsline with its inline notice styling—the repo has an Alert primitive that might be more appropriate here. I'm also checking whether other settings pages use Alert for similar notices or if they reconstruct it with raw divs like this one does. TheComputerHistorySettingsintro paragraphs use bare<p>tags with muted-foreground styling, which I need to verify against existing patterns. The timeline list uses a raw<ul>with custom row styling instead ofSettingsRow, which is acceptable for a list. The button state management looks fine—busyis only set during clear operations. But there's a consistency gap: the "Permissions"SettingsSectioninComputerUseSettingsis missing anid, which means search items won't work for it. Looking at the status indicators and icons, theExtensionStatusdot usesbg-success/bg-warningbutComputerHistorySettingsdoesn't have one—not a real issue. TheComputerUseMoreBrowsersrow usesEdgeIconas the title icon, which is semantic. However, I'm spotting an actual consistency problem: the icon sizes vary across sibling rows in the same section—AgentCursorIconis 24px whileChromeIconandEdgeIconare 20px, creating a visual inconsistency. Now I'm checking theCollapsibleprimitive to see ifCollapsiblePanelsupports the necessary attributes, and looking at howComputerUseMoreBrowsersnestsSettingsRowinsideCollapsible—the padding adjustments look correct. I need to examine the hooks being used. The settings hooks show different patterns:ComputerHistorySettingspasses a partial object whileComputerUseSettingsspreads the full object, but that's not a UI concern. I'm also verifying the Tailwind class ordering across the codebase—there doesn't appear to be any index.css changes in this PR, and the class order suggests the repo might be using a Tailwind plugin for formatting. Looking for a concrete violation now, I'm checking whether the "remote environment" notice pattern reuses a shared Alert component elsewhere in the settings, and examining whether the Delete buttons in the timeline rows should use a more explicit destructive variant. I'm noticing that the "remote environment" notice in ComputerUseSettings is just a plain div with an icon and text, missing the accessibility role that the Alert primitive provides. The Alert component has a proper structure with an icon slot, rounded corners, border, and padding, while this div is deliberately minimal without those styling touches. I should check if Alert is being used elsewhere in the settings to understand the pattern better. The Alert component isn't heavily used in settings, so using a raw notice div might not be a clear departure from the page's conventions—probably not worth flagging. Let me search for how other settings pages handle desktop-only notices to see what the standard approach is. I'm noticing an inconsistency in how the Switch controls are disabled in ComputerHistorySettings. The "Mirror to Codex" switch remains interactive when not on desktop, while the Enabled and Paused switches are properly disabled in that state. Even though the mirror switch's patch operation still persists settings to the server, it's odd that it's enabled within a section labeled "recording runs in the desktop app" where the other controls are disabled. I need to verify that theInputcomponent supports theonValueChangeprop, and check whether it needs explicit width styling when used inside a flex container in the PrivacyExclusionList form. Looking at the sizing alignment between the Input and Button components — the Input defaults to h-8.5 (34px) while a size="sm" Button is h-8 (32px), which is a minor height mismatch. I'm checking whether the common pattern in the repo pairs them with matching sizes, likely usingsize="sm"on the Input as well. Actually, I'm getting into implementation details here. Let me refocus on the strongest findings from my earlier comments and move on to examining the ComputerUseSettings and ComputerUseMoreBrowsers components more thoroughly. I'm thinking through how the CollapsiblePanel's overflow-hidden and height animation interact with the child element's margin-top. Since the panel has overflow hidden, margin collapsing is prevented, which means the child's margin-top gets included in the scrollHeight measurement that Base UI uses to set the panel's height variable. The bigger issue is the aria-controls mismatch: Base UI's Collapsible generates a panelId and passes it to the Trigger for aria-controls, but when an explicit id is set on the Panel, it overrides that generated id. This means the Trigger's aria-controls attribute points to a panelId that no longer exists in the DOM, breaking the accessibility relationship even though the author manually set aria-controls on the trigger. The manual override doesn't actually break anything since both approaches end up working, so it's not a real regression—just redundant. Let me check the actualComputerUseSettingscomponent to see if there are any genuine issues there. I need to verify that thetext-destructivetoken actually exists in the codebase since I'm seeingtext-destructive-foregroundused in button variants and other destructive-related classes likeborder-destructiveandbg-destructive. Let me search through the repo to check iftext-destructiveis used elsewhere or if the convention is consistentlytext-destructive-foregroundlike what I see in the alert component. I'm checking whethertext-destructiveis actually a problem—it's not, it's widely used. Since I already have two unresolved findings on this PR, the check will fail anyway, so I need to decide if posting additional comments is worthwhile. I'm looking for maybe one or two more strong findings to flag, and theRowTitleduplication I already caught seems worth keeping. The Badge override was already flagged too. The plain string wrapping inRowTitleis minor and arguably covered by my second comment. But the Mirror-to-Codex switch missingdisabled={!onDesktop}while its siblings have it—that's a concrete inconsistency worth calling out, even though the setting does persist to the server the same way the other switches do. Timeline buttons aren't disabled off-desktop either, but they'd never render there anyway since items only populate on desktop. The Input/Button height mismatch in the add-entry form is real though—Input defaults to 34/30px while Button size="sm" is 32/28px, so with flex and default alignment the button sits 2px short. Addingitems-centerto the flex container would fix that alignment issue cleanly. The 2px misalignment is borderline enough that I'd rather not include it. But there's a real keyboard accessibility issue in thePrivacyExclusionList"Add" flow — after committing, focus gets lost to the body instead of staying with the user, which breaks keyboard navigation. That's worth flagging since the check emphasizes preserving keyboard behavior. There's also a potential event propagation problem where pressing Escape inside the input doesn't stop the event from bubbling up to the dialog handler. The issue is that Base UI's escape handling happens at the document level via floating-ui'suseDismiss, so when the input's onKeyDown handler callssetAdding(false)without stopping propagation, the native event continues bubbling up and triggers the dialog's close handler too. The fix is to callstopPropagation()andpreventDefault()on the Escape key event. Actually, I need to verify thatstopPropagation()on the React synthetic event actually prevents the document-level listener from firing — React attaches listeners at the root container, so callingstopPropagation()there stops the event from bubbling further up to document, which means the dialog's escape handler won't fire. That should work. Let me check the Dialog component to confirm Escape actually closes it. The dialog uses the standard Base UI component with Escape to close, and the DialogPopup has a Cancel button in the footer so that's handled. The PrivacyExclusionList form is nested inside ComputerHistoryPrivacyDialog without an outer form wrapper, which means Enter will submit the inner form as expected. I should check if there are other examples in the repo of this Escape-inside-dialog pattern with stopPropagation. I'm noticing the new code is missing the standard repo pattern for handling Escape in inline editing — it should be callingevent.preventDefault()andevent.stopPropagation()like the examples in SettingsSidebarNav, ModelPickerContent, and SettingsPanels show. Without these, pressing Escape will both cancel the draft and close the privacy dialog, losing both exclusion lists. I should also check routes/settings.ts for context. There's a second issue: the Mirror to Codex switch is missing thedisabled={!onDesktop}attribute that keeps it consistent with other controls in that section. I'll flag both of these. Before I post though, I need to verify whether my previous findings are still visible on the PR — the force push may have made them outdated, but the comments themselves should persist. I'm checking that the two UI Consistency comments are still open and noting that the PR head has moved to a newer commit, though my context references an earlier one. I'll post my review against the earlier commit since the tool handles line mapping automatically on the latest diff. I need to check the current PR head at ad0a0ea and verify the ComputerHistorySettings.tsx file to get the correct line numbers for my review, since the scope is at an earlier commit. The file's line numbers shifted slightly, so I need to check the current state at that commit to make sure my comments reference the right sections. The RowTitle duplication has been resolved with the sharedSettingRowTitlecomponent, so I should verify whether the Badge issue was also addressed in that commit. Now I'm posting the findings against the current head. The first issue is in ComputerHistorySettings.tsx around line 97-100 where the Escape key handler doesn't call stopPropagation, which causes the entire privacy dialog to close instead of just the nested component. The second issue is on line 351 where the mirrorToCodex Switch is missing thedisabled={!onDesktop}prop to prevent interaction when not on desktop. Writing the second suggestion...
1. modify_pr — UI consistency review of the changed web UI (apps/web/src/**). The two findings from the previous run are resolved on this head: RowTitle is now the shared SettingRowTitle in settingsLayout.tsx, and the exclusion chips no longer override Badge's geometry. Two new interaction-consistency issues in ComputerHistorySettings.tsx are noted inline.
Agent Credits: 217 credits