authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-09-22 12:22:16-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-22 12:22:16-07:00
logd7736e06b6a4f8211f452908963fd23c4a04b2c4
tree3980323f0e24ea3231c583211a83541213722374
parent4c9fbf1f647c83179af1c2acd78eded04ef07fe8
signature Signed by PGP key B5690EEEBB952194

refactor(web): one minimum width for menus, three widths for popovers (#13035)

Co-authored-by: Claude Fable 5 <noreply@anthropic.com>

43 files changed, 69 insertions(+), 98 deletions(-)

apps/web/src/browser/BrowserDeviceToolbar.tsx+1-1
......@@ -197,7 +197,7 @@ export function BrowserDeviceToolbar({
197197 >
198198 <SelectValue />
199199 </SelectTrigger>
200 <SelectPopup align="start" alignItemWithTrigger={false} className="min-w-64">
200 <SelectPopup align="start" alignItemWithTrigger={false}>
201201 <SelectItem value={RESPONSIVE_VALUE}>Responsive</SelectItem>
202202 <SelectGroup>
203203 <SelectGroupLabel>Standard</SelectGroupLabel>
apps/web/src/components/BranchToolbar.tsx+1-1
......@@ -217,7 +217,7 @@ const MobileRunContextSelector = memo(function MobileRunContextSelector({
217217 {triggerContent}
218218 <ChevronDownIcon className="size-3 shrink-0 opacity-50" />
219219 </MenuTrigger>
220 <MenuPopup align="start" side="top" className="w-64" {...composerFloatingLayerProps}>
220 <MenuPopup align="start" side="top" {...composerFloatingLayerProps}>
221221 {showEnvironmentPicker && availableEnvironments && onEnvironmentChange ? (
222222 <>
223223 <MenuGroup>
apps/web/src/components/DiffPanel.tsx+2-2
......@@ -678,7 +678,7 @@ export default function DiffPanel({
678678 <span className="truncate">{selectedScopeLabel}</span>
679679 <ChevronDownIcon className="size-3.5 shrink-0 opacity-70" />
680680 </DropdownMenuTrigger>
681 <DropdownMenuContent align="start" className="w-60">
681 <DropdownMenuContent align="start">
682682 <DropdownMenuRadioGroup value={selectedScopeValue} onValueChange={selectScopeValue}>
683683 <DropdownMenuRadioItem value="unstaged" closeOnClick>
684684 <span>Working tree</span>
......@@ -692,7 +692,7 @@ export default function DiffPanel({
692692 </DropdownMenuRadioGroup>
693693 <DropdownMenuSub>
694694 <DropdownMenuSubTrigger>Turn</DropdownMenuSubTrigger>
695 <DropdownMenuSubContent className="w-64">
695 <DropdownMenuSubContent>
696696 <DropdownMenuRadioGroup value={selectedTurnValue} onValueChange={selectScopeValue}>
697697 {orderedTurnDiffSummaries.map((summary) => {
698698 const turnCount =
apps/web/src/components/GitActionsControl.tsx+2-4
......@@ -1793,7 +1793,7 @@ export default function GitActionsControl({
17931793 <SourceControlIcon className="size-4" />
17941794 <MenuItemLabel>Git actions</MenuItemLabel>
17951795 </MenuSubTrigger>
1796 <MenuSubPopup className="min-w-32 max-w-[calc(100vw-2rem)]">{gitItems}</MenuSubPopup>
1796 <MenuSubPopup>{gitItems}</MenuSubPopup>
17971797 </MenuSub>
17981798 </>
17991799 )
......@@ -1859,9 +1859,7 @@ export default function GitActionsControl({
18591859 >
18601860 <ChevronDownIcon aria-hidden="true" className="size-4" />
18611861 </MenuTrigger>
1862 <MenuPopup align="end" className="w-full">
1863 {gitItems}
1864 </MenuPopup>
1862 <MenuPopup align="end">{gitItems}</MenuPopup>
18651863 </Menu>
18661864 </Group>
18671865 )}
apps/web/src/components/LegacySidebar.tsx+1-1
......@@ -2747,7 +2747,7 @@ function ProjectSortMenu({
27472747 </TooltipTrigger>
27482748 <TooltipPopup side="right">Sidebar options</TooltipPopup>
27492749 </Tooltip>
2750 <MenuPopup align="end" side="bottom" className="min-w-52">
2750 <MenuPopup align="end" side="bottom">
27512751 <MenuGroup>
27522752 <div className="px-2 py-1 sm:text-xs font-medium text-muted-foreground">
27532753 Sort projects
apps/web/src/components/ProjectScriptsControl.tsx+1-3
......@@ -258,9 +258,7 @@ export default function ProjectScriptsControl({
258258 <ScriptIcon icon="play" className="size-4" />
259259 <MenuItemLabel>Project actions</MenuItemLabel>
260260 </MenuSubTrigger>
261 <MenuSubPopup className="min-w-32 max-w-[calc(100vw-2rem)]">
262 {scriptItems}
263 </MenuSubPopup>
261 <MenuSubPopup>{scriptItems}</MenuSubPopup>
264262 </MenuSub>
265263 ) : (
266264 <MenuItem
apps/web/src/components/RightPanelTabs.tsx+2-8
......@@ -567,12 +567,7 @@ function RightPanelEmptyState(props: {
567567 >
568568 <ChevronDown className="size-3.5" />
569569 </MenuTrigger>
570 <MenuPopup
571 align="end"
572 side="bottom"
573 sideOffset={6}
574 className="min-w-40 max-w-56"
575 >
570 <MenuPopup align="end" side="bottom" sideOffset={6} className="max-w-56">
576571 {props.browserProfiles.map((profile) => (
577572 <MenuItem
578573 key={profile.id}
......@@ -1273,7 +1268,6 @@ export function RightPanelTabs(props: RightPanelTabsProps) {
12731268 align="start"
12741269 side="bottom"
12751270 sideOffset={6}
1276 className="min-w-44"
12771271 onKeyDownCapture={handleAddSurfaceMenuKeyDown}
12781272 >
12791273 {addSurfaceActions.map((action) => {
......@@ -1314,7 +1308,7 @@ export function RightPanelTabs(props: RightPanelTabsProps) {
13141308 and run to 48 characters, which would otherwise widen
13151309 the popup to fit-content and wrap.
13161310 */}
1317 <MenuSubPopup className="min-w-40 max-w-56">
1311 <MenuSubPopup className="max-w-56">
13181312 {browserProfiles.map((profile) => (
13191313 <MenuItem
13201314 key={profile.id}
apps/web/src/components/Sidebar.tsx+1-1
......@@ -475,7 +475,7 @@ function SnoozePopoverButton(props: {
475475 </TooltipTrigger>
476476 <TooltipPopup>Snooze thread</TooltipPopup>
477477 </Tooltip>
478 <PopoverPopup side="bottom" align="end" className="w-56" viewportClassName="p-1">
478 <PopoverPopup side="bottom" align="end" width="sm" viewportClassName="p-1">
479479 {presets.map((preset) => (
480480 <button
481481 key={preset.id}
apps/web/src/components/chat/AssistantCitationChip.tsx+1-1
......@@ -186,7 +186,7 @@ export function AssistantCitationChip({
186186 return false;
187187 }}
188188 aria-label="Edit citation comment"
189 className="w-72 max-w-[calc(100vw-1rem)]"
189 width="md"
190190 viewportClassName="p-3"
191191 onPointerDown={(event) => event.stopPropagation()}
192192 >
apps/web/src/components/chat/ChatHeader.tsx-1
......@@ -516,7 +516,6 @@ export const ChatHeader = memo(function ChatHeader({
516516 keepMounted
517517 aria-label="Header actions"
518518 align="end"
519 className="min-w-56 max-w-[calc(100vw-2rem)]"
520519 finalFocus={actionsCollapsed ? undefined : false}
521520 >
522521 <div ref={mountMenuActions} className="contents" />
apps/web/src/components/chat/ComposerPendingApprovalActions.tsx+1-6
......@@ -73,12 +73,7 @@ export const ComposerPendingApprovalActions = memo(function ComposerPendingAppro
7373 >
7474 <EllipsisIcon />
7575 </MenuTrigger>
76 <MenuPopup
77 {...composerFloatingLayerProps}
78 side="top"
79 align="end"
80 className="w-56 max-w-[calc(100vw-2rem)]"
81 >
76 <MenuPopup {...composerFloatingLayerProps} side="top" align="end">
8277 {moreOptions.map((option) => {
8378 const item = (
8479 <MenuItem
apps/web/src/components/chat/ContextWindowMeter.tsx+2-1
......@@ -89,7 +89,8 @@ export function ContextWindowMeter(props: {
8989 side="top"
9090 align="end"
9191 viewportClassName="p-0"
92 className="w-64 max-w-none text-left whitespace-normal"
92 width="sm"
93 className="text-left whitespace-normal"
9394 >
9495 <div className="flex flex-col gap-2 p-[var(--floating-content-inset)]">
9596 <div className="flex items-center justify-between gap-3">
apps/web/src/components/chat/DraftHeroHeadline.tsx+1-1
......@@ -149,7 +149,7 @@ export function DraftHeroHeadline({
149149 <TooltipPopup side="top">{activeProjectDisplayName}</TooltipPopup>
150150 ) : null}
151151 </Tooltip>
152 <MenuPopup align="center" className="max-h-80 min-w-40! w-max max-w-64 overflow-y-auto">
152 <MenuPopup align="center" className="max-h-80 overflow-y-auto">
153153 <MenuRadioGroup
154154 value={activeProjectKey}
155155 onValueChange={(value) => {
apps/web/src/components/chat/MessagesTimeline.tsx+2-1
......@@ -2571,7 +2571,8 @@ function BackgroundWorktreeSetupChip({ snapshot }: { snapshot: WorktreeSetupSnap
25712571 <PopoverPopup
25722572 side="bottom"
25732573 align="end"
2574 className="surface-glass! w-[28rem] max-w-[calc(100vw-2rem)]"
2574 width="lg"
2575 className="surface-glass!"
25752576 viewportClassName="py-3 [--viewport-inline-padding:--spacing(3)]"
25762577 >
25772578 <WorktreeSetupCard
apps/web/src/components/chat/OpenInPicker.tsx+1-1
......@@ -343,7 +343,7 @@ export const OpenInPicker = memo(function OpenInPicker({
343343 <SquareArrowOutUpRightIcon className="size-4" />
344344 <MenuItemLabel>Open in…</MenuItemLabel>
345345 </MenuSubTrigger>
346 <MenuSubPopup className="min-w-32 max-w-[calc(100vw-2rem)]">{editorItems}</MenuSubPopup>
346 <MenuSubPopup>{editorItems}</MenuSubPopup>
347347 </MenuSub>
348348 </>
349349 );
apps/web/src/components/chat/SnapShotAttachmentDetails.tsx+1-1
......@@ -166,7 +166,7 @@ export function SnapShotContentsButton({
166166 <PopoverPopup
167167 side={side}
168168 align="center"
169 className="w-[min(24rem,calc(100vw-2rem))]"
169 width="md"
170170 viewportClassName="max-h-[min(28rem,70vh)]"
171171 >
172172 <div className="space-y-2">
apps/web/src/components/chat/useAutoBalanceUpdateBanner.tsx+1-1
......@@ -100,7 +100,7 @@ export function useAutoBalanceUpdateBanner(
100100 >
101101 {title}
102102 </PopoverTrigger>
103 <PopoverPopup side="top" align="start" className="w-80 max-w-[calc(100vw-2rem)]">
103 <PopoverPopup side="top" align="start" width="md">
104104 <div className="space-y-3 text-xs">
105105 {machines.map((machine) => (
106106 <div key={machine.environmentId} className="space-y-1">
apps/web/src/components/contextChipParts.tsx+2-1
......@@ -102,7 +102,8 @@ export function ContextChipPopover(props: {
102102 </PopoverTrigger>
103103 <PopoverPopup
104104 side="top"
105 className={cn("w-[min(36rem,calc(100vw-2rem))]", props.popupClassName)}
105 width="lg"
106 className={props.popupClassName}
106107 viewportClassName={cn("overflow-x-auto p-2", props.viewportClassName)}
107108 >
108109 <PopoverTitle className="sr-only">{props.accessibleLabel}</PopoverTitle>
apps/web/src/components/device/DeviceToolVersions.tsx+1-1
......@@ -44,7 +44,7 @@ export function DeviceToolVersions({
4444 ? "Versions unavailable"
4545 : "Versions"}
4646 </PopoverTrigger>
47 <PopoverPopup align="end" className="w-80">
47 <PopoverPopup align="end" width="md">
4848 <PopoverTitle>{kind ? label : "Device tools"}</PopoverTitle>
4949 {tools ? (
5050 <div className="mt-4 divide-y divide-border/50">
apps/web/src/components/files/FileBreadcrumbs.tsx-1
......@@ -104,7 +104,6 @@ function BreadcrumbMenuContent(props: {
104104 <MenuPopup
105105 align="start"
106106 side="bottom"
107 className="w-max min-w-32 max-w-[min(19rem,var(--available-width))]"
108107 onKeyDown={(event) => {
109108 if (event.key !== "ArrowLeft" || !canGoBack || parentPath === null) return;
110109 event.preventDefault();
apps/web/src/components/preview/PreviewMoreMenu.tsx+2-2
......@@ -108,7 +108,7 @@ export function PreviewMoreMenu({
108108 </TooltipTrigger>
109109 <TooltipPopup>More</TooltipPopup>
110110 </Tooltip>
111 <MenuPopup align="end" sideOffset={6} className="min-w-56">
111 <MenuPopup align="end" sideOffset={6}>
112112 <MenuItem onClick={callTab(bridge.hardReload)} disabled={tabDisabled}>
113113 Hard reload
114114 </MenuItem>
......@@ -125,7 +125,7 @@ export function PreviewMoreMenu({
125125 </MenuItem>
126126 <MenuSub>
127127 <MenuSubTrigger disabled={tabDisabled}>Appearance</MenuSubTrigger>
128 <MenuSubPopup className="min-w-32">
128 <MenuSubPopup>
129129 <MenuRadioGroup
130130 value={colorScheme}
131131 onValueChange={(value) => {
apps/web/src/components/pullRequest/PullRequestChecksPopover.tsx+1-1
......@@ -147,7 +147,7 @@ export function PullRequestChecksPopover({
147147 >
148148 <presentation.Icon aria-hidden className={cn("size-3.5", presentation.toneClassName)} />
149149 </PopoverTrigger>
150 <PopoverPopup align="start" className="w-80 max-w-full" side="bottom">
150 <PopoverPopup align="start" width="md" side="bottom">
151151 <p className="mb-2 font-medium text-sm">{presentation.label}</p>
152152 {summary === null ? null : <p className="mb-2 text-muted-foreground text-xs">{summary}</p>}
153153 {stale ? (
apps/web/src/components/pullRequest/PullRequestCodeTab.tsx+1-1
......@@ -1077,7 +1077,7 @@ function PullRequestCodeTab({
10771077 <span className="truncate">{scopeLabel}</span>
10781078 <ChevronDownIcon className="size-3.5 shrink-0 opacity-70" />
10791079 </DropdownMenuTrigger>
1080 <DropdownMenuContent align="start" className="w-80">
1080 <DropdownMenuContent align="start">
10811081 <DropdownMenuRadioGroup
10821082 value={commit ?? "all"}
10831083 onValueChange={(value) => onSelectedCommitChange(value === "all" ? null : value)}
apps/web/src/components/pullRequest/PullRequestComposer.tsx+1-1
......@@ -110,7 +110,7 @@ export function PullRequestComposer({
110110 side="top"
111111 align="end"
112112 sideOffset={8}
113 className="w-96 max-w-[calc(100vw-2rem)]"
113 width="lg"
114114 initialFocus={mode === "review" ? reviewRef : commentRef}
115115 aria-label="Pull request composer"
116116 >
apps/web/src/components/pullRequest/PullRequestDetailPanel.tsx+2-2
......@@ -1569,7 +1569,7 @@ export function PullRequestDetailPanel({
15691569 />
15701570 <TooltipPopup>Check out this pull request</TooltipPopup>
15711571 </Tooltip>
1572 <MenuPopup align="end" side="bottom" className="min-w-72">
1572 <MenuPopup align="end" side="bottom">
15731573 <MenuItem onClick={() => startCheckout("worktree")}>
15741574 <GitBranchIcon className="mt-1 size-3.5 shrink-0 self-start" />
15751575 <span className="flex min-w-0 flex-col">
......@@ -2020,7 +2020,7 @@ export function PullRequestDetailPanel({
20202020 {refreshing ? "Refreshing pull request" : "More pull request actions"}
20212021 </TooltipPopup>
20222022 </Tooltip>
2023 <MenuPopup align="end" side="bottom" className="min-w-72">
2023 <MenuPopup align="end" side="bottom">
20242024 <PullRequestThreadLinks
20252025 display="menu-item"
20262026 environmentId={environmentId}
apps/web/src/components/pullRequest/PullRequestListFilters.tsx+4-4
......@@ -232,7 +232,7 @@ function PullRequestFilterRadioSubmenu<Value extends string>({
232232 {current.label}
233233 </span>
234234 </MenuSubTrigger>
235 <MenuSubPopup className="min-w-56">
235 <MenuSubPopup>
236236 <PullRequestFilterRadioGroup
237237 label={label}
238238 value={value}
......@@ -277,7 +277,7 @@ function PullRequestAuthorFilter({
277277 {value ?? "Anyone"}
278278 </span>
279279 </MenuSubTrigger>
280 <MenuSubPopup className="w-80">
280 <MenuSubPopup>
281281 <div className="p-1 pb-2">
282282 <InputGroup>
283283 <InputGroupAddon>
......@@ -346,7 +346,7 @@ function PullRequestLabelFilter({
346346 {value.length === 0 ? "Any" : `${value.length} selected`}
347347 </span>
348348 </MenuSubTrigger>
349 <MenuSubPopup className="w-72">
349 <MenuSubPopup>
350350 {visible.length === 0 ? (
351351 <MenuItem disabled>No labels in this view</MenuItem>
352352 ) : (
......@@ -509,7 +509,7 @@ export function PullRequestFiltersMenu({
509509 </span>
510510 ) : null}
511511 </MenuTrigger>
512 <MenuPopup align="end" side="bottom" className="w-56">
512 <MenuPopup align="end" side="bottom">
513513 <PullRequestFilterRadioSubmenu
514514 label="State"
515515 value={state}
apps/web/src/components/pullRequest/PullRequestReactions.tsx+1-1
......@@ -134,7 +134,7 @@ export function PullRequestReactionBar({
134134 >
135135 <SmilePlusIcon aria-hidden className="size-3.5" />
136136 </PopoverTrigger>
137 <PopoverPopup align="start" className="w-auto" side="top" viewportClassName="py-2">
137 <PopoverPopup align="start" side="top" viewportClassName="py-2">
138138 <div className="flex items-center gap-0.5">
139139 {PULL_REQUEST_REACTION_ORDER.map((content) => {
140140 const reacted =
apps/web/src/components/pullRequest/PullRequestStackMenu.tsx+1-1
......@@ -145,7 +145,7 @@ export function PullRequestStackMenu({
145145 {notice ? ` · ${notice}` : null}
146146 </TooltipPopup>
147147 </Tooltip>
148 <MenuPopup align="start" className="w-96 max-w-[calc(100vw-2rem)]">
148 <MenuPopup align="start">
149149 <MenuGroup>
150150 <PullRequestStackHeader number={stack.number} notice={notice} stale={!!onRetry} />
151151 {onRetry ? <MenuItem onClick={onRetry}>Retry stack refresh</MenuItem> : null}
apps/web/src/components/pullRequest/PullRequestStackPopover.tsx-1
......@@ -85,7 +85,6 @@ export function PullRequestStackPopover({
8585 </Tooltip>
8686 <MenuPopup
8787 align="start"
88 className="w-96 max-w-[calc(100vw-2rem)]"
8988 onClick={(event) => event.stopPropagation()}
9089 onKeyDown={(event) => event.stopPropagation()}
9190 >
apps/web/src/components/settings/ConnectionsSettings.tsx+2-2
......@@ -1631,7 +1631,7 @@ function SavedBackendListRow({
16311631 >
16321632 <EllipsisIcon className="size-3.5" />
16331633 </MenuTrigger>
1634 <MenuPopup align="end" className="min-w-52">
1634 <MenuPopup align="end">
16351635 <EnvironmentIconMenu
16361636 environmentId={environmentId}
16371637 serverConfig={environment.serverConfig}
......@@ -3295,7 +3295,7 @@ export function ConnectionsSettings() {
32953295 >
32963296 <EllipsisIcon className="size-3.5" />
32973297 </MenuTrigger>
3298 <MenuPopup align="end" className="min-w-52">
3298 <MenuPopup align="end">
32993299 <EnvironmentIconMenu
33003300 environmentId={primaryEnvironmentId}
33013301 serverConfig={primaryServerConfig}
apps/web/src/components/settings/EnvironmentIconPicker.tsx+1-1
......@@ -99,7 +99,7 @@ export function EnvironmentIconMenu({
9999 <EnvironmentMachineIcon kind={resolved} />
100100 Icon
101101 </MenuSubTrigger>
102 <MenuSubPopup className="min-w-44">
102 <MenuSubPopup>
103103 {lock !== null ? (
104104 <>
105105 <MenuItem disabled className="whitespace-normal text-xs">
apps/web/src/components/settings/IntegrationsSettings.tsx+3-3
......@@ -304,7 +304,7 @@ function BrowserViewportSetting({ disabled }: { readonly disabled: boolean }) {
304304 >
305305 <SelectValue>{viewportSelectLabel(viewport)}</SelectValue>
306306 </SelectTrigger>
307 <SelectPopup align="end" alignItemWithTrigger={false} className="min-w-64">
307 <SelectPopup align="end" alignItemWithTrigger={false}>
308308 <SelectItem value={FILL_VALUE}>Fill panel</SelectItem>
309309 <SelectItem value={RESPONSIVE_VALUE}>Responsive</SelectItem>
310310 <SelectGroup>
......@@ -1183,7 +1183,7 @@ function BrowserProfilesSetting({ disabled }: { readonly disabled: boolean }) {
11831183 <PlusIcon />
11841184 Add profile
11851185 </MenuTrigger>
1186 <MenuPopup align="end" className="min-w-56">
1186 <MenuPopup align="end">
11871187 <MenuItem
11881188 disabled={!settingsHydrated || atProfileLimit}
11891189 onClick={() => createProfile("New profile")}
......@@ -1301,7 +1301,7 @@ function BrowserProfilesSetting({ disabled }: { readonly disabled: boolean }) {
13011301 >
13021302 <MoreVertical />
13031303 </MenuTrigger>
1304 <MenuPopup align="end" className="min-w-44">
1304 <MenuPopup align="end">
13051305 <MenuItem
13061306 disabled={!settingsHydrated || isDefault}
13071307 onClick={() => {
apps/web/src/components/settings/KeybindingsSettings.tsx+4-18
......@@ -324,12 +324,7 @@ function WhenVariableSelect({
324324 <UnknownWhenVariableWarning identifiers={unknownIdentifiers} focusable={false} />
325325 ) : null}
326326 </SelectTrigger>
327 <SelectContent
328 alignItemWithTrigger={false}
329 matchTriggerWidth={false}
330 popupClassName="w-fit"
331 className="max-h-72 w-fit min-w-44"
332 >
327 <SelectContent alignItemWithTrigger={false} matchTriggerWidth={false} className="max-h-72">
333328 {options.map((option) => (
334329 <SelectItem key={option} value={option} className="min-h-7 w-full py-1 text-[12px]">
335330 <span className="truncate">{option}</span>
......@@ -534,12 +529,7 @@ function WhenExpressionNodeEditor({
534529 <SelectTrigger size="compact" className="w-24">
535530 <SelectValue />
536531 </SelectTrigger>
537 <SelectContent
538 alignItemWithTrigger={false}
539 matchTriggerWidth={false}
540 popupClassName="w-fit"
541 className="w-fit min-w-24"
542 >
532 <SelectContent alignItemWithTrigger={false} matchTriggerWidth={false}>
543533 <SelectItem value="and" className="min-h-7 py-1 text-[12px]">
544534 and
545535 </SelectItem>
......@@ -956,7 +946,7 @@ function KeybindingRowMenu({
956946 >
957947 <EllipsisIcon className="size-3.5" />
958948 </MenuTrigger>
959 <MenuPopup align="end" className="min-w-36">
949 <MenuPopup align="end">
960950 {canReset ? (
961951 <MenuItem disabled={isSaving} onClick={() => onReset(row)}>
962952 Reset to default
......@@ -1153,11 +1143,7 @@ function NewKeybindingCommandSelect({
11531143 <SelectTrigger size="sm" className={className}>
11541144 <SelectValue placeholder="Command" />
11551145 </SelectTrigger>
1156 <SelectContent
1157 alignItemWithTrigger={false}
1158 matchTriggerWidth={false}
1159 className="max-h-72 w-fit min-w-56"
1160 >
1146 <SelectContent alignItemWithTrigger={false} matchTriggerWidth={false} className="max-h-72">
11611147 {commandOptions.map((command) => (
11621148 <SelectItem key={command} value={command} className="min-h-7 w-full py-1 text-[12px]">
11631149 <span className="truncate">{commandLabel(command)}</span>
apps/web/src/components/settings/ProjectActionsSettings.tsx+1-1
......@@ -156,7 +156,7 @@ export function ProjectActionsSettings() {
156156 Import scripts
157157 <ChevronDownIcon className="size-3.5" />
158158 </MenuTrigger>
159 <MenuPopup align="end" className="w-72">
159 <MenuPopup align="end">
160160 <MenuGroup>
161161 <MenuGroupLabel>Import from t3.json</MenuGroupLabel>
162162 <p className="px-2 pb-2 text-pretty text-sm text-muted-foreground">
apps/web/src/components/settings/ProviderInstanceCard.tsx+1-5
......@@ -708,11 +708,7 @@ export function ProviderInstanceCard({
708708 </Button>
709709 }
710710 />
711 <PopoverPopup
712 side="bottom"
713 align="end"
714 className="w-[min(21rem,calc(100vw-1.5rem))] [--popup-width:min(21rem,calc(100vw-1.5rem))]"
715 >
711 <PopoverPopup side="bottom" align="end" width="md">
716712 <div className="grid min-w-0 gap-3">
717713 <div className="grid gap-0.5">
718714 <p className="text-[13px] font-semibold leading-tight text-foreground">
apps/web/src/components/settings/SettingInheritance.tsx+1-1
......@@ -225,7 +225,7 @@ export function SettingInheritance({
225225 </Tooltip>
226226 <PopoverPopup
227227 align="start"
228 className="w-72 max-w-[calc(100vw-2rem)]"
228 width="md"
229229 viewportClassName="p-0 [--viewport-inline-padding:0px]"
230230 >
231231 <div className="divide-y divide-border/60">
apps/web/src/components/settings/SettingsBreadcrumb.tsx+1-3
......@@ -119,9 +119,7 @@ function ScopeMenu({
119119 {icon}
120120 <span className="min-w-0 truncate">{label}</span>
121121 </MenuTrigger>
122 <MenuPopup align="start" className="w-64 max-w-[calc(100vw-2rem)]">
123 {children}
124 </MenuPopup>
122 <MenuPopup align="start">{children}</MenuPopup>
125123 </Menu>
126124 );
127125}
apps/web/src/components/sidebar/SidebarUpdatePill.tsx+1-1
......@@ -393,7 +393,7 @@ function SidebarUpdateControl() {
393393 <PopoverPopup
394394 align="center"
395395 aria-label="Nightly update release notes"
396 className="max-w-none text-balance shadow-xl shadow-black/25"
396 className="text-balance shadow-xl shadow-black/25"
397397 initialFocus={false}
398398 onKeyDownCapture={(event) => {
399399 if (
apps/web/src/components/ui/menu.tsx+2-8
......@@ -35,13 +35,6 @@ function MenuPopup({
3535 anchor?: MenuPrimitive.Positioner.Props["anchor"];
3636 keepMounted?: boolean;
3737}) {
38 const hasExplicitWidthClass =
39 typeof className === "string" &&
40 className.split(/\s+/).some((classToken) => {
41 const utility = classToken.split(":").at(-1) ?? classToken;
42 return /^(?:min-|max-)?w-/.test(utility);
43 });
44
4538 return (
4639 <MenuPrimitive.Portal keepMounted={keepMounted}>
4740 <MenuPrimitive.Positioner
......@@ -60,7 +53,8 @@ function MenuPopup({
6053 // the Review panel header). Drag hit-testing ignores z-index, so
6154 // the topmost row would stay unhoverable without this opt-out.
6255 "[-webkit-app-region:no-drag]",
63 !hasExplicitWidthClass && "min-w-32",
56 // Menus size to their content from one minimum, never past the viewport.
57 "min-w-[min(10rem,calc(100vw-2rem))] max-w-[calc(100vw-2rem)]",
6458 className,
6559 )}
6660 data-slot="menu-popup"
apps/web/src/components/ui/popover.tsx+12
......@@ -16,10 +16,20 @@ function PopoverTrigger({ className, children, ...props }: PopoverPrimitive.Trig
1616 );
1717}
1818
19// Popovers hold prose and forms, so a width is fixed rather than a minimum,
20// and every width is capped to the viewport.
21const popoverPopupWidthClassName = {
22 auto: "",
23 sm: "w-64",
24 md: "w-80",
25 lg: "w-96",
26} as const;
27
1928function PopoverPopup({
2029 children,
2130 className,
2231 viewportClassName,
32 width = "auto",
2333 side = "bottom",
2434 align = "center",
2535 sideOffset = 4,
......@@ -37,6 +47,7 @@ function PopoverPopup({
3747 tooltipStyle?: boolean;
3848 keepMounted?: PopoverPrimitive.Portal.Props["keepMounted"];
3949 anchor?: PopoverPrimitive.Positioner.Props["anchor"];
50 width?: keyof typeof popoverPopupWidthClassName;
4051}) {
4152 // Viewport rekeys its children when the active trigger clears on close. Persistent
4253 // single-trigger forms need a stable container to retain drafts and submit guards.
......@@ -59,6 +70,7 @@ function PopoverPopup({
5970 "w-fit text-balance rounded-md text-xs shadow-md/5 before:rounded-[calc(var(--radius-md)-1px)]",
6071 !tooltipStyle &&
6172 "shadow-[0_16px_40px_-18px_rgb(0_0_0/55%)] dark:shadow-[0_18px_44px_-18px_rgb(0_0_0/80%)]",
73 width !== "auto" && ["max-w-[calc(100vw-2rem)]", popoverPopupWidthClassName[width]],
6274 className,
6375 )}
6476 data-slot="popover-popup"
apps/web/src/components/usage/UsagePage.tsx+1-1
......@@ -762,7 +762,7 @@ function UsageEnvironmentFilter({
762762 )}
763763 </span>
764764 </MenuTrigger>
765 <MenuPopup align="start" className="w-80 max-w-[calc(100vw-2rem)]">
765 <MenuPopup align="start">
766766 <MenuCheckboxItem
767767 checked={allSelected}
768768 closeOnClick={false}
apps/web/src/components/usage/UsagePriceOverrides.tsx+1-1
......@@ -269,7 +269,7 @@ export function UsagePriceOverrides({
269269 </span>
270270 <ChevronDownIcon className="size-3.5 shrink-0" aria-hidden />
271271 </MenuTrigger>
272 <MenuPopup align="start" className="w-80 max-w-[calc(100vw-2rem)]">
272 <MenuPopup align="start">
273273 <MenuCheckboxItem
274274 checked={selectedIds === null}
275275 closeOnClick={false}
apps/web/src/routes/_chat.pull-requests.tsx+1-1
......@@ -2259,7 +2259,7 @@ function CompactFilterMenu<Value extends string>({
22592259 </>
22602260 )}
22612261 </MenuTrigger>
2262 <MenuPopup align="start" side="bottom" className="min-w-40">
2262 <MenuPopup align="start" side="bottom">
22632263 <MenuRadioGroup value={value} onValueChange={(next) => onChange(next as Value)}>
22642264 {options.map((option) => {
22652265 const item = (