Skip to content

Commit ea8c710

Browse files
committed
Merge branch 'fix/select' of github.com:simstudioai/sim into fix/select
2 parents b41d17c + 3f37b5c commit ea8c710

File tree

56 files changed

+1620
-1273
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

56 files changed

+1620
-1273
lines changed

apps/sim/app/api/webhooks/route.ts

Lines changed: 55 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -744,7 +744,7 @@ export async function POST(request: NextRequest) {
744744
if (savedWebhook && provider === 'grain') {
745745
logger.info(`[${requestId}] Grain provider detected. Creating Grain webhook subscription.`)
746746
try {
747-
const grainHookId = await createGrainWebhookSubscription(
747+
const grainResult = await createGrainWebhookSubscription(
748748
request,
749749
{
750750
id: savedWebhook.id,
@@ -754,11 +754,12 @@ export async function POST(request: NextRequest) {
754754
requestId
755755
)
756756

757-
if (grainHookId) {
758-
// Update the webhook record with the external Grain hook ID
757+
if (grainResult) {
758+
// Update the webhook record with the external Grain hook ID and event types for filtering
759759
const updatedConfig = {
760760
...(savedWebhook.providerConfig as Record<string, any>),
761-
externalId: grainHookId,
761+
externalId: grainResult.id,
762+
eventTypes: grainResult.eventTypes,
762763
}
763764
await db
764765
.update(webhook)
@@ -770,7 +771,8 @@ export async function POST(request: NextRequest) {
770771

771772
savedWebhook.providerConfig = updatedConfig
772773
logger.info(`[${requestId}] Successfully created Grain webhook`, {
773-
grainHookId,
774+
grainHookId: grainResult.id,
775+
eventTypes: grainResult.eventTypes,
774776
webhookId: savedWebhook.id,
775777
})
776778
}
@@ -1176,10 +1178,10 @@ async function createGrainWebhookSubscription(
11761178
request: NextRequest,
11771179
webhookData: any,
11781180
requestId: string
1179-
): Promise<string | undefined> {
1181+
): Promise<{ id: string; eventTypes: string[] } | undefined> {
11801182
try {
11811183
const { path, providerConfig } = webhookData
1182-
const { apiKey, includeHighlights, includeParticipants, includeAiSummary } =
1184+
const { apiKey, triggerId, includeHighlights, includeParticipants, includeAiSummary } =
11831185
providerConfig || {}
11841186

11851187
if (!apiKey) {
@@ -1191,12 +1193,53 @@ async function createGrainWebhookSubscription(
11911193
)
11921194
}
11931195

1196+
// Map trigger IDs to Grain API hook_type (only 2 options: recording_added, upload_status)
1197+
const hookTypeMap: Record<string, string> = {
1198+
grain_webhook: 'recording_added',
1199+
grain_recording_created: 'recording_added',
1200+
grain_recording_updated: 'recording_added',
1201+
grain_highlight_created: 'recording_added',
1202+
grain_highlight_updated: 'recording_added',
1203+
grain_story_created: 'recording_added',
1204+
grain_upload_status: 'upload_status',
1205+
}
1206+
1207+
const eventTypeMap: Record<string, string[]> = {
1208+
grain_webhook: [],
1209+
grain_recording_created: ['recording_added'],
1210+
grain_recording_updated: ['recording_updated'],
1211+
grain_highlight_created: ['highlight_created'],
1212+
grain_highlight_updated: ['highlight_updated'],
1213+
grain_story_created: ['story_created'],
1214+
grain_upload_status: ['upload_status'],
1215+
}
1216+
1217+
const hookType = hookTypeMap[triggerId] ?? 'recording_added'
1218+
const eventTypes = eventTypeMap[triggerId] ?? []
1219+
1220+
if (!hookTypeMap[triggerId]) {
1221+
logger.warn(
1222+
`[${requestId}] Unknown triggerId for Grain: ${triggerId}, defaulting to recording_added`,
1223+
{
1224+
webhookId: webhookData.id,
1225+
}
1226+
)
1227+
}
1228+
1229+
logger.info(`[${requestId}] Creating Grain webhook`, {
1230+
triggerId,
1231+
hookType,
1232+
eventTypes,
1233+
webhookId: webhookData.id,
1234+
})
1235+
11941236
const notificationUrl = `${getBaseUrl()}/api/webhooks/trigger/${path}`
11951237

11961238
const grainApiUrl = 'https://api.grain.com/_/public-api/v2/hooks/create'
11971239

11981240
const requestBody: Record<string, any> = {
11991241
hook_url: notificationUrl,
1242+
hook_type: hookType,
12001243
}
12011244

12021245
// Build include object based on configuration
@@ -1226,8 +1269,10 @@ async function createGrainWebhookSubscription(
12261269

12271270
const responseBody = await grainResponse.json()
12281271

1229-
if (!grainResponse.ok || responseBody.error) {
1272+
if (!grainResponse.ok || responseBody.error || responseBody.errors) {
1273+
logger.warn('[App] Grain response body:', responseBody)
12301274
const errorMessage =
1275+
responseBody.errors?.detail ||
12311276
responseBody.error?.message ||
12321277
responseBody.error ||
12331278
responseBody.message ||
@@ -1255,10 +1300,11 @@ async function createGrainWebhookSubscription(
12551300
`[${requestId}] Successfully created webhook in Grain for webhook ${webhookData.id}.`,
12561301
{
12571302
grainWebhookId: responseBody.id,
1303+
eventTypes,
12581304
}
12591305
)
12601306

1261-
return responseBody.id
1307+
return { id: responseBody.id, eventTypes }
12621308
} catch (error: any) {
12631309
logger.error(
12641310
`[${requestId}] Exception during Grain webhook creation for webhook ${webhookData.id}.`,

apps/sim/app/chat/[identifier]/chat.tsx

Lines changed: 1 addition & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
175175
const distanceFromBottom = scrollHeight - scrollTop - clientHeight
176176
setShowScrollButton(distanceFromBottom > 100)
177177

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

194-
// Reset user scroll tracking when streaming starts
195193
useEffect(() => {
196194
if (isStreamingResponse) {
197-
// Reset userHasScrolled when streaming starts
198195
setUserHasScrolled(false)
199196

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

217213
if (!response.ok) {
218-
// Check if auth is required
219214
if (response.status === 401) {
220215
const errorData = await response.json()
221216

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

239-
// Reset auth required state when authentication is successful
240234
setAuthRequired(null)
241235

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

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

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

311-
// Reset userHasScrolled when sending a new message
312303
setUserHasScrolled(false)
313304

314305
const userMessage: ChatMessage = {
@@ -325,24 +316,20 @@ export default function ChatClient({ identifier }: { identifier: string }) {
325316
})),
326317
}
327318

328-
// Add the user's message to the chat
329319
setMessages((prev) => [...prev, userMessage])
330320
setInputValue('')
331321
setIsLoading(true)
332322

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

338-
// Create abort controller for request cancellation
339327
const abortController = new AbortController()
340328
const timeoutId = setTimeout(() => {
341329
abortController.abort()
342330
}, CHAT_REQUEST_TIMEOUT_MS)
343331

344332
try {
345-
// Send structured payload to maintain chat context
346333
const payload: any = {
347334
input:
348335
typeof userMessage.content === 'string'
@@ -351,7 +338,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
351338
conversationId,
352339
}
353340

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

382-
// Clear timeout since request succeeded
383368
clearTimeout(timeoutId)
384369

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

395-
// Use the streaming hook with audio support
396380
const shouldPlayAudio = isVoiceInput || isVoiceFirstMode
397381
const audioHandler = shouldPlayAudio
398382
? createAudioStreamHandler(
@@ -421,7 +405,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
421405
}
422406
)
423407
} catch (error: any) {
424-
// Clear timeout in case of error
425408
clearTimeout(timeoutId)
426409

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

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

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

459-
// Stop any ongoing streaming response
460440
if (isStreamingResponse) {
461441
stopStreaming(setMessages)
462442
}
463443
}, [isStreamingResponse, stopStreaming, setMessages, stopAudio])
464444

465-
// Handle voice mode activation
466445
const handleVoiceStart = useCallback(() => {
467446
setIsVoiceFirstMode(true)
468447
}, [])
469448

470-
// Handle exiting voice mode
471449
const handleExitVoiceMode = useCallback(() => {
472450
setIsVoiceFirstMode(false)
473-
stopAudio() // Stop any playing audio when exiting
451+
stopAudio()
474452
}, [stopAudio])
475453

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

485-
// If error, show error message using the extracted component
486462
if (error) {
487463
return <ChatErrorState error={error} starCount={starCount} />
488464
}
489465

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

529-
// Loading state while fetching config using the extracted component
530503
if (!chatConfig) {
531504
return <ChatLoadingState />
532505
}
533506

534-
// Voice-first mode interface
535507
if (isVoiceFirstMode) {
536508
return (
537509
<VoiceInterface
@@ -551,7 +523,6 @@ export default function ChatClient({ identifier }: { identifier: string }) {
551523
)
552524
}
553525

554-
// Standard text-based chat interface
555526
return (
556527
<div className='fixed inset-0 z-[100] flex flex-col bg-white text-foreground'>
557528
{/* Header component */}

apps/sim/app/templates/[id]/template.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ import { useSession } from '@/lib/auth/auth-client'
3636
import { cn } from '@/lib/core/utils/cn'
3737
import { getBaseUrl } from '@/lib/core/utils/urls'
3838
import type { CredentialRequirement } from '@/lib/workflows/credentials/credential-extractor'
39-
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
39+
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/preview'
4040
import { getBlock } from '@/blocks/registry'
4141
import { useStarTemplate, useTemplate } from '@/hooks/queries/templates'
4242

apps/sim/app/templates/components/template-card.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { Star, User } from 'lucide-react'
44
import { useParams, useRouter } from 'next/navigation'
55
import { VerifiedBadge } from '@/components/ui/verified-badge'
66
import { cn } from '@/lib/core/utils/cn'
7-
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/workflow-preview/workflow-preview'
7+
import { WorkflowPreview } from '@/app/workspace/[workspaceId]/w/components/preview'
88
import { getBlock } from '@/blocks/registry'
99
import { useStarTemplate } from '@/hooks/queries/templates'
1010
import type { WorkflowState } from '@/stores/workflows/workflow/types'

apps/sim/app/workspace/[workspaceId]/logs/components/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
export { Dashboard } from './dashboard'
22
export { LogDetails } from './log-details'
3+
export { ExecutionSnapshot } from './log-details/components/execution-snapshot'
34
export { FileCards } from './log-details/components/file-download'
4-
export { FrozenCanvas } from './log-details/components/frozen-canvas'
55
export { TraceSpans } from './log-details/components/trace-spans'
66
export { LogRowContextMenu } from './log-row-context-menu'
77
export { LogsList } from './logs-list'

0 commit comments

Comments
 (0)