Skip to content

Commit f8c0de3

Browse files
committed
Fix AI landing chat demo height
1 parent ff1e6e1 commit f8c0de3

1 file changed

Lines changed: 73 additions & 35 deletions

File tree

src/components/landing/AiLanding.tsx

Lines changed: 73 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -420,6 +420,8 @@ function AiGraphChatPanel() {
420420
>([])
421421
const [typingUserMessage, setTypingUserMessage] = React.useState('')
422422
const primaryServerNode = graphServerNodes[0]
423+
const chatScrollRef = React.useRef<HTMLDivElement>(null)
424+
const chatLockedToBottomRef = React.useRef(true)
423425

424426
React.useEffect(() => {
425427
const clientIntervalId = window.setInterval(() => {
@@ -549,6 +551,37 @@ function AiGraphChatPanel() {
549551
}
550552
}, [])
551553

554+
React.useEffect(() => {
555+
const element = chatScrollRef.current
556+
if (!element) {
557+
return
558+
}
559+
560+
const handleScroll = () => {
561+
const distanceFromBottom =
562+
element.scrollHeight - element.scrollTop - element.clientHeight
563+
564+
chatLockedToBottomRef.current = distanceFromBottom < 72
565+
}
566+
567+
element.addEventListener('scroll', handleScroll, { passive: true })
568+
return () => {
569+
element.removeEventListener('scroll', handleScroll)
570+
}
571+
}, [])
572+
573+
React.useEffect(() => {
574+
const frameId = window.requestAnimationFrame(() => {
575+
const element = chatScrollRef.current
576+
577+
if (element && chatLockedToBottomRef.current) {
578+
element.scrollTop = element.scrollHeight
579+
}
580+
})
581+
582+
return () => window.cancelAnimationFrame(frameId)
583+
}, [chatMessages])
584+
552585
return (
553586
<div className="grid w-full min-w-0 max-w-full items-start gap-5 lg:grid-cols-[1.05fr_0.95fr]">
554587
<AiDemoWindow title="client graph">
@@ -643,46 +676,51 @@ function AiGraphChatPanel() {
643676
</AiDemoWindow>
644677

645678
<AiDemoWindow title="chat runtime">
646-
<div className="flex min-h-[26rem] min-w-0 flex-col bg-zinc-50 dark:bg-zinc-900">
647-
<div className="flex flex-1 flex-col justify-end gap-2.5 p-4">
648-
{chatMessages.map((message) => (
649-
<React.Fragment key={message.id}>
650-
<div className="ml-auto max-w-[86%] rounded-xl bg-pink-500 px-3 py-2 text-xs font-bold leading-5 text-white shadow-sm">
651-
{message.user}
652-
</div>
653-
{message.assistant || message.isStreaming ? (
654-
<div className="max-w-[90%] rounded-xl border border-zinc-200 bg-white px-3 py-2 text-xs leading-5 text-zinc-800 shadow-sm dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-200">
655-
{message.assistant}
656-
{message.isStreaming ? (
657-
<span className="ml-1 inline-block h-3.5 w-1 rounded-sm bg-pink-500 align-[-0.2rem] motion-safe:animate-pulse" />
658-
) : null}
679+
<div className="flex h-[26rem] min-w-0 flex-col bg-zinc-50 dark:bg-zinc-900">
680+
<div
681+
ref={chatScrollRef}
682+
className="min-h-0 flex-1 overflow-y-auto [scrollbar-width:none] [&::-webkit-scrollbar]:hidden"
683+
>
684+
<div className="flex min-h-full flex-col justify-end gap-2.5 p-4">
685+
{chatMessages.map((message) => (
686+
<React.Fragment key={message.id}>
687+
<div className="ml-auto max-w-[86%] rounded-xl bg-pink-500 px-3 py-2 text-xs font-bold leading-5 text-white shadow-sm">
688+
{message.user}
659689
</div>
660-
) : null}
661-
</React.Fragment>
662-
))}
663-
<div className="grid gap-2 pt-2 text-xs font-bold sm:grid-cols-2">
664-
{[
665-
['event', 'assistant.delta'],
666-
['tool', 'approval required'],
667-
['provider', aiHeroProviders[activeProvider]],
668-
['server', 'TanStack AI'],
669-
].map(([label, value]) => (
670-
<div
671-
key={label}
672-
className="rounded-lg bg-white px-3 py-2 dark:bg-zinc-950"
673-
>
674-
<p className="text-[0.58rem] uppercase text-zinc-500 dark:text-zinc-500">
675-
{label}
676-
</p>
677-
<p className="mt-1 truncate text-pink-700 dark:text-pink-300">
678-
{value}
679-
</p>
680-
</div>
690+
{message.assistant || message.isStreaming ? (
691+
<div className="max-w-[90%] rounded-xl border border-zinc-200 bg-white px-3 py-2 text-xs leading-5 text-zinc-800 shadow-sm dark:border-zinc-800 dark:bg-zinc-950 dark:text-zinc-200">
692+
{message.assistant}
693+
{message.isStreaming ? (
694+
<span className="ml-1 inline-block h-3.5 w-1 rounded-sm bg-pink-500 align-[-0.2rem] motion-safe:animate-pulse" />
695+
) : null}
696+
</div>
697+
) : null}
698+
</React.Fragment>
681699
))}
700+
<div className="grid gap-2 pt-2 text-xs font-bold sm:grid-cols-2">
701+
{[
702+
['event', 'assistant.delta'],
703+
['tool', 'approval required'],
704+
['provider', aiHeroProviders[activeProvider]],
705+
['server', 'TanStack AI'],
706+
].map(([label, value]) => (
707+
<div
708+
key={label}
709+
className="rounded-lg bg-white px-3 py-2 dark:bg-zinc-950"
710+
>
711+
<p className="text-[0.58rem] uppercase text-zinc-500 dark:text-zinc-500">
712+
{label}
713+
</p>
714+
<p className="mt-1 truncate text-pink-700 dark:text-pink-300">
715+
{value}
716+
</p>
717+
</div>
718+
))}
719+
</div>
682720
</div>
683721
</div>
684722

685-
<div className="border-t border-zinc-200 p-4 dark:border-zinc-800">
723+
<div className="shrink-0 border-t border-zinc-200 p-4 dark:border-zinc-800">
686724
<div
687725
className={
688726
typingUserMessage

0 commit comments

Comments
 (0)