Skip to content

Commit 41b1357

Browse files
waleedlatif1Sg312greptile-apps[bot]
authored
fix(copilot): added user scrolling, fixed code block, fixed code copying and styling (#872)
* fix(copilot-ui): added user scrolling, fixed code block, fixed code copying and styling * use console logger instead of console * fix(copilot): make chat history non-interfering (#869) * Add basic personalizatoin * Make chat history non-interfering * Always personalize * improvement(copilot): add subblock enums to block metadata (#870) * Add subblock enums to metadata * Update apps/sim/lib/copilot/tools/server-tools/blocks/get-blocks-metadata.ts Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> --------- Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com> * fix(copilot): fix state message sent on move to background (#871) * Initial fix * Add execution start time to message * Lint * autofocus on new tab open --------- Co-authored-by: Siddharth Ganesan <[email protected]> Co-authored-by: greptile-apps[bot] <165735046+greptile-apps[bot]@users.noreply.github.com>
1 parent 221a473 commit 41b1357

File tree

7 files changed

+749
-431
lines changed

7 files changed

+749
-431
lines changed

apps/sim/app/chat/[subdomain]/components/input/input.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -168,7 +168,7 @@ export const ChatInput: React.FC<{
168168
ref={textareaRef}
169169
value={inputValue}
170170
onChange={handleInputChange}
171-
className='flex w-full resize-none items-center overflow-hidden bg-transparent text-sm outline-none placeholder:text-gray-400 md:font-[330] md:text-base'
171+
className='flex w-full resize-none items-center overflow-hidden bg-transparent text-base outline-none placeholder:text-gray-400 md:font-[330]'
172172
placeholder={isActive ? '' : ''}
173173
rows={1}
174174
style={{
@@ -191,14 +191,14 @@ export const ChatInput: React.FC<{
191191
<>
192192
{/* Mobile placeholder */}
193193
<div
194-
className='-translate-y-1/2 absolute top-1/2 left-0 transform select-none text-gray-400 text-sm md:hidden md:text-base'
194+
className='-translate-y-1/2 absolute top-1/2 left-0 transform select-none text-base text-gray-400 md:hidden'
195195
style={{ paddingTop: '3px', paddingBottom: '3px' }}
196196
>
197197
{PLACEHOLDER_MOBILE}
198198
</div>
199199
{/* Desktop placeholder */}
200200
<div
201-
className='-translate-y-1/2 absolute top-1/2 left-0 hidden transform select-none font-[330] text-gray-400 text-sm md:block md:text-base'
201+
className='-translate-y-1/2 absolute top-1/2 left-0 hidden transform select-none font-[330] text-base text-gray-400 md:block'
202202
style={{ paddingTop: '4px', paddingBottom: '4px' }}
203203
>
204204
{PLACEHOLDER_DESKTOP}

apps/sim/app/chat/[subdomain]/components/message/message.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export const ClientChatMessage = memo(
4242
<div className='mx-auto max-w-3xl'>
4343
<div className='flex justify-end'>
4444
<div className='max-w-[80%] rounded-3xl bg-[#F4F4F4] px-4 py-3 dark:bg-gray-600'>
45-
<div className='whitespace-pre-wrap break-words text-gray-800 text-lg leading-relaxed dark:text-gray-100'>
45+
<div className='whitespace-pre-wrap break-words text-base text-gray-800 leading-relaxed dark:text-gray-100'>
4646
{isJsonObject ? (
4747
<pre>{JSON.stringify(message.content, null, 2)}</pre>
4848
) : (

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/chat/chat.tsx

Lines changed: 85 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
'use client'
22

33
import { type KeyboardEvent, useCallback, useEffect, useMemo, useRef, useState } from 'react'
4-
import { ArrowUp } from 'lucide-react'
4+
import { ArrowDown, ArrowUp } from 'lucide-react'
55
import { Button } from '@/components/ui/button'
66
import { Input } from '@/components/ui/input'
77
import { ScrollArea } from '@/components/ui/scroll-area'
@@ -42,6 +42,7 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
4242
} = useChatStore()
4343
const { entries } = useConsoleStore()
4444
const messagesEndRef = useRef<HTMLDivElement>(null)
45+
const scrollAreaRef = useRef<HTMLDivElement>(null)
4546
const inputRef = useRef<HTMLInputElement>(null)
4647
const timeoutRef = useRef<NodeJS.Timeout | null>(null)
4748
const abortControllerRef = useRef<AbortController | null>(null)
@@ -50,6 +51,10 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
5051
const [promptHistory, setPromptHistory] = useState<string[]>([])
5152
const [historyIndex, setHistoryIndex] = useState(-1)
5253

54+
// Scroll state
55+
const [isNearBottom, setIsNearBottom] = useState(true)
56+
const [showScrollButton, setShowScrollButton] = useState(false)
57+
5358
// Use the execution store state to track if a workflow is executing
5459
const { isExecuting } = useExecutionStore()
5560

@@ -125,6 +130,31 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
125130
}, delay)
126131
}, [])
127132

133+
// Scroll to bottom function
134+
const scrollToBottom = useCallback(() => {
135+
if (messagesEndRef.current) {
136+
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
137+
}
138+
}, [])
139+
140+
// Handle scroll events to track user position
141+
const handleScroll = useCallback(() => {
142+
const scrollArea = scrollAreaRef.current
143+
if (!scrollArea) return
144+
145+
// Find the viewport element inside the ScrollArea
146+
const viewport = scrollArea.querySelector('[data-radix-scroll-area-viewport]')
147+
if (!viewport) return
148+
149+
const { scrollTop, scrollHeight, clientHeight } = viewport
150+
const distanceFromBottom = scrollHeight - scrollTop - clientHeight
151+
152+
// Consider "near bottom" if within 100px of bottom
153+
const nearBottom = distanceFromBottom <= 100
154+
setIsNearBottom(nearBottom)
155+
setShowScrollButton(!nearBottom)
156+
}, [])
157+
128158
// Cleanup on unmount
129159
useEffect(() => {
130160
return () => {
@@ -137,12 +167,47 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
137167
}
138168
}, [])
139169

140-
// Auto-scroll to bottom when new messages are added
170+
// Attach scroll listener
141171
useEffect(() => {
142-
if (messagesEndRef.current) {
172+
const scrollArea = scrollAreaRef.current
173+
if (!scrollArea) return
174+
175+
// Find the viewport element inside the ScrollArea
176+
const viewport = scrollArea.querySelector('[data-radix-scroll-area-viewport]')
177+
if (!viewport) return
178+
179+
viewport.addEventListener('scroll', handleScroll, { passive: true })
180+
181+
// Also listen for scrollend event if available (for smooth scrolling)
182+
if ('onscrollend' in viewport) {
183+
viewport.addEventListener('scrollend', handleScroll, { passive: true })
184+
}
185+
186+
// Initial scroll state check with small delay to ensure DOM is ready
187+
setTimeout(handleScroll, 100)
188+
189+
return () => {
190+
viewport.removeEventListener('scroll', handleScroll)
191+
if ('onscrollend' in viewport) {
192+
viewport.removeEventListener('scrollend', handleScroll)
193+
}
194+
}
195+
}, [handleScroll])
196+
197+
// Auto-scroll to bottom when new messages are added, but only if user is near bottom
198+
// Exception: Always scroll when sending a new message
199+
useEffect(() => {
200+
if (workflowMessages.length === 0) return
201+
202+
const lastMessage = workflowMessages[workflowMessages.length - 1]
203+
const isNewUserMessage = lastMessage?.type === 'user'
204+
205+
// Always scroll for new user messages, or only if near bottom for assistant messages
206+
if ((isNewUserMessage || isNearBottom) && messagesEndRef.current) {
143207
messagesEndRef.current.scrollIntoView({ behavior: 'smooth' })
208+
// Let the scroll event handler update the state naturally after animation completes
144209
}
145-
}, [workflowMessages])
210+
}, [workflowMessages, isNearBottom])
146211

147212
// Handle send message
148213
const handleSendMessage = useCallback(async () => {
@@ -449,7 +514,7 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
449514
No messages yet
450515
</div>
451516
) : (
452-
<ScrollArea className='h-full pb-2' hideScrollbar={true}>
517+
<ScrollArea ref={scrollAreaRef} className='h-full pb-2' hideScrollbar={true}>
453518
<div>
454519
{workflowMessages.map((message) => (
455520
<ChatMessage key={message.id} message={message} />
@@ -458,6 +523,21 @@ export function Chat({ panelWidth, chatMessage, setChatMessage }: ChatProps) {
458523
</div>
459524
</ScrollArea>
460525
)}
526+
527+
{/* Scroll to bottom button */}
528+
{showScrollButton && (
529+
<div className='-translate-x-1/2 absolute bottom-20 left-1/2 z-10'>
530+
<Button
531+
onClick={scrollToBottom}
532+
size='sm'
533+
variant='outline'
534+
className='flex items-center gap-1 rounded-full border border-gray-200 bg-white px-3 py-1 shadow-lg transition-all hover:bg-gray-50'
535+
>
536+
<ArrowDown className='h-3.5 w-3.5' />
537+
<span className='sr-only'>Scroll to bottom</span>
538+
</Button>
539+
</div>
540+
)}
461541
</div>
462542

463543
{/* Input section - Fixed height */}

0 commit comments

Comments
 (0)