feat!: file download in skill file tree menu

This commit is contained in:
yyh
2026-01-20 13:16:27 +08:00
parent 552f9a8989
commit fc83e2b1c4
5 changed files with 90 additions and 1 deletions

View File

@ -15,6 +15,7 @@ import {
import * as React from 'react'
import { useTranslation } from 'react-i18next'
import Confirm from '@/app/components/base/confirm'
import { Download02 } from '@/app/components/base/icons/src/vender/solid/general'
import { cn } from '@/utils/classnames'
import { NODE_MENU_TYPE } from '../constants'
import { useFileOperations } from '../hooks/use-file-operations'
@ -52,6 +53,7 @@ const NodeMenu: FC<NodeMenuProps> = ({
showDeleteConfirm,
isLoading,
isDeleting,
handleDownload,
handleNewFile,
handleNewFolder,
handleFileChange,
@ -126,6 +128,18 @@ const NodeMenu: FC<NodeMenuProps> = ({
</>
)}
{!isFolder && (
<>
<MenuItem
icon={Download02}
label={t('skillSidebar.menu.download')}
onClick={handleDownload}
disabled={isLoading}
/>
<div className="my-1 h-px bg-divider-subtle" />
</>
)}
{showRenameDelete && (
<>
<MenuItem

View File

@ -0,0 +1,56 @@
'use client'
// Handles file download operation - opens download URL in new tab
import { useCallback, useState } from 'react'
import { useTranslation } from 'react-i18next'
import Toast from '@/app/components/base/toast'
import { consoleClient } from '@/service/client'
type UseDownloadOperationOptions = {
appId: string
nodeId: string
onClose: () => void
}
export function useDownloadOperation({
appId,
nodeId,
onClose,
}: UseDownloadOperationOptions) {
const { t } = useTranslation('workflow')
const [isDownloading, setIsDownloading] = useState(false)
const handleDownload = useCallback(async () => {
if (!nodeId || !appId)
return
// Close menu immediately before any async operation
onClose()
setIsDownloading(true)
try {
const { download_url } = await consoleClient.appAsset.getFileDownloadUrl({
params: { appId, nodeId },
})
// Open download URL in new tab (consistent with UnsupportedFileDownload)
if (typeof window !== 'undefined')
window.open(download_url, '_blank', 'noopener,noreferrer')
}
catch {
Toast.notify({
type: 'error',
message: t('skillSidebar.menu.downloadError'),
})
}
finally {
setIsDownloading(false)
}
}, [appId, nodeId, onClose, t])
return {
handleDownload,
isDownloading,
}
}

View File

@ -9,6 +9,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
import { useWorkflowStore } from '@/app/components/workflow/store'
import { toApiParentId } from '../utils/tree-utils'
import { useCreateOperations } from './use-create-operations'
import { useDownloadOperation } from './use-download-operation'
import { useModifyOperations } from './use-modify-operations'
import { useSkillAssetTreeData } from './use-skill-asset-tree'
@ -51,6 +52,12 @@ export function useFileOperations({
onClose,
})
const downloadOps = useDownloadOperation({
appId,
nodeId,
onClose,
})
return {
// Create operations
fileInputRef: createOps.fileInputRef,
@ -67,8 +74,12 @@ export function useFileOperations({
handleDeleteConfirm: modifyOps.handleDeleteConfirm,
handleDeleteCancel: modifyOps.handleDeleteCancel,
// Download operation
handleDownload: downloadOps.handleDownload,
// Combined loading states
isLoading: createOps.isCreating || modifyOps.isDeleting,
isLoading: createOps.isCreating || modifyOps.isDeleting || downloadOps.isDownloading,
isDeleting: modifyOps.isDeleting,
isDownloading: downloadOps.isDownloading,
}
}