|
4 | 4 | * Height matches side panel headers (40px). |
5 | 5 | */ |
6 | 6 |
|
7 | | -import React, { useEffect, useMemo, useRef, useState, useCallback } from 'react'; |
| 7 | +import React, { useEffect, useLayoutEffect, useMemo, useRef, useState, useCallback } from 'react'; |
8 | 8 | import { Activity, Bot, ChevronDown, ChevronUp, GitPullRequest, Keyboard, List, MoreHorizontal, Search, Square, Terminal, X } from 'lucide-react'; |
9 | 9 | import { Tooltip, IconButton, Input } from '@/component-library'; |
10 | 10 | import { useTranslation } from 'react-i18next'; |
@@ -140,6 +140,9 @@ export const FlowChatHeader: React.FC<FlowChatHeaderProps> = ({ |
140 | 140 | const [openBackgroundSubagentMenuId, setOpenBackgroundSubagentMenuId] = useState<string | null>(null); |
141 | 141 | const [openBackgroundCommandMenuId, setOpenBackgroundCommandMenuId] = useState<string | null>(null); |
142 | 142 | const [isSearchOpen, setIsSearchOpen] = useState(false); |
| 143 | + const headerRef = useRef<HTMLDivElement | null>(null); |
| 144 | + const leftActionsRef = useRef<HTMLDivElement | null>(null); |
| 145 | + const rightActionsRef = useRef<HTMLDivElement | null>(null); |
143 | 146 | const turnListRef = useRef<HTMLDivElement | null>(null); |
144 | 147 | const backgroundActivityPanelRef = useRef<HTMLDivElement | null>(null); |
145 | 148 | const activeTurnItemRef = useRef<HTMLButtonElement | null>(null); |
@@ -263,6 +266,34 @@ export const FlowChatHeader: React.FC<FlowChatHeaderProps> = ({ |
263 | 266 | }; |
264 | 267 | }, [currentTurn, displayTurns.length, isTurnListOpen]); |
265 | 268 |
|
| 269 | + useLayoutEffect(() => { |
| 270 | + const header = headerRef.current; |
| 271 | + const leftActions = leftActionsRef.current; |
| 272 | + const rightActions = rightActionsRef.current; |
| 273 | + if (!header || !leftActions || !rightActions) return; |
| 274 | + |
| 275 | + const updateSideWidth = () => { |
| 276 | + const sideWidth = Math.ceil(Math.max( |
| 277 | + leftActions.getBoundingClientRect().width, |
| 278 | + rightActions.getBoundingClientRect().width, |
| 279 | + )); |
| 280 | + header.style.setProperty('--flowchat-header-side-width', `${sideWidth}px`); |
| 281 | + }; |
| 282 | + |
| 283 | + updateSideWidth(); |
| 284 | + |
| 285 | + if (typeof ResizeObserver === 'undefined') { |
| 286 | + window.addEventListener('resize', updateSideWidth); |
| 287 | + return () => window.removeEventListener('resize', updateSideWidth); |
| 288 | + } |
| 289 | + |
| 290 | + const observer = new ResizeObserver(updateSideWidth); |
| 291 | + observer.observe(leftActions); |
| 292 | + observer.observe(rightActions); |
| 293 | + |
| 294 | + return () => observer.disconnect(); |
| 295 | + }, [isSearchOpen, totalTurns, visible]); |
| 296 | + |
266 | 297 | const handleOpenSearch = useCallback(() => { |
267 | 298 | setIsSearchOpen(true); |
268 | 299 | }, []); |
@@ -533,8 +564,11 @@ export const FlowChatHeader: React.FC<FlowChatHeaderProps> = ({ |
533 | 564 | } |
534 | 565 |
|
535 | 566 | return ( |
536 | | - <div className="flowchat-header"> |
537 | | - <div className="flowchat-header__actions flowchat-header__actions--left"> |
| 567 | + <div className="flowchat-header" ref={headerRef}> |
| 568 | + <div |
| 569 | + className="flowchat-header__actions flowchat-header__actions--left" |
| 570 | + ref={leftActionsRef} |
| 571 | + > |
538 | 572 | <SessionFilesBadge sessionId={sessionId} /> |
539 | 573 | </div> |
540 | 574 |
|
@@ -563,7 +597,7 @@ export const FlowChatHeader: React.FC<FlowChatHeaderProps> = ({ |
563 | 597 | </div> |
564 | 598 | </Tooltip> |
565 | 599 |
|
566 | | - <div className="flowchat-header__actions"> |
| 600 | + <div className="flowchat-header__actions" ref={rightActionsRef}> |
567 | 601 | <div className="flowchat-header__background-activity-nav" ref={backgroundActivityPanelRef}> |
568 | 602 | <IconButton |
569 | 603 | className={[ |
|
0 commit comments