Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
39 changes: 25 additions & 14 deletions apps/sim/app/academy/components/sandbox-canvas-provider.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,12 @@ import type {
import { validateExercise } from '@/lib/academy/validation'
import { cn } from '@/lib/core/utils/cn'
import { getEffectiveBlockOutputs } from '@/lib/workflows/blocks/block-outputs'
import { getQueryClient } from '@/app/_shell/providers/get-query-client'
import { GlobalCommandsProvider } from '@/app/workspace/[workspaceId]/providers/global-commands-provider'
import { SandboxWorkspacePermissionsProvider } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
import Workflow from '@/app/workspace/[workspaceId]/w/[workflowId]/workflow'
import { getBlock } from '@/blocks/registry'
import { workflowKeys } from '@/hooks/queries/workflows'
import { SandboxBlockConstraintsContext } from '@/hooks/use-sandbox-block-constraints'
import { useExecutionStore } from '@/stores/execution/store'
import { useTerminalConsoleStore } from '@/stores/terminal/console/store'
Expand Down Expand Up @@ -218,8 +220,13 @@ export function SandboxCanvasProvider({

useWorkflowStore.getState().replaceWorkflowState(workflowState)
useSubBlockStore.getState().initializeFromWorkflow(workflowId, workflowState.blocks)
useWorkflowRegistry.setState((state) => ({
workflows: { ...state.workflows, [workflowId]: syntheticMetadata },

const qc = getQueryClient()
const cacheKey = workflowKeys.list(SANDBOX_WORKSPACE_ID, 'active')
const cached = qc.getQueryData<WorkflowMetadata[]>(cacheKey) ?? []
qc.setQueryData(cacheKey, [...cached.filter((w) => w.id !== workflowId), syntheticMetadata])

useWorkflowRegistry.setState({
activeWorkflowId: workflowId,
hydration: {
phase: 'ready',
Expand All @@ -228,7 +235,7 @@ export function SandboxCanvasProvider({
requestId: null,
error: null,
},
}))
})

logger.info('Sandbox stores hydrated', { workflowId })
setIsReady(true)
Expand Down Expand Up @@ -262,17 +269,21 @@ export function SandboxCanvasProvider({
unsubWorkflow()
unsubSubBlock()
unsubExecution()
useWorkflowRegistry.setState((state) => {
const { [workflowId]: _removed, ...rest } = state.workflows
return {
workflows: rest,
activeWorkflowId: state.activeWorkflowId === workflowId ? null : state.activeWorkflowId,
hydration:
state.hydration.workflowId === workflowId
? { phase: 'idle', workspaceId: null, workflowId: null, requestId: null, error: null }
: state.hydration,
}
})
const cleanupQc = getQueryClient()
const cleanupKey = workflowKeys.list(SANDBOX_WORKSPACE_ID, 'active')
const cleanupCached = cleanupQc.getQueryData<WorkflowMetadata[]>(cleanupKey) ?? []
cleanupQc.setQueryData(
cleanupKey,
cleanupCached.filter((w) => w.id !== workflowId)
)

useWorkflowRegistry.setState((state) => ({
activeWorkflowId: state.activeWorkflowId === workflowId ? null : state.activeWorkflowId,
hydration:
state.hydration.workflowId === workflowId
? { phase: 'idle', workspaceId: null, workflowId: null, requestId: null, error: null }
: state.hydration,
}))
useWorkflowStore.setState({ blocks: {}, edges: [], loops: {}, parallels: {} })
useSubBlockStore.setState((state) => {
const { [workflowId]: _removed, ...rest } = state.workflowValues
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ export function useAvailableResources(
workspaceId: string,
existingKeys: Set<string>
): AvailableItemsByType[] {
const { data: workflows = [] } = useWorkflows(workspaceId, { syncRegistry: false })
const { data: workflows = [] } = useWorkflows(workspaceId)
const { data: tables = [] } = useTablesList(workspaceId)
const { data: files = [] } = useWorkspaceFiles(workspaceId)
const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ import {
import { Table } from '@/app/workspace/[workspaceId]/tables/[tableId]/components'
import { useUsageLimits } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/hooks'
import { useWorkflowExecution } from '@/app/workspace/[workspaceId]/w/[workflowId]/hooks/use-workflow-execution'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useWorkspaceFiles } from '@/hooks/queries/workspace-files'
import { useSettingsNavigation } from '@/hooks/use-settings-navigation'
import { useExecutionStore } from '@/stores/execution/store'
Expand Down Expand Up @@ -375,10 +376,12 @@ interface EmbeddedWorkflowProps {
}

function EmbeddedWorkflow({ workspaceId, workflowId }: EmbeddedWorkflowProps) {
const workflowExists = useWorkflowRegistry((state) => Boolean(state.workflows[workflowId]))
const isMetadataLoaded = useWorkflowRegistry(
(state) => state.hydration.phase !== 'idle' && state.hydration.phase !== 'metadata-loading'
const { data: workflowList } = useWorkflows(workspaceId)
const workflowExists = useMemo(
() => (workflowList ?? []).some((w) => w.id === workflowId),
[workflowList, workflowId]
)
const isMetadataLoaded = useWorkflowRegistry((state) => state.hydration.phase !== 'idle')
const hasLoadError = useWorkflowRegistry(
(state) => state.hydration.phase === 'error' && state.hydration.workflowId === workflowId
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
'use client'

import type { ElementType, ReactNode } from 'react'
import { type ElementType, type ReactNode, useMemo } from 'react'
import type { QueryClient } from '@tanstack/react-query'
import { useParams } from 'next/navigation'
import {
Database,
File as FileIcon,
Expand All @@ -17,9 +18,8 @@ import type {
} from '@/app/workspace/[workspaceId]/home/types'
import { knowledgeKeys } from '@/hooks/queries/kb/knowledge'
import { tableKeys } from '@/hooks/queries/tables'
import { workflowKeys } from '@/hooks/queries/workflows'
import { useWorkflows, workflowKeys } from '@/hooks/queries/workflows'
import { workspaceFilesKeys } from '@/hooks/queries/workspace-files'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'

interface DropdownItemRenderProps {
item: { id: string; name: string; [key: string]: unknown }
Expand All @@ -34,7 +34,12 @@ export interface ResourceTypeConfig {
}

function WorkflowTabSquare({ workflowId, className }: { workflowId: string; className?: string }) {
const color = useWorkflowRegistry((state) => state.workflows[workflowId]?.color ?? '#888')
const { workspaceId } = useParams<{ workspaceId: string }>()
const { data: workflowList } = useWorkflows(workspaceId)
const color = useMemo(() => {
const wf = (workflowList ?? []).find((w) => w.id === workflowId)
return wf?.color ?? '#888'
}, [workflowList, workflowId])
return (
<div
className={cn('flex-shrink-0 rounded-[3px] border-[2px]', className)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ const PREVIEW_MODE_LABELS: Record<PreviewMode, string> = {
* tabs always reflect the latest name even after a rename.
*/
function useResourceNameLookup(workspaceId: string): Map<string, string> {
const { data: workflows = [] } = useWorkflows(workspaceId, { syncRegistry: false })
const { data: workflows = [] } = useWorkflows(workspaceId)
const { data: tables = [] } = useTablesList(workspaceId)
const { data: files = [] } = useWorkspaceFiles(workspaceId)
const { data: knowledgeBases } = useKnowledgeBasesQuery(workspaceId)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,8 +45,8 @@ import {
computeMentionHighlightRanges,
extractContextTokens,
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/copilot/components/user-input/utils'
import { getWorkflows } from '@/hooks/queries/workflows'
import type { ChatContext } from '@/stores/panel'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'

export type { FileAttachmentForApi } from '@/app/workspace/[workspaceId]/home/types'

Expand Down Expand Up @@ -639,7 +639,7 @@ export function UserInput({
case 'workflow':
case 'current_workflow': {
const wfId = (matchingCtx as { workflowId: string }).workflowId
const wfColor = useWorkflowRegistry.getState().workflows[wfId]?.color ?? '#888'
const wfColor = getWorkflows().find((w) => w.id === wfId)?.color ?? '#888'
mentionIconNode = (
<div
className='absolute inset-0 m-auto h-[12px] w-[12px] rounded-[3px] border-[2px]'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,11 @@
'use client'

import { useMemo } from 'react'
import { useParams } from 'next/navigation'
import { Database, Table as TableIcon } from '@/components/emcn/icons'
import { getDocumentIcon } from '@/components/icons/document-icons'
import type { ChatMessageContext } from '@/app/workspace/[workspaceId]/home/types'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflows } from '@/hooks/queries/workflows'

const USER_MESSAGE_CLASSES =
'whitespace-pre-wrap break-words [overflow-wrap:anywhere] font-[430] font-[family-name:var(--font-inter)] text-base text-[var(--text-primary)] leading-[23px] tracking-[0] antialiased'
Expand Down Expand Up @@ -44,12 +46,12 @@ function computeMentionRanges(text: string, contexts: ChatMessageContext[]): Men
}

function MentionHighlight({ context }: { context: ChatMessageContext }) {
const workflowColor = useWorkflowRegistry((state) => {
if (context.kind === 'workflow' || context.kind === 'current_workflow') {
return state.workflows[context.workflowId || '']?.color ?? null
}
return null
})
const { workspaceId } = useParams<{ workspaceId: string }>()
const { data: workflowList } = useWorkflows(workspaceId)
const workflowColor = useMemo(() => {
if (context.kind !== 'workflow' && context.kind !== 'current_workflow') return null
return (workflowList ?? []).find((w) => w.id === context.workflowId)?.color ?? null
}, [workflowList, context.kind, context.workflowId])

let icon: React.ReactNode = null
const iconClasses = 'h-[12px] w-[12px] flex-shrink-0 text-[var(--text-icon)]'
Expand Down
44 changes: 23 additions & 21 deletions apps/sim/app/workspace/[workspaceId]/home/hooks/use-chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import {
import { VFS_DIR_TO_RESOURCE } from '@/lib/copilot/resource-types'
import { isWorkflowToolName } from '@/lib/copilot/workflow-tools'
import { getNextWorkflowColor } from '@/lib/workflows/colors'
import { getQueryClient } from '@/app/_shell/providers/get-query-client'
import { invalidateResourceQueries } from '@/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-registry'
import { deploymentKeys } from '@/hooks/queries/deployments'
import {
Expand All @@ -35,7 +36,7 @@ import {
useChatHistory,
} from '@/hooks/queries/tasks'
import { getTopInsertionSortOrder } from '@/hooks/queries/utils/top-insertion-sort-order'
import { workflowKeys } from '@/hooks/queries/workflows'
import { getWorkflows, workflowKeys } from '@/hooks/queries/workflows'
import { useExecutionStream } from '@/hooks/use-execution-stream'
import { useExecutionStore } from '@/stores/execution/store'
import { useFolderStore } from '@/stores/folders/store'
Expand Down Expand Up @@ -301,31 +302,32 @@ function getPayloadData(payload: SSEPayload): SSEPayloadData | undefined {
return typeof payload.data === 'object' ? payload.data : undefined
}

/** Adds a workflow to the registry with a top-insertion sort order if it doesn't already exist. */
/** Adds a workflow to the React Query cache with a top-insertion sort order if it doesn't already exist. */
function ensureWorkflowInRegistry(resourceId: string, title: string, workspaceId: string): boolean {
const registry = useWorkflowRegistry.getState()
if (registry.workflows[resourceId]) return false
const workflows = getWorkflows(workspaceId)
if (workflows.find((w) => w.id === resourceId)) return false
const sortOrder = getTopInsertionSortOrder(
registry.workflows,
Object.fromEntries(workflows.map((w) => [w.id, w])),
useFolderStore.getState().folders,
workspaceId,
null
)
useWorkflowRegistry.setState((state) => ({
workflows: {
...state.workflows,
[resourceId]: {
id: resourceId,
name: title,
lastModified: new Date(),
createdAt: new Date(),
color: getNextWorkflowColor(),
workspaceId,
folderId: null,
sortOrder,
},
},
}))
const newMetadata: import('@/stores/workflows/registry/types').WorkflowMetadata = {
id: resourceId,
name: title,
lastModified: new Date(),
createdAt: new Date(),
color: getNextWorkflowColor(),
workspaceId,
folderId: null,
sortOrder,
}
const queryClient = getQueryClient()
const key = workflowKeys.list(workspaceId, 'active')
const current =
queryClient.getQueryData<import('@/stores/workflows/registry/types').WorkflowMetadata[]>(key) ??
[]
queryClient.setQueryData(key, [...current, newMetadata])
return true
}

Expand Down Expand Up @@ -1253,7 +1255,7 @@ export function useChat(
? ((args as Record<string, unknown>).workflowId as string)
: useWorkflowRegistry.getState().activeWorkflowId
if (targetWorkflowId) {
const meta = useWorkflowRegistry.getState().workflows[targetWorkflowId]
const meta = getWorkflows().find((w) => w.id === targetWorkflowId)
const wasAdded = addResource({
type: 'workflow',
id: targetWorkflowId,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { memo } from 'react'
import { useParams } from 'next/navigation'
import { cn } from '@/lib/core/utils/cn'
import {
DELETED_WORKFLOW_COLOR,
DELETED_WORKFLOW_LABEL,
} from '@/app/workspace/[workspaceId]/logs/utils'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { useWorkflowMap } from '@/hooks/queries/workflows'
import { StatusBar, type StatusBarSegment } from '..'

export interface WorkflowExecutionItem {
Expand Down Expand Up @@ -36,7 +37,8 @@ function WorkflowsListInner({
searchQuery: string
segmentDurationMs: number
}) {
const workflows = useWorkflowRegistry((s) => s.workflows)
const { workspaceId } = useParams<{ workspaceId: string }>()
const { data: workflows = {} } = useWorkflowMap(workspaceId)

return (
<div className='flex h-full flex-col overflow-hidden rounded-md bg-[var(--surface-2)] dark:bg-[var(--surface-1)]'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@

import { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react'
import { Loader2 } from 'lucide-react'
import { useParams } from 'next/navigation'
import { useShallow } from 'zustand/react/shallow'
import { Skeleton } from '@/components/emcn'
import { formatLatency } from '@/app/workspace/[workspaceId]/logs/utils'
import type { DashboardStatsResponse, WorkflowStats } from '@/hooks/queries/logs'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useFilterStore } from '@/stores/logs/filters/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
import { LineChart, WorkflowsList } from './components'

interface WorkflowExecution {
Expand Down Expand Up @@ -156,7 +157,8 @@ function DashboardInner({ stats, isLoading, error }: DashboardProps) {
}))
)

const allWorkflows = useWorkflowRegistry((state) => state.workflows)
const { workspaceId } = useParams<{ workspaceId: string }>()
const { data: allWorkflowList = [] } = useWorkflows(workspaceId)

const expandedWorkflowId = workflowIds.length === 1 ? workflowIds[0] : null

Expand Down Expand Up @@ -459,7 +461,7 @@ function DashboardInner({ stats, isLoading, error }: DashboardProps) {
)
}

if (Object.keys(allWorkflows).length === 0) {
if (allWorkflowList.length === 0) {
return (
<div className='mt-6 flex flex-1 items-center justify-center'>
<div className='text-center text-[var(--text-secondary)]'>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ export function WorkflowSelector({
onChange,
error,
}: WorkflowSelectorProps) {
const { data: workflows = [], isPending: isLoading } = useWorkflows(workspaceId, {
syncRegistry: false,
})
const { data: workflows = [], isPending: isLoading } = useWorkflows(workspaceId)

const options: ComboboxOption[] = useMemo(() => {
return workflows.map((w) => ({
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
import { useEffect, useMemo, useRef, useState } from 'react'
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'
import { Search, X } from 'lucide-react'
import { useParams } from 'next/navigation'
import { Badge } from '@/components/emcn'
import { cn } from '@/lib/core/utils/cn'
import { getTriggerOptions } from '@/lib/logs/get-trigger-options'
Expand All @@ -14,8 +15,8 @@ import {
type WorkflowData,
} from '@/lib/logs/search-suggestions'
import { useSearchState } from '@/app/workspace/[workspaceId]/logs/hooks/use-search-state'
import { useWorkflows } from '@/hooks/queries/workflows'
import { useFolderStore } from '@/stores/folders/store'
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'

function truncateFilterValue(field: string, value: string): string {
if ((field === 'executionId' || field === 'workflowId') && value.length > 12) {
Expand All @@ -42,16 +43,17 @@ export function AutocompleteSearch({
className,
onOpenChange,
}: AutocompleteSearchProps) {
const workflows = useWorkflowRegistry((state) => state.workflows)
const { workspaceId } = useParams<{ workspaceId: string }>()
const { data: workflowList = [] } = useWorkflows(workspaceId)
const folders = useFolderStore((state) => state.folders)

const workflowsData = useMemo<WorkflowData[]>(() => {
return Object.values(workflows).map((w) => ({
return workflowList.map((w) => ({
id: w.id,
name: w.name,
description: w.description,
}))
}, [workflows])
}, [workflowList])

const foldersData = useMemo<FolderData[]>(() => {
return Object.values(folders).map((f) => ({
Expand Down
Loading
Loading