Repository navigation
UI Consistency: 2 issues found
Two consistency issues on the new Window Capture settings page:
apps/web/src/components/settings/WindowCaptureSettings.tsx(L72-74): while recording a shortcut, every keydown ispreventDefault()-ed, including Tab, so keyboard focus cannot leave the recorder button (only Escape exits). The existing keybindings recorder deliberately skips Tab beforepreventDefault.apps/web/src/components/settings/WindowCaptureSettings.tsx(L170-174, L192-197): the capture sound and animation switches remain interactive when no desktop bridge is present, while the enable / shortcut / flash controls in the same section are disabled. Without the bridge these toggles are inert, so the section's availability state is inconsistent.
Both findings were posted as inline review comments with suggested fixes.
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/** changes only (ChatView.tsx, CommandPalette.tsx, chat/ChatComposer.tsx, chat/MessagesTimeline.tsx, chat/WindowCaptureAttachmentDetails.tsx, desktop/WindowCaptureCoordinator.tsx, desktop/WindowCaptureOnboardingDialog.tsx, settings/WindowCaptureSettings.tsx, settings/SettingsSidebarNav.tsx, settings/settingsSearch.ts, composerDraftStore.ts, hooks/useSettings.ts, hooks/useHandleNewThread.ts, lib/desktopWindowCapture.ts, lib/attachmentUploadQueue.ts, routes/*, index.css). Desktop, server, and contracts changes were read for context only.
Checked and found consistent:
- Onboarding dialog now composes
Dialog/DialogHeader/DialogPanel/DialogFooter(earlier flagged raw body wrapper resolved), so header/footer padding coordination and the panelScrollAreaapply. window-capture-card-enterkeyframe lives inindex.cssalongside the other one-shot keyframes, with the animation named at the call site viaanimate-[...]plusmotion-reduce:animate-none(earlier flagged unlayered global override resolved).- Shortcut recorder button carries
data-keybinding-capture="", matching the app-level keydown-capture contract (earlier finding resolved). - Settings page composes
SettingsPageContainer/SettingsSection/SettingsRow/SettingResetButtonwithsearchableSettinganchors; new section registered inSETTINGS_SECTION_LABELS,SETTINGS_SECTION_ICONS, search catalog, and the generated route tree. - Attachment cards reuse existing card geometry,
cnmerging,text-secondary-labeltoken, and the existing zoom/remove affordances; capture-specific classes are additive and scoped tosource.kind === "window-capture". - Command palette action follows the existing
actionItemsshape (value, searchTerms,ITEM_ICON_CLASSicon,stackedThreadToasterror path).
🔍 Agent activity — 31 tool calls
💭 Checking the current state of the PR, it looks like several previously flagged issues have already been fixed — the keybinding capture attribute, the window-capture-card animation class, and the keyframes-only CSS are all in place, and the onboarding dialog now uses DialogPanel. Now I want to review the current diff for new findings, starting with the in-scope files like ChatView, CommandPalette, and the chat composer/message components. I need to check whether SettingsRow requires a "title" prop, how searchableSetting supplies it, and whether SettingResetButton is used correctly in WindowCaptureSettings. I also want to confirm whether text-secondary-label is a valid token used elsewhere in the repo before flagging it in WindowCaptureAttachmentDetails. Let me pull up these files to verify.
1. browse_code — apps/web/src/components/settings/WindowCaptureSettings.tsx
2. browse_code — apps/web/src/components/settings/settingsLayout.tsx
💭 I want to check KeybindingsSettings.tsx to see how it handles shortcut recording for consistency, and also verify whether the "Capture now" row's direct use of
titleversus other rows relying on searchableSetting for their title is intentional.
3. browse_code — apps/web/src/components/settings/settingsSearch.ts
4. browse_code — apps/web/src/components/settings/Keybindings*
💭 I notice the window-capture search items aren't marked desktopOnly, unlike rows like quit-confirmation which are since they only render in desktop. Here the rows still render on web, just disabled with a message, so that's consistent. Now I want to check whether SettingsSidebarNav filters sections for desktop, so I should look at that component.
5. browse_code — apps/web/src/components/settings/SettingsSidebarNav.tsx
💭 I should check whether the WindowCaptureAttachmentDetails component uses a valid
text-secondary-labeltoken by searching the web CSS/Tailwind theme files.
6. git_tools — secondary-label
💭 Good, the token exists. Now I want to check the ChatComposer around the attachment card section to review the layout, the remove button positioning, and whether the window-capture variant preserves things correctly, plus look at the MessagesTimeline attachment grid.
7. browse_code — apps/web/src/components/chat/ChatComposer.tsx
8. browse_code — apps/web/src/components/chat/ChatComposer.tsx
💭 The upload progress overlay for window-capture cards positions itself at the bottom, slightly overlapping the details row, but that's a minor issue since it spans the full width anyway. Now I'm checking how the window capture card renders inside MessagesTimeline, particularly the column-span and height styling within its grid container.
9. browse_code — apps/web/src/components/chat/MessagesTimeline.tsx
💭 These look consistent enough. Now I want to check a few other things: whether the WindowCaptureOnboardingDialog reflects the prior comment, whether the CommandPalette addition follows the same conventions as other action items (searchTerms, run, hook ordering), and how the index.css keyframes are structured. I also notice WindowCaptureCoordinator dispatches a custom
t3:focus-composerDOM event that ChatView listens for. Before treating this as fine, I should check whether the codebase already has an established mechanism for focus-composer or cross-component events, rather than this bespoke global event — worth grepping for existing patterns.
10. git_tools — t3:[a-z-]+
11. git_tools — dispatchEvent(new (Custom)?Event
💭 I'm noticing the new code duplicates a raw event string across modules instead of using the existing typed bus pattern, but I'm weighing whether that's really a UI-consistency violation versus just a minor code-organization nitpick outside the stated scope. I should check whether there's already an established composer focus mechanism, like
scheduleComposerFocusin ChatView or an imperative focus store used elsewhere (e.g., a command palette action), before flagging duplication as an issue.
12. browse_code — apps/web/src/components/preview/previewActionBus.ts
13. git_tools — scheduleComposerFocus|focusComposer
💭 I notice the new event uses a raw string literal in two modules instead of following the repo's typed event bus convention with its established prefix, which is a minor consistency gap worth flagging—maybe with an inline comment suggesting a shared module for the event name to keep the contract small and durable. This feels like a valid but low-severity finding, so let me move on and check other things for more concrete UI issues, starting with the onboarding dialog and its dialog primitives.
14. browse_code — apps/web/src/components/desktop/WindowCaptureOnboardingDialog.tsx
15. browse_code — apps/web/src/components/ui/dialog.tsx
💭 Good, the onboarding dialog issue is resolved with DialogPanel. Now I want to check the CommandPalette addition against surrounding code for consistency—looking at how other action items handle error toasts and whether they follow the same async run pattern with keywords or search terms.
16. browse_code — apps/web/src/components/CommandPalette.tsx
💭 The command palette addition looks fine, though the window capture shortcut isn't shown since it's a global shortcut rather than a keybinding command—not really an issue. Now I want to check the settings shortcut recorder against the KeybindingsSettings recorder UI to see if it's consistent, whether it uses a Button plus Kbd or a dedicated recorder component. If there's already a reusable recorder component, building a local version instead would count as a finding of duplicated primitives, so I should look at KeybindingsSettings.tsx's recording UI directly.
17. git_tools — data-keybinding-capture|recording|keybindingFromKeyboardEvent|function
18. browse_code — apps/web/src/components/settings/KeybindingsSettings.tsx
💭 I'm comparing how the keybindings recorder treats Tab specially to avoid trapping keyboard focus, versus WindowCaptureSettings.recordShortcut, which calls preventDefault on every key including Tab—meaning keyboard users might get stuck unable to tab away while recording a shortcut. Escape does exit recording, so it's not a full trap, just a minor inconsistency—though since recording only starts via onClick, a keyboard user activating the button with Enter/Space would still trigger recording and then hit this Tab-blocking behavior. Worth flagging as an accessibility inconsistency against the existing recorder's pattern.
19. browse_code — apps/web/src/components/settings/KeybindingsSettings.logic.ts
💭 I notice that
keybindingFromKeyboardEventreturns null when there are no modifiers, so pressing Tab alone in WindowCaptureSettings gets swallowed by preventDefault before returning early, trapping keyboard focus until Escape—whereas the Keybindings recorder explicitly excludes Tab before calling preventDefault. This is a real accessibility inconsistency, fixable by adding a Tab check before the preventDefault call. I should also check how Escape behaves while recording. Escape while recording calls preventDefault and stopPropagation, which looks fine, and the visual width change when the button shows "Press shortcut…" versus the Kbd component isn't a real issue. I'm confirming the recording button gets focus properly on click so onKeyDown works, and that nesting Kbd inside a button isn't problematic since Kbd is just a span. The Kbd component and formatShortcutLabel logic both check out, as does the window focus listener in ChatView. Now I want to verify whether WindowCaptureCoordinator's use of stackedThreadToast is actually the appropriate helper for generic errors, so I'm looking at the toast module more closely.
20. git_tools — export function|export const
21. git_tools — stackedThreadToast
💭 The window capture behavior isn't really UI consistency scope, so I should stay focused on UI/primitives/Tailwind concerns instead. I want to double check whether useClientSettings' selector pattern is actually supported by the hook in WindowCaptureSettings. js — I need to look at hooks/useSettings.ts since other components call useClientSettings() without arguments.
22. browse_code — apps/web/src/hooks/useSettings.ts
💭 The inline selector in WindowCaptureCoordinator changes each render but returns a primitive, so that's fine despite the useMemo dependency. I'm noticing that WindowCaptureSettings uses a different settings hook pattern (useClientSettings/useUpdateClientSettings) compared to other settings panels that use usePrimarySettings/useUpdatePrimarySettings, which seems worth flagging as an inconsistency.
23. git_tools — useUpdateClientSettings|useClientSettings(
💭 I should check the settings route file to see if there's a titles map for section paths that needs updating elsewhere, like for document titles or mobile navigation.
24. git_tools — /settings/keybindings
💭 I should also check the settings.keybindings.tsx file to compare naming conventions and structure.
25. browse_code — apps/web/src/routes/settings.keybindings.tsx
26. browse_code — apps/web/src/index.css
💭 The keyframes convention looks consistent, though the new one lacks a comment like the others have—minor nit, not worth flagging. The animation gating with motion-reduce fallback looks fine too. Now I'm checking the window-capture card sizing in ChatComposer for potential layout issues. Comparing the composer and timeline card dimensions, they seem reasonable. But I notice the "uploading" progress bar overlay at the bottom of the capture card might now partially cover the window title text since the details are vertically centered—previously it only overlaid the thumbnail. It's a marginal visual overlap issue, probably not significant enough to flag but worth noting. Actually, checking the padding classes more carefully—the
px-2.5 pr-8combination should merge correctly with tailwind-merge so the right padding wins, which is what's needed for the remove button spacing. The timeline version doesn't need this since there's no remove button there. I want to also check accessibility on the decorative image alt text, and verify the fallback div sizing when there's no preview URL. Then I should look at how the Kbd component behaves inside Button variants, particularly for small sizes, to make sure the icon-only sizing rules don't affect the layout unexpectedly. Another thing worth checking: whether the "Capture now" row's hardcoded title breaks from the pattern used elsewhere, since other settings rows are typically registered in a searchable catalog. I should grep for similar plain-title rows in other settings files to see if this is a common exception for action rows versus actual settings.
27. git_tools — SettingsRow$
💭 Plain titles are fine, so I'm narrowing down to two real candidates: the recordShortcut function swallowing Tab during recording, which breaks keyboard focus flow and is inconsistent with how KeybindingsSettings handles it, and the duplicated raw window event name used instead of the typed bus convention. I'm fairly confident in the first one and moderately confident in the second. I also want to check the disabled state pattern in that settings section — enabled, flash, and shortcut controls get disabled without a desktop bridge, but sound and animation switches stay interactive even though the whole feature is unavailable in the browser. That's a genuine UI consistency issue worth flagging: a user without the bridge sees a disabled main switch yet can still toggle controls that do nothing. I should verify where
settingscomes from in ChatComposer to confirmwindowCaptureAnimationsis actually accessible there — if it's UnifiedSettings from useEnvironmentSettings, it would include client settings and be fine, but I want to check it's not a narrower subset selector.
28. git_tools — const settings =
29. git_tools — settings
💭 Good, UnifiedSettings checks out, and the MessagesTimeline test correctly asserts the icon markup appears. I'm also checking sizing consistency between the WindowCaptureAttachmentDetails icon and text styling versus the composer card's dimensions and thumbnail sizing. Doing the math, the composer card's remaining space after the thumbnail and padding leaves only about 50px for text, which could truncate app names like "Terminal" down to just a few characters—that's a tight squeeze worth noting. By contrast, the timeline card has much more room since it uses a wider grid layout with a bigger thumbnail offset, giving plenty of space for text there. I'm hesitant to flag this as a definite regression without visual confirmation, since it's part of a new component's own geometry rather than a broken existing pattern. Let me check if the PR includes screenshots or comments showing the composer card in action to verify how severe the truncation actually looks.
30. github_api_read_only — repos/{owner}/{repo}/pulls/{pull_number}
💭 Since the composer attachment card lacks screenshots and I can't visually verify it, I'll note it as unverified. I'm settling on two concrete findings to flag: the shortcut recorder swallowing Tab creates an unintended keyboard focus trap that should just return early on Tab, and the sound/animation toggles staying enabled without the desktop bridge while the other three disable — leaving inert controls that mislead users. I'm also weighing a third finding about the duplicated event-name string diverging from the typed bus convention elsewhere; it's a small but real consistency issue. Given prior runs already posted several comments, I'm debating whether adding this one risks being noisy rather than useful. Rechecking #2, the sound and animation only matter on desktop since both are gated by the missing bridge on web—so disabling the toggle there is consistent and worth keeping. And re-verifying #1, Tab is only intercepted while actively recording, so normal keyboard behavior is preserved otherwise, and Escape properly stops propagation without affecting parent dialogs. Now I need to figure out the exact line numbers for inline review comments in the new WindowCaptureSettings.tsx file, since GitHub requires comments to map to lines within the diff hunks. I'm working out where to place a suggested edit around the Tab-key handling logic, adding an early return so Tab presses don't get preventDefault'd while not recording, matching the existing indentation in that block. I also want to leave a suggestion for the sound-toggle Switch component, adding an aria-label for accessibility, and I'm checking the exact indentation (12 spaces for the tag, 14 for props) so the suggested diff applies cleanly. Finally, I'm considering whether to add a comment on the WindowCaptureCoordinator regarding the event bus, but leaning toward keeping that comment brief and separate. Since findings exist, the check defaults to failure state. Now I need exact line numbers for the diff positions in WindowCaptureSettings.tsx — it's a new file so the whole hunk is included. For the first comment, I'll flag that preventDefault is called on every keydown during recording, including Tab, trapping keyboard focus with only Escape as an exit — and note that KeybindingsSettings.tsx's captureKeybinding already skips Tab for this exact reason, so this should match that pattern. For the second comment, I want to point out that the sound and animation toggles remain interactive even without a desktop bridge, while the other controls above them are disabled — even though the row shows "Available in the desktop app." and these toggles have no effect without WindowCaptureCoordinator, so they should be disabled too for consistency. Now I'm thinking about how to submit this via github_pr_review — whether to just submit with comments and a short top-level body, and how the schema handles start_line/side fields for single-line comments since passing the same value as line might trigger a validation error. I'm working out the exact line ranges for two suggested edits: one replacing a keyboard event handler block, and another replacing a Switch component to add a disabled condition based on bridge availability. Verifying line numbers...
31. modify_pr — UI consistency review of the window-capture web surfaces. Two findings on the new settings page; the composer/timeline attachment cards, the onboarding dialog, the keyframe ownership, and the command palette action all follow the existing primitives and conventions.
Agent Credits: 305 credits