From efb3657cfef3e843a0fa44189c73095436256e6b Mon Sep 17 00:00:00 2001 From: yyh Date: Thu, 29 Jan 2026 12:49:15 +0800 Subject: [PATCH] fix(skill): use downloadUrl utility instead of window.open for file downloads Replaces window.open with the downloadUrl helper from utils/download.ts to trigger proper browser download behavior via instead of opening a new tab that may display garbled content. --- .../workflow/skill/hooks/use-download-operation.ts | 12 +++++------- .../workflow/skill/hooks/use-file-operations.ts | 1 + .../skill/viewer/unsupported-file-download.tsx | 7 ++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/web/app/components/workflow/skill/hooks/use-download-operation.ts b/web/app/components/workflow/skill/hooks/use-download-operation.ts index da3cb1fc06..1d96c5bf2a 100644 --- a/web/app/components/workflow/skill/hooks/use-download-operation.ts +++ b/web/app/components/workflow/skill/hooks/use-download-operation.ts @@ -1,21 +1,22 @@ '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' +import { downloadUrl } from '@/utils/download' type UseDownloadOperationOptions = { appId: string nodeId: string + fileName?: string onClose: () => void } export function useDownloadOperation({ appId, nodeId, + fileName, onClose, }: UseDownloadOperationOptions) { const { t } = useTranslation('workflow') @@ -25,7 +26,6 @@ export function useDownloadOperation({ if (!nodeId || !appId) return - // Close menu immediately before any async operation onClose() setIsDownloading(true) @@ -34,9 +34,7 @@ export function useDownloadOperation({ params: { appId, nodeId }, }) - // Open download URL in new tab (consistent with UnsupportedFileDownload) - if (typeof window !== 'undefined') - window.open(download_url, '_blank', 'noopener,noreferrer') + downloadUrl({ url: download_url, fileName }) } catch { Toast.notify({ @@ -47,7 +45,7 @@ export function useDownloadOperation({ finally { setIsDownloading(false) } - }, [appId, nodeId, onClose, t]) + }, [appId, nodeId, fileName, onClose, t]) return { handleDownload, diff --git a/web/app/components/workflow/skill/hooks/use-file-operations.ts b/web/app/components/workflow/skill/hooks/use-file-operations.ts index 6da35b9470..6dde784ada 100644 --- a/web/app/components/workflow/skill/hooks/use-file-operations.ts +++ b/web/app/components/workflow/skill/hooks/use-file-operations.ts @@ -55,6 +55,7 @@ export function useFileOperations({ const downloadOps = useDownloadOperation({ appId, nodeId, + fileName: node?.data.name, onClose, }) diff --git a/web/app/components/workflow/skill/viewer/unsupported-file-download.tsx b/web/app/components/workflow/skill/viewer/unsupported-file-download.tsx index 6eb18f3af0..9b0c286798 100644 --- a/web/app/components/workflow/skill/viewer/unsupported-file-download.tsx +++ b/web/app/components/workflow/skill/viewer/unsupported-file-download.tsx @@ -4,6 +4,7 @@ import { useTranslation } from 'react-i18next' import Button from '@/app/components/base/button' import FileTypeIcon from '@/app/components/base/file-uploader/file-type-icon' import { FileAppearanceTypeEnum } from '@/app/components/base/file-uploader/types' +import { downloadUrl as triggerDownloadUrl } from '@/utils/download' import { formatFileSize } from '@/utils/format' type UnsupportedFileDownloadProps = { @@ -17,10 +18,10 @@ const UnsupportedFileDownload = ({ name, size, downloadUrl }: UnsupportedFileDow const fileSize = size ? formatFileSize(size) : '' const handleDownload = useCallback(() => { - if (!downloadUrl || typeof window === 'undefined') + if (!downloadUrl) return - window.open(downloadUrl, '_blank', 'noopener,noreferrer') - }, [downloadUrl]) + triggerDownloadUrl({ url: downloadUrl, fileName: name }) + }, [downloadUrl, name]) return (