From 9dbb06fccc51cc1417589dca5fda3574403b7708 Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 11 Jul 2025 15:58:26 +0800 Subject: [PATCH 1/5] fix: Fix node panel positioning issue when chat log modal is open --- .../_base/components/workflow-panel/index.tsx | 28 +++++++++++-------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx index a321c23430..cd2a707794 100644 --- a/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx +++ b/web/app/components/workflow/nodes/_base/components/workflow-panel/index.tsx @@ -120,6 +120,7 @@ const BasePanel: FC = ({ return if (workflowCanvasWidth - 400 <= nodePanelWidth + otherPanelWidth) debounceUpdate(workflowCanvasWidth - 400 - otherPanelWidth) + // eslint-disable-next-line react-hooks/exhaustive-deps }, [nodePanelWidth, otherPanelWidth, workflowCanvasWidth, updateNodePanelWidth]) const { handleNodeSelect } = useNodesInteractions() @@ -151,11 +152,11 @@ const BasePanel: FC = ({ const [isPaused, setIsPaused] = useState(false) useEffect(() => { - if(data._singleRunningStatus === NodeRunningStatus.Running) { + if (data._singleRunningStatus === NodeRunningStatus.Running) { hasClickRunning.current = true setIsPaused(false) } - else if(data._isSingleRun && data._singleRunningStatus === undefined && hasClickRunning) { + else if (data._isSingleRun && data._singleRunningStatus === undefined && hasClickRunning) { setIsPaused(true) hasClickRunning.current = false } @@ -216,9 +217,9 @@ const BasePanel: FC = ({ return {} })() - if(logParams.showSpecialResultPanel) { + if (logParams.showSpecialResultPanel) { return ( -
= ({ } return ( -
+
@@ -280,7 +286,7 @@ const BasePanel: FC = ({
= ({
{ - if(isSingleRunning) { + if (isSingleRunning) { handleNodeDataUpdate({ id, data: { @@ -324,7 +330,7 @@ const BasePanel: FC = ({ > { isSingleRunning ? - : + : }
From 50e16f83625a8df313232d2c0760473e181f65ba Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 11 Jul 2025 16:41:01 +0800 Subject: [PATCH 2/5] refactor: optimize state selection in data source components using useShallow for improved performance --- web/app/components/app-sidebar/navLink.tsx | 8 ++-- .../data-source/online-documents/index.tsx | 34 +++++++++------ .../data-source/online-drive/index.tsx | 35 +++++++++------ .../data-source/website-crawl/index.tsx | 16 +++++-- .../documents/create-from-pipeline/hooks.ts | 43 ++++++++++++++----- .../components/panel/test-run/index.tsx | 16 +++++-- 6 files changed, 105 insertions(+), 47 deletions(-) diff --git a/web/app/components/app-sidebar/navLink.tsx b/web/app/components/app-sidebar/navLink.tsx index 26d4b4d8dd..b204a1cea4 100644 --- a/web/app/components/app-sidebar/navLink.tsx +++ b/web/app/components/app-sidebar/navLink.tsx @@ -1,5 +1,5 @@ 'use client' - +import React from 'react' import { useSelectedLayoutSegment } from 'next/navigation' import Link from 'next/link' import classNames from '@/utils/classnames' @@ -22,13 +22,13 @@ export type NavLinkProps = { disabled?: boolean } -export default function NavLink({ +const NavLink = ({ name, href, iconMap, mode = 'expand', disabled = false, -}: NavLinkProps) { +}: NavLinkProps) => { const segment = useSelectedLayoutSegment() const formattedSegment = (() => { let res = segment?.toLowerCase() @@ -90,3 +90,5 @@ export default function NavLink({ ) } + +export default React.memo(NavLink) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx index 4308fa3068..a36345e28c 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-documents/index.tsx @@ -11,6 +11,7 @@ import Toast from '@/app/components/base/toast' import type { DataSourceNodeCompletedResponse, DataSourceNodeErrorResponse } from '@/types/pipeline' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store' +import { useShallow } from 'zustand/react/shallow' type OnlineDocumentsProps = { isInPipeline?: boolean @@ -24,11 +25,17 @@ const OnlineDocuments = ({ nodeData, }: OnlineDocumentsProps) => { const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) - const documentsData = useDataSourceStoreWithSelector(state => state.documentsData) - const searchValue = useDataSourceStoreWithSelector(state => state.searchValue) - const selectedPagesId = useDataSourceStoreWithSelector(state => state.selectedPagesId) - const currentWorkspaceId = useDataSourceStoreWithSelector(state => state.currentWorkspaceId) - const currentNodeIdRef = useDataSourceStoreWithSelector(state => state.currentNodeIdRef) + const { + documentsData, + searchValue, + selectedPagesId, + currentWorkspaceId, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + documentsData: state.documentsData, + searchValue: state.searchValue, + selectedPagesId: state.selectedPagesId, + currentWorkspaceId: state.currentWorkspaceId, + }))) const dataSourceStore = useDataSourceStore() const PagesMapAndSelectedPagesId: DataSourceNotionPageMap = useMemo(() => { @@ -75,15 +82,16 @@ const OnlineDocuments = ({ }, [dataSourceStore, datasourceNodeRunURL]) useEffect(() => { + const { + setDocumentsData, + setCurrentWorkspaceId, + setSearchValue, + setSelectedPagesId, + setOnlineDocuments, + setCurrentDocument, + currentNodeIdRef, + } = dataSourceStore.getState() if (nodeId !== currentNodeIdRef.current) { - const { - setDocumentsData, - setCurrentWorkspaceId, - setSearchValue, - setSelectedPagesId, - setOnlineDocuments, - setCurrentDocument, - } = dataSourceStore.getState() setDocumentsData([]) setCurrentWorkspaceId('') setSearchValue('') diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx index 6f3d149fe4..82a88e31bd 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/online-drive/index.tsx @@ -11,6 +11,7 @@ import Toast from '@/app/components/base/toast' import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store' import { convertOnlineDriveData } from './utils' import produce from 'immer' +import { useShallow } from 'zustand/react/shallow' type OnlineDriveProps = { nodeId: string @@ -24,12 +25,19 @@ const OnlineDrive = ({ isInPipeline = false, }: OnlineDriveProps) => { const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) - const prefix = useDataSourceStoreWithSelector(state => state.prefix) - const keywords = useDataSourceStoreWithSelector(state => state.keywords) - const bucket = useDataSourceStoreWithSelector(state => state.bucket) - const selectedFileKeys = useDataSourceStoreWithSelector(state => state.selectedFileKeys) - const fileList = useDataSourceStoreWithSelector(state => state.fileList) - const currentNodeIdRef = useDataSourceStoreWithSelector(state => state.currentNodeIdRef) + const { + prefix, + keywords, + bucket, + selectedFileKeys, + fileList, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + prefix: state.prefix, + keywords: state.keywords, + bucket: state.bucket, + selectedFileKeys: state.selectedFileKeys, + fileList: state.fileList, + }))) const dataSourceStore = useDataSourceStore() const [isLoading, setIsLoading] = useState(false) @@ -83,14 +91,15 @@ const OnlineDrive = ({ }, [datasourceNodeRunURL, dataSourceStore]) useEffect(() => { + const { + setFileList, + setBucket, + setPrefix, + setKeywords, + setSelectedFileKeys, + currentNodeIdRef, + } = dataSourceStore.getState() if (nodeId !== currentNodeIdRef.current) { - const { - setFileList, - setBucket, - setPrefix, - setKeywords, - setSelectedFileKeys, - } = dataSourceStore.getState() setFileList([]) setBucket('') setPrefix([]) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx index 2753f5f8c9..32d783c2aa 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx @@ -22,6 +22,7 @@ import type { } from '@/types/pipeline' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import { useDataSourceStore, useDataSourceStoreWithSelector } from '../store' +import { useShallow } from 'zustand/react/shallow' const I18N_PREFIX = 'datasetCreation.stepOne.website' @@ -42,10 +43,17 @@ const WebsiteCrawl = ({ const [crawledNum, setCrawledNum] = useState(0) const [crawlErrorMessage, setCrawlErrorMessage] = useState('') const pipelineId = useDatasetDetailContextWithSelector(s => s.dataset?.pipeline_id) - const crawlResult = useDataSourceStoreWithSelector(state => state.crawlResult) - const step = useDataSourceStoreWithSelector(state => state.step) - const checkedCrawlResult = useDataSourceStoreWithSelector(state => state.websitePages) - const previewIndex = useDataSourceStoreWithSelector(state => state.previewIndex) + const { + crawlResult, + step, + checkedCrawlResult, + previewIndex, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + crawlResult: state.crawlResult, + step: state.step, + checkedCrawlResult: state.websitePages, + previewIndex: state.previewIndex, + }))) const dataSourceStore = useDataSourceStore() const usePreProcessingParams = useRef(!isInPipeline ? usePublishedPipelinePreProcessingParams : useDraftPipelinePreProcessingParams) diff --git a/web/app/components/datasets/documents/create-from-pipeline/hooks.ts b/web/app/components/datasets/documents/create-from-pipeline/hooks.ts index 6007402cc8..498bb8acdd 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/hooks.ts +++ b/web/app/components/datasets/documents/create-from-pipeline/hooks.ts @@ -6,6 +6,7 @@ import { BlockEnum, type Node } from '@/app/components/workflow/types' import type { DataSourceNodeType } from '@/app/components/workflow/nodes/data-source/types' import { useDataSourceStore, useDataSourceStoreWithSelector } from './data-source/store' import type { DataSourceNotionPageMap, DataSourceNotionWorkspace } from '@/models/common' +import { useShallow } from 'zustand/react/shallow' export const useAddDocumentsSteps = () => { const { t } = useTranslation() @@ -62,8 +63,13 @@ export const useDatasourceOptions = (pipelineNodes: Node[]) } export const useLocalFile = () => { - const fileList = useDataSourceStoreWithSelector(state => state.localFileList) - const currentLocalFile = useDataSourceStoreWithSelector(state => state.currentLocalFile) + const { + localFileList: fileList, + currentLocalFile, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + localFileList: state.localFileList, + currentLocalFile: state.currentLocalFile, + }))) const dataSourceStore = useDataSourceStore() const allFileLoaded = useMemo(() => (fileList.length > 0 && fileList.every(file => file.file.id)), [fileList]) @@ -82,10 +88,17 @@ export const useLocalFile = () => { } export const useOnlineDocuments = () => { - const documentsData = useDataSourceStoreWithSelector(state => state.documentsData) - const currentWorkspaceId = useDataSourceStoreWithSelector(state => state.currentWorkspaceId) - const onlineDocuments = useDataSourceStoreWithSelector(state => state.onlineDocuments) - const currentDocument = useDataSourceStoreWithSelector(state => state.currentDocument) + const { + documentsData, + currentWorkspaceId, + onlineDocuments, + currentDocument, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + documentsData: state.documentsData, + currentWorkspaceId: state.currentWorkspaceId, + onlineDocuments: state.onlineDocuments, + currentDocument: state.currentDocument, + }))) const dataSourceStore = useDataSourceStore() const currentWorkspace = documentsData.find(workspace => workspace.workspace_id === currentWorkspaceId) @@ -119,8 +132,13 @@ export const useOnlineDocuments = () => { } export const useWebsiteCrawl = () => { - const websitePages = useDataSourceStoreWithSelector(state => state.websitePages) - const currentWebsite = useDataSourceStoreWithSelector(state => state.currentWebsite) + const { + websitePages, + currentWebsite, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + websitePages: state.websitePages, + currentWebsite: state.currentWebsite, + }))) const dataSourceStore = useDataSourceStore() const hideWebsitePreview = useCallback(() => { @@ -137,8 +155,13 @@ export const useWebsiteCrawl = () => { } export const useOnlineDrive = () => { - const fileList = useDataSourceStoreWithSelector(state => state.fileList) - const selectedFileKeys = useDataSourceStoreWithSelector(state => state.selectedFileKeys) + const { + fileList, + selectedFileKeys, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + fileList: state.fileList, + selectedFileKeys: state.selectedFileKeys, + }))) const selectedOnlineDriveFileList = useMemo(() => { return selectedFileKeys.map(key => fileList.find(item => item.key === key)!) diff --git a/web/app/components/rag-pipeline/components/panel/test-run/index.tsx b/web/app/components/rag-pipeline/components/panel/test-run/index.tsx index 075e9e4f46..be333b2f48 100644 --- a/web/app/components/rag-pipeline/components/panel/test-run/index.tsx +++ b/web/app/components/rag-pipeline/components/panel/test-run/index.tsx @@ -17,13 +17,21 @@ import Header from './header' import FooterTips from './footer-tips' import DataSourceProvider from '@/app/components/datasets/documents/create-from-pipeline/data-source/store/provider' import { useDataSourceStore, useDataSourceStoreWithSelector } from '@/app/components/datasets/documents/create-from-pipeline/data-source/store' +import { useShallow } from 'zustand/react/shallow' const TestRunPanel = () => { const setShowDebugAndPreviewPanel = useWorkflowStoreWithSelector(state => state.setShowDebugAndPreviewPanel) - const fileList = useDataSourceStoreWithSelector(state => state.localFileList) - const onlineDocuments = useDataSourceStoreWithSelector(state => state.onlineDocuments) - const websitePages = useDataSourceStoreWithSelector(state => state.websitePages) - const selectedFileKeys = useDataSourceStoreWithSelector(state => state.selectedFileKeys) + const { + localFileList: fileList, + onlineDocuments, + websitePages, + selectedFileKeys, + } = useDataSourceStoreWithSelector(useShallow(state => ({ + localFileList: state.localFileList, + onlineDocuments: state.onlineDocuments, + websitePages: state.websitePages, + selectedFileKeys: state.selectedFileKeys, + }))) const dataSourceStore = useDataSourceStore() const [datasource, setDatasource] = useState() From 90d0f12ee933c150947350f51bcb7c217bb50e9e Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 11 Jul 2025 16:53:03 +0800 Subject: [PATCH 3/5] refactor: update file extension handling in ChunkPreview component to use dynamic extension retrieval --- .../documents/create-from-pipeline/preview/chunk-preview.tsx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx index 763b5f38af..09c274e42e 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/preview/chunk-preview.tsx @@ -16,6 +16,7 @@ import Badge from '@/app/components/base/badge' import Button from '@/app/components/base/button' import type { OnlineDriveFile } from '@/models/pipeline' import { DatasourceType } from '@/models/pipeline' +import { getFileExtension } from '../data-source/online-drive/file-list/list/utils' type ChunkPreviewProps = { dataSourceType: DatasourceType @@ -122,7 +123,7 @@ const ChunkPreview = ({ onlineDriveFiles.map(file => ({ id: file.key, name: file.displayName, - extension: 'md', + extension: getFileExtension(previewOnlineDriveFile?.displayName), })) } onChange={(selected) => { @@ -134,7 +135,7 @@ const ChunkPreview = ({ { id: previewOnlineDriveFile?.key || '', name: previewOnlineDriveFile?.displayName || '', - extension: 'md', + extension: getFileExtension(previewOnlineDriveFile?.displayName), } } /> From 23a6fe3259ca738f1f0e791db8e146581a4daacd Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 11 Jul 2025 18:02:23 +0800 Subject: [PATCH 4/5] fix: adjust default selection of crawl results based on pipeline status --- .../create-from-pipeline/data-source/website-crawl/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx index 32d783c2aa..6ccd06fc19 100644 --- a/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx +++ b/web/app/components/datasets/documents/create-from-pipeline/data-source/website-crawl/index.tsx @@ -140,7 +140,7 @@ const WebsiteCrawl = ({ time_consuming: time_consuming ?? 0, } setCrawlResult(crawlResultData) - handleCheckedCrawlResultChange(crawlData || []) // default select the crawl result + handleCheckedCrawlResultChange(isInPipeline ? [crawlData[0]] : crawlData) // default select the crawl result setCrawlErrorMessage('') setStep(CrawlStep.finished) }, @@ -150,7 +150,7 @@ const WebsiteCrawl = ({ }, }, ) - }, [dataSourceStore, datasourceNodeRunURL, handleCheckedCrawlResultChange, t]) + }, [dataSourceStore, datasourceNodeRunURL, handleCheckedCrawlResultChange, isInPipeline, t]) const handleSubmit = useCallback((value: Record) => { handleRun(value) From 0fdb1fedb0c4aa1bf597f4a9636a8a095e4f58a4 Mon Sep 17 00:00:00 2001 From: twwu Date: Fri, 11 Jul 2025 18:38:18 +0800 Subject: [PATCH 5/5] feat: add PluginDependency component to RagPipelineChildren and WorkflowChildren for enhanced functionality --- .../rag-pipeline/components/rag-pipeline-children.tsx | 6 ++++-- .../workflow-app/components/workflow-children.tsx | 2 ++ 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx index 87ff86255b..7a2e3df4eb 100644 --- a/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx +++ b/web/app/components/rag-pipeline/components/rag-pipeline-children.tsx @@ -4,6 +4,7 @@ import { } from 'react' import { useStore } from '../../workflow/store' import InputField from './input-field' +import PluginDependency from '../../workflow/plugin-dependency' import RagPipelinePanel from './panel' import RagPipelineHeader from './rag-pipeline-header' import type { EnvironmentVariable } from '@/app/components/workflow/types' @@ -26,8 +27,8 @@ const RagPipelineChildren = () => { handlePaneContextmenuCancel, } = usePanelInteractions() const { - exportCheck, - handleExportDSL, + exportCheck, + handleExportDSL, } = useDSL() eventEmitter?.useSubscription((v: any) => { @@ -37,6 +38,7 @@ const RagPipelineChildren = () => { return ( <> + { showImportDSLModal && ( { return ( <> + { showFeaturesPanel && }