From 17990512ce0aa77bf3e191beb1210169083bda05 Mon Sep 17 00:00:00 2001 From: yyh Date: Fri, 16 Jan 2026 11:07:56 +0800 Subject: [PATCH] fix(skill): add throttle to folder toggle and validate pinTab - Use es-toolkit throttle with leading edge to prevent folder toggle flickering on double-click (3 toggles reduced to 1) - Add validation in pinTab to check if file exists in openTabIds --- .../workflow/skill/file-tree/tree-node.tsx | 14 ++++++++++---- web/app/components/workflow/skill/store/index.ts | 4 +++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/web/app/components/workflow/skill/file-tree/tree-node.tsx b/web/app/components/workflow/skill/file-tree/tree-node.tsx index c24e12a03a..42b49937b4 100644 --- a/web/app/components/workflow/skill/file-tree/tree-node.tsx +++ b/web/app/components/workflow/skill/file-tree/tree-node.tsx @@ -4,8 +4,9 @@ import type { NodeRendererProps } from 'react-arborist' import type { TreeNodeData } from '../type' import type { FileAppearanceType } from '@/app/components/base/file-uploader/types' import { RiFolderLine, RiFolderOpenLine, RiMoreFill } from '@remixicon/react' +import { throttle } from 'es-toolkit/function' import * as React from 'react' -import { useCallback, useState } from 'react' +import { useCallback, useMemo, useState } from 'react' import { useTranslation } from 'react-i18next' import FileTypeIcon from '@/app/components/base/file-uploader/file-type-icon' import { @@ -34,11 +35,16 @@ const TreeNode = ({ node, style, dragHandle }: NodeRendererProps) const fileIconType = !isFolder ? getFileIconType(node.data.name) : null + const throttledToggle = useMemo( + () => throttle(() => node.toggle(), 300, { edges: ['leading'] }), + [node], + ) + const handleClick = (e: React.MouseEvent) => { e.stopPropagation() node.select() if (isFolder) - node.toggle() + throttledToggle() else storeApi.getState().openTab(node.data.id, { pinned: false }) } @@ -46,14 +52,14 @@ const TreeNode = ({ node, style, dragHandle }: NodeRendererProps) const handleDoubleClick = (e: React.MouseEvent) => { e.stopPropagation() if (isFolder) - node.toggle() + throttledToggle() else storeApi.getState().openTab(node.data.id, { pinned: true }) } const handleToggle = (e: React.MouseEvent) => { e.stopPropagation() - node.toggle() + throttledToggle() } const handleContextMenu = useCallback((e: React.MouseEvent) => { diff --git a/web/app/components/workflow/skill/store/index.ts b/web/app/components/workflow/skill/store/index.ts index d3a555bf86..71aa212fe3 100644 --- a/web/app/components/workflow/skill/store/index.ts +++ b/web/app/components/workflow/skill/store/index.ts @@ -91,7 +91,9 @@ export const createTabSlice: StateCreator = (set, get) => ({ }, pinTab: (fileId: string) => { - const { previewTabId } = get() + const { previewTabId, openTabIds } = get() + if (!openTabIds.includes(fileId)) + return if (previewTabId === fileId) set({ previewTabId: null }) },