| ... | @@ -315,7 +315,7 @@ import { PullRequestThreadDialog } from "./PullRequestThreadDialog"; | ... | @@ -315,7 +315,7 @@ import { PullRequestThreadDialog } from "./PullRequestThreadDialog"; |
| 315 | import { MessagesTimeline } from "./chat/MessagesTimeline"; | 315 | import { MessagesTimeline } from "./chat/MessagesTimeline"; |
| 316 | import type { AssistantCitationRequest } from "./chat/AssistantCitationSource"; | 316 | import type { AssistantCitationRequest } from "./chat/AssistantCitationSource"; |
| 317 | import { resolveTimelineIsAtEnd } from "./chat/MessagesTimeline.logic"; | 317 | import { resolveTimelineIsAtEnd } from "./chat/MessagesTimeline.logic"; |
| 318 | import { resolveComposerTimelineInset } from "./composerFooterLayout"; | 318 | import { resolveComposerTimelineInset, resolveScrollToEndClearance } from "./composerFooterLayout"; |
| 319 | import { ChatHeader } from "./chat/ChatHeader"; | 319 | import { ChatHeader } from "./chat/ChatHeader"; |
| 320 | import { PanelLayoutControls, RightPanelMaximizeControl } from "./chat/PanelLayoutControls"; | 320 | import { PanelLayoutControls, RightPanelMaximizeControl } from "./chat/PanelLayoutControls"; |
| 321 | import { expandedImageKey, type ExpandedImagePreview } from "./chat/ExpandedImagePreview"; | 321 | import { expandedImageKey, type ExpandedImagePreview } from "./chat/ExpandedImagePreview"; |
| ... | @@ -5163,27 +5163,48 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -5163,27 +5163,48 @@ export default function ChatView(props: ChatViewProps) { |
| 5163 | ? activePlan.steps | 5163 | ? activePlan.steps |
| 5164 | : null; | 5164 | : null; |
| 5165 | | 5165 | |
| 5166 | const publishComposerOverlayHeight = useCallback((height: number) => { | 5166 | const publishComposerOverlayHeight = useCallback( |
| 5167 | const nextHeight = Math.ceil(height); | 5167 | (height: number) => { |
| 5168 | if (nextHeight <= 0) return; | 5168 | const nextHeight = Math.ceil(height); |
| 5169 | const previousHeight = composerOverlayHeightRef.current; | 5169 | if (nextHeight <= 0) return; |
| 5170 | if (previousHeight !== nextHeight) { | 5170 | const previousHeight = composerOverlayHeightRef.current; |
| 5171 | composerOverlayHeightRef.current = nextHeight; | 5171 | if (previousHeight !== nextHeight) { |
| 5172 | setComposerOverlayHeight(nextHeight); | 5172 | composerOverlayHeightRef.current = nextHeight; |
| 5173 | } | 5173 | setComposerOverlayHeight(nextHeight); |
| 5174 | const nextInset = resolveComposerTimelineInset({ | 5174 | } |
| 5175 | currentInset: composerTimelineInsetRef.current, | 5175 | const nextInset = resolveComposerTimelineInset({ |
| 5176 | overlayHeight: nextHeight, | 5176 | currentInset: composerTimelineInsetRef.current, |
| 5177 | isResting: composerRestingRef.current, | 5177 | overlayHeight: nextHeight, |
| 5178 | }); | 5178 | isResting: composerRestingRef.current, |
| 5179 | if (composerTimelineInsetRef.current !== nextInset) { | 5179 | }); |
| 5180 | composerTimelineInsetRef.current = nextInset; | 5180 | if (composerTimelineInsetRef.current !== nextInset) { |
| 5181 | setComposerTimelineInset(nextInset); | 5181 | composerTimelineInsetRef.current = nextInset; |
| 5182 | } | 5182 | setComposerTimelineInset(nextInset); |
| 5183 | setScrollToEndClearance((currentClearance) => | 5183 | } |
| 5184 | currentClearance === nextHeight ? currentClearance : nextHeight, | 5184 | const mainSurface = composerOverlayElement?.querySelector<HTMLElement>( |
| 5185 | ); | 5185 | '[data-chat-composer-main-surface="true"]', |
| 5186 | }, []); | 5186 | ); |
| | 5187 | const button = composerOverlayElement?.parentElement?.querySelector<HTMLElement>( |
| | 5188 | 'button[aria-label="Scroll to end"]', |
| | 5189 | ); |
| | 5190 | const clearance = |
| | 5191 | composerOverlayElement && mainSurface && button |
| | 5192 | ? resolveScrollToEndClearance({ |
| | 5193 | overlayHeight: nextHeight, |
| | 5194 | mainSurfaceTop: mainSurface.getBoundingClientRect().top, |
| | 5195 | button: button.getBoundingClientRect(), |
| | 5196 | attachments: Array.from( |
| | 5197 | composerOverlayElement.querySelectorAll<HTMLElement>( |
| | 5198 | '[data-composer-banner-surface="attached"]', |
| | 5199 | ), |
| | 5200 | (element) => element.getBoundingClientRect(), |
| | 5201 | ), |
| | 5202 | }) |
| | 5203 | : nextHeight; |
| | 5204 | setScrollToEndClearance(clearance); |
| | 5205 | }, |
| | 5206 | [composerOverlayElement], |
| | 5207 | ); |
| 5187 | // The composer reports its resting flag from a layout effect, which runs | 5208 | // The composer reports its resting flag from a layout effect, which runs |
| 5188 | // before this component's own layout effects and before any resize | 5209 | // before this component's own layout effects and before any resize |
| 5189 | // observation, so every measurement below sees the flag for its layout. | 5210 | // observation, so every measurement below sees the flag for its layout. |
| ... | @@ -5216,7 +5237,7 @@ export default function ChatView(props: ChatViewProps) { | ... | @@ -5216,7 +5237,7 @@ export default function ChatView(props: ChatViewProps) { |
| 5216 | return () => { | 5237 | return () => { |
| 5217 | resizeObserver.disconnect(); | 5238 | resizeObserver.disconnect(); |
| 5218 | }; | 5239 | }; |
| 5219 | }, [composerOverlayElement, publishComposerOverlayHeight]); | 5240 | }, [composerOverlayElement, publishComposerOverlayHeight, showScrollToBottom]); |
| 5220 | const openPanelPullRequestUrl = useOpenPanelPullRequestUrl(activeThreadRef); | 5241 | const openPanelPullRequestUrl = useOpenPanelPullRequestUrl(activeThreadRef); |
| 5221 | const activeThreadReferenceCopyTarget = useMemo( | 5242 | const activeThreadReferenceCopyTarget = useMemo( |
| 5222 | () => | 5243 | () => |