Files
coder/site
Jaayden Halko eea427f288 fix: inline ports panel in workspace pill on mobile (#25042)
closes CODAGT-326

<img width="335" height="658" alt="Screenshot 2026-06-02 at 18 29 39"
src="https://github.com/user-attachments/assets/ee103047-67ad-403c-b67b-eb2147726db3"
/>


On viewports below the `md` Tailwind breakpoint, the agents-chat
workspace pill becomes full width via the existing
`mobile-full-width-dropdown` CSS hook. The `Ports (X)` item used a Radix
`DropdownMenuSub` which opened a flyout sub-content to the right of the
parent menu, so on mobile the sub-content had nowhere to render and
clipped off the right edge.

Mirror the inline sub-panel pattern already used by the agent chat input
plus menu: lift a `view: "main" | "ports"` state into `WorkspacePill`,
and on mobile swap the same dropdown's contents to a `Back` + ports list
panel instead of opening a flyout. Desktop keeps the existing flyout
sub-menu unchanged.

Closes
[CODAGT-326](https://linear.app/codercom/issue/CODAGT-326/port-forward-menu-is-cut-off-on-mobile-viewports).

Visual coverage is added via two new Storybook stories at the `mobile1`
(375 px) viewport: `MobilePortsInlinePanel` (full interaction +
assertions) and `MobilePortsInlinePanelOpen` (visual / Chromatic capture
stop point).

<details>
<summary>Implementation plan</summary>

### Why

The dropdown content already gets full viewport width on `< 768 px` via
the `mobile-full-width-dropdown` CSS hook in `site/src/index.css`. A
Radix `DropdownMenuSub` opens to the right of its trigger, so on mobile
it has no room and clips. Forcing the sub-content to also be full-width
would overlap the parent and break keyboard / focus flow. The existing
convention for nested menus on mobile in this codebase is the inline
sub-panel (see `plusMenuView` in `AgentChatInput.tsx`).

### `WorkspacePill.tsx`

- Add `view: "main" | "ports"` state and reset to `"main"` on close.
- Extract `usePortsData(workspace, agent, enabled)` and a shared
`PortsList` so desktop sub-content and mobile inline panel share one
renderer.
- Replace `PortsSubMenuItem` with `PortsMenuItem`, which uses
`useIsBelowMdViewport()` to render either:
- **Mobile:** a regular `DropdownMenuItem` whose `onSelect` calls
`event.preventDefault()` (keeps the dropdown open) and switches to the
inline view.
- **Desktop:** the existing `DropdownMenuSub` flyout, behavior
unchanged.
- Add `MobilePortsPanel` rendered inside the parent
`DropdownMenuContent` when `view === "ports"`. Includes a `Back` item
that returns to the main view.
- Add a small reactive `useIsBelowMdViewport()` hook around the existing
`isBelowMdViewport` helper.

</details>

> Created on behalf of @jaayden by Coder Agents.
2026-06-02 14:22:15 +01:00
..