authorgravatar for 136640686+MatthewFeroz@users.noreply.github.comMatthew Feroz <136640686+MatthewFeroz@users.noreply.github.com> 2026-08-31 18:59:31-04:00
committergravatar for noreply@github.comGitHub <noreply@github.com> 2026-08-31 18:59:31-04:00
logf47e74004af232f0e3df8dc10093601d1c2c3ea3
treef71a6148843dad621f5cb293bbfaa6f3f4f7ae3b
parent31c1c5996f88e3acf1566adc11c9b51ac7561554
signature Signed by PGP key B5690EEEBB952194

fix(web): prevent chat metadata overlap (#8851)


3 files changed, 28 insertions(+), 18 deletions(-)

apps/web/src/components/BranchToolbar.tsx+7-5
......@@ -265,8 +265,10 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {
265265 let needed = 0;
266266 let groups = 0;
267267 for (const child of current.children) {
268 if (!(child instanceof HTMLElement) || child.offsetWidth <= 1) continue;
269 needed += contentWidth(child);
268 if (!(child instanceof HTMLElement)) continue;
269 const width = contentWidth(child);
270 if (width <= 1) continue;
271 needed += width;
270272 groups += 1;
271273 }
272274 needed += stripGap * Math.max(0, groups - 1);
......@@ -356,7 +358,7 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {
356358 // Label widths can change without the strip box moving (font family or
357359 // size preferences), so re-measure on every render as well as on resize
358360 // and font loads.
359 useEffect(() => {
361 useLayoutEffect(() => {
360362 measure();
361363 });
362364
......@@ -487,7 +489,7 @@ export const BranchToolbar = memo(function BranchToolbar({
487489 onUsePreviousWorktree={onUsePreviousWorktree}
488490 />
489491 ) : (
490 <div className="flex min-w-0 flex-1 items-center gap-1">
492 <div className="flex min-w-10 flex-1 items-center gap-1">
491493 {showEnvironmentIndicator && availableEnvironments && (
492494 <>
493495 <BranchToolbarEnvironmentSelector
......@@ -520,7 +522,7 @@ export const BranchToolbar = memo(function BranchToolbar({
520522
521523 {showGitControls ? (
522524 <BranchToolbarBranchSelector
523 className="min-w-0 flex-1 justify-end md:ml-auto md:flex-none"
525 className="min-w-0 flex-1 justify-end md:ml-auto md:flex-initial"
524526 environmentId={environmentId}
525527 threadId={threadId}
526528 {...(draftId ? { draftId } : {})}
apps/web/src/components/BranchToolbarEnvModeSelector.tsx+14-9
......@@ -51,20 +51,25 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
5151 if (envLocked) {
5252 return (
5353 <span
54 className="inline-flex h-7 shrink-0 items-center gap-1 border border-transparent px-[calc(--spacing(3)-1px)] text-sm font-medium text-muted-foreground/70 sm:h-6 sm:text-xs"
54 className="inline-flex h-7 min-w-0 items-center gap-1 border border-transparent px-[calc(--spacing(3)-1px)] text-sm font-medium text-muted-foreground/70 sm:h-6 sm:text-xs"
5555 data-composer-context-control
5656 >
5757 {activeWorktreePath ? (
58 <>
59 <FolderGitIcon className="size-3" />
60 {resolveLockedWorkspaceLabel(activeWorktreePath)}
61 </>
58 <FolderGitIcon className="size-3 shrink-0" />
6259 ) : (
63 <>
64 <FolderIcon className="size-3" />
65 {resolveLockedWorkspaceLabel(activeWorktreePath)}
66 </>
60 <FolderIcon className="size-3 shrink-0" />
6761 )}
62 <span
63 data-composer-label
64 className="min-w-0 max-w-[240px] group-data-[compact]/composer-context:max-w-0"
65 >
66 <span
67 data-composer-label-motion
68 className="block w-full min-w-0 max-w-[240px] origin-left truncate transition-[opacity,transform] duration-180 ease-[cubic-bezier(0.32,0.72,0,1)] group-data-[compact]/composer-context:[transform:translateX(-0.25rem)_scaleX(0.95)] group-data-[compact]/composer-context:opacity-0 motion-reduce:transform-none motion-reduce:transition-opacity"
69 >
70 {resolveLockedWorkspaceLabel(activeWorktreePath)}
71 </span>
72 </span>
6873 </span>
6974 );
7075 }
apps/web/src/components/chat/ChatHeader.tsx+7-4
......@@ -288,13 +288,16 @@ export const ChatHeader = memo(function ChatHeader({
288288 className="@container/header-actions flex min-w-0 flex-1 items-center gap-2 sm:gap-3"
289289 onContextMenu={handleHeaderContextMenu}
290290 >
291 <WorkspaceBreadcrumb ariaLabel="Thread breadcrumb" className="flex-1">
291 <WorkspaceBreadcrumb
292 ariaLabel="Thread breadcrumb"
293 className="flex-1 overflow-clip [overflow-clip-margin:2px]"
294 >
292295 {/* The project always leads the header: knowing which project a
293296 thread lives in is priority zero, and the thread title alone
294297 doesn't answer it. */}
295298 {activeProjectName ? (
296299 <>
297 <WorkspaceBreadcrumbItem>
300 <WorkspaceBreadcrumbItem className="shrink">
298301 <Tooltip>
299302 <TooltipTrigger
300303 render={
......@@ -302,7 +305,7 @@ export const ChatHeader = memo(function ChatHeader({
302305 type="button"
303306 aria-label={`New thread in ${activeProjectName}`}
304307 onClick={onNewThreadInProject}
305 className="inline-flex min-w-0 cursor-pointer items-center gap-1.5 rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
308 className="inline-flex min-w-0 max-w-full cursor-pointer items-center gap-1.5 rounded-sm text-muted-foreground transition-colors hover:text-foreground focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
306309 />
307310 }
308311 >
......@@ -320,7 +323,7 @@ export const ChatHeader = memo(function ChatHeader({
320323 <WorkspaceBreadcrumbSeparator />
321324 </>
322325 ) : null}
323 <WorkspaceBreadcrumbItem current className="flex-1">
326 <WorkspaceBreadcrumbItem current className="min-w-10 flex-1">
324327 {renamingTitle !== null ? (
325328 <input
326329 autoFocus