diff --git a/web/app/components/workflow/skill/file-tree/drag-action-tooltip.tsx b/web/app/components/workflow/skill/file-tree/drag-action-tooltip.tsx new file mode 100644 index 0000000000..9b5f20f82b --- /dev/null +++ b/web/app/components/workflow/skill/file-tree/drag-action-tooltip.tsx @@ -0,0 +1,56 @@ +'use client' + +import type { FC } from 'react' +import * as React from 'react' +import { useMemo } from 'react' +import { useTranslation } from 'react-i18next' +import { useStore } from '@/app/components/workflow/store' +import { ROOT_ID } from '../constants' +import { useSkillAssetNodeMap } from '../hooks/use-skill-asset-tree' + +export type DragAction = 'upload' | 'move' + +type DragActionTooltipProps = { + action: DragAction +} + +const DragActionTooltip: FC = ({ action }) => { + const { t } = useTranslation('workflow') + const dragOverFolderId = useStore(s => s.dragOverFolderId) + const { data: nodeMap } = useSkillAssetNodeMap() + + // Resolve target path from dragOverFolderId + const targetPath = useMemo(() => { + if (!dragOverFolderId) + return null + + if (dragOverFolderId === ROOT_ID) + return t('skillSidebar.rootFolder') + + const node = nodeMap?.get(dragOverFolderId) + // Strip leading slash from path (e.g., "/skills/assets" -> "skills/assets") + const path = node?.path + return path?.startsWith('/') ? path.slice(1) : path ?? null + }, [dragOverFolderId, nodeMap, t]) + + // Don't render if not dragging over a valid target + if (!targetPath) + return null + + const actionText = action === 'upload' + ? t('skillSidebar.dragAction.uploadTo') + : t('skillSidebar.dragAction.moveTo') + + return ( +
+
+

+ {actionText} + {targetPath} +

+
+
+ ) +} + +export default React.memo(DragActionTooltip) diff --git a/web/app/components/workflow/skill/file-tree/index.tsx b/web/app/components/workflow/skill/file-tree/index.tsx index cb46875c5f..d3c030e137 100644 --- a/web/app/components/workflow/skill/file-tree/index.tsx +++ b/web/app/components/workflow/skill/file-tree/index.tsx @@ -20,6 +20,7 @@ import { useInlineCreateNode } from '../hooks/use-inline-create-node' import { useRootFileDrop } from '../hooks/use-root-file-drop' import { useSkillAssetTreeData } from '../hooks/use-skill-asset-tree' import { useSyncTreeWithActiveTab } from '../hooks/use-sync-tree-with-active-tab' +import DragActionTooltip from './drag-action-tooltip' import TreeContextMenu from './tree-context-menu' import TreeNode from './tree-node' @@ -246,7 +247,9 @@ const FileTree: React.FC = ({ className }) => { - + {dragOverFolderId + ? + : } ) diff --git a/web/i18n/en-US/workflow.json b/web/i18n/en-US/workflow.json index 5a10ff7b41..edeedeb5e4 100644 --- a/web/i18n/en-US/workflow.json +++ b/web/i18n/en-US/workflow.json @@ -1015,6 +1015,8 @@ "skillEditor.unsupportedPreview": "This file type is not supported for preview", "skillSidebar.addFile": "Upload File", "skillSidebar.addFolder": "New Folder", + "skillSidebar.dragAction.moveTo": "Move to ", + "skillSidebar.dragAction.uploadTo": "Upload to ", "skillSidebar.dropTip": "Drop files here to upload", "skillSidebar.empty": "No files yet", "skillSidebar.fileNamePlaceholder": "File name", @@ -1051,6 +1053,7 @@ "skillSidebar.menu.uploadFolder": "Upload Folder", "skillSidebar.newFolder": "New folder", "skillSidebar.resetFilter": "Reset filter", + "skillSidebar.rootFolder": "root folder", "skillSidebar.searchNoResults": "No file were found", "skillSidebar.searchPlaceholder": "Search files…", "skillSidebar.toggleFolder": "Toggle folder", diff --git a/web/i18n/zh-Hans/workflow.json b/web/i18n/zh-Hans/workflow.json index 27e8aac942..01056786e2 100644 --- a/web/i18n/zh-Hans/workflow.json +++ b/web/i18n/zh-Hans/workflow.json @@ -1007,6 +1007,8 @@ "skillEditor.unsupportedPreview": "该文件类型不支持预览", "skillSidebar.addFile": "上传文件", "skillSidebar.addFolder": "新建文件夹", + "skillSidebar.dragAction.moveTo": "移动到 ", + "skillSidebar.dragAction.uploadTo": "上传到 ", "skillSidebar.dropTip": "拖放文件到此处上传", "skillSidebar.empty": "暂无文件", "skillSidebar.fileNamePlaceholder": "文件名称", @@ -1042,6 +1044,7 @@ "skillSidebar.menu.uploadFolder": "上传文件夹", "skillSidebar.newFolder": "新建文件夹", "skillSidebar.resetFilter": "重置筛选", + "skillSidebar.rootFolder": "根目录", "skillSidebar.searchNoResults": "未找到文件", "skillSidebar.searchPlaceholder": "搜索文件...", "skillSidebar.unsavedChanges.confirmClose": "放弃",