dify/web/app/components/workflow/skill/start-tab/create-import-section.tsx
yyh ea88bcfbd2
feat: add ZIP skill import with client-side extraction
Add import skill modal that accepts .zip files via drag-and-drop or
file picker, extracts them client-side using fflate, validates structure
and security constraints, then batch uploads via presigned URLs.

- Add fflate dependency for browser-side ZIP decompression
- Create zip-extract.ts with fflate filter API for validation
- Create zip-to-upload-tree.ts for BatchUploadNodeInput tree building
- Create import-skill-modal.tsx with drag-and-drop support
- Lazy-load ImportSkillModal via next/dynamic for bundle optimization
- Add en-US and zh-Hans i18n keys for import modal
2026-01-30 21:54:00 +08:00

46 lines
1.5 KiB
TypeScript

'use client'
import { RiAddCircleFill, RiUploadLine } from '@remixicon/react'
import dynamic from 'next/dynamic'
import { memo, useState } from 'react'
import { useTranslation } from 'react-i18next'
import ActionCard from './action-card'
import CreateBlankSkillModal from './create-blank-skill-modal'
const ImportSkillModal = dynamic(() => import('./import-skill-modal'))
const CreateImportSection = () => {
const { t } = useTranslation('workflow')
const [isCreateModalOpen, setIsCreateModalOpen] = useState(false)
const [isImportModalOpen, setIsImportModalOpen] = useState(false)
return (
<>
<div className="grid grid-cols-3 gap-2 px-6 pb-4 pt-6">
<ActionCard
icon={<RiAddCircleFill className="size-5 text-text-accent" />}
title={t('skill.startTab.createBlankSkill')}
description={t('skill.startTab.createBlankSkillDesc')}
onClick={() => setIsCreateModalOpen(true)}
/>
<ActionCard
icon={<RiUploadLine className="size-5 text-text-accent" />}
title={t('skill.startTab.importSkill')}
description={t('skill.startTab.importSkillDesc')}
onClick={() => setIsImportModalOpen(true)}
/>
</div>
<CreateBlankSkillModal
isOpen={isCreateModalOpen}
onClose={() => setIsCreateModalOpen(false)}
/>
<ImportSkillModal
isOpen={isImportModalOpen}
onClose={() => setIsImportModalOpen(false)}
/>
</>
)
}
export default memo(CreateImportSection)