Skip to content

fix(web): preserve vcs refresh and scripts by hiding visually and mai…

MacroscopeApp / Macroscope - UI Consistency failed Aug 16, 2026 in 6m 23s

UI Consistency: 1 issue found

apps/web/src/components/chat/ChatHeader.tsx:364 — h-full on the data-chat-header-actions container is a no-op (its containing block, the ChatHeader root, is a content-height flex item of the items-center topbar), so when all three controls are toggled off the container collapses to 24x0 and the right-click menu that is the only way to restore them becomes unreachable. Suggested self-stretch instead.

Previously flagged items are resolved at this head: the controls are now hidden visually rather than unmounted (VCS focus refresh, scripts, and the editor.openFavorite shortcut survive), the unchecked menu prefix uses non-collapsing \u00A0 so it is no longer stripped by the web fallback's textContent/truncate rendering, and breadcrumb right-click still opens the thread action menu.

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: apps/web/src/components/chat/ChatHeader.tsx, apps/web/src/headerControlsStore.ts, apps/web/src/contextMenuFallback.ts, apps/web/src/localApi.ts, apps/web/src/components/ui/{dialog,menu,group}.tsx, apps/web/src/components/ChatView.tsx (render site).

Checks performed and found clean:

  • Wrapper <div className={cn(!visibility.x && "hidden")}> around each control: ProjectScriptsControl and GitActionsControl return fragments whose extra siblings are Dialogs, and both DialogPopup and MenuPopup portal out of the subtree, so display:none on the wrapper does not hide open dialogs/menus.
  • Container queries: @container/header-actions stays on the ChatHeader root, so descendant @3xl/header-actions:* utilities still match through the new wrappers.
  • Flex geometry: the actions container is shrink-0 with content-derived width, so the wrappers not carrying shrink-0 does not introduce compression.
  • Store: headerControlsStore follows the existing persisted-zustand pattern (createJSONStorage + resolveStorage(window.localStorage), t3code:*:v1 key naming) used by diffPanelStore/rightPanelStore.
  • Menu contract: ContextMenuItem has no checked/type field and the web fallback renders labels via textContent, so the check-glyph prefix is the available approach; the non-breaking-space prefix no longer collapses.
  • Behavior coverage: focused unit tests exist for the store and for menu-item label formatting.

🔍 Agent activity — 1 tool call

💭 I'm checking the current state of the code at commit 9d21704 against earlier versions to see what's changed and identify any remaining issues. Looking at ChatHeader.tsx specifically, I notice the unchecked prefix has been updated to use non-breaking spaces instead of the previous format. Looking back at the prior findings, I see that earlier runs flagged unmounting issues with components like OpenInPicker and GitActionsControl, but the current implementation uses hidden div wrappers to keep components mounted rather than removing them from the DOM. So those previous concerns about losing shortcuts and state through unmounting should already be resolved by this approach. I'm also noticing the whitespace issue with non-breaking spaces appears to be fixed now. On the breadcrumb right-click behavior, there's still a potential duplication where the header actions container has its own context menu handler, and the event might be falling through to the parent handler as well. Now I'm checking the head section for any new issues that might've cropped up. The main concern is whether hiding wrapper divs with display: none properly collapses the flex gap—which it does. I'm also considering if wrapping controls in extra divs breaks the flex layout, but since the parent uses flex with centered items and a gap, the block-level wrapper should work fine unless the control itself depends on being a direct flex item with properties like full height. The actions container now has h-full and min-w-6 to maintain a clickable area for the context menu even when all controls are hidden. For the non-breaking space prefix, I'm verifying that the alignment works despite width differences between the checkmark and spaces in proportional fonts—it's acceptable since they won't collapse. I need to trace where readLocalApi comes from, check the ContextMenuItem type import, verify the storage utility exists with the right signature, and confirm that hidden controls remain inaccessible to focus but still respond to keyboard shortcuts since they're mounted in the DOM. I need to check whether the ContextMenuItem contract includes support for a checked property by looking at the contracts package. I'm wondering if this check-mark-in-label approach is actually a known pattern in the codebase, so I should search for any existing uses of "✓" prefixes in context menu labels. The check-glyph-in-label approach is working well enough despite imperfect alignment—the author already addressed my earlier feedback by switching to a non-breaking space, so I won't re-flag that. Now I'm looking at the new changes in the head commit, starting with the h-full min-w-6 sizing on the actions container and how it interacts with its parent's height constraints. Each control component might render a fragment with multiple elements or rely on being a direct flex child, so wrapping them in a plain block div could break the layout if they're expecting flex behavior or using gap spacing. Container queries with the @3xl/header-actions prefix should still work since the named container is on the outer div, so that's not an issue. I should also verify whether other parts of the codebase use similar persisted zustand stores to ensure I'm following the same naming and hydration patterns, and check if any of these hidden controls render popovers or other elements that might still be visible even when the parent is hidden. I'm realizing that ProjectScriptsControl and GitActionsControl return fragments with multiple top-level elements including control groups and dialogs, so wrapping them in a hidden div would hide the control but not necessarily the dialogs since they typically render into portals that exist outside the DOM hierarchy. However, if a dialog renders inline without using a portal, hiding the wrapper would break it, and I need to check whether Base UI Dialog uses Portal by default. There's also a layout issue: wrapping the fragment children in a plain block div changes the flex behavior. Previously the Group was a direct flex child with w-fit and shrink-0, but now the wrapper div becomes the flex item instead, which could cause it to shrink below its content width since the shrink-0 constraint was on the Group, not the wrapper. The container query setup looks fine — the outer header div has the @container/header-actions name, and the wrapper doesn't create its own container, so descendants using @3xl/header-actions: queries should still work correctly. Since the actions container itself is shrink-0, it maintains its full width and prevents child compression, so losing shrink-0 on the git Group isn't actually a regression. Now I need to check whether the dialogs from ProjectScriptEditorDialog and GitActionsControl use portals — if they do, they'll stay visible even when the wrapper is hidden, which could be odd but not broken. icking that empty area should trigger the context menu handler, which is the intended behavior. Now I'm thinking through the height calculation — the actions container uses h-full, but if the parent ChatHeader doesn't have a definite height, the percentage height might resolve to auto instead, which could affect the clickable area. I need to trace up through the component hierarchy to see what's actually constraining the height. The h-full utility isn't doing anything since it's trying to apply a percentage height to a container with auto height. The real issue is that when all three controls are hidden, the container collapses to zero height — even though it's a flex item in a row with items-center alignment, its cross-axis size is determined by its content, which is empty. This creates a 24px-wide by 0px-tall region that can't be right-clicked to restore the hidden controls, making the feature unrecoverable once all three are toggled off. The user would need to right-click elsewhere on the header, but that triggers a different context menu instead. The real issue is that h-full doesn't actually resolve to anything meaningful here — the parent's height is content-based and indefinite, so the actions container collapses to zero. The fix is to use self-stretch on the actions container instead, which stretches it to match the ChatHeader's height (around 20-24px from the breadcrumb), giving it a clickable hit area again. This falls within scope as a layout preservation and interaction finding. I should also verify there's no other way to restore the controls — checking for other consumers of the headerControlsStore confirms ChatHeader is the only one, so right-clicking that zone is the only recovery path. Now looking at the wrapper div approach: these controls might accept a className prop directly, which could be cleaner than adding wrapper divs, though wrapping is acceptable. The real consideration is that ProjectScriptsControl and GitActionsControl return fragments with portaled dialogs as siblings — wrapping them in a hidden div won't affect the dialogs since they're portaled, which is good. From an accessibility standpoint, using hidden (display:none) correctly removes the controls from the a11y tree when they're not visible. The context menu label alignment issue was already flagged and addressed with a non-collapsing prefix, so that's settled. The handleHeaderContextMenu function has a closest selector that's now redundant given the container's own onContextMenu with stopPropagation, but it's harmless dead code and not worth flagging since it doesn't violate UI consistency. There's a minor UX issue where the "Action scripts" menu item shows as checked even when activeProjectScripts is undefined and the control isn't rendered. I'm also checking whether the persisted store naming convention t3code:header-controls:v1 is consistent with other stores in the codebase, and verifying that the storage resolution signature matches how it's used elsewhere. The naming checks out. I'm seeing that h-full becomes a no-op when all controls are hidden, resulting in a zero-height hit area—the container's three conditional children are all hidden, so there's no height. This doesn't cause issues even when only some controls are hidden. There's also another way to access functionality: right-clicking the header outside the actions region opens the thread action menu, whereas the controls menu is only reachable through the actions container. The actions container itself is a flex element with padding and a minimum width, but zero height means no clickable region at all. The fix would be replacing h-full with self-stretch on the actions container so it matches the header row height—this gives roughly a 24×20 pixel target. Since the ChatHeader root gets its height from the breadcrumb content, self-stretch should work, though I need to be careful because the root also has items-center, which self-stretch would override. I'm also checking whether the hidden class and gap behavior cause issues when scripts are hidden, whether right-click context menus are accessible on touch devices (they're not, but that's an existing pattern in the repo), and whether the wrapper divs and justify-end alignment interact with any nested group selectors—none of these seem problematic. Looking at the container queries and the ChatHeader's height, it's content-based rather than fixed, so h-full wouldn't cause issues. I'll add an inline comment on line 364 to document this, and since this touches hit-area behavior, the check should probably fail if there's any recoverability or regression risk. I'm also noticing that readLocalApi() could return null in web browsers, which would silently break the context menu. Let me check whether the fallback is properly set up in non-Electron environments—if readLocalApi() returns null, the preventDefault() has already fired, so the native context menu gets suppressed anyway. The web fallback is confirmed. Now I'm adding an inline comment to line 364 of ChatHeader.tsx, which contains a flexbox utility class string with specific spacing and alignment properties. I'm considering whether to add self-stretch to make the actions container match the root's height, since the root height is determined by its tallest child (the breadcrumb at around 20-24px). The self-stretch approach should work fine since the breadcrumb will always have content, but I could also suggest min-h-6 as a safer alternative if needed. I'll go with self-stretch as the primary suggestion and mention the min-h-6 option as a fallback. Ready to post the review.

1. modify_pr — One finding on the header actions hit area.


Agent Credits: 284 credits