Skip to content

Commit 241ee2f

Browse files
waleedlatif1claude
andcommitted
improvement(workflow-search): include block names in in-workflow search
Adds block names to the workflow search index alongside subblock content. Selecting a block-name match navigates to the block and highlights its title in the editor header with the same orange treatment used for subblock labels. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
1 parent 6c755cb commit 241ee2f

6 files changed

Lines changed: 73 additions & 1 deletion

File tree

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

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import {
3232
SubBlock,
3333
SubflowEditor,
3434
} from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components'
35+
import { WORKFLOW_SEARCH_HIGHLIGHT_CLASS } from '@/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/editor/components/constants'
3536
import {
3637
useBlockConnections,
3738
useConnectionsResize,
@@ -104,6 +105,8 @@ export function Editor() {
104105
const currentBlock = currentBlockId ? currentWorkflow.getBlockById(currentBlockId) : null
105106
const blockConfig = currentBlock ? getBlock(currentBlock.type) : null
106107
const title = currentBlock?.name || 'Editor'
108+
const isBlockNameSearchHighlighted =
109+
activeSearchTarget?.targetKind === 'block-name' && activeSearchTarget.blockId === currentBlockId
107110

108111
const isSubflow =
109112
currentBlock && (currentBlock.type === 'loop' || currentBlock.type === 'parallel')
@@ -253,6 +256,7 @@ export function Editor() {
253256

254257
useEffect(() => {
255258
if (!activeSearchTarget || activeSearchTarget.blockId !== currentBlockId) return
259+
if (activeSearchTarget.targetKind === 'block-name') return
256260
const container = subBlocksRef.current
257261
if (!container) return
258262

@@ -402,7 +406,11 @@ export function Editor() {
402406
}
403407
}}
404408
>
405-
{title}
409+
{isBlockNameSearchHighlighted ? (
410+
<mark className={WORKFLOW_SEARCH_HIGHLIGHT_CLASS}>{title}</mark>
411+
) : (
412+
title
413+
)}
406414
</h2>
407415
)}
408416
</div>

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/search-replace/workflow-search-replace.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -265,6 +265,7 @@ export function WorkflowSearchReplace() {
265265
canonicalSubBlockId: match.canonicalSubBlockId,
266266
valuePath: match.valuePath,
267267
kind: match.kind,
268+
targetKind: match.target.kind,
268269
resourceGroupKey: match.resource?.resourceGroupKey,
269270
})
270271
},

apps/sim/lib/workflows/search-replace/indexer.test.ts

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,39 @@ describe('indexWorkflowSearchMatches', () => {
3131
expect(matches.at(-1)?.reason).toBe('Block is locked')
3232
})
3333

34+
it('finds matches in block names', () => {
35+
const workflow = createSearchReplaceWorkflowFixture()
36+
37+
const matches = indexWorkflowSearchMatches({
38+
workflow,
39+
query: 'agent',
40+
mode: 'text',
41+
blockConfigs: SEARCH_REPLACE_BLOCK_CONFIGS,
42+
})
43+
44+
const blockNameMatches = matches.filter((match) => match.target.kind === 'block-name')
45+
expect(blockNameMatches.map((match) => [match.blockId, match.rawValue])).toEqual([
46+
['agent-1', 'Agent'],
47+
['locked-1', 'Agent'],
48+
])
49+
expect(blockNameMatches.every((match) => match.editable === false)).toBe(true)
50+
expect(blockNameMatches.every((match) => match.navigable === true)).toBe(true)
51+
expect(blockNameMatches[0]?.fieldTitle).toBe('Block name')
52+
})
53+
54+
it('does not include block-name matches in resource-only mode', () => {
55+
const workflow = createSearchReplaceWorkflowFixture()
56+
57+
const matches = indexWorkflowSearchMatches({
58+
workflow,
59+
query: 'agent',
60+
mode: 'resource',
61+
blockConfigs: SEARCH_REPLACE_BLOCK_CONFIGS,
62+
})
63+
64+
expect(matches.some((match) => match.target.kind === 'block-name')).toBe(false)
65+
})
66+
3467
it('does not index internal row metadata in structured subblock values', () => {
3568
const workflow = createSearchReplaceWorkflowFixture()
3669

apps/sim/lib/workflows/search-replace/indexer.ts

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,32 @@ export function indexWorkflowSearchMatches(
982982
const protectedByLock = isWorkflowBlockProtected(block.id, workflow.blocks)
983983
const editable = !protectedByLock && !isReadOnly
984984

985+
if (mode !== 'resource' && query && typeof block.name === 'string' && block.name.length > 0) {
986+
const blockNameRanges = findTextRanges(block.name, query, caseSensitive)
987+
blockNameRanges.forEach((range, occurrenceIndex) => {
988+
matches.push({
989+
id: createMatchId(['block-name', block.id, range.start, occurrenceIndex]),
990+
blockId: block.id,
991+
blockName: block.name,
992+
blockType: block.type,
993+
subBlockId: '',
994+
canonicalSubBlockId: '',
995+
subBlockType: 'short-input',
996+
fieldTitle: 'Block name',
997+
valuePath: [],
998+
target: { kind: 'block-name' },
999+
kind: 'text',
1000+
rawValue: block.name.slice(range.start, range.end),
1001+
searchText: block.name,
1002+
range,
1003+
editable: false,
1004+
navigable: true,
1005+
protected: protectedByLock,
1006+
reason: 'Block names cannot be edited via replace',
1007+
})
1008+
})
1009+
}
1010+
9851011
if (mode !== 'resource') {
9861012
for (const field of getWorkflowSearchSubflowFields(block)) {
9871013
const fieldEditable = editable && field.editable

apps/sim/lib/workflows/search-replace/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ export interface WorkflowSearchResourceMeta {
4545
export type WorkflowSearchTarget =
4646
| { kind: 'subblock' }
4747
| { kind: 'subflow'; fieldId: WorkflowSearchSubflowFieldId }
48+
| { kind: 'block-name' }
4849

4950
export interface WorkflowSearchMatch {
5051
id: string

apps/sim/stores/panel/editor/store.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,13 +7,16 @@ import { usePanelStore } from '../store'
77

88
let renameCallback: (() => void) | null = null
99

10+
export type ActiveSearchTargetKind = 'subblock' | 'subflow' | 'block-name'
11+
1012
export interface ActiveSearchTarget {
1113
matchId: string
1214
blockId: string
1315
subBlockId: string
1416
canonicalSubBlockId: string
1517
valuePath: Array<string | number>
1618
kind: string
19+
targetKind: ActiveSearchTargetKind
1720
resourceGroupKey?: string
1821
}
1922

0 commit comments

Comments
 (0)