Skip to content

Commit 89fbd5e

Browse files
committed
feat(web): keep brief view pinned to bottom on new messages
1 parent 41f6fce commit 89fbd5e

2 files changed

Lines changed: 40 additions & 4 deletions

File tree

web/src/components/AssistantChat/BriefTurnList.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -353,6 +353,7 @@ export function BriefTurnList(props: {
353353
} = useBriefModeCardSettings()
354354
const listRef = useRef<VirtuosoHandle | null>(null)
355355
const autoScrollToBottomDoneRef = useRef(false)
356+
const isAtBottomRef = useRef(true)
356357
const [activeTurnId, setActiveTurnId] = useState<string | null>(null)
357358
const [turnDetailStateById, setTurnDetailStateById] = useState<TurnDetailStateMap>({})
358359
const [isMobileViewport, setIsMobileViewport] = useState(() => (
@@ -590,6 +591,20 @@ export function BriefTurnList(props: {
590591
autoScrollToBottomDoneRef.current = false
591592
}, [props.session.id])
592593

594+
const latestTurnUpdateToken = useMemo(() => {
595+
if (props.turns.length === 0) {
596+
return 'empty'
597+
}
598+
const latestTurn = props.turns[props.turns.length - 1]
599+
return [
600+
props.turns.length,
601+
latestTurn.id,
602+
latestTurn.messageCount,
603+
latestTurn.status,
604+
latestTurn.updatedAt
605+
].join(':')
606+
}, [props.turns])
607+
593608
const renderTurnRow = useCallback((turn: ConversationTurn) => {
594609
const userPreview = turn.userPreview?.trim() ?? ''
595610
const assistantPreviewRaw = turn.assistantPreview?.trim() ?? ''
@@ -669,7 +684,7 @@ export function BriefTurnList(props: {
669684
if (props.isLoading) {
670685
return
671686
}
672-
if (autoScrollToBottomDoneRef.current) {
687+
if (autoScrollToBottomDoneRef.current && !isAtBottomRef.current) {
673688
return
674689
}
675690

@@ -690,7 +705,7 @@ export function BriefTurnList(props: {
690705
window.cancelAnimationFrame(rafId)
691706
window.clearTimeout(timeoutId)
692707
}
693-
}, [props.isLoading, props.turns.length])
708+
}, [latestTurnUpdateToken, props.isLoading, props.turns.length])
694709

695710
return (
696711
<>
@@ -717,6 +732,9 @@ export function BriefTurnList(props: {
717732
style={{ height: '100%' }}
718733
data={props.turns}
719734
overscan={320}
735+
atBottomStateChange={(isAtBottom) => {
736+
isAtBottomRef.current = isAtBottom
737+
}}
720738
startReached={() => {
721739
if (!props.hasMore || props.isLoadingMore) {
722740
return

web/src/routes/groups/detail.tsx

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1697,6 +1697,7 @@ function GroupBriefTurnList(props: {
16971697
} = useBriefModeCardSettings()
16981698
const listRef = useRef<VirtuosoHandle | null>(null)
16991699
const autoScrollToBottomDoneRef = useRef(false)
1700+
const isAtBottomRef = useRef(true)
17001701
const [activeTurnId, setActiveTurnId] = useState<string | null>(null)
17011702
const [turnDetailStateById, setTurnDetailStateById] = useState<GroupTurnDetailStateMap>({})
17021703
const [isMobileViewport, setIsMobileViewport] = useState(() => (
@@ -1875,6 +1876,20 @@ function GroupBriefTurnList(props: {
18751876
autoScrollToBottomDoneRef.current = false
18761877
}, [props.groupId])
18771878

1879+
const latestTurnUpdateToken = useMemo(() => {
1880+
if (props.turns.length === 0) {
1881+
return 'empty'
1882+
}
1883+
const latestTurn = props.turns[props.turns.length - 1]
1884+
return [
1885+
props.turns.length,
1886+
latestTurn.id,
1887+
latestTurn.messageCount,
1888+
latestTurn.status,
1889+
latestTurn.updatedAt
1890+
].join(':')
1891+
}, [props.turns])
1892+
18781893
useEffect(() => {
18791894
if (props.turns.length === 0) {
18801895
autoScrollToBottomDoneRef.current = false
@@ -1883,7 +1898,7 @@ function GroupBriefTurnList(props: {
18831898
if (props.isLoading) {
18841899
return
18851900
}
1886-
if (autoScrollToBottomDoneRef.current) {
1901+
if (autoScrollToBottomDoneRef.current && !isAtBottomRef.current) {
18871902
return
18881903
}
18891904
autoScrollToBottomDoneRef.current = true
@@ -1897,7 +1912,7 @@ function GroupBriefTurnList(props: {
18971912
window.cancelAnimationFrame(rafId)
18981913
window.clearTimeout(timeoutId)
18991914
}
1900-
}, [props.isLoading, props.turns.length])
1915+
}, [latestTurnUpdateToken, props.isLoading, props.turns.length])
19011916

19021917
return (
19031918
<>
@@ -1922,6 +1937,9 @@ function GroupBriefTurnList(props: {
19221937
data={props.turns}
19231938
style={{ height: '100%' }}
19241939
increaseViewportBy={{ top: 320, bottom: 320 }}
1940+
atBottomStateChange={(isAtBottom) => {
1941+
isAtBottomRef.current = isAtBottom
1942+
}}
19251943
startReached={() => {
19261944
if (!props.hasMore || props.isLoadingMore) {
19271945
return

0 commit comments

Comments
 (0)