> 🤖 This PR was modified by Coder Agents on behalf of Jake Howell.
Until we meet again.
## Stack
- #27636
- #27718
- #27719
- #27722
- #27723
- #27724
- #27728
- #27730
- #27732
- #27762
- #27763
- #27786
- #27787
- #27788
- #27789
- #27790
- #27791
- #27817
- #27820
- #28009
## Final removal (`c39b664`)
Removes the last of MUI and Emotion now that every surface has been
migrated:
- **Dependencies**: drops `@mui/material` and
`@emotion/{cache,css,react,styled}` from `package.json` /
`pnpm-lock.yaml`, and deletes the `@types/emotion.d.ts` and
`@types/mui.d.ts` module augmentations.
- **Theming**: replaces the Emotion `CacheProvider`, MUI `ThemeProvider`
/ `StyledEngineProvider`, and `CssBaseline` in `ThemeProvider` with a
lightweight `theme/context.tsx` that exposes `ThemeContextProvider` and
a `useTheme` hook.
- **Global styles**: moves the base `body` styles (background, text
color, font, antialiasing) that `CssBaseline` previously provided into
`index.css`, and drops the temporary MUI modal/popover scrollbar-gutter
workaround.
- **Cleanup**: removes the MUI → shadcn / Emotion → Tailwind migration
guidance from `site/AGENTS.md`, updates the Storybook `preview.tsx`, and
adjusts assorted components (`Command`, `Slider`, `Switch`, `Tabs`,
`SyntaxHighlighter`, timing charts) and theme files to consume the new
context instead of MUI/Emotion.
The descendant selector `[&_img]:size-icon-sm` in `menuItemClass` was
matching the `<img>` nested inside `<Avatar>` on the User settings row
of the mobile menu, pinning the avatar image to `1.125rem` instead of
letting it fill its Avatar container. The same descendant rule also
applied to nested SVGs.
Switch both rules to direct-child selectors (`[&>svg]`, `[&>img]`),
matching the symmetric `shrink-0` rules already next to them and the
same pattern used in `Button.tsx`. Menu items that contain nested
`<img>`s already set their own sizes explicitly (`w-4 h-4` in
`ProxySettingsSub`, `!size-3.5` in `WorkspacePill`), so this only stops
overriding components like `Avatar` that manage their own internal
sizing.
Also removes the now-dead `[&_img]:w-full [&_img]:h-full` workaround in
`UserDropdownContent.tsx` that fought the same bug per-item; that menu
item no longer renders an `<img>`, and the root fix makes the workaround
unnecessary anyway.
Refs
[CODAGT-552](https://linear.app/codercom/issue/CODAGT-552/mobile-menu-user-avatar-image-is-undersized-inside-its-container)
<details>
<summary>Investigation notes</summary>
**Repro (before):** mobile viewport, user has an image avatar set, open
hamburger menu, observe the avatar in the User settings row is smaller
than its bordered square.
**Root cause:** `site/src/components/DropdownMenu/menuClasses.ts` set
`[&_img]:size-icon-sm` using a Tailwind descendant selector (`_`). That
matches every `<img>` *inside* a menu item, including the one nested
inside `<AvatarPrimitive.Root>`. The Avatar's inner image is supposed to
be `aspect-square size-full object-contain` and inherit its size from
the Avatar root (`--avatar-default` ≈ 24px by default), but the
descendant rule overrides that and pins it to 1.125rem.
**Prior workaround:** `UserDropdownContent.tsx` previously added
`[&_img]:w-full [&_img]:h-full` per-item to fight the same bug on
desktop. That menu item no longer renders an `<img>`, so the override
was already dead code; removed here for hygiene.
**Safety check on the wider change:** every other menu item that
contains a nested `<img>` already sets its own sizes explicitly:
- `ProxySettingsSub` (`MobileMenu.tsx`): `<ExternalImage className="w-4
h-4" />`
- `WorkspacePill.tsx`: `[&_svg]:!size-3.5 [&_img]:!size-3.5` on the menu
content
Direct-child icons (`ChevronRightIcon`, `CircleHelpIcon`, `XIcon`,
lucide icons in `UserDropdownContent.tsx`) remain direct children of the
menu item, so `[&>svg]:size-icon-sm` keeps sizing them as before.
</details>
---
_Authored by Coder Agent on behalf of @tracyjohnsonux._
Adds a **Ports (n) >** submenu item to the `WorkspacePill` dropdown
shown in the `/agents` chat UI when a workspace is attached, sitting
alongside VS Code, Terminal, and other app items.
The submenu shows a **Listening Ports** section with clickable port
links that open in a new tab, a **Shared Ports** section with
sharing-level icons when any ports are shared, and a **Manage sharing**
footer link to the workspace detail page. Port data is fetched on a 5s
polling interval while the dropdown is open and only when the agent is
connected. The trigger is disabled when the workspace is not running.
Also adds `DropdownMenuSub`, `DropdownMenuSubTrigger`, and
`DropdownMenuSubContent` to the shared `DropdownMenu` component for use
here and future consumers.

<details>
<summary>Implementation notes</summary>
- `host` for port-forward URL construction comes from
`useProxy().proxy.preferredWildcardHostname`, the same source used by
`PortForwardButton` on the workspace detail page.
- Port queries are gated on `isOpen && agent.status === "connected"` so
no requests fire when the dropdown is closed or the agent is
disconnected.
- Shared ports that overlap with listening ports are deduplicated: they
only appear in the Shared section.
- Port sharing controls (create/update/delete) are intentionally
excluded to keep the agents toolbar lightweight; the "Manage sharing"
link surfaces the workspace page for that.
- The port count badge on the trigger (`Ports (n)`) uses the raw
listening-port count, matching the existing `PortForwardButton` behavior
on the workspace detail page.
</details>
> Generated by [Coder Agent](https://coder.com)
This branch tightens import hygiene and editor guidance to reduce
accidental use of legacy or discouraged patterns.
It also updates consumers too, by propagating the new `lucide-react`
import convention across the existing UI surfaces that reference those
icons.
- Updated `.vscode/settings.json` to prefer non-relative imports and
improve TypeScript auto-import behavior.
- Re-enabled and expanded Biome restricted-import enforcement in
`biome.jsonc` for migration guardrails.
- Added/used `lucide-react` `-Icon` naming conventions for clarity and
consistency.
- Updated consumers too across components, modules, and pages so the new
import rules are applied end-to-end.
This pull-request moves using to using the plain `radix-ui` package over
`@radix-ui/react-*` packages. Put simply, now we're not going to run
into issues with inconsistent radix dependencies. This will have no
effect to how the code is built, but will give us a single place to
import from.