PR #20390 Research: Workflow Graph Search
Metadata
| Field | Value |
|---|---|
| Title | Workflow Graph Search |
| Author | Laila Los (ElectronicBlueberry) |
| Status | Merged |
| Merged | 2025-06-10T17:52:02Z |
| Merged By | Marius van den Beek (mvdbeek) |
| Base Branch | dev |
| Head Branch | workflow-search |
| Additions | 568 |
| Deletions | 3 |
| Labels | area/UI-UX, kind/feature, highlight |
| Closes | #20341 (Search workflow graph) |
| Commits | 16 |
High-Level Summary
Adds a search activity panel to the Galaxy workflow editor that lets users search across all elements in the workflow graph (steps, inputs, outputs, comments). Search results are displayed in a side panel list; clicking a result pans the canvas to center on that element and highlights it with a blinking green border animation.
Motivated by issue #20341: users with large workflows (25+ steps) had no way to locate specific steps/outputs by name.
Architecture Overview
- Activity registration — new “Search” activity added to the workflow editor activity bar
- Scoped Pinia store (
workflowSearchStore) — collects searchable data from steps/comments, performs fuzzy multi-keyword search with weighted scoring - Side panel component (
SearchPanel.vue) — wrapsDelayedInput+ result list insideActivityPanel - Canvas highlight component (
AreaHighlight.vue) — positioned absolutely in the workflow graph, uses CSS blink animation - Coordinate transform — converts screen-space DOM bounding rects to workflow-space coordinates using the
Transformclass from the geometry module
Data Flow
User types query -> SearchPanel -> workflowSearchStore.searchWorkflow(query)
-> collectSearchDataCached() -> iterates stepStore.steps + commentStore.comments
-> DOM queries for bounding rects -> inverse canvas transform -> SearchData[]
-> multi-keyword fuzzy match with weighted scoring -> sorted SearchResult[]
User clicks result -> emit('result-clicked', searchData)
-> Index.vue.onSearchResultClicked -> WorkflowGraph.moveToAndHighlightRegion(bounds)
-> d3Zoom.moveTo(center) + AreaHighlight.show(bounds)
Detailed File-by-File Breakdown
New Files (4)
1. client/src/stores/workflowSearchStore.ts (267 lines)
Core search logic. A scoped Pinia store (defineScopedStore) keyed by workflow ID.
Key exports:
SearchData— discriminated union type with variants:step,input,output,commentSearchResult— wrapsSearchDatawithmatchedKeys,score,weightedScoreuseWorkflowSearchStore(workflowId)— factory function
Search algorithm:
- Splits query into space-separated lowercase parts
- Two match modes: soft match (substring
includes) forname,label,annotation,text; exact match (===) for other fields liketype,stepType, etc. - Results must match ALL query parts (score >= queryParts.length)
- Weighted scoring:
toolId= 10,type= 5,name= 2, others default to occurrence count or 1 - Caches collected search data keyed by
undoRedoStore.changeId
Coordinate transform:
- Uses
getInverseCanvasTransform()to convert DOMgetBoundingClientRect()positions to workflow canvas coordinates - Depends on
stateStore.positionandstateStore.scale
2. client/src/components/Panels/SearchPanel.vue (112 lines)
UI panel for the search activity. Uses DelayedInput with 200ms delay, ActivityPanel wrapper, FontAwesomeIcon for result type icons, GButton for each result. Watches both currentQuery and undoRedoStore.changeId to re-run search on workflow changes. Emits result-clicked with SearchData payload.
Keyword filtering: Supports keywords step, input, output, comment to narrow results.
3. client/src/components/Workflow/Editor/AreaHighlight.vue (83 lines)
Visual highlight overlay. CSS @keyframes blink animation (1s, step-end), green border. show(area: Rectangle) method exposed via defineExpose. Resets blink state with 100ms wait to retrigger animation on repeated clicks.
4. client/src/components/Workflow/Editor/modules/itemIcons.ts (43 lines)
Icon mapping for search result types. Maps step subtypes (tool, data_input, etc.), input/output, and comment subtypes to FontAwesome 6 icons.
Modified Files (8)
5. client/src/components/Workflow/Editor/Index.vue (+8 lines)
- Imports/registers
SearchPanel, adds to template viaisActiveSideBar('workflow-editor-search') - Adds
onSearchResultClickedhandler callingworkflowGraph.value.moveToAndHighlightRegion()
6. client/src/components/Workflow/Editor/WorkflowGraph.vue (+20/-1 lines)
- Imports
AreaHighlightandRectangletype - Adds
watchontransformto sync position tostateStore.position - Adds
<AreaHighlight ref="areaHighlight" />inside canvas div - Exposes
moveToAndHighlightRegion(bounds: Rectangle)
7. client/src/components/Workflow/Editor/modules/activities.ts (+10/-1 lines)
- Adds search activity:
{ id: "workflow-editor-search", title: "Search", icon: faSearch, panel: true, visible: true }
8. client/src/composables/workflowStores.ts (+7 lines)
- Instantiates
searchStorein bothprovideScopedWorkflowStores()anduseWorkflowStores()
9. client/src/stores/undoRedoStore/index.ts (+9/-1 lines)
- Adds
changeIdref (monotonic counter for cache invalidation)
10. client/src/stores/workflowEditorStateStore.ts (+3 lines)
- Adds
positionref ([number, number]), synced from WorkflowGraph transform watcher
11. client/src/stores/workflowStepStore.ts (+1 line)
- Adds optional
type?: "data"toDataOutputinterface
12. client/src/components/Workflow/Editor/modules/terminals.test.ts (+3 lines)
- Adds
searchStoretouseStores()helper (required by updated composable)
Cross-Reference to Current Codebase
All 12 files exist at original paths. No files moved, renamed, or deleted.
| PR File Path | Current Status | Post-PR Changes |
|---|---|---|
client/src/stores/workflowSearchStore.ts | EXISTS, unchanged | Only original PR commit |
client/src/components/Panels/SearchPanel.vue | EXISTS, minor formatting | Prettier reformatting |
client/src/components/Workflow/Editor/AreaHighlight.vue | EXISTS, debug log removed | 1af60a25c0 removed console.log |
client/src/components/Workflow/Editor/modules/itemIcons.ts | EXISTS, unchanged | Only original PR commits |
client/src/components/Workflow/Editor/Index.vue | EXISTS, additional changes | Import standardization, SCSS fixes, type annotations |
client/src/components/Workflow/Editor/WorkflowGraph.vue | EXISTS, additional changes | Running step animations (added then reverted), prettier |
client/src/components/Workflow/Editor/modules/activities.ts | EXISTS | Unchanged from PR |
client/src/composables/workflowStores.ts | EXISTS, additional changes | Store ref-counting disposal, prettier, auto-layout |
client/src/stores/undoRedoStore/index.ts | EXISTS, minor changes | Prettier, best practice panel |
client/src/stores/workflowEditorStateStore.ts | EXISTS | Unchanged from PR additions |
client/src/stores/workflowStepStore.ts | EXISTS | Unchanged from PR addition |
client/src/components/Workflow/Editor/modules/terminals.test.ts | EXISTS | Unchanged from PR addition |
Key Dependencies (all present)
| Module | Path | Role |
|---|---|---|
defineScopedStore | client/src/stores/scopedStore.ts | Store factory for workflow-scoped Pinia stores |
Transform / Rectangle | client/src/components/Workflow/Editor/modules/geometry.ts | Coordinate math for canvas transform |
ActivityPanel | client/src/components/Panels/ActivityPanel.vue | Side panel wrapper |
DelayedInput | client/src/components/Common/DelayedInput.vue | Debounced text input |
GButton | client/src/components/BaseComponents/GButton.vue | Button component |
useD3Zoom | client/src/components/Workflow/Editor/composables/d3Zoom.ts | Canvas zoom/pan (provides moveTo) |
Key Architectural Decisions
-
Scoped store pattern —
defineScopedStorekeyed by workflow ID, ensuring search data isolation per workflow. A store scoping bug was fixed during review. -
DOM-based bounding rect collection — Bounds computed from live DOM via
getBoundingClientRect()+ inverse canvas transform. Pragmatic but couples search to rendered state. -
changeIdfor cache invalidation — Monotonic counter in undo/redo store avoids expensive DOM queries on every keystroke. -
Multi-keyword AND search — Query split on spaces; results must match ALL parts. Type keywords (
step,input,output,comment) act as filters. -
Weighted scoring —
toolId: 10,type: 5,name: 2. Results sorted byweightedScoredesc. -
Separation of highlight from search —
AreaHighlightis standalone in WorkflowGraph, reusable by other features.
Review Discussion Summary
- mvdbeek noted original issue was about searching the invocation graph (job results), not just editor. Author proposed merging editor search first, then adapting for invocation view.
- mvdbeek found a store scoping bug: opening a second workflow showed results from the first. Fixed via
changeIdcache invalidation. - PR merged without
kind/label initially (bot flagged), later corrected tokind/feature.
Tests
Minimal: only terminals.test.ts updated to include searchStore in useStores() helper. No dedicated unit tests for search algorithm or search store. PR marked for manual testing.
Verification
Date: 2026-02-11
Branch: fix_package_tests (based on current master)
Verified by: Cross-reference audit of all file paths, exports, APIs, and data flow.
Result: All references verified. No substantive discrepancies found.
- All 12 PR file paths exist at their original locations.
- All 6 key dependency modules exist and serve the described roles (
defineScopedStore,Transform/Rectangle,ActivityPanel,DelayedInput,GButton,useD3ZoomwithmoveTo). - Line counts for the 4 new files match exactly:
workflowSearchStore.ts(267),SearchPanel.vue(112),AreaHighlight.vue(83),itemIcons.ts(43). - All described exports confirmed:
SearchData(discriminated union withstep/input/output/commentvariants),SearchResult(withmatchedKeys/score/weightedScore/searchData),useWorkflowSearchStore,iconForType,getIconForSearchData. - Scoped store pattern via
defineScopedStoreconfirmed. Weighted scoring (toolId: 10,type: 5,name: 2) confirmed.changeIdcache invalidation confirmed. searchStorepresent in bothprovideScopedWorkflowStores()anduseWorkflowStores()inworkflowStores.ts, with post-PR addition ofuseTimeoutStoreDisposefor disposal.changeIdref (monotonic counter viawatchon undo stack length) confirmed inundoRedoStore/index.ts.positionref as[number, number]confirmed inworkflowEditorStateStore.ts.DataOutput.type?: "data"confirmed inworkflowStepStore.ts.- Search activity registration confirmed in
activities.tswithid: "workflow-editor-search",icon: faSearch. SearchPanelintegration inIndex.vueconfirmed:isActiveSideBar('workflow-editor-search'),onSearchResultClickedhandler.AreaHighlightintegration inWorkflowGraph.vueconfirmed:<AreaHighlight ref="areaHighlight" />,moveToAndHighlightRegion(bounds)exposed.- Data flow diagram verified end-to-end through code tracing.
terminals.test.tsincludessearchStoreviauseWorkflowSearchStoreas described.
Minor notes (no correction needed):
- The
workflowStores.tspost-PR changes now includeuseTimeoutStoreDisposeandonScopeDisposecleanup logic (store lifetime management), accurately noted in the cross-reference table as “Store ref-counting disposal”.