From 76c4d7f62c66ad6ac9a17ece84f73b7809f82a8e Mon Sep 17 00:00:00 2001 From: yyh Date: Wed, 28 Jan 2026 20:17:21 +0800 Subject: [PATCH] feat(skill): add preprocessing for markdown files before upload Introduce prepareSkillUploadFile utility that wraps markdown file content in a JSON payload format before uploading. This ensures consistent handling of skill files across file upload, folder upload, and drag-and-drop operations. --- .../skill/hooks/use-create-operations.ts | 16 +++++++++++----- .../workflow/skill/hooks/use-file-drop.ts | 4 +++- .../workflow/skill/utils/skill-upload-utils.ts | 15 +++++++++++++++ 3 files changed, 29 insertions(+), 6 deletions(-) create mode 100644 web/app/components/workflow/skill/utils/skill-upload-utils.ts diff --git a/web/app/components/workflow/skill/hooks/use-create-operations.ts b/web/app/components/workflow/skill/hooks/use-create-operations.ts index 0673b8776f..0d240af91f 100644 --- a/web/app/components/workflow/skill/hooks/use-create-operations.ts +++ b/web/app/components/workflow/skill/hooks/use-create-operations.ts @@ -9,6 +9,7 @@ import { useCreateAppAssetFolder, useUploadFileWithPresignedUrl, } from '@/service/use-app-asset' +import { prepareSkillUploadFile } from '../utils/skill-upload-utils' type UseCreateOperationsOptions = { parentId: string | null @@ -59,8 +60,9 @@ export function useCreateOperations({ storeApi.getState().setUploadProgress({ uploaded: 0, total, failed: 0 }) try { + const uploadFiles = await Promise.all(files.map(file => prepareSkillUploadFile(file))) await Promise.all( - files.map(async (file) => { + uploadFiles.map(async (file) => { try { await uploadFile.mutateAsync({ appId, file, parentId }) uploaded++ @@ -98,10 +100,14 @@ export function useCreateOperations({ const fileMap = new Map() const tree: BatchUploadNodeInput[] = [] const folderMap = new Map() - - for (const file of files) { + const uploadFiles = await Promise.all(files.map(async (file) => { const relativePath = getRelativePath(file) - fileMap.set(relativePath, file) + const uploadFile = await prepareSkillUploadFile(file) + return { relativePath, uploadFile } + })) + + for (const { relativePath, uploadFile } of uploadFiles) { + fileMap.set(relativePath, uploadFile) const parts = relativePath.split('/') let currentLevel = tree @@ -116,7 +122,7 @@ export function useCreateOperations({ currentLevel.push({ name: part, node_type: 'file', - size: file.size, + size: uploadFile.size, }) } else { diff --git a/web/app/components/workflow/skill/hooks/use-file-drop.ts b/web/app/components/workflow/skill/hooks/use-file-drop.ts index 10e8e133f7..5c6c0f101c 100644 --- a/web/app/components/workflow/skill/hooks/use-file-drop.ts +++ b/web/app/components/workflow/skill/hooks/use-file-drop.ts @@ -10,6 +10,7 @@ import Toast from '@/app/components/base/toast' import { useWorkflowStore } from '@/app/components/workflow/store' import { useUploadFileWithPresignedUrl } from '@/service/use-app-asset' import { ROOT_ID } from '../constants' +import { prepareSkillUploadFile } from '../utils/skill-upload-utils' type FileDropTarget = { folderId: string | null @@ -78,8 +79,9 @@ export function useFileDrop() { return try { + const uploadFiles = await Promise.all(files.map(file => prepareSkillUploadFile(file))) await Promise.all( - files.map(file => + uploadFiles.map(file => uploadFile.mutateAsync({ appId, file, diff --git a/web/app/components/workflow/skill/utils/skill-upload-utils.ts b/web/app/components/workflow/skill/utils/skill-upload-utils.ts new file mode 100644 index 0000000000..b44051a50a --- /dev/null +++ b/web/app/components/workflow/skill/utils/skill-upload-utils.ts @@ -0,0 +1,15 @@ +import { getFileExtension, isMarkdownFile } from './file-utils' + +const buildSkillUploadPayload = (content: string) => { + return JSON.stringify({ content, metadata: {} }) +} + +export async function prepareSkillUploadFile(file: File): Promise { + const extension = getFileExtension(file.name) + if (!isMarkdownFile(extension)) + return file + + const content = await file.text() + const payload = buildSkillUploadPayload(content) + return new File([payload], file.name, { type: file.type || 'text/plain' }) +}