d25d482dc2
Publish CLI Package / publish-npm (push) Waiting to run
Publish Python SDK / publish-pypi (push) Waiting to run
Publish TypeScript SDK / publish-npm (push) Waiting to run
CI / Migrate Dev DB (push) Has been skipped
CI / Detect Version (push) Has been cancelled
CI / Migrate DB (push) Has been cancelled
CI / Build Dev ECR (./docker/app.Dockerfile, ECR_APP) (push) Has been cancelled
CI / Build Dev ECR (./docker/db.Dockerfile, ECR_MIGRATIONS) (push) Has been cancelled
CI / Build Dev ECR (./docker/pii.Dockerfile, ECR_PII) (push) Has been cancelled
CI / Build Dev ECR (./docker/realtime.Dockerfile, ECR_REALTIME) (push) Has been cancelled
CI / Deploy Trigger.dev (Dev) (push) Has been cancelled
CI / Build AMD64 (./docker/app.Dockerfile, ECR_APP, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build AMD64 (./docker/db.Dockerfile, ECR_MIGRATIONS, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build AMD64 (./docker/pii.Dockerfile, ECR_PII, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build AMD64 (./docker/realtime.Dockerfile, ECR_REALTIME, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/app.Dockerfile, ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/db.Dockerfile, ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/pii.Dockerfile, ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Build ARM64 (GHCR Only) (./docker/realtime.Dockerfile, ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/migrations) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/pii) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/realtime) (push) Has been cancelled
CI / Create GHCR Manifests (ghcr.io/simstudioai/simstudio) (push) Has been cancelled
CI / Check Docs Changes (push) Has been cancelled
CI / Process Docs (push) Has been cancelled
CI / Create GitHub Release (push) Has been cancelled
CI / Test and Build (push) Has been cancelled
205 lines
5.8 KiB
TypeScript
205 lines
5.8 KiB
TypeScript
import { RepeatIcon, SplitIcon } from 'lucide-react'
|
|
import { create } from 'zustand'
|
|
import { devtools } from 'zustand/middleware'
|
|
import { getToolOperationsIndex } from '@/lib/search/tool-operations'
|
|
import { getTriggersForSidebar } from '@/lib/workflows/triggers/trigger-utils'
|
|
import { getAllBlocks } from '@/blocks'
|
|
import type { BlockConfig, SubBlockConfig } from '@/blocks/types'
|
|
import type {
|
|
SearchBlockItem,
|
|
SearchData,
|
|
SearchDocItem,
|
|
SearchModalState,
|
|
SearchToolOperationItem,
|
|
} from './types'
|
|
|
|
const initialData: SearchData = {
|
|
blocks: [],
|
|
tools: [],
|
|
triggers: [],
|
|
toolOperations: [],
|
|
docs: [],
|
|
isInitialized: false,
|
|
}
|
|
|
|
type CommandSearchableOption = {
|
|
label: string
|
|
id: string
|
|
hidden?: boolean
|
|
}
|
|
|
|
function getCommandSearchableOptions(subBlock: SubBlockConfig): CommandSearchableOption[] {
|
|
if (!subBlock.options) return []
|
|
|
|
try {
|
|
const options = typeof subBlock.options === 'function' ? subBlock.options() : subBlock.options
|
|
return Array.isArray(options) ? options : []
|
|
} catch {
|
|
return []
|
|
}
|
|
}
|
|
|
|
export function buildCommandSearchableOptionSearchValue(block: BlockConfig): string {
|
|
const terms = new Set<string>()
|
|
|
|
for (const subBlock of block.subBlocks) {
|
|
if (
|
|
(subBlock.type !== 'dropdown' && subBlock.type !== 'combobox') ||
|
|
!subBlock.commandSearchable
|
|
) {
|
|
continue
|
|
}
|
|
|
|
for (const option of getCommandSearchableOptions(subBlock)) {
|
|
if (option.hidden) continue
|
|
|
|
const subBlockTitle = subBlock.title ?? subBlock.id
|
|
terms.add(subBlockTitle)
|
|
terms.add(option.label)
|
|
terms.add(option.id)
|
|
}
|
|
}
|
|
|
|
return Array.from(terms).join(' ')
|
|
}
|
|
|
|
export const useSearchModalStore = create<SearchModalState>()(
|
|
devtools(
|
|
(set, _) => ({
|
|
isOpen: false,
|
|
sections: null,
|
|
pendingConnect: null,
|
|
data: initialData,
|
|
|
|
setOpen: (open: boolean) => {
|
|
set({ isOpen: open, sections: null, pendingConnect: null })
|
|
},
|
|
|
|
open: (options) => {
|
|
set({
|
|
isOpen: true,
|
|
sections: options?.sections ?? null,
|
|
pendingConnect: options?.pendingConnect ?? null,
|
|
})
|
|
},
|
|
|
|
close: () => {
|
|
set({ isOpen: false, sections: null, pendingConnect: null })
|
|
},
|
|
|
|
initializeData: (filterBlocks) => {
|
|
const allBlocks = getAllBlocks()
|
|
const filteredAllBlocks = filterBlocks(allBlocks) as typeof allBlocks
|
|
|
|
const regularBlocks: SearchBlockItem[] = []
|
|
const tools: SearchBlockItem[] = []
|
|
const docs: SearchDocItem[] = []
|
|
|
|
for (const block of filteredAllBlocks) {
|
|
if (block.hideFromToolbar) continue
|
|
|
|
const searchItem: SearchBlockItem = {
|
|
id: block.type,
|
|
name: block.name,
|
|
icon: block.icon,
|
|
bgColor: block.bgColor || '#6B7280',
|
|
type: block.type,
|
|
searchValue: `${block.name} ${block.type} ${buildCommandSearchableOptionSearchValue(block)}`,
|
|
}
|
|
|
|
if (block.category === 'blocks' && block.type !== 'starter') {
|
|
regularBlocks.push(searchItem)
|
|
} else if (block.category === 'tools') {
|
|
tools.push(searchItem)
|
|
}
|
|
|
|
if (block.docsLink) {
|
|
docs.push({
|
|
id: `docs-${block.type}`,
|
|
name: block.name,
|
|
icon: block.icon,
|
|
href: block.docsLink,
|
|
})
|
|
}
|
|
}
|
|
|
|
const specialBlocks: SearchBlockItem[] = [
|
|
{
|
|
id: 'loop',
|
|
name: 'Loop',
|
|
icon: RepeatIcon,
|
|
bgColor: '#2FB3FF',
|
|
type: 'loop',
|
|
},
|
|
{
|
|
id: 'parallel',
|
|
name: 'Parallel',
|
|
icon: SplitIcon,
|
|
bgColor: '#FEE12B',
|
|
type: 'parallel',
|
|
},
|
|
]
|
|
|
|
const blocks = [...regularBlocks, ...(filterBlocks(specialBlocks) as SearchBlockItem[])]
|
|
|
|
const allTriggers = getTriggersForSidebar()
|
|
const filteredTriggers = filterBlocks(allTriggers) as typeof allTriggers
|
|
const priorityOrder = ['Start', 'Schedule', 'Webhook']
|
|
|
|
const sortedTriggers = [...filteredTriggers].sort(
|
|
(a: (typeof filteredTriggers)[number], b: (typeof filteredTriggers)[number]) => {
|
|
const aIndex = priorityOrder.indexOf(a.name)
|
|
const bIndex = priorityOrder.indexOf(b.name)
|
|
const aHasPriority = aIndex !== -1
|
|
const bHasPriority = bIndex !== -1
|
|
|
|
if (aHasPriority && bHasPriority) return aIndex - bIndex
|
|
if (aHasPriority) return -1
|
|
if (bHasPriority) return 1
|
|
return a.name.localeCompare(b.name)
|
|
}
|
|
)
|
|
|
|
const triggers = sortedTriggers.map(
|
|
(block): SearchBlockItem => ({
|
|
id: block.type,
|
|
name: block.name,
|
|
icon: block.icon,
|
|
bgColor: block.bgColor || '#6B7280',
|
|
type: block.type,
|
|
config: block,
|
|
})
|
|
)
|
|
|
|
const allowedBlockTypes = new Set(tools.map((t) => t.type))
|
|
const toolOperations: SearchToolOperationItem[] = getToolOperationsIndex()
|
|
.filter((op) => allowedBlockTypes.has(op.blockType))
|
|
.map((op) => {
|
|
const aliasesStr = op.aliases?.length ? ` ${op.aliases.join(' ')}` : ''
|
|
return {
|
|
id: op.id,
|
|
name: op.operationName,
|
|
searchValue: `${op.serviceName} ${op.operationName}${aliasesStr}`,
|
|
icon: op.icon,
|
|
bgColor: op.bgColor,
|
|
blockType: op.blockType,
|
|
operationId: op.operationId,
|
|
}
|
|
})
|
|
|
|
set({
|
|
data: {
|
|
blocks,
|
|
tools,
|
|
triggers,
|
|
toolOperations,
|
|
docs,
|
|
isInitialized: true,
|
|
},
|
|
})
|
|
},
|
|
}),
|
|
{ name: 'search-modal-store' }
|
|
)
|
|
)
|