authorgravatar for 97427849+flamboh@users.noreply.github.comoliver <97427849+flamboh@users.noreply.github.com> 2026-09-16 15:07:08-07:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-09-16 19:07:08-03:00
logdda44ed33280106aa46612fd9e1f1475bb5a0712
treeceff4a641e602cb98ad089f103b716cb08f9857b
parent01e64193d9d2abfae8f7b3d20f66824e38397bd6
signature Signed by PGP key B5690EEEBB952194

fix(web): show tooltips for composer environment and workspace controls (#11787)


3 files changed, 132 insertions(+), 90 deletions(-)

apps/web/src/components/BranchToolbar.tsx+24-10
......@@ -53,6 +53,7 @@ import {
5353 MenuTrigger,
5454} from "./ui/menu";
5555import { Separator } from "./ui/separator";
56import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
5657import { ComposerSurface } from "./chat/ComposerSurface";
5758import { useComposerMenuProps } from "./chat/composerEventScope";
5859import { measureRestingComposerControls } from "./chat/restingComposerControlsMeasurement";
......@@ -137,22 +138,35 @@ const MobileRunContextSelector = memo(function MobileRunContextSelector({
137138 ? resolveEnvModeLabel("worktree")
138139 : resolveCurrentWorkspaceLabel(activeWorktreePath);
139140 const isLocked = envLocked || envModeLocked;
141 const workspaceIcon = (
142 <Tooltip>
143 <TooltipTrigger render={<span className="inline-flex shrink-0" />}>
144 <WorkspaceIcon className={cn("size-3 shrink-0", showEnvironmentIndicator && "mx-0!")} />
145 </TooltipTrigger>
146 <TooltipPopup>{workspaceLabel}</TooltipPopup>
147 </Tooltip>
148 );
140149 const icon = showEnvironmentIndicator ? (
141150 // Button's base styles apply `-mx-0.5` to descendant SVGs, which eats 4px
142151 // out of whatever gap we set. mx-0! cancels that so gap-0.5 reads as 2px.
143152 <span className="inline-flex shrink-0 items-center gap-0.5">
144 {autoEnvironmentLabel ? (
145 <ScaleIcon className="size-3 shrink-0 mx-0!" aria-hidden="true" />
146 ) : (
147 <EnvironmentMachineIcon
148 kind={activeEnvironment?.machine ?? "server"}
149 className="size-3 shrink-0 mx-0!"
150 />
151 )}
152 <WorkspaceIcon className="size-3 shrink-0 mx-0!" />
153 <Tooltip>
154 <TooltipTrigger render={<span className="inline-flex shrink-0" />}>
155 {autoEnvironmentLabel ? (
156 <ScaleIcon className="size-3 shrink-0 mx-0!" aria-hidden="true" />
157 ) : (
158 <EnvironmentMachineIcon
159 kind={activeEnvironment?.machine ?? "server"}
160 className="size-3 shrink-0 mx-0!"
161 />
162 )}
163 </TooltipTrigger>
164 <TooltipPopup>{autoEnvironmentLabel ?? activeEnvironment?.label ?? "Run on"}</TooltipPopup>
165 </Tooltip>
166 {workspaceIcon}
153167 </span>
154168 ) : (
155 <WorkspaceIcon className="size-3 shrink-0" />
169 workspaceIcon
156170 );
157171 const triggerContent = (
158172 <>
apps/web/src/components/BranchToolbarEnvModeSelector.tsx+56-40
......@@ -17,6 +17,7 @@ import {
1717 SelectTrigger,
1818 SelectValue,
1919} from "./ui/select";
20import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
2021
2122const PREVIOUS_WORKTREE_SELECT_VALUE = "previous-worktree";
2223
......@@ -52,27 +53,31 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
5253
5354 if (envLocked) {
5455 return (
55 <span
56 className="inline-flex h-7 min-w-0 items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"
57 data-composer-context-control
58 >
59 {activeWorktreePath ? (
60 <FolderGitIcon className="size-3 shrink-0" />
61 ) : (
62 <FolderIcon className="size-3 shrink-0" />
63 )}
64 <span
65 data-composer-label
66 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
56 <Tooltip>
57 <TooltipTrigger
58 render={<span />}
59 className="inline-flex h-7 min-w-0 items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"
60 data-composer-context-control
6761 >
62 {activeWorktreePath ? (
63 <FolderGitIcon className="size-3 shrink-0" />
64 ) : (
65 <FolderIcon className="size-3 shrink-0" />
66 )}
6867 <span
69 data-composer-label-motion
70 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
68 data-composer-label
69 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
7170 >
72 {resolveLockedWorkspaceLabel(activeWorktreePath)}
71 <span
72 data-composer-label-motion
73 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
74 >
75 {resolveLockedWorkspaceLabel(activeWorktreePath)}
76 </span>
7377 </span>
74 </span>
75 </span>
78 </TooltipTrigger>
79 <TooltipPopup>{resolveLockedWorkspaceLabel(activeWorktreePath)}</TooltipPopup>
80 </Tooltip>
7681 );
7782 }
7883
......@@ -89,33 +94,44 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
8994 }}
9095 items={envModeItems}
9196 >
92 <SelectTrigger
93 variant="ghost"
94 size="xs"
95 className="min-w-0 shrink font-normal text-xs!"
96 aria-label="Workspace"
97 data-composer-shortcut="composer.workspace"
98 data-composer-context-control
99 >
100 {effectiveEnvMode === "worktree" ? (
101 <FolderGit2Icon className="size-3" />
102 ) : activeWorktreePath ? (
103 <FolderGitIcon className="size-3" />
104 ) : (
105 <FolderIcon className="size-3" />
106 )}
107 <span
108 data-composer-label
109 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
97 <Tooltip>
98 <TooltipTrigger
99 render={
100 <SelectTrigger
101 variant="ghost"
102 size="xs"
103 className="min-w-0 shrink font-normal text-xs!"
104 aria-label="Workspace"
105 data-composer-shortcut="composer.workspace"
106 data-composer-context-control
107 />
108 }
110109 >
110 {effectiveEnvMode === "worktree" ? (
111 <FolderGit2Icon className="size-3" />
112 ) : activeWorktreePath ? (
113 <FolderGitIcon className="size-3" />
114 ) : (
115 <FolderIcon className="size-3" />
116 )}
111117 <span
112 data-composer-label-motion
113 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
118 data-composer-label
119 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
114120 >
115 <SelectValue />
121 <span
122 data-composer-label-motion
123 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
124 >
125 <SelectValue />
126 </span>
116127 </span>
117 </span>
118 </SelectTrigger>
128 </TooltipTrigger>
129 <TooltipPopup>
130 {effectiveEnvMode === "worktree"
131 ? resolveEnvModeLabel("worktree")
132 : resolveCurrentWorkspaceLabel(activeWorktreePath)}
133 </TooltipPopup>
134 </Tooltip>
119135 <SelectPopup alignItemWithTrigger={false} {...composerFloatingLayerProps}>
120136 <SelectGroup>
121137 <SelectGroupLabel>Workspace</SelectGroupLabel>
apps/web/src/components/BranchToolbarEnvironmentSelector.tsx+52-40
......@@ -14,6 +14,7 @@ import {
1414 SelectTrigger,
1515 SelectValue,
1616} from "./ui/select";
17import { Tooltip, TooltipPopup, TooltipTrigger } from "./ui/tooltip";
1718
1819interface BranchToolbarEnvironmentSelectorProps {
1920 autoEnvironmentLabel?: string | undefined;
......@@ -59,26 +60,30 @@ export const BranchToolbarEnvironmentSelector = memo(function BranchToolbarEnvir
5960 // only thing in the strip.
6061 if (envLocked || onEnvironmentChange === undefined) {
6162 return (
62 <span
63 className="inline-flex h-7 min-w-0 max-w-full items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"
64 data-composer-context-control
65 >
66 <EnvironmentMachineIcon
67 kind={activeEnvironment?.machine ?? "server"}
68 className="size-3 shrink-0"
69 />
70 <span
71 data-composer-label
72 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
63 <Tooltip>
64 <TooltipTrigger
65 render={<span />}
66 className="inline-flex h-7 min-w-0 max-w-full items-center gap-1 border border-transparent px-[calc(--spacing(2)-1px)] font-normal text-muted-foreground/70 text-xs sm:h-6"
67 data-composer-context-control
7368 >
69 <EnvironmentMachineIcon
70 kind={activeEnvironment?.machine ?? "server"}
71 className="size-3 shrink-0"
72 />
7473 <span
75 data-composer-label-motion
76 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
74 data-composer-label
75 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
7776 >
78 {activeEnvironment?.label ?? "Run on"}
77 <span
78 data-composer-label-motion
79 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
80 >
81 {activeEnvironment?.label ?? "Run on"}
82 </span>
7983 </span>
80 </span>
81 </span>
84 </TooltipTrigger>
85 <TooltipPopup>{activeEnvironment?.label ?? "Run on"}</TooltipPopup>
86 </Tooltip>
8287 );
8388 }
8489
......@@ -91,34 +96,41 @@ export const BranchToolbarEnvironmentSelector = memo(function BranchToolbarEnvir
9196 }
9297 items={environmentItems}
9398 >
94 <SelectTrigger
95 variant="ghost"
96 size="xs"
97 className="min-w-0 max-w-full font-normal text-xs!"
98 aria-label="Run on"
99 data-composer-shortcut="composer.host"
100 data-composer-context-control
101 >
102 {autoEnvironmentLabel ? (
103 <ScaleIcon className="size-3 shrink-0" aria-hidden="true" />
104 ) : (
105 <EnvironmentMachineIcon
106 kind={activeEnvironment?.machine ?? "server"}
107 className="size-3 shrink-0"
108 />
109 )}
110 <span
111 data-composer-label
112 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
99 <Tooltip>
100 <TooltipTrigger
101 render={
102 <SelectTrigger
103 variant="ghost"
104 size="xs"
105 className="min-w-0 max-w-full font-normal text-xs!"
106 aria-label="Run on"
107 data-composer-shortcut="composer.host"
108 data-composer-context-control
109 />
110 }
113111 >
112 {autoEnvironmentLabel ? (
113 <ScaleIcon className="size-3 shrink-0" aria-hidden="true" />
114 ) : (
115 <EnvironmentMachineIcon
116 kind={activeEnvironment?.machine ?? "server"}
117 className="size-3 shrink-0"
118 />
119 )}
114120 <span
115 data-composer-label-motion
116 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
121 data-composer-label
122 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
117123 >
118 <SelectValue />
124 <span
125 data-composer-label-motion
126 className="block w-full min-w-0 max-w-[240px] truncate transition-opacity duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transition-none"
127 >
128 <SelectValue />
129 </span>
119130 </span>
120 </span>
121 </SelectTrigger>
131 </TooltipTrigger>
132 <TooltipPopup>{autoEnvironmentLabel ?? activeEnvironment?.label ?? "Run on"}</TooltipPopup>
133 </Tooltip>
122134 <SelectPopup alignItemWithTrigger={false} {...composerFloatingLayerProps}>
123135 <SelectGroup>
124136 <SelectGroupLabel>Run on</SelectGroupLabel>