# fix\(web\): close composer menus when their controls hide · PR \#9541 · pingdotgg/t3code

[View on GitCafe](https://git.cafe/pingdotgg/t3code/pull/9541)

Repository: [pingdotgg/t3code](https://git.cafe/pingdotgg/t3code)

Visibility: public

State: merged

Source repository: pingdotgg/t3code

Source branch: composer-followups/popup-cleanup

Target branch: composer-followups/hidden-label-width

Head commit: b150cbbffb426840dc8ac7736514e3d174b09443

[Discussion and reviews](https://git.cafe/pingdotgg/t3code/pull/9541)

[Commits](https://git.cafe/pingdotgg/t3code/pull/9541/commits?format=markdown)

[Files changed](https://git.cafe/pingdotgg/t3code/pull/9541/diffs?format=markdown)

## Description

---

The resting composer keeps hidden controls mounted for measurement, but their portaled menus live outside the hidden wrapper. Runtime and traits menus can outlive the move into overflow; model and overflow menus can outlive the whole control cluster becoming hidden.

Close the menus and clear their stored open state when their triggers hide, without unmounting the measured controls. Revealing the controls does not reopen an old menu. Model-picker commands reveal the composer when the shortcut would otherwise target a hidden trigger.

Verification:

- React lifecycle tests cover open, hide, reveal, reopen, and attempted opening while hidden, under Strict Mode.
- All 145 focused tests across composer layout, measurement, menu state, toolbar layout, traits, provider state, and composer event scope pass.
- Web typecheck passes. Targeted lint reports no errors and the same 34 existing ChatComposer warnings as the base.
- Integrated resize/focus verification, before/after screenshots, and an interaction recording have not been performed.

Stacked on [#9540](https://github.com/pingdotgg/t3code/pull/9540). Covers web/desktop composer controls and the model-picker command entry points, including mobile-width web layouts. Native mobile, provider adapters, and connection protocols are unchanged.

Implemented with Codex through T3 Code.

<!-- CURSOR_SUMMARY -->
---

> [!NOTE]
> **Low Risk**
> UI-only composer menu and focus behavior with targeted tests; no auth, data, or API changes.
> 
> **Overview**
> Fixes **orphaned portaled menus** when the resting/collapsed composer keeps control triggers mounted but visually hidden for layout measurement.
> 
> Introduces **`useComposerMenuState`**, shared by the runtime mode select, traits picker, and compact overflow menu. When a trigger is marked `hidden`, open menus close and internal open state resets so controls do not **reopen stale menus** when they become visible again. Opening while hidden is not committed to the effective open state.
> 
> **`ChatComposer`** threads a `composerControlsHidden` flag (strip visible but measured controls not shown) into those pickers and closes the model picker if it was open when the cluster hides. **`openModelPicker`** / **`toggleModelPicker`** now expand and focus the composer when the shortcut would target a hidden trigger, then open the picker.
> 
> Adds **Strict Mode lifecycle tests** for hide, reveal, reopen, and open-while-hidden behavior.
> 
> <sup>Reviewed by [Cursor Bugbot](https://cursor.com/bugbot) for commit b150cbbffb426840dc8ac7736514e3d174b09443. Bugbot is set up for automated code reviews on this repo. Configure [here](https://www.cursor.com/dashboard/bugbot).</sup>
<!-- /CURSOR_SUMMARY -->

<!-- Macroscope's pull request summary starts here -->
<!-- Macroscope will only edit the content between these invisible markers, and the markers themselves will not be visible in the GitHub rendered markdown. -->
<!-- If you delete either of the start / end markers from your PR's description, Macroscope will append its summary at the bottom of the description. -->
> [!NOTE]
> ### Fix composer menus staying open when their controls hide
> - Adds shared hook `useComposerMenuState` in [useComposerMenuState.ts](https://github.com/pingdotgg/t3code/pull/9541/files#diff-f8980c48d6e8eedb18cb30261c0cd36dcbcd598272fc1ffbf9e32de5a17731db) that reports menus closed while hidden, clears stored open state, and does not auto-reopen on visibility return
> - Adopts the hook across `ComposerFooterModeControls`, `CompactComposerControlsMenu`, and `TraitsPicker`, replacing local menu state and hidden-transition bookkeeping
> - `ChatComposer` now resets an open model picker at render time when relocated controls hide, and opening/toggling the picker while controls are hidden cancels pending blur, expands the composer, restores focus, then opens the picker
> - Adds unit tests in [useComposerMenuState.test.tsx](https://github.com/pingdotgg/t3code/pull/9541/files#diff-1b6e9f0ffca2042941df9a87b4b5f5288ad0298d638584544738a801720fce6f) covering popup close on hide, no open while hidden, and explicit reopen/close behavior
>
> <!-- Macroscope's review summary starts here -->
>
> <sup><a href="https://app.macroscope.com">Macroscope</a> summarized b150cbb.</sup>
> <!-- Macroscope's review summary ends here -->
>
<!-- Macroscope's pull request summary ends here -->
