mirror of
https://github.com/langgenius/dify.git
synced 2026-05-02 16:38:04 +08:00
feat(skill-editor): implement StartTabContent with modular component structure
Refactor StartTabContent into separate components following Figma design specs: - ActionCard: reusable card with icon, title, description - SectionHeader: title/xl-semi-bold header with description - CreateImportSection: 3-column grid layout for Create/Import cards - SkillTemplatesSection: templates area with placeholder Align styles with Figma: 3-col grid, 16px title, proper spacing and padding. Add i18n translations for all user-facing text (en-US, zh-Hans).
This commit is contained in:
@ -20,7 +20,7 @@ import { useFileTypeInfo } from './hooks/use-file-type-info'
|
||||
import { useSkillAssetNodeMap } from './hooks/use-skill-asset-tree'
|
||||
import { useSkillFileData } from './hooks/use-skill-file-data'
|
||||
import { useSkillFileSave } from './hooks/use-skill-file-save'
|
||||
import StartTabContent from './start-tab-content'
|
||||
import StartTabContent from './start-tab'
|
||||
import { getFileLanguage } from './utils/file-utils'
|
||||
import MediaFilePreview from './viewer/media-file-preview'
|
||||
import UnsupportedFileDownload from './viewer/unsupported-file-download'
|
||||
|
||||
@ -1,23 +0,0 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import * as React from 'react'
|
||||
import Home from '@/app/components/base/icons/src/vender/workflow/Home'
|
||||
|
||||
// TODO: use translations
|
||||
const StartTabContent: FC = () => {
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center bg-components-panel-bg">
|
||||
<div className="flex flex-col items-center gap-3">
|
||||
<div className="flex size-12 items-center justify-center rounded-xl bg-components-panel-bg-blur">
|
||||
<Home className="size-6 text-text-tertiary" />
|
||||
</div>
|
||||
<span className="system-sm-regular text-text-tertiary">
|
||||
Coming soon...
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default React.memo(StartTabContent)
|
||||
49
web/app/components/workflow/skill/start-tab/action-card.tsx
Normal file
49
web/app/components/workflow/skill/start-tab/action-card.tsx
Normal file
@ -0,0 +1,49 @@
|
||||
'use client'
|
||||
|
||||
import type { FC, ReactNode } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { cn } from '@/utils/classnames'
|
||||
|
||||
type ActionCardProps = {
|
||||
icon: ReactNode
|
||||
title: string
|
||||
description: string
|
||||
onClick?: () => void
|
||||
}
|
||||
|
||||
const ActionCard: FC<ActionCardProps> = ({
|
||||
icon,
|
||||
title,
|
||||
description,
|
||||
onClick,
|
||||
}) => {
|
||||
return (
|
||||
<button
|
||||
type="button"
|
||||
className={cn(
|
||||
'flex items-start gap-3 rounded-xl border border-components-panel-border-subtle bg-components-panel-on-panel-item-bg p-4',
|
||||
'hover:bg-components-panel-on-panel-item-bg-hover',
|
||||
'focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-components-input-border-active',
|
||||
'text-left transition-colors',
|
||||
)}
|
||||
onClick={onClick}
|
||||
>
|
||||
<div
|
||||
className="flex size-10 shrink-0 items-center justify-center rounded-[10px] border border-dashed border-divider-regular bg-background-section"
|
||||
aria-hidden="true"
|
||||
>
|
||||
{icon}
|
||||
</div>
|
||||
<div className="flex min-w-0 flex-1 flex-col gap-0.5 py-px">
|
||||
<span className="system-md-semibold truncate text-text-secondary">
|
||||
{title}
|
||||
</span>
|
||||
<span className="system-xs-regular text-text-tertiary">
|
||||
{description}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(ActionCard)
|
||||
@ -0,0 +1,28 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { RiAddCircleFill, RiUploadLine } from '@remixicon/react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import ActionCard from './action-card'
|
||||
|
||||
const CreateImportSection: FC = () => {
|
||||
const { t } = useTranslation('workflow')
|
||||
|
||||
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')}
|
||||
/>
|
||||
<ActionCard
|
||||
icon={<RiUploadLine className="size-5 text-text-accent" />}
|
||||
title={t('skill.startTab.importSkill')}
|
||||
description={t('skill.startTab.importSkillDesc')}
|
||||
/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(CreateImportSection)
|
||||
17
web/app/components/workflow/skill/start-tab/index.tsx
Normal file
17
web/app/components/workflow/skill/start-tab/index.tsx
Normal file
@ -0,0 +1,17 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
import CreateImportSection from './create-import-section'
|
||||
import SkillTemplatesSection from './skill-templates-section'
|
||||
|
||||
const StartTabContent: FC = () => {
|
||||
return (
|
||||
<div className="h-full w-full overflow-auto bg-components-panel-bg">
|
||||
<CreateImportSection />
|
||||
<SkillTemplatesSection />
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(StartTabContent)
|
||||
@ -0,0 +1,27 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
|
||||
type SectionHeaderProps = {
|
||||
title: string
|
||||
description: string
|
||||
}
|
||||
|
||||
const SectionHeader: FC<SectionHeaderProps> = ({
|
||||
title,
|
||||
description,
|
||||
}) => {
|
||||
return (
|
||||
<header className="mb-3 flex flex-col gap-0.5">
|
||||
<h2 className="title-xl-semi-bold text-text-primary">
|
||||
{title}
|
||||
</h2>
|
||||
<p className="system-xs-regular text-text-tertiary">
|
||||
{description}
|
||||
</p>
|
||||
</header>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(SectionHeader)
|
||||
@ -0,0 +1,26 @@
|
||||
'use client'
|
||||
|
||||
import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import SectionHeader from './section-header'
|
||||
|
||||
const SkillTemplatesSection: FC = () => {
|
||||
const { t } = useTranslation('workflow')
|
||||
|
||||
return (
|
||||
<section className="px-6">
|
||||
<SectionHeader
|
||||
title={t('skill.startTab.templatesTitle')}
|
||||
description={t('skill.startTab.templatesDesc')}
|
||||
/>
|
||||
<div className="flex min-h-[200px] items-center justify-center rounded-xl border border-dashed border-divider-regular bg-background-section-burn">
|
||||
<span className="system-sm-regular text-text-quaternary">
|
||||
{t('skill.startTab.templatesComingSoon')}
|
||||
</span>
|
||||
</div>
|
||||
</section>
|
||||
)
|
||||
}
|
||||
|
||||
export default memo(SkillTemplatesSection)
|
||||
@ -1031,6 +1031,13 @@
|
||||
"singleRun.testRun": "Test Run",
|
||||
"singleRun.testRunIteration": "Test Run Iteration",
|
||||
"singleRun.testRunLoop": "Test Run Loop",
|
||||
"skill.startTab.createBlankSkill": "Create Blank Skill",
|
||||
"skill.startTab.createBlankSkillDesc": "Start with an empty folder structure",
|
||||
"skill.startTab.importSkill": "Import Skill",
|
||||
"skill.startTab.importSkillDesc": "Import skill from skill.zip file",
|
||||
"skill.startTab.templatesComingSoon": "Templates coming soon…",
|
||||
"skill.startTab.templatesDesc": "Choose a template to bootstrap your agent's capabilities",
|
||||
"skill.startTab.templatesTitle": "Skill Templates",
|
||||
"skillEditor.authorizationRequired": "Authorization required before use.",
|
||||
"skillEditor.previewUnavailable": "Preview unavailable",
|
||||
"skillEditor.referenceFiles": "Reference files",
|
||||
|
||||
@ -1023,6 +1023,13 @@
|
||||
"singleRun.testRun": "测试运行",
|
||||
"singleRun.testRunIteration": "测试运行迭代",
|
||||
"singleRun.testRunLoop": "测试运行循环",
|
||||
"skill.startTab.createBlankSkill": "创建空白 Skill",
|
||||
"skill.startTab.createBlankSkillDesc": "从空文件夹结构开始",
|
||||
"skill.startTab.importSkill": "导入 Skill",
|
||||
"skill.startTab.importSkillDesc": "从 skill.zip 文件导入",
|
||||
"skill.startTab.templatesComingSoon": "模板即将推出…",
|
||||
"skill.startTab.templatesDesc": "选择模板来快速构建你的 Agent 能力",
|
||||
"skill.startTab.templatesTitle": "Skill 模板",
|
||||
"skillEditor.authorizationRequired": "使用前需要授权。",
|
||||
"skillEditor.previewUnavailable": "无法预览",
|
||||
"skillEditor.referenceFiles": "引用文件",
|
||||
|
||||
Reference in New Issue
Block a user