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 {...@@ -265,8 +265,10 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {
265 let needed = 0;265 let needed = 0;
266 let groups = 0;266 let groups = 0;
267 for (const child of current.children) {267 for (const child of current.children) {
268 if (!(child instanceof HTMLElement) || child.offsetWidth <= 1) continue;268 if (!(child instanceof HTMLElement)) continue;
269 needed += contentWidth(child);269 const width = contentWidth(child);
270 if (width <= 1) continue;
271 needed += width;
270 groups += 1;272 groups += 1;
271 }273 }
272 needed += stripGap * Math.max(0, groups - 1);274 needed += stripGap * Math.max(0, groups - 1);
...@@ -356,7 +358,7 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {...@@ -356,7 +358,7 @@ function useLabelsOverflow(element: HTMLDivElement | null): boolean {
356 // Label widths can change without the strip box moving (font family or358 // Label widths can change without the strip box moving (font family or
357 // size preferences), so re-measure on every render as well as on resize359 // size preferences), so re-measure on every render as well as on resize
358 // and font loads.360 // and font loads.
359 useEffect(() => {361 useLayoutEffect(() => {
360 measure();362 measure();
361 });363 });
362364
...@@ -487,7 +489,7 @@ export const BranchToolbar = memo(function BranchToolbar({...@@ -487,7 +489,7 @@ export const BranchToolbar = memo(function BranchToolbar({
487 onUsePreviousWorktree={onUsePreviousWorktree}489 onUsePreviousWorktree={onUsePreviousWorktree}
488 />490 />
489 ) : (491 ) : (
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">
491 {showEnvironmentIndicator && availableEnvironments && (493 {showEnvironmentIndicator && availableEnvironments && (
492 <>494 <>
493 <BranchToolbarEnvironmentSelector495 <BranchToolbarEnvironmentSelector
...@@ -520,7 +522,7 @@ export const BranchToolbar = memo(function BranchToolbar({...@@ -520,7 +522,7 @@ export const BranchToolbar = memo(function BranchToolbar({
520522
521 {showGitControls ? (523 {showGitControls ? (
522 <BranchToolbarBranchSelector524 <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"
524 environmentId={environmentId}526 environmentId={environmentId}
525 threadId={threadId}527 threadId={threadId}
526 {...(draftId ? { draftId } : {})}528 {...(draftId ? { draftId } : {})}
apps/web/src/components/BranchToolbarEnvModeSelector.tsx+14-9
...@@ -51,20 +51,25 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe...@@ -51,20 +51,25 @@ export const BranchToolbarEnvModeSelector = memo(function BranchToolbarEnvModeSe
51 if (envLocked) {51 if (envLocked) {
52 return (52 return (
53 <span53 <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"
55 data-composer-context-control55 data-composer-context-control
56 >56 >
57 {activeWorktreePath ? (57 {activeWorktreePath ? (
58 <>58 <FolderGitIcon className="size-3 shrink-0" />
59 <FolderGitIcon className="size-3" />
60 {resolveLockedWorkspaceLabel(activeWorktreePath)}
61 </>
62 ) : (59 ) : (
63 <>60 <FolderIcon className="size-3 shrink-0" />
64 <FolderIcon className="size-3" />
65 {resolveLockedWorkspaceLabel(activeWorktreePath)}
66 </>
67 )}61 )}
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>
68 </span>73 </span>
69 );74 );
70 }75 }
apps/web/src/components/chat/ChatHeader.tsx+7-4
...@@ -288,13 +288,16 @@ export const ChatHeader = memo(function ChatHeader({...@@ -288,13 +288,16 @@ export const ChatHeader = memo(function ChatHeader({
288 className="@container/header-actions flex min-w-0 flex-1 items-center gap-2 sm:gap-3"288 className="@container/header-actions flex min-w-0 flex-1 items-center gap-2 sm:gap-3"
289 onContextMenu={handleHeaderContextMenu}289 onContextMenu={handleHeaderContextMenu}
290 >290 >
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 >
292 {/* The project always leads the header: knowing which project a295 {/* The project always leads the header: knowing which project a
293 thread lives in is priority zero, and the thread title alone296 thread lives in is priority zero, and the thread title alone
294 doesn't answer it. */}297 doesn't answer it. */}
295 {activeProjectName ? (298 {activeProjectName ? (
296 <>299 <>
297 <WorkspaceBreadcrumbItem>300 <WorkspaceBreadcrumbItem className="shrink">
298 <Tooltip>301 <Tooltip>
299 <TooltipTrigger302 <TooltipTrigger
300 render={303 render={
...@@ -302,7 +305,7 @@ export const ChatHeader = memo(function ChatHeader({...@@ -302,7 +305,7 @@ export const ChatHeader = memo(function ChatHeader({
302 type="button"305 type="button"
303 aria-label={`New thread in ${activeProjectName}`}306 aria-label={`New thread in ${activeProjectName}`}
304 onClick={onNewThreadInProject}307 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"
306 />309 />
307 }310 }
308 >311 >
...@@ -320,7 +323,7 @@ export const ChatHeader = memo(function ChatHeader({...@@ -320,7 +323,7 @@ export const ChatHeader = memo(function ChatHeader({
320 <WorkspaceBreadcrumbSeparator />323 <WorkspaceBreadcrumbSeparator />
321 </>324 </>
322 ) : null}325 ) : null}
323 <WorkspaceBreadcrumbItem current className="flex-1">326 <WorkspaceBreadcrumbItem current className="min-w-10 flex-1">
324 {renamingTitle !== null ? (327 {renamingTitle !== null ? (
325 <input328 <input
326 autoFocus329 autoFocus