feat(skill): implement file tree search with debounced filtering

Add search functionality to skill sidebar using react-arborist's built-in
searchTerm and searchMatch props. Search input is debounced at 300ms and
filters tree nodes by name (case-insensitive). Also add success toast for
rename operations.
This commit is contained in:
yyh
2026-01-16 14:40:55 +08:00
parent eb4f57fb8b
commit 06b6625c01
5 changed files with 39 additions and 5 deletions

View File

@ -23,6 +23,7 @@ import TreeNode from './tree-node'
type FileTreeProps = {
className?: string
searchTerm?: string
}
const DropTip = () => {
@ -37,7 +38,7 @@ const DropTip = () => {
)
}
const FileTree: React.FC<FileTreeProps> = ({ className }) => {
const FileTree: React.FC<FileTreeProps> = ({ className, searchTerm = '' }) => {
const { t } = useTranslation('workflow')
const treeRef = useRef<TreeApi<TreeNodeData>>(null)
const containerRef = useRef<HTMLDivElement>(null)
@ -77,6 +78,11 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
appId,
nodeId: id,
payload: { name },
}).then(() => {
Toast.notify({
type: 'success',
message: t('skillSidebar.menu.renamed'),
})
}).catch(() => {
Toast.notify({
type: 'error',
@ -85,6 +91,13 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
})
}, [appId, renameNode, t])
const searchMatch = useCallback(
(node: NodeApi<TreeNodeData>, term: string) => {
return node.data.name.toLowerCase().includes(term.toLowerCase())
},
[],
)
useSyncTreeWithActiveTab({
treeRef,
activeTabId,
@ -150,6 +163,8 @@ const FileTree: React.FC<FileTreeProps> = ({ className }) => {
onToggle={handleToggle}
onActivate={handleActivate}
onRename={handleRename}
searchTerm={searchTerm}
searchMatch={searchMatch}
disableDrag
disableDrop
>

View File

@ -2,6 +2,7 @@
import type { FC } from 'react'
import * as React from 'react'
import { useCallback, useState } from 'react'
import EditorArea from './editor-area'
import EditorBody from './editor-body'
import EditorTabs from './editor-tabs'
@ -12,12 +13,18 @@ import SkillDocEditor from './skill-doc-editor'
import SkillPageLayout from './skill-page-layout'
const SkillMain: FC = () => {
const [searchTerm, setSearchTerm] = useState('')
const handleSearchChange = useCallback((term: string) => {
setSearchTerm(term)
}, [])
return (
<div className="h-full bg-workflow-canvas-workflow-top-bar-1 pl-3 pt-[52px]">
<SkillPageLayout>
<Sidebar>
<SidebarSearchAdd />
<FileTree />
<SidebarSearchAdd onSearchChange={handleSearchChange} />
<FileTree searchTerm={searchTerm} />
</Sidebar>
<EditorArea>
<EditorTabs />

View File

@ -8,8 +8,9 @@ import {
RiFolderUploadLine,
RiUploadLine,
} from '@remixicon/react'
import { useDebounce } from 'ahooks'
import * as React from 'react'
import { useMemo, useState } from 'react'
import { useEffect, useMemo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Button from '@/app/components/base/button'
import {
@ -24,6 +25,10 @@ import { useFileOperations } from './hooks/use-file-operations'
import { useSkillAssetTreeData } from './hooks/use-skill-asset-tree'
import { getTargetFolderIdFromSelection } from './utils/tree-utils'
type SidebarSearchAddProps = {
onSearchChange?: (searchTerm: string) => void
}
type MenuItemProps = {
icon: React.ElementType
label: string
@ -49,11 +54,16 @@ const MenuItem: React.FC<MenuItemProps> = ({ icon: Icon, label, onClick, disable
</button>
)
const SidebarSearchAdd: FC = () => {
const SidebarSearchAdd: FC<SidebarSearchAddProps> = ({ onSearchChange }) => {
const { t } = useTranslation('workflow')
const [searchValue, setSearchValue] = useState('')
const debouncedSearchValue = useDebounce(searchValue, { wait: 300 })
const [showMenu, setShowMenu] = useState(false)
useEffect(() => {
onSearchChange?.(debouncedSearchValue)
}, [debouncedSearchValue, onSearchChange])
const { data: treeData } = useSkillAssetTreeData()
const activeTabId = useStore(s => s.activeTabId)

View File

@ -1025,6 +1025,7 @@
"skillSidebar.menu.newFolderPrompt": "Enter folder name:",
"skillSidebar.menu.rename": "Rename",
"skillSidebar.menu.renameError": "Failed to rename",
"skillSidebar.menu.renamed": "Renamed successfully",
"skillSidebar.menu.uploadError": "Failed to upload",
"skillSidebar.menu.uploadFile": "Upload File",
"skillSidebar.menu.uploadFolder": "Upload Folder",

View File

@ -1018,6 +1018,7 @@
"skillSidebar.menu.newFolderPrompt": "请输入文件夹名称:",
"skillSidebar.menu.rename": "重命名",
"skillSidebar.menu.renameError": "重命名失败",
"skillSidebar.menu.renamed": "重命名成功",
"skillSidebar.menu.uploadError": "上传失败",
"skillSidebar.menu.uploadFile": "上传文件",
"skillSidebar.menu.uploadFolder": "上传文件夹",