From 253d695714e478d85a9dfa6de5f7c4cf818a6c19 Mon Sep 17 00:00:00 2001 From: GuanMu Date: Sat, 19 Jul 2025 02:50:08 +0000 Subject: [PATCH] feat: Enhance node search functionality, add search result node data interface, optimize layout constants and search result processing logic --- .../workflow/operator/node-search.tsx | 47 +++++++++++++++---- web/app/components/workflow/panel/record.tsx | 8 ++-- 2 files changed, 43 insertions(+), 12 deletions(-) diff --git a/web/app/components/workflow/operator/node-search.tsx b/web/app/components/workflow/operator/node-search.tsx index e889ebae36..455806e6e7 100644 --- a/web/app/components/workflow/operator/node-search.tsx +++ b/web/app/components/workflow/operator/node-search.tsx @@ -9,8 +9,36 @@ import { useStore } from '../store' import { getKeyboardKeyCodeBySystem, isEventTargetInputArea } from '../utils' import cn from '@/utils/classnames' import { PortalToFollowElem, PortalToFollowElemContent, PortalToFollowElemTrigger } from '@/app/components/base/portal-to-follow-elem' +import type { BlockEnum, CommonNodeType } from '../types' + +// Constants for layout dimensions and behavior +const SEARCH_LAYOUT_CONSTANTS = { + DEFAULT_CANVAS_WIDTH: 800, + CANVAS_MARGIN: 40, + MAX_RESULTS_WIDTH: 320, + MAX_SEARCH_RESULTS: 8, + BLUR_DELAY_MS: 200, +} as const + +// Interface for search result node data +type SearchResultNode = { + blockType: BlockEnum + nodeData: CommonNodeType + title: string + type: string + desc?: string +} + +// Props interface for SearchResultItem component +type SearchResultItemProps = { + node: SearchResultNode + searchQuery: string + isSelected: boolean + onClick: () => void + highlightMatch: (text: string, query: string) => React.ReactNode +} -const SearchResultItem = ({ node, searchQuery, isSelected, onClick, highlightMatch }: any) => { +const SearchResultItem = ({ node, searchQuery, isSelected, onClick, highlightMatch }: SearchResultItemProps) => { const toolIcon = useToolIcon(node.nodeData) return ( @@ -106,7 +134,7 @@ const NodeSearch = () => { const filteredNodes = nodes.filter((node) => { if (!node.id || !node.data || node.type === 'sticky') return false - const nodeData = node.data as any + const nodeData = node.data as CommonNodeType const nodeType = nodeData?.type const internalStartNodes = ['iteration-start', 'loop-start'] @@ -115,7 +143,7 @@ const NodeSearch = () => { const result = filteredNodes .map((node) => { - const nodeData = node.data as any + const nodeData = node.data as CommonNodeType const processedNode = { id: node.id, @@ -155,7 +183,7 @@ const NodeSearch = () => { }) .filter((node): node is NonNullable => node !== null) .sort((a, b) => b.score - a.score) - .slice(0, 8) + .slice(0, SEARCH_LAYOUT_CONSTANTS.MAX_SEARCH_RESULTS) return results }, [searchableNodes, searchQuery]) @@ -234,10 +262,13 @@ const NodeSearch = () => { setTimeout(() => { setIsOpen(false) setSelectedIndex(-1) - }, 200) + }, SEARCH_LAYOUT_CONSTANTS.BLUR_DELAY_MS) }, []) - const maxResultsWidth = Math.min((workflowCanvasWidth || 800) - 40, 320) + const maxResultsWidth = Math.min( + (workflowCanvasWidth || SEARCH_LAYOUT_CONSTANTS.DEFAULT_CANVAS_WIDTH) - SEARCH_LAYOUT_CONSTANTS.CANVAS_MARGIN, + SEARCH_LAYOUT_CONSTANTS.MAX_RESULTS_WIDTH, + ) return (
@@ -250,7 +281,7 @@ const NodeSearch = () => {
{ setSearchQuery(e.target.value) @@ -305,7 +336,7 @@ const NodeSearch = () => {
) : searchQuery.trim() && ( -
+
{t('workflow.operator.noNodesFound')}
{t('workflow.operator.searchHint')}
diff --git a/web/app/components/workflow/panel/record.tsx b/web/app/components/workflow/panel/record.tsx index 534bc633a6..c8a63f094c 100644 --- a/web/app/components/workflow/panel/record.tsx +++ b/web/app/components/workflow/panel/record.tsx @@ -1,5 +1,5 @@ import { memo, useCallback } from 'react' -import type { WorkflowDataUpdater } from '../types' +import type { WorkflowRunDetailResponse } from '@/models/log' import Run from '../run' import { useStore } from '../store' import { useWorkflowUpdate } from '../hooks' @@ -9,12 +9,12 @@ const Record = () => { const historyWorkflowData = useStore(s => s.historyWorkflowData) const { handleUpdateWorkflowCanvas } = useWorkflowUpdate() - const handleResultCallback = useCallback((res: any) => { - const graph: WorkflowDataUpdater = res.graph + const handleResultCallback = useCallback((res: WorkflowRunDetailResponse) => { + const graph = res.graph handleUpdateWorkflowCanvas({ nodes: graph.nodes, edges: graph.edges, - viewport: graph.viewport, + viewport: graph.viewport || { x: 0, y: 0, zoom: 1 }, }) }, [handleUpdateWorkflowCanvas])