Repository navigation
UI Consistency: 1 issue found
apps/web/src/components/chat/ExpandedImageDialog.tsx — The lightbox was migrated from a hand-rolled role="dialog" div to Base UI Dialog, but the full-viewport cursor-zoom-out overlay <button> remains the first tabbable element inside Dialog.Popup. Base UI's default initial focus therefore lands on an invisible control: no visible focus indicator on open, and a stray Enter/Space closes the preview (previously focus stayed on the thumbnail trigger, since the old markup had no focus management). Suggested fix posted inline: mark the overlay as mouse-only (aria-hidden, tabIndex={-1}) so initial focus lands on the visible close button, which already carries the "Close image preview" label.
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.
Reviewed files in scope: ChatView.tsx, ChatView.logic.ts, CommandPalette.tsx, GitActionsControl.tsx, PullRequestThreadDialog.tsx, RightPanelSheet.tsx, chat/ExpandedImageDialog.tsx, desktop/SshPasswordPromptDialog.tsx, settings/AddProviderInstanceDialog.tsx, settings/ConnectionsSettings.tsx, settings/ProviderSettingsPanel.tsx.
Checked and found consistent:
- Shared dialog contract: all converted call sites keep using
ui/dialog,ui/alert-dialog,ui/command, andui/sheetwrappers; only the lightbox composes raw Base UI parts, which is justified becauseDialogPopupwould imposedialog-glass/rounded-2xl/max-w-lgchrome andDialogBackdrop'sdialog-backdroputility setsbackground(it cannot be overridden by abg-black/75utility). The previousbg-black/75lightbox appearance,[-webkit-app-region:no-drag], z-index and image geometry are preserved, and the popup gains an accessible name viaDialog.Title. RightPanelSheetkeepMountedremoval: the only remaining consumer isChatView, which previously unmounted the sheet entirely while closed (rightPanelOpen && ...), so panel/terminal/preview surfaces already unmounted on close. The former diff-panel consumer that relied onkeepMountedno longer exists, so no persistence behavior is lost and the sheet now plays its exit transition.- State-preserving close pattern:
pullRequestDialogState,expandedImageDialog,pendingDefaultBranchAction,pendingWslChange, andpendingTailscaleServeSetupnow hold content untilonOpenChangeComplete, which removes the mid-exit content flash (e.g. the WSL confirm dialog no longer falls back to "Re-enable the Windows backend?" while fading out). - Reset completeness:
AddProviderInstanceDialog'sonOpenChangeCompleteresets all seven pieces of local state, so keeping it permanently mounted fromProviderSettingsPanelcannot leak stale wizard state into the next open. Commit-dialog and pairing-dialog resets moved to close-complete without leaving a stale path. - Double-submit guards: confirm buttons in the WSL flow stay
disabled={isUpdatingWslBackend}, andGitActionsControladded?.openguards, so keeping pending state non-null during the exit transition does not allow re-running actions. SshPasswordPromptDialog: the focus concern raised on the previous run was addressed viainitialFocus={inputRef}onDialogPopup; the dropped.select()is a no-op for an initially empty password field.
🔍 Agent activity — 1 tool call
💭 I'm checking whether the SshPasswordPromptDialog fix from the prior review comment was actually applied between commit 3a07c1e and the current head at 5d842d5, looking at whether the initialFocus prop was removed as suggested to let the primitive manage focus itself. Now I'm shifting to scan the rest of the diff for UI consistency issues — particularly ExpandedImageDialog, which seems to bypass the shared Dialog/DialogPopup wrappers in favor of raw Base UI primitives, possibly justified by its full-screen zoom-out styling. I also want to check RightPanelSheet's removal of keepMounted and its new always-rendered open state against SheetPopup's semantics before digging into the actual files. I should check ui/dialog.tsx to see what shared primitives are already available there. I'm noticing ExpandedImageDialog builds its own dialog from raw Base UI primitives instead of the shared DialogBackdrop/DialogViewport/DialogPopup components, which could count as a locally reconstructed primitive. But the layout is deliberately different—a full-screen image lightbox with a zoom-out cursor backdrop and no card styling—so I need to check whether the shared DialogPopup's constraints would actually fit this use case. I'm also checking whether other files import the base-ui dialog directly outside the ui/ folder, to see if this pattern is consistent with repo conventions. Then I'm going through potential real issues: how content renders conditionally inside Dialog.Root, whether the exit animation and forceRender logic on the backdrop work correctly, whether the popup needs a focus ring, and whether accessibility attributes like role and aria labeling are still properly applied. Now I'm looking at whether removing the Escape key handler is actually fine since Base UI's Dialog likely handles Escape internally already. I'm also digging into why
keepMountedwas removed from RightPanelSheet even though ChatView always renders the sheet component withopentoggling — I want to check the full file and git history to understand whykeepMountedwas added in the first place before deciding if removing it breaks anything. That commit about keeping the diff panel mounted and reusing the worker pool across layouts looks relevant—the keepMounted flag may exist specifically to preserve diff panel state. I want to check the git log for RightPanelSheet.tsx to trace where that keepMounted behavior originated. I'm checking the merge base version of RightPanelSheet to see how keepMounted was used alongside the conditional rendering in ChatView, since it only mounted the panel when open and I need to understand how that interacts with exit animations. With keepMounted removed and the panel now always rendered with anopenprop, Base UI should handle the exit animation itself and unmount afterward, which seems equivalent to the previous behavior since children were unmounted either way. I want to double-check why keepMounted was originally added in commit 19d4740 though, in case it was meant to preserve some state like an iframe in the diff panel. I'm noting that RightPanelSheet is used in two places, and in the chat thread view the keepMounted behavior was intentional—the diff panel mount state keeps it mounted across close so it doesn't lose state. Removing keepMounted from SheetPopup would cause the DiffPanel to unmount whenever the sheet closes, losing scroll position, worker pool, and loaded diffs—exactly what the mount-state logic was meant to preserve. I also want to check the PlanSidebar/RightPanelSheet usage in ChatView and look at the diffPanelMountState logic in the thread view file to confirm current behavior. I'm realizing that removing keepMounted causes terminals, browser previews, and diffs inside the right panel to fully unmount on close, losing state like scroll position and connections — a real regression for narrow/mobile layouts. But I also recall that before this PR, ChatView already conditionally rendered the sheet based on rightPanelOpen, so I need to check what that actually meant for mounting behavior. For ChatView specifically, keepMounted was effectively irrelevant since the sheet was only rendered when open anyway — so the new behavior is equivalent, just with an added exit animation. The real concern is the other consumer in the chat route file, which always renders RightPanelSheet and relied on keepMounted to keep the DiffPanel mounted while closed, preserving its state and warm workers. I want to check that file directly to confirm how diffPanelMountState and shouldRenderDiffContent behave now that keepMounted is gone. The old diff-panel consumer is gone now, so ChatView appears to be the only remaining consumer of RightPanelSheet. Let me grep to confirm that. I'm looking at the diff around line 3106 in ConnectionsSettings, which adds a handler that clears the pending WSL change state when the dialog closes. I want to view the surrounding region to see the complete open-change handler logic. That looks consistent, and the WSL dialog text persisting during close is intentional for this PR. Now I'm checking the GitActionsControl changes, looking at how the PublishRepositoryDialog's handleOpenChange dependencies changed and verifying the commit dialog's onOpenChange wiring for any behavioral regressions. I'm also concerned about the removed resets around lines 1530 and 1629 — they now rely on onOpenChangeComplete firing, but if the dialog unmounts instead of closing normally, that reset might never trigger. I need to look at those regions directly to confirm. I'm checking that opening the dialog from the menu item properly resets the excluded files and editing state without clearing the commit message, since that reset now happens on close completion instead. I'm also considering an edge case: if the dialog gets reopened quickly while it's still closing, the close-complete callback might not fire correctly with Base UI's transition handling, which could cause a stale reset to leak into the new open state. I'm also reviewing thehandleOpenChangecallback in GitActionsControl — its dependency array looks fine since it referencesprops.onOpenChangedirectly rather than a destructured value, though the callback itself is now trivial since it just forwards the call. Now I'm shifting focus to the main review priorities: making sure the shared component system stays consistent, Tailwind styling ownership is respected, and accessibility/interaction semantics are preserved across these changes. Looking at ExpandedImageDialog, I notice it now constructs a dialog from raw Base UI primitives with hardcoded backdrop/transition classes rather than reusing the sharedDialogBackdrop/DialogViewportcomponents that already own the backdrop styling — I want to check the dialog-styles file to see if the bg-black/75 treatment is an intentional lightbox look or a regression. I'm also noticing a nested interactive element: a full-screen "close" button now sits as the first tabbable element inside the popup, which changes where Base UI's initial focus lands compared to before when focus simply stayed put. There's also some redundant fixed-positioning between the popup and its viewport that seems harmless, but I suspect there's a more substantive concern here worth digging into further. Since the popup covers the whole viewport, it functions fine as a lightbox, and I don't think a missing Dialog.Description matters since Base UI doesn't require one. The matching z-50 on both backdrop and viewport is fine since DOM order determines stacking. The escape-key handling moved from manual stopPropagation logic to Base UI's internal layering, which should work correctly. The expandedImageDialog key changing from image src+index to generation looks fine too. Now I want to check CommandPalette's dialogContentMounted gating — previously the dialog was always rendered, so I need to see whether gating the content on mount state could break anything, particularly around how DialogPopup is nested inside. I also want to look at what prior comment existed in SshPasswordPromptDialog before the changes. For the SSH password dialog, I need to verify the final state with initialFocus pointing to the input ref and the effect calling setOpen(true) — previously the code focused and selected the input text, but initialFocus alone won't select text, so I should check whether the field is prefilled or empty to see if that matters. Let me pull up both CommandPalette and SshPasswordPromptDialog to confirm. I'm checking whetherdialogContentMountedgets set true consistently across all the reducer paths that open the palette, including the edge case where toggling mode while already open might close it instead. That seems fine since the mounted flag is already true in that case. I'm also considering whether the palette could be opened from elsewhere, like a store or a nested trigger, but confirming thatstate.openis the sole source of truth, so the mounted flag tracking seems redundant with what the Portal's keepMounted behavior already handles. Actually, I realize the real question is whether unmounting content after close changes behavior — previously the dialog content stayed mounted so search state persisted, but since the Popup itself unmounts children when closed regardless, any internal state inside CommandPaletteDialog's children would already reset. So the new gating mainly affects state held directly in CommandPaletteDialog itself, not its descendants. It duplicates backdrop/animation classes instead of reusing the shared DialogBackdrop/DialogViewport primitives, though the lightbox geometry is intentionally different so only those two pieces are reusable—worth checking if the shared backdrop styling actually matches visually. On accessibility, the full-screen close button is the first tabbable element, so default focus lands there before the nav buttons; previously there was no focus trap at all, so this is a minor but real behavior change for keyboard users. Looking at RightPanelSheet's keepMounted removal, I'm checking whether unmounting behavior stays equivalent to before—since ChatView already conditionally rendered the panel, removing keepMounted seems consistent rather than a regression. Now I'm moving on to check the SshPasswordPromptDialog's input ref handling. Since the password field starts empty, dropping the select() call on the ref is a no-op, so there's no real loss there. I want to confirm Base UI's DialogPopup supports initialFocus and that the Input component properly forwards its ref to the underlying DOM element. Next I'm checking whether PullRequestThreadDialog's new required onOpenChangeComplete prop breaks any other call sites, so I should grep for other usages. Then I'm looking at ProviderSettingsPanel, where AddProviderInstanceDialog is now always mounted instead of conditionally—previously unmounting reset its internal state, but now that reset has to happen through the onOpenChangeComplete handler, so I need to verify that handler actually covers all the state that needs resetting. I'm going through AddProviderInstanceDialog's useState hooks to see if anything like error, isSubmitting, or testResult isn't being reset, which would leak stale state into the next time the dialog opens. I also need to check whether keeping it permanently mounted means effects or queries (like fetching the models list or subscriptions) now keep running even while the dialog is closed. Similarly, for ConnectionsSettings' AuthorizedClientsHeaderAction, the reset logic moved from the open-click handler to the close-complete handler, removing two lines that cleared the pairing label and scopes—that should still be fine since the reset now happens on close instead. I'll dig into AddProviderInstanceDialog in detail now. All seven useState hooks are covered by the reset list, and the dialog subscription in ProviderSettingsPanel is already active in the parent, so that's fine. Now I want to check how ChatView's PullRequestThreadDialog and PullRequestDetailPanel handle reopening after close, making sure the pull request dialog state logic resets properly. I notice that opening a new pull request dialog while one is closing creates a fresh key, which remounts the component immediately and kills the exit animation—minor but worth flagging. I'm also wondering if there's a bug where the pull request dialog state is nested inside a conditional tied to activeThread, which could cause issues if the thread unmounts while closing. Now I'm looking more closely at ExpandedImageDialog to check for similar regressions. Since the dialog mounts with open=true immediately when an image is expanded, I'm checking whether Base UI still plays the starting-style transition on initial mount—it seems like it does, since the starting style is applied then removed after insertion, so that should be fine. I'm also verifying that Dialog.Viewport and the forceRender behavior on Dialog.Backdrop are consistent with how they're used elsewhere, and now I'm checking whether the ending-style classes on the popup match the shared dialog styles file to rule out any inconsistency there. I notice the dialog backdrop and popup classes tie into global CSS that likely handles nested dialog stacking, with a variable controlling scaling behavior across stacked dialogs. Since ExpandedImageDialog uses a custom backdrop without the shareddialog-backdropclass, it probably doesn't participate in that nested-dialog scaling logic, which seems to be driven by Base UI's own data attributes on the Popup component rather than anything local. This isn't a strong finding, but I want to check index.css directly for how.dialog-backdropand the nested-dialogs variable are actually defined. I'm confirming that the shared DialogBackdrop contract handles theming and blur consistently, and that ExpandedImageDialog's raw bg-black/75 backdrop matches the original lightbox styling intentionally rather than being a regression. Now I'm checking whether the new code's use of raw Base UI Dialog primitives elsewhere introduces any inconsistency. The lightbox's geometry genuinely differs from DialogPopup's glass-card styling, and composing DialogBackdrop with a custom background class would conflict with the utility's own background declaration due to specificity issues — so a hand-rolled backdrop here is defensible and shouldn't be flagged. I'm now moving on to evaluate the first real regression candidate: ExpandedImageDialog's full-viewport Dialog.Popup with its full-screen close button. Base UI's default focus behavior moves initial focus to the first focusable element inside the popup when opened, which in this case is that invisible full-screen close button rather than anything visible. Previously, with a plain div and no focus trap, focus simply stayed on the thumbnail that triggered it — so this is a real behavioral shift where keyboard users now land on an unstyled, invisible element with no visible focus ring, even though Escape and arrow-key navigation still function correctly. I'm weighing whether this is worth flagging: it's a mild but legitimate accessibility regression tied to preserving interaction semantics during the migration, and a reasonable fix would be settinginitialFocusexplicitly to the popup or to the visible close button. I also checked a few other candidate issues — the arrow-key window listener scoped toopenbehaves the same as before, and the nullableitem/previewprop doesn't create a real broken state since nothing renders without a portal — so neither of those seem worth reporting. Now I'm looking atcloseExpandedImage, where there appear to be two separate handlers: one nulling state immediately and another just togglingopento false, and I want to check which one skips the exit animation. I'm also looking at the commit dialog'sonOpenChangewiring, which now passes the setter directly instead of filtering for close-only — seems harmless since it also handles the open case. And I'm checking the SSH password dialog's initial focus and mount-timeopenstate, since there's a question of whetheronOpenChangeCompletecould fire prematurely on mount and trigger an unwanted removal. Thinking through it more, Base UI likely only callsonOpenChangeCompleteafter an actual transition, so a false initial state shouldn't trigger it — seems like a low risk. I'm also confirming thatcancelPromptandrespondboth safely callsetOpen(false)without conflict, and noting the image dialog's popup is missing aDialog.Description, though I'll skip that for now. Next I want to look at lines 4080-4100 in ChatView to pin down the exact behavior. I'm noticing that bumping the generation counter in onExpandTimelineImage forces a remount of ExpandedImageDialog via the key change, which cuts off the exit animation and resets imageOffset when a new image opens while another is closing—this seems acceptable, but I want to check whether generation gets bumped even when the dialog is already open and the user clicks a different image. Now I'm tracing the CommandPalette's dialogContentMounted flow to see if there's another dispatch path that could set open outside the normal reducer actions, like an effect tied to route or store changes. I'm confirming that closing the palette properly unmounts its content after onOpenChangeComplete fires, and now I'm checking the escape-key path in non-command mode to see how onOpenChange handles it. I'm also considering a rapid reopen-during-close edge case: if setOpen(true) fires while a close transition is still in progress, could mounted flip to false while open stays true, causing an empty popup flash? Looking at Base UI's onOpenChangeComplete implementation, it only fires for the open state that actually completes its transition, so an interrupted close would instead trigger onOpenChangeComplete(true) when reopening finishes — meaning this scenario should be safe. Stepping back, I'm rescanning the diff for actual styling changes worth flagging: the ExpandedImageDialog restructuring from a single wrapping div into separate Backdrop, Viewport, and Popup elements with transition classes, plus the RightPanelSheet keepMounted removal. Everything else is just state plumbing, so the real question is whether this new three-layer structure introduces any visual regression compared to the original single-div layout. One subtlety I'm noticing: theno-dragElectron attribute moved from the original container onto the new Popup element, while the Backdrop layer beneath it doesn't have that attribute. Since the popup sits on top and catches pointer events first, this might still be fine, but it's worth tracing through carefully to confirm there's no drag-region gap. I'm also checking the z-index and portal behavior — since Base UI now portals the dialog to body instead of rendering it inline within ChatView's tree, the stacking context concerns from before are likely resolved rather than introduced. ThekeepMountedprop on SheetPopup now has no consumers since RightPanelSheet was removed, but that's just a minor cleanliness issue rather than a real problem. I want to double-check by grepping for other usages ofkeepMountedto be sure nothing else depends on it. I'm noticing the RightPanelSheet condition changed from gating on rightPanelOpen to always rendering when there's an active thread, just toggling the open state instead. That means the tabs and content JSX now get created on every render even when closed, relying on the Sheet's portal to not actually display them. Since the sheet is closed, nothing renders visibly, so this seems fine. I'm now doing a final pass checking a few remaining items, starting with the GitActionsControl publish behavior. Checking the PublishRepositoryDialog's handleOpenChange dependency array, then verifying PendingDefaultBranchAction's newopenfield is set consistently at its single creation site, and confirming ConnectionsSettings' spread updates to pendingWslChange are type-safe. I also want to trace whether PullRequestDialogState's new requiredopenprop is handled correctly everywhere it's consumed, particularly in ChatView. tsx and PullRequestThreadDialog.tsx, since PullRequestDetailPanel uses its own onOpenChangeComplete instead. Let me grep for all usages of PullRequestThreadDialog to be sure. I'm checking whether PullRequestThreadDialog focuses the input on mount, and tracing how theopenstate was previously hardcoded versus how it behaves now in ChatView. Closing now sets open=false and unmounting happens on complete, so internal state still resets properly via unmount—that checks out. I'm also verifying thatonOpenChangeCompleteis correctly guarded against firing on open, and confirming Base UI's Dialog Root supports that prop as used elsewhere. Overall the PR seems fine for UI consistency, though I want to double-check the ExpandedImageDialog focus behavior and the removed Escape handler, since Base UI should now properly stop propagation for the top-most layer instead of relying on the old window-level interception. ation was largely ineffective anyway. The real concern is whether ChatView or other components have global Escape handlers that would now also fire while the lightbox is open, potentially closing the right panel or clearing the composer. I should check whether those handlers guard against an open dialog, like checking for a dialog role element before acting. No Escape handlers in ChatView, so that's fine, and the arrow key navigation in ExpandedImageDialog should still work the same way. I also notice the new Base UI Dialog locks page scroll and traps focus by default, which is actually an improvement over the old plain-div lightbox. Checking the Viewport/Popup structure, the Popup being fixed makes the Viewport's own centering layout irrelevant — slightly redundant but not a real issue. Since the Popup fills the whole viewport there's no "outside" region for click-to-close, which explains why they kept an explicit full-screen close button — that's consistent behavior. I'm also considering the nested-dialog case: if the lightbox opens over another Base UI dialog, the parent gets a nested-dialog flag and applies a scale/transform effect, which is pre-existing behavior for any dialog, though this might be a new scenario worth double-checking. Previously the lightbox was a plain div, so opening it wouldn't shrink an underlying dialog — now it could, but sinceonExpandTimelineImageis triggered from the chat timeline rather than from within another dialog, this isn't actually a concern here. I'm fairly convinced there's no real UI-consistency violation, but one thing still nags at me: where initial focus lands when the dialog opens. Base UI's Dialog.Popup defaults to focusing the first focusable element, which in this case would be the invisible full-screen close button labeled "Close image preview" — meaning a keyboard user opening the lightbox would land focus on an element they can't see. Pressing Enter or Space there would close the dialog with no visible focus ring to warn them, which is a real regression in keyboard affordance compared to typical dialog patterns where the first focusable element is visible content or an actual close button. This seems worth flagging since it touches on preserving focus-management and keyboard behavior during the migration — I'm leaning toward suggesting they setinitialFocusexplicitly on the popup, similar to how PullRequestThreadDialog handles it. I want to double-check whether this is actually a new issue versus pre-existing behavior. The invisible overlay button was already first in the DOM before this change, so its presence isn't new—only the auto-focus behavior is. I need to confirm Base UI's actual default: by default it focuses the first focusable element when the dialog opens, which matches what I recall from their docs for both Dialog and Popover. Checking for consistency, I notice SshPasswordPromptDialog is the only other dialog in the repo that explicitly passes initialFocus, and it was added in this same PR after my earlier comment about Base UI's default focus behavior—meaning the author already accepted that model. That gives me confidence flagging ExpandedImageDialog's auto-focus landing on the invisible close button is a real, consistent concern: pressing Space or Enter could close the dialog unexpectedly with no visible focus indicator. I still want to verify whether the Popup itself might capture focus instead, given it has outline-none but no explicit tabIndex, and how Base UI's internal focus trap handles that case. A bigger refactor would remove the invisible button entirely, but the smaller fix is just pointinginitialFocusat the visible close button ref. I think this is worth flagging once since focus landing on an invisible element that closes on Enter/Space is a genuine interaction regression compared to the prior behavior, not just an aesthetic nitpick. Let me also double-check whether there's a stronger issue I'm overlooking, like the commit dialog reset move in GitActionsControl. Tracing through the commit flow: opening the dialog sets excludedFiles/isEditingFiles, and the commitMessage reset now happens only after the close transition completes rather than immediately. That seems fine for the normal commit path and for the default-branch prompt path too, since pendingDefaultBranchAction captures the commit message before the reset fires — though I want to verify the timing is safe across bothrunDialogActionandrunDialogActionOnNewBranch. I'm also checking whether a new pendingDefaultBranchAction could be set while a previous one is still mid-close, but that just reopens the dialog with fresh content, which seems okay. I'm now probing for edge cases where onOpenChangeComplete might never fire, leaving stale state that blocks future dialogs — unmounting during transition seems irrelevant, and reduced-motion or transition-less cases still trigger the callback. I suspect there's a more subtle bug lurking here though. I'm checking whether any code treatspendingWslChangeorpendingTailscaleServeEndpointas a truthy guard rather than checking.open, since that could cause a brief incorrect block during the closing transition window. I'm noticing a potential bug in ConnectionsSettings: the confirm handlers check pendingWslChange's truthiness instead of its open state, so during the 200ms close transition the dialog's buttons could still be clickable in the DOM even though it's visually closing, potentially letting a user re-trigger the confirm action. Interestingly, GitActionsControl already guards against this exact issue by checking?.openexplicitly, so the WSL handlers are inconsistent with that established pattern — this seems like a concrete fix worth flagging. I'm also checking whether the popup actually remains pointer-interactive during the exit transition, since Base UI may not disable pointer events on the popup while it's closing. Pressing Enter rapidly on the autofocused Confirm button is another realistic way to trigger a double-fire, so I want to check whetherapplyWslSettingChangehas its own internal guard likeisUpdatingWslBackendthat would prevent the action from running twice regardless of the dialog state. I'm checking whether the Confirm button is disabled while isUpdatingWslBackend is true, and noting that the dialog's onOpenChange also blocks closing during that update. Disabling the buttons while updating mitigates double-firing since the state change happens synchronously before the first await, within the same render tick. But I'm noticing a subtler issue: the existing early-return guard inonOpenChangebased on the updating flag interacts with the new confirmation flow in a way that could cause unexpected behavior after the update completes. Now I'm tracing through what happens once the backend flag flips true and the dialog's open state goes false — the dialog animates closed, thenonOpenChangeCompletefires and clears the pending change. This matches the old behavior of clearing state immediately, except now the dialog content stays consistent during the fade instead of flashing the fallback text, which is exactly the bug this PR fixes. I want to double check there isn't another spot in ConnectionsSettings still nulling the pending state early that would reintroduce that flash. Checking the similarpendingDesktopServerExposureModepattern and the PublishRepositoryDialog'sresetState()— both look fine since they're deferred to completion too. The one remaining concern is ChatView's immediate clearing ofexpandedImageDialogon thread change, but that's intentional since it's not meant to animate. Now I'm weighing whether to flag the focus-handling change in the image dialog migration — moving from a plain div to a Base UI Dialog means focus now lands on the first tabbable element, which here is an invisible full-viewport overlay button, so keyboard users would lose any visible focus indicator. The fix would be passing aninitialFocusprop toDialog.Popup, pointing it at a ref for the visible close button, or reordering the DOM so a visible element comes first — though since the overlay uses z-0 and content uses z-10, DOM order doesn't affect stacking, so reordering is safe and would make the close button or nav button the natural first focus target. I think the real issue is that focus now lands on the invisible full-screen close button, so I should flag it as a finding — maybe suggesting an explicitinitialFocuson the visible close button or reordering the overlay button after the image content — without providing a diff. I'm weighing whether this counts as a genuine regression worth failing the check, given the instruction that a clear consistency or regression risk may fail it, and I'll report it as a finding with a neutral or failing state. I'm also double-checking other smaller details — backdrop transition classes, the popup's scale-down animation on close, and whether the Viewport component has any additional requirements — but these all seem intentional or equivalent enough not to warrant separate findings. I should also verify whether any other component besides ChatView consumes ExpandedImageDialog, since it now needs to pass four props. I should check if there are existing tests for ExpandedImageDialog that might break, like ones querying for role="dialog" or the onClose prop. No tests needed here. I notice the dialog now gets its accessible name from Dialog.Title via Base UI's aria-labelledby wiring, which is good. I'm planning to leave an inline comment on ExpandedImageDialog flagging that initial focus lands on the invisible full-viewport close button, and suggest explicit initialFocus or reordering elements to fix that. Actually, thinking it through more, that overlay button exists because Base UI treats clicks inside the popup as "inside clicks" that won't trigger dismissal, so the button is a necessary click-to-close affordance — the real issue is just that it's tabbable and first in the tab order. The cleanest fix is making itaria-hiddenwithtabIndex={-1}since it's purely a mouse affordance duplicating the visible X close button, letting keyboard focus land on the real close button instead. Writing the suggested diff... Given this is a real but fairly minor accessibility regression on a lightbox, I'm weighing whether to mark the check as a failure or neutral — since the check defaults to failure unless things are clearly clean, and I do have a genuine finding, I'll lean toward failure. Now I'm drafting the inline review comment targeting lines 69-74 of the diff, explaining that the overlay close button becomes the first tabbable element inside the dialog, causing keyboard focus to land on an invisible full-viewport button with no visible indicator, and suggesting making that element mouse-only since the visible X button already provides proper accessible labeling.
1. modify_pr — One finding on the lightbox migration to Base UI Dialog — the rest of the dialog lifecycle plumbing (state kept alive until onOpenChangeComplete, resets moved to close-complete, sheet now animating instead of being conditionally unmounted) looks consistent with the shared dialog contract.
Agent Credits: 270 credits