Skip to content
31 changes: 1 addition & 30 deletions apps/sim/app/chat/[identifier]/chat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
const distanceFromBottom = scrollHeight - scrollTop - clientHeight
setShowScrollButton(distanceFromBottom > 100)

// Track if user is manually scrolling during streaming
if (isStreamingResponse && !isUserScrollingRef.current) {
setUserHasScrolled(true)
}
Expand All @@ -191,13 +190,10 @@ export default function ChatClient({ identifier }: { identifier: string }) {
return () => container.removeEventListener('scroll', handleScroll)
}, [handleScroll])

// Reset user scroll tracking when streaming starts
useEffect(() => {
if (isStreamingResponse) {
// Reset userHasScrolled when streaming starts
setUserHasScrolled(false)

// Give a small delay to distinguish between programmatic scroll and user scroll
isUserScrollingRef.current = true
setTimeout(() => {
isUserScrollingRef.current = false
Expand All @@ -215,7 +211,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
})

if (!response.ok) {
// Check if auth is required
if (response.status === 401) {
const errorData = await response.json()

Expand All @@ -236,7 +231,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
throw new Error(`Failed to load chat configuration: ${response.status}`)
}

// Reset auth required state when authentication is successful
setAuthRequired(null)

const data = await response.json()
Expand All @@ -260,7 +254,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}
}

// Fetch chat config on mount and generate new conversation ID
useEffect(() => {
fetchChatConfig()
setConversationId(uuidv4())
Expand All @@ -285,7 +278,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}, 800)
}

// Handle sending a message
const handleSendMessage = async (
messageParam?: string,
isVoiceInput = false,
Expand All @@ -308,7 +300,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
filesCount: files?.length,
})

// Reset userHasScrolled when sending a new message
setUserHasScrolled(false)

const userMessage: ChatMessage = {
Expand All @@ -325,24 +316,20 @@ export default function ChatClient({ identifier }: { identifier: string }) {
})),
}

// Add the user's message to the chat
setMessages((prev) => [...prev, userMessage])
setInputValue('')
setIsLoading(true)

// Scroll to show only the user's message and loading indicator
setTimeout(() => {
scrollToMessage(userMessage.id, true)
}, 100)

// Create abort controller for request cancellation
const abortController = new AbortController()
const timeoutId = setTimeout(() => {
abortController.abort()
}, CHAT_REQUEST_TIMEOUT_MS)

try {
// Send structured payload to maintain chat context
const payload: any = {
input:
typeof userMessage.content === 'string'
Expand All @@ -351,7 +338,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
conversationId,
}

// Add files if present (convert to base64 for JSON transmission)
if (files && files.length > 0) {
payload.files = await Promise.all(
files.map(async (file) => ({
Expand Down Expand Up @@ -379,7 +365,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
signal: abortController.signal,
})

// Clear timeout since request succeeded
clearTimeout(timeoutId)

if (!response.ok) {
Expand All @@ -392,7 +377,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
throw new Error('Response body is missing')
}

// Use the streaming hook with audio support
const shouldPlayAudio = isVoiceInput || isVoiceFirstMode
const audioHandler = shouldPlayAudio
? createAudioStreamHandler(
Expand Down Expand Up @@ -421,7 +405,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}
)
} catch (error: any) {
// Clear timeout in case of error
clearTimeout(timeoutId)

if (error.name === 'AbortError') {
Expand All @@ -442,7 +425,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}
}

// Stop audio when component unmounts or when streaming is stopped
useEffect(() => {
return () => {
stopAudio()
Expand All @@ -452,28 +434,23 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}
}, [stopAudio])

// Voice interruption - stop audio when user starts speaking
const handleVoiceInterruption = useCallback(() => {
stopAudio()

// Stop any ongoing streaming response
if (isStreamingResponse) {
stopStreaming(setMessages)
}
}, [isStreamingResponse, stopStreaming, setMessages, stopAudio])

// Handle voice mode activation
const handleVoiceStart = useCallback(() => {
setIsVoiceFirstMode(true)
}, [])

// Handle exiting voice mode
const handleExitVoiceMode = useCallback(() => {
setIsVoiceFirstMode(false)
stopAudio() // Stop any playing audio when exiting
stopAudio()
}, [stopAudio])

// Handle voice transcript from voice-first interface
const handleVoiceTranscript = useCallback(
(transcript: string) => {
logger.info('Received voice transcript:', transcript)
Expand All @@ -482,14 +459,11 @@ export default function ChatClient({ identifier }: { identifier: string }) {
[handleSendMessage]
)

// If error, show error message using the extracted component
if (error) {
return <ChatErrorState error={error} starCount={starCount} />
}

// If authentication is required, use the extracted components
if (authRequired) {
// Get title and description from the URL params or use defaults
const title = new URLSearchParams(window.location.search).get('title') || 'chat'
const primaryColor =
new URLSearchParams(window.location.search).get('color') || 'var(--brand-primary-hover-hex)'
Expand Down Expand Up @@ -526,12 +500,10 @@ export default function ChatClient({ identifier }: { identifier: string }) {
}
}

// Loading state while fetching config using the extracted component
if (!chatConfig) {
return <ChatLoadingState />
}

// Voice-first mode interface
if (isVoiceFirstMode) {
return (
<VoiceInterface
Expand All @@ -551,7 +523,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
)
}

// Standard text-based chat interface
return (
<div className='fixed inset-0 z-[100] flex flex-col bg-white text-foreground'>
{/* Header component */}
Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/templates/[id]/template.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { useSession } from '@/lib/auth/auth-client'
import { cn } from '@/lib/core/utils/cn'
import { getBaseUrl } from '@/lib/core/utils/urls'
import type { CredentialRequirement } from '@/lib/workflows/credentials/credential-extractor'
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/preview'
import { getBlock } from '@/blocks/registry'
import { useStarTemplate, useTemplate } from '@/hooks/queries/templates'

Expand Down
2 changes: 1 addition & 1 deletion apps/sim/app/templates/components/template-card.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Star, User } from 'lucide-react'
import { useParams, useRouter } from 'next/navigation'
import { VerifiedBadge } from '@/components/ui/verified-badge'
import { cn } from '@/lib/core/utils/cn'
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/preview'
import { getBlock } from '@/blocks/registry'
import { useStarTemplate } from '@/hooks/queries/templates'
import type { WorkflowState } from '@/stores/workflows/workflow/types'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
export { Dashboard } from './dashboard'
export { LogDetails } from './log-details'
export { ExecutionSnapshot } from './log-details/components/execution-snapshot'
export { FileCards } from './log-details/components/file-download'
export { FrozenCanvas } from './log-details/components/frozen-canvas'
export { TraceSpans } from './log-details/components/trace-spans'
export { LogRowContextMenu } from './log-row-context-menu'
export { LogsList } from './logs-list'
Expand Down
Loading
Loading