| | |
| | | import DSLExportConfirmModal from '@/app/components/workflow/dsl-export-confirm-modal' |
| | | import { fetchWorkflowDraft } from '@/service/workflow' |
| | | import { fetchInstalledAppList } from '@/service/explore' |
| | | import { AppTypeIcon } from '@/app/components/app/type-selector' |
| | | // import { AppTypeIcon } from '@/app/components/app/type-selector' |
| | | |
| | | export type AppCardProps = { |
| | | app: App |
| | |
| | | iconType={app.icon_type} |
| | | icon={app.icon} |
| | | background={app.icon_background} |
| | | imageUrl={app.icon_url} |
| | | imageUrl={app.mode === 'agent-chat' ? '/avatar/agent.png' : '/avatar/workflow.png'} |
| | | /> |
| | | <AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='w-3 h-3' /> |
| | | {/* <AppTypeIcon type={app.mode} wrapperClassName='absolute -bottom-0.5 -right-0.5 w-4 h-4 shadow-sm' className='w-3 h-3' /> */} |
| | | </div> |
| | | <div className='grow w-0 py-[1px]'> |
| | | <div className='flex items-center text-sm leading-5 font-semibold text-text-secondary'> |
| | |
| | | import { useDebounceFn } from 'ahooks' |
| | | import { |
| | | RiApps2Line, |
| | | RiExchange2Line, |
| | | RiMessage3Line, |
| | | RiRobot3Line, |
| | | } from '@remixicon/react' |
| | | import AppCard from './AppCard' |
| | | import NewAppCard from './NewAppCard' |
| | |
| | | import { useStore as useTagStore } from '@/app/components/base/tag-management/store' |
| | | import TagManagementModal from '@/app/components/base/tag-management' |
| | | import TagFilter from '@/app/components/base/tag-management/filter' |
| | | import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' |
| | | // import CheckboxWithLabel from '@/app/components/datasets/create/website/base/checkbox-with-label' |
| | | |
| | | const getKey = ( |
| | | pageIndex: number, |
| | |
| | | keywords: string, |
| | | ) => { |
| | | if (!pageIndex || previousPageData.has_more) { |
| | | const params: any = { url: 'apps', params: { page: pageIndex + 1, limit: 30, name: keywords, is_created_by_me: isCreatedByMe } } |
| | | const params: any = { url: 'apps', params: { page: pageIndex + 1, limit: 30, name: keywords, is_created_by_me: true } } |
| | | |
| | | if (activeTab !== 'all') |
| | | params.params.mode = activeTab |
| | |
| | | const anchorRef = useRef<HTMLDivElement>(null) |
| | | const options = [ |
| | | { value: 'all', text: t('app.types.all'), icon: <RiApps2Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | // { value: 'chat', text: t('app.types.chatbot'), icon: <RiMessage3Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | // { value: 'agent-chat', text: t('app.types.agent'), icon: <RiRobot3Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | // { value: 'workflow', text: t('app.types.workflow'), icon: <RiExchange2Line className='w-[14px] h-[14px] mr-1' /> }, |
| | | ] |
| | | |
| | | useEffect(() => { |
| | |
| | | options={options} |
| | | /> |
| | | <div className='flex items-center gap-2'> |
| | | <CheckboxWithLabel |
| | | {/* <CheckboxWithLabel |
| | | className='mr-2' |
| | | label={t('app.showMyCreatedAppsOnly')} |
| | | isChecked={isCreatedByMe} |
| | | onChange={() => setIsCreatedByMe(!isCreatedByMe)} |
| | | /> |
| | | /> */} |
| | | <TagFilter type='app' value={tagFilterValue} onChange={handleTagsChange} /> |
| | | <Input |
| | | showLeftIcon |
| | |
| | | import CreateAppModal from '@/app/components/app/create-app-modal' |
| | | import CreateFromDSLModal, { CreateFromDSLModalTab } from '@/app/components/app/create-from-dsl-modal' |
| | | import { useProviderContext } from '@/context/provider-context' |
| | | import { FileArrow01, FilePlus01, FilePlus02 } from '@/app/components/base/icons/src/vender/line/files' |
| | | import { FilePlus01 } from '@/app/components/base/icons/src/vender/line/files' |
| | | import cn from '@/utils/classnames' |
| | | |
| | | export type CreateAppCardProps = { |
| | |
| | | <div className='px-6 pt-2 pb-1 text-xs font-medium leading-[18px] text-text-tertiary'>{t('app.createApp')}</div> |
| | | <button className='w-full flex items-center mb-1 px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppModal(true)}> |
| | | <FilePlus01 className='shrink-0 mr-2 w-4 h-4' /> |
| | | {t('app.newApp.startFromBlank')} |
| | | 创建智能体 |
| | | </button> |
| | | <button className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppTemplateDialog(true)}> |
| | | {/* <button className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover' onClick={() => setShowNewAppTemplateDialog(true)}> |
| | | <FilePlus02 className='shrink-0 mr-2 w-4 h-4' /> |
| | | {t('app.newApp.startFromTemplate')} |
| | | </button> |
| | |
| | | className='w-full flex items-center px-6 py-[7px] rounded-lg text-[13px] font-medium leading-[18px] text-text-tertiary cursor-pointer hover:text-text-secondary hover:bg-state-base-hover'> |
| | | <FileArrow01 className='shrink-0 mr-2 w-4 h-4' /> |
| | | {t('app.importDSL')} |
| | | </button> |
| | | </button> */} |
| | | </div> |
| | | |
| | | <CreateAppModal |
| | |
| | | 'use client' |
| | | import { useContextSelector } from 'use-context-selector' |
| | | import { useTranslation } from 'react-i18next' |
| | | import { RiDiscordFill, RiGithubFill } from '@remixicon/react' |
| | | import Link from 'next/link' |
| | | import style from '../list.module.css' |
| | | // import { useContextSelector } from 'use-context-selector' |
| | | // import { useTranslation } from 'react-i18next' |
| | | // import { RiDiscordFill, RiGithubFill } from '@remixicon/react' |
| | | // import Link from 'next/link' |
| | | // import style from '../list.module.css' |
| | | import Apps from './Apps' |
| | | import AppContext from '@/context/app-context' |
| | | import { LicenseStatus } from '@/types/feature' |
| | | // import AppContext from '@/context/app-context' |
| | | // import { LicenseStatus } from '@/types/feature' |
| | | |
| | | const AppList = () => { |
| | | const { t } = useTranslation() |
| | | const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures) |
| | | // const { t } = useTranslation() |
| | | // const systemFeatures = useContextSelector(AppContext, v => v.systemFeatures) |
| | | |
| | | return ( |
| | | <div className='relative flex flex-col overflow-y-auto bg-background-body shrink-0 h-0 grow'> |
| | | <Apps /> |
| | | {systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'> |
| | | {/* {systemFeatures.license.status === LicenseStatus.NONE && <footer className='px-12 py-6 grow-0 shrink-0'> |
| | | <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('app.join')}</h3> |
| | | <p className='mt-1 system-sm-regular text-text-tertiary'>{t('app.communityIntro')}</p> |
| | | <div className='flex items-center gap-2 mt-3'> |
| | |
| | | <RiDiscordFill className='w-5 h-5 text-text-tertiary' /> |
| | | </Link> |
| | | </div> |
| | | </footer>} |
| | | </footer>} */} |
| | | </div > |
| | | ) |
| | | } |
| | |
| | | const options = useMemo(() => { |
| | | return [ |
| | | { value: 'dataset', text: t('dataset.datasets') }, |
| | | ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]), |
| | | // ...(currentWorkspace.role === 'dataset_operator' ? [] : [{ value: 'api', text: t('dataset.datasetsApi') }]), |
| | | ] |
| | | }, [currentWorkspace.role, t]) |
| | | |
| | |
| | | onClear={() => handleKeywordsChange('')} |
| | | /> |
| | | <div className="w-[1px] h-4 bg-divider-regular" /> |
| | | <Button |
| | | {/* <Button |
| | | className='gap-0.5 shadows-shadow-xs' |
| | | onClick={() => setShowExternalApiPanel(true)} |
| | | > |
| | | <ApiConnectionMod className='w-4 h-4 text-components-button-secondary-text' /> |
| | | <div className='flex px-0.5 justify-center items-center gap-1 text-components-button-secondary-text system-sm-medium'>{t('dataset.externalAPIPanelTitle')}</div> |
| | | </Button> |
| | | </Button> */} |
| | | </div> |
| | | )} |
| | | {activeTab === 'api' && data && <ApiServer apiBaseUrl={data.api_base_url || ''} />} |
| | |
| | | |
| | | return ( |
| | | <footer className='px-12 py-6 grow-0 shrink-0'> |
| | | <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('dataset.didYouKnow')}</h3> |
| | | {/* <h3 className='text-xl font-semibold leading-tight text-gradient'>{t('dataset.didYouKnow')}</h3> |
| | | <p className='mt-1 text-sm font-normal leading-tight text-gray-700'> |
| | | {t('dataset.intro1')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro2')}</span>{t('dataset.intro3')}<br /> |
| | | {t('dataset.intro4')}<span className='inline-flex items-center gap-1 text-blue-600'>{t('dataset.intro5')}</span>{t('dataset.intro6')} |
| | | </p> |
| | | </p> */} |
| | | </footer> |
| | | ) |
| | | } |
| | |
| | | > |
| | | <div className='relative'> |
| | | <PortalToFollowElemTrigger |
| | | onClick={() => { |
| | | if (isCurrentWorkspaceEditor) |
| | | setOpen(v => !v) |
| | | }} |
| | | className='block' |
| | | > |
| | | <div className={cn('flex p-1 rounded-lg', open && 'bg-gray-100', isCurrentWorkspaceEditor && 'hover:bg-gray-100 cursor-pointer')}> |
| | |
| | | iconType={appDetail.icon_type} |
| | | icon={appDetail.icon} |
| | | background={appDetail.icon_background} |
| | | imageUrl={appDetail.icon_url} |
| | | imageUrl={appDetail.mode === 'agent-chat' ? '/avatar/agent.png' : '/avatar/workflow.png' } |
| | | /> |
| | | <span className={cn( |
| | | 'absolute bottom-[-3px] right-[-3px] w-4 h-4 p-0.5 bg-white rounded border-[0.5px] border-[rgba(0,0,0,0.02)] shadow-sm', |
| | | !expand && '!w-3.5 !h-3.5 !bottom-[-2px] !right-[-2px]', |
| | | )}> |
| | | {appDetail.mode === 'advanced-chat' && ( |
| | | <ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} /> |
| | | )} |
| | | {appDetail.mode === 'agent-chat' && ( |
| | | <CuteRobot className={cn('w-3 h-3 text-indigo-600', !expand && '!w-2.5 !h-2.5')} /> |
| | | )} |
| | | {appDetail.mode === 'chat' && ( |
| | | <ChatBot className={cn('w-3 h-3 text-[#1570EF]', !expand && '!w-2.5 !h-2.5')} /> |
| | | )} |
| | | {appDetail.mode === 'completion' && ( |
| | | <AiText className={cn('w-3 h-3 text-[#0E9384]', !expand && '!w-2.5 !h-2.5')} /> |
| | | )} |
| | | {appDetail.mode === 'workflow' && ( |
| | | <Route className={cn('w-3 h-3 text-[#f79009]', !expand && '!w-2.5 !h-2.5')} /> |
| | | )} |
| | | </span> |
| | | </div> |
| | | {expand && ( |
| | | <div className="grow w-0"> |
| | |
| | | > |
| | | {navigation.map((item, index) => { |
| | | return ( |
| | | <NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} /> |
| | | item.name === '编排' && <NavLink key={index} mode={appSidebarExpand} iconMap={{ selected: item.selectedIcon, normal: item.icon }} name={item.name} href={item.href} /> |
| | | ) |
| | | })} |
| | | </nav> |
| | |
| | | |
| | | const handlePublish = async (modelAndParameter?: ModelAndParameter) => { |
| | | try { |
| | | const messageData = { agentInfo: appDetail?.model_config } |
| | | window.parent.postMessage(messageData, '*') |
| | | await onPublish?.(modelAndParameter) |
| | | setPublished(true) |
| | | } |
| | |
| | | /> |
| | | )} |
| | | </div> |
| | | <div className='flex items-center'> |
| | | {/* <div className='flex items-center'> |
| | | {!readonly && !isMobile && ( |
| | | <AutomaticBtn onClick={showAutomaticTrue} /> |
| | | )} |
| | | </div> |
| | | </div> */} |
| | | </div> |
| | | )} |
| | | |
| New file |
| | |
| | | 'use client' |
| | | import type { FC } from 'react' |
| | | import React, { useState } from 'react' |
| | | import { useTranslation } from 'react-i18next' |
| | | import { useContext } from 'use-context-selector' |
| | | import produce from 'immer' |
| | | import { useFormattingChangedDispatcher } from '../../debug/hooks' |
| | | import CreateAppModal from '@/app/components/app/create-app-modal' |
| | | import Panel from '@/app/components/app/configuration/base/feature-panel' |
| | | import OperationBtn from '@/app/components/app/configuration/base/operation-btn' |
| | | import ConfigContext from '@/context/debug-configuration' |
| | | import type { AgentTool } from '@/types/app' |
| | | import { type Collection } from '@/app/components/tools/types' |
| | | import { useProviderContext } from '@/context/provider-context' |
| | | |
| | | type AgentToolWithMoreInfo = AgentTool & { icon: any; collection?: Collection } | null |
| | | const CreateWorkFlow: FC = () => { |
| | | const { t } = useTranslation() |
| | | const [isShowChooseTool, setIsShowChooseTool] = useState(false) |
| | | const [showNewAppTemplateDialog, setShowNewAppTemplateDialog] = useState(false) |
| | | const { modelConfig, setModelConfig, collectionList } = useContext(ConfigContext) |
| | | const formattingChangedDispatcher = useFormattingChangedDispatcher() |
| | | const [showNewAppModal, setShowNewAppModal] = useState(false) |
| | | const { onPlanInfoChanged } = useProviderContext() |
| | | |
| | | const [currentTool, setCurrentTool] = useState<AgentToolWithMoreInfo>(null) |
| | | const [isShowSettingTool, setIsShowSettingTool] = useState(false) |
| | | const tools = (modelConfig?.agentConfig?.tools as AgentTool[] || []).map((item) => { |
| | | const collection = collectionList.find(collection => collection.id === item.provider_id && collection.type === item.provider_type) |
| | | const icon = collection?.icon |
| | | return { |
| | | ...item, |
| | | icon, |
| | | collection, |
| | | } |
| | | }) |
| | | |
| | | const handleToolSettingChange = (value: Record<string, any>) => { |
| | | const newModelConfig = produce(modelConfig, (draft) => { |
| | | const tool = (draft.agentConfig.tools).find((item: any) => item.provider_id === currentTool?.collection?.id && item.tool_name === currentTool?.tool_name) |
| | | if (tool) |
| | | (tool as AgentTool).tool_parameters = value |
| | | }) |
| | | setModelConfig(newModelConfig) |
| | | setIsShowSettingTool(false) |
| | | formattingChangedDispatcher() |
| | | } |
| | | |
| | | return ( |
| | | <> |
| | | <Panel |
| | | className="mt-2" |
| | | noBodySpacing={tools.length === 0} |
| | | headerIcon={ |
| | | <span> - </span> |
| | | } |
| | | title={ |
| | | <div className='flex items-center'> |
| | | <div className='mr-1'>创建工作流</div> |
| | | </div> |
| | | } |
| | | headerRight={ |
| | | <div className='flex items-center'> |
| | | <OperationBtn type="add" actionName="创建" onClick={() => setShowNewAppModal(true)} /> |
| | | </div> |
| | | } |
| | | > |
| | | <div className='grid gap-1 grid-cols-1 2xl:grid-cols-2 items-center flex-wrap justify-between'></div> |
| | | </Panel > |
| | | <CreateAppModal |
| | | show={showNewAppModal} |
| | | onClose={() => setShowNewAppModal(false)} |
| | | isWorkFlow={true} |
| | | onSuccess={() => { |
| | | onPlanInfoChanged() |
| | | }} |
| | | onCreateFromTemplate={() => { |
| | | setShowNewAppTemplateDialog(true) |
| | | setShowNewAppModal(false) |
| | | }} |
| | | /> |
| | | </> |
| | | ) |
| | | } |
| | | export default React.memo(CreateWorkFlow) |
| | |
| | | import ConfigVision from '../config-vision' |
| | | import ConfigDocument from './config-document' |
| | | import AgentTools from './agent/agent-tools' |
| | | import CreateWorkflow from './agent/create-workFlow' |
| | | import ConfigContext from '@/context/debug-configuration' |
| | | import ConfigPrompt from '@/app/components/app/configuration/config-prompt' |
| | | import ConfigVar from '@/app/components/app/configuration/config-var' |
| | |
| | | <AgentTools /> |
| | | )} |
| | | |
| | | {isAgent && ( |
| | | <CreateWorkflow /> |
| | | )} |
| | | |
| | | <ConfigVision /> |
| | | |
| | | <ConfigDocument /> |
| | |
| | | |
| | | import { useRouter } from 'next/navigation' |
| | | import { useContext, useContextSelector } from 'use-context-selector' |
| | | import { RiArrowRightLine, RiCommandLine, RiCornerDownLeftLine, RiExchange2Fill } from '@remixicon/react' |
| | | import Link from 'next/link' |
| | | import { RiCommandLine, RiCornerDownLeftLine, RiExchange2Fill } from '@remixicon/react' |
| | | import { useDebounceFn, useKeyPress } from 'ahooks' |
| | | import Image from 'next/image' |
| | | import AppIcon from '../../base/app-icon' |
| | | import AppIconPicker from '../../base/app-icon-picker' |
| | | import type { AppIconSelection } from '../../base/app-icon-picker' |
| | | import Button from '@/app/components/base/button' |
| | |
| | | import { createApp } from '@/service/apps' |
| | | import Input from '@/app/components/base/input' |
| | | import Textarea from '@/app/components/base/textarea' |
| | | import AppIcon from '@/app/components/base/app-icon' |
| | | import AppsFull from '@/app/components/billing/apps-full-in-dialog' |
| | | import { BubbleTextMod, ChatBot, ListSparkle, Logic } from '@/app/components/base/icons/src/vender/solid/communication' |
| | | import { Logic } from '@/app/components/base/icons/src/vender/solid/communication' |
| | | import { NEED_REFRESH_APP_LIST_KEY } from '@/config' |
| | | import { getRedirection } from '@/utils/app-redirection' |
| | | import FullScreenModal from '@/app/components/base/fullscreen-modal' |
| | |
| | | onSuccess: () => void |
| | | onClose: () => void |
| | | onCreateFromTemplate?: () => void |
| | | isWorkFlow?: boolean |
| | | } |
| | | |
| | | function CreateApp({ onClose, onSuccess, onCreateFromTemplate }: CreateAppProps) { |
| | | function CreateApp({ onClose, onSuccess, onCreateFromTemplate, isWorkFlow }: CreateAppProps) { |
| | | const { t } = useTranslation() |
| | | const { push } = useRouter() |
| | | const { notify } = useContext(ToastContext) |
| | |
| | | icon_type: appIcon.type, |
| | | icon: appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId, |
| | | icon_background: appIcon.type === 'emoji' ? appIcon.background : undefined, |
| | | mode: appMode, |
| | | mode: isWorkFlow ? 'workflow' : 'agent-chat', |
| | | }) |
| | | notify({ type: 'success', message: t('app.newApp.appCreated') }) |
| | | onSuccess() |
| | |
| | | }) |
| | | return <> |
| | | <div className='flex justify-center h-full overflow-y-auto overflow-x-hidden'> |
| | | <div className='flex-1 shrink-0 flex justify-end'> |
| | | <div className='flex-1 shrink-0 flex justify-center'> |
| | | <div className='px-10'> |
| | | <div className='w-full h-6 2xl:h-[139px]' /> |
| | | <div className='w-full h-6 2xl:h-[20px]' /> |
| | | <div className='pt-1 pb-6'> |
| | | <span className='title-2xl-semi-bold text-text-primary'>{t('app.newApp.startFromBlank')}</span> |
| | | <span className='title-2xl-semi-bold text-text-primary'>{ isWorkFlow ? '创建工作流' : '创建智能体' }</span> |
| | | </div> |
| | | <div className='leading-6 mb-2'> |
| | | {!isWorkFlow && <div className='leading-6 mb-2'> |
| | | <span className='system-sm-semibold text-text-secondary'>{t('app.newApp.chooseAppType')}</span> |
| | | </div> |
| | | </div>} |
| | | <div className='flex flex-col w-[660px] gap-4'> |
| | | <div> |
| | | <div className='mb-2'> |
| | | {!isWorkFlow && <div> |
| | | {/* <div className='mb-2'> |
| | | <span className='system-2xs-medium-uppercase text-text-tertiary'>{t('app.newApp.forBeginners')}</span> |
| | | </div> |
| | | </div> */} |
| | | <div className='flex flex-row gap-2'> |
| | | <AppTypeCard |
| | | {/* <AppTypeCard |
| | | active={appMode === 'chat'} |
| | | title={t('app.types.chatbot')} |
| | | description={t('app.newApp.chatbotShortDescription')} |
| | |
| | | </div>} |
| | | onClick={() => { |
| | | setAppMode('chat') |
| | | }} /> |
| | | }} /> */} |
| | | <AppTypeCard |
| | | active={appMode === 'agent-chat'} |
| | | title={t('app.types.agent')} |
| | |
| | | onClick={() => { |
| | | setAppMode('agent-chat') |
| | | }} /> |
| | | <AppTypeCard |
| | | {/* <AppTypeCard |
| | | active={appMode === 'completion'} |
| | | title={t('app.newApp.completeApp')} |
| | | description={t('app.newApp.completionShortDescription')} |
| | |
| | | </div>} |
| | | onClick={() => { |
| | | setAppMode('completion') |
| | | }} /> |
| | | }} /> */} |
| | | </div> |
| | | </div> |
| | | </div>} |
| | | <div> |
| | | <div className='mb-2'> |
| | | {/* <div className='mb-2'> |
| | | <span className='system-2xs-medium-uppercase text-text-tertiary'>{t('app.newApp.forAdvanced')}</span> |
| | | </div> |
| | | </div> */} |
| | | <div className='flex flex-row gap-2'> |
| | | <AppTypeCard |
| | | {/* <AppTypeCard |
| | | beta |
| | | active={appMode === 'advanced-chat'} |
| | | title={t('app.types.advanced')} |
| | |
| | | </div>} |
| | | onClick={() => { |
| | | setAppMode('advanced-chat') |
| | | }} /> |
| | | <AppTypeCard |
| | | }} /> */} |
| | | {/* <AppTypeCard |
| | | beta |
| | | active={appMode === 'workflow'} |
| | | title={t('app.types.workflow')} |
| | |
| | | </div>} |
| | | onClick={() => { |
| | | setAppMode('workflow') |
| | | }} /> |
| | | }} /> */} |
| | | </div> |
| | | </div> |
| | | <Divider style={{ margin: 0 }} /> |
| | | <div className='flex space-x-3 items-center'> |
| | | <div className='flex-1'> |
| | | <div className='h-6 flex items-center mb-1'> |
| | | <label className='system-sm-semibold text-text-secondary'>{t('app.newApp.captionName')}</label> |
| | | <label className='system-sm-semibold text-text-secondary'>应用名称</label> |
| | | </div> |
| | | <Input |
| | | value={name} |
| | |
| | | placeholder={t('app.newApp.appNamePlaceholder') || ''} |
| | | /> |
| | | </div> |
| | | <AppIcon |
| | | {/* <AppIcon |
| | | iconType={appIcon.type} |
| | | icon={appIcon.type === 'emoji' ? appIcon.icon : appIcon.fileId} |
| | | background={appIcon.type === 'emoji' ? appIcon.background : undefined} |
| | | imageUrl={appIcon.type === 'image' ? appIcon.url : undefined} |
| | | size='xxl' className='cursor-pointer rounded-2xl' |
| | | onClick={() => { setShowAppIconPicker(true) }} |
| | | /> |
| | | /> */} |
| | | {showAppIconPicker && <AppIconPicker |
| | | onSelect={(payload) => { |
| | | setAppIcon(payload) |
| | |
| | | </div> |
| | | </div> |
| | | <div className='pt-5 pb-10 flex justify-between items-center'> |
| | | <div className='flex gap-1 items-center system-xs-regular text-text-tertiary cursor-pointer' onClick={onCreateFromTemplate}> |
| | | {/* <div className='flex gap-1 items-center system-xs-regular text-text-tertiary cursor-pointer' onClick={onCreateFromTemplate}> |
| | | <span>{t('app.newApp.noIdeaTip')}</span> |
| | | <div className='p-[1px]'> |
| | | <RiArrowRightLine className='w-3.5 h-3.5' /> |
| | | </div> |
| | | </div> |
| | | </div> */} |
| | | <div className='flex gap-2'> |
| | | <Button onClick={onClose}>{t('app.newApp.Cancel')}</Button> |
| | | <Button disabled={isAppsFull || !name} className='gap-1' variant="primary" onClick={handleCreateApp}> |
| | |
| | | </Button> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | <div className='flex-1 shrink h-full flex justify-start relative overflow-hidden'> |
| | | <div className='h-6 2xl:h-[139px] absolute left-0 top-0 right-0 border-b border-b-divider-subtle'></div> |
| | | <div className='max-w-[760px] border-x border-x-divider-subtle'> |
| | | <div className='h-6 2xl:h-[139px]' /> |
| | | <AppPreview mode={appMode} /> |
| | | <div className='absolute left-0 right-0 border-b border-b-divider-subtle'></div> |
| | | <div className='w-[664px] h-[448px] flex items-center justify-center' style={{ background: 'repeating-linear-gradient(135deg, transparent, transparent 2px, rgba(16,24,40,0.04) 4px,transparent 3px, transparent 6px)' }}> |
| | | <AppScreenShot show={appMode === 'chat'} mode='chat' /> |
| | | <AppScreenShot show={appMode === 'advanced-chat'} mode='advanced-chat' /> |
| | | <AppScreenShot show={appMode === 'agent-chat'} mode='agent-chat' /> |
| | | <AppScreenShot show={appMode === 'completion'} mode='completion' /> |
| | | <AppScreenShot show={appMode === 'workflow'} mode='workflow' /> |
| | | </div> |
| | | <div className='absolute left-0 right-0 border-b border-b-divider-subtle'></div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | |
| | | type CreateAppDialogProps = CreateAppProps & { |
| | | show: boolean |
| | | } |
| | | const CreateAppModal = ({ show, onClose, onSuccess, onCreateFromTemplate }: CreateAppDialogProps) => { |
| | | const CreateAppModal = ({ show, onClose, onSuccess, onCreateFromTemplate, isWorkFlow }: CreateAppDialogProps) => { |
| | | return ( |
| | | <FullScreenModal |
| | | overflowVisible |
| | |
| | | open={show} |
| | | onClose={onClose} |
| | | > |
| | | <CreateApp onClose={onClose} onSuccess={onSuccess} onCreateFromTemplate={onCreateFromTemplate} /> |
| | | <CreateApp isWorkFlow={isWorkFlow} onClose={onClose} onSuccess={onSuccess} onCreateFromTemplate={onCreateFromTemplate} /> |
| | | </FullScreenModal> |
| | | ) |
| | | } |
| | |
| | | {beta && <div className='px-[5px] py-[3px] |
| | | rounded-[5px] min-w-[18px] absolute top-3 right-3 |
| | | border border-divider-deep system-2xs-medium-uppercase text-text-tertiary'>{t('common.menus.status')}</div>} |
| | | {icon} |
| | | <AppIcon |
| | | imageUrl='/avatar/agent.png' |
| | | /> |
| | | <div className='system-sm-semibold text-text-secondary mt-2 mb-0.5'>{title}</div> |
| | | <div className='system-xs-regular text-text-tertiary'>{description}</div> |
| | | </div> |
| | |
| | | link: 'https://docs.dify.ai/guides/workflow', |
| | | }, |
| | | } |
| | | const previewInfo = modeToPreviewInfoMap[mode] |
| | | // const previewInfo = modeToPreviewInfoMap[mode] |
| | | return <div className='px-8 py-4'> |
| | | <h4 className='system-sm-semibold-uppercase text-text-secondary'>{previewInfo.title}</h4> |
| | | {/* <h4 className='system-sm-semibold-uppercase text-text-secondary'>{previewInfo.title}</h4> |
| | | <div className='mt-1 system-xs-regular text-text-tertiary max-w-96 min-h-8'> |
| | | <span>{previewInfo.description}</span> |
| | | {previewInfo.link && <Link target='_blank' href={previewInfo.link} className='text-text-accent ml-1'>{t('app.newApp.learnMore')}</Link>} |
| | | </div> |
| | | </div> */} |
| | | </div> |
| | | } |
| | | |
| | |
| | | const AppIcon: FC<AppIconProps> = ({ |
| | | size = 'medium', |
| | | rounded = false, |
| | | iconType, |
| | | icon, |
| | | background, |
| | | imageUrl, |
| | |
| | | innerIcon, |
| | | onClick, |
| | | }) => { |
| | | const isValidImageIcon = iconType === 'image' && imageUrl |
| | | const isValidImageIcon = true |
| | | |
| | | return <span |
| | | className={classNames(appIconVariants({ size, rounded }), className)} |
| | |
| | | > |
| | | {isValidImageIcon |
| | | // eslint-disable-next-line @next/next/no-img-element |
| | | ? <img src={imageUrl} className="w-full h-full" alt="app icon" /> |
| | | ? <img src={imageUrl || ''} className="w-full h-full" alt="app icon" /> |
| | | : (innerIcon || ((icon && icon !== '') ? <em-emoji id={icon} /> : <em-emoji id='🤖' />)) |
| | | } |
| | | </span> |
| | |
| | | |
| | | return ( |
| | | <div className='-translate-y-2 m-1 mt-0 px-2.5 py-2 pt-4 bg-util-colors-indigo-indigo-50 rounded-b-[10px] border-l border-b border-r border-components-panel-border-subtle'> |
| | | {noFeatureEnabled && ( |
| | | {/* {noFeatureEnabled && ( |
| | | <div className='flex items-end gap-1 cursor-pointer' onClick={() => onFeatureBarClick?.(true)}> |
| | | <RiApps2AddLine className='w-3.5 h-3.5 text-text-accent' /> |
| | | <div className='text-text-accent body-xs-medium'>{t('appDebug.feature.bar.empty')}</div> |
| | |
| | | <RiArrowRightLine className='w-3.5 h-3.5 text-text-accent' /> |
| | | </Button> |
| | | </div> |
| | | )} |
| | | )} */} |
| | | </div> |
| | | ) |
| | | } |
| | |
| | | import Textarea from '@/app/components/base/textarea' |
| | | import Switch from '@/app/components/base/switch' |
| | | import Toast from '@/app/components/base/toast' |
| | | import AppIcon from '@/app/components/base/app-icon' |
| | | // import AppIcon from '@/app/components/base/app-icon' |
| | | import { useProviderContext } from '@/context/provider-context' |
| | | import AppsFull from '@/app/components/billing/apps-full-in-dialog' |
| | | import type { AppIconType } from '@/types/app' |
| | |
| | | <div className='mb-9'> |
| | | {/* icon & name */} |
| | | <div className='pt-2'> |
| | | <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.newApp.captionName')}</div> |
| | | <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>应用名称</div> |
| | | <div className='flex items-center justify-between space-x-2'> |
| | | <AppIcon |
| | | {/* <AppIcon |
| | | size='large' |
| | | onClick={() => { setShowAppIconPicker(true) }} |
| | | className='cursor-pointer' |
| | |
| | | icon={appIcon.type === 'image' ? appIcon.fileId : appIcon.icon} |
| | | background={appIcon.type === 'image' ? undefined : appIcon.background} |
| | | imageUrl={appIcon.type === 'image' ? appIcon.url : undefined} |
| | | /> |
| | | /> */} |
| | | <Input |
| | | value={name} |
| | | onChange={e => setName(e.target.value)} |
| | |
| | | /> |
| | | </div> |
| | | {/* answer icon */} |
| | | {isEditModal && (appMode === 'chat' || appMode === 'advanced-chat' || appMode === 'agent-chat') && ( |
| | | {/* {isEditModal && (appMode === 'chat' || appMode === 'advanced-chat' || appMode === 'agent-chat') && ( |
| | | <div className='pt-2'> |
| | | <div className='flex justify-between items-center'> |
| | | <div className='py-2 text-sm font-medium leading-[20px] text-gray-900'>{t('app.answerIcon.title')}</div> |
| | |
| | | </div> |
| | | <p className='body-xs-regular text-gray-500'>{t('app.answerIcon.descriptionInExplore')}</p> |
| | | </div> |
| | | )} |
| | | )} */} |
| | | {!isEditModal && isAppsFull && <AppsFull loc='app-explore-create' />} |
| | | </div> |
| | | <div className='flex flex-row-reverse'> |
| | |
| | | return ( |
| | | <div className='flex flex-1 items-center justify-between px-4 bg-background-body'> |
| | | <div className='flex items-center'> |
| | | {isMobile && <div |
| | | {/* {isMobile && <div |
| | | className='flex items-center justify-center h-8 w-8 cursor-pointer' |
| | | onClick={toggle} |
| | | > |
| | | <Bars3Icon className="h-4 w-4 text-gray-500" /> |
| | | </div>} |
| | | </div>} */} |
| | | {!isMobile && <> |
| | | <Link href="/apps" className='flex items-center mr-4'> |
| | | {/* <Link href="/apps" className='flex items-center mr-4'> |
| | | <LogoSite className='object-contain' /> |
| | | </Link> |
| | | {systemFeatures.license.status === LicenseStatus.NONE && <GithubStar />} |
| | | </Link> */} |
| | | {/* {systemFeatures.license.status === LicenseStatus.NONE && <GithubStar />} */} |
| | | </>} |
| | | </div> |
| | | {isMobile && ( |
| | | {/* {isMobile && ( |
| | | <div className='flex'> |
| | | <Link href="/apps" className='flex items-center mr-4'> |
| | | <LogoSite /> |
| | | </Link> |
| | | {systemFeatures.license.status === LicenseStatus.NONE && <GithubStar />} |
| | | </div> |
| | | )} |
| | | )} */} |
| | | {!isMobile && ( |
| | | <div className='flex items-center'> |
| | | {!isCurrentWorkspaceDatasetOperator && <ExploreNav className={navClassName} />} |
| | | {/* {!isCurrentWorkspaceDatasetOperator && <ExploreNav className={navClassName} />} */} |
| | | {!isCurrentWorkspaceDatasetOperator && <AppNav />} |
| | | {(isCurrentWorkspaceEditor || isCurrentWorkspaceDatasetOperator) && <DatasetNav />} |
| | | {!isCurrentWorkspaceDatasetOperator && <ToolsNav className={navClassName} />} |
| | | {/* {!isCurrentWorkspaceDatasetOperator && <ToolsNav className={navClassName} />} */} |
| | | </div> |
| | | )} |
| | | <div className='flex items-center flex-shrink-0'> |
| | | <LicenseNav /> |
| | | <EnvNav /> |
| | | {enableBilling && ( |
| | | {/* <EnvNav /> */} |
| | | {/* {enableBilling && ( |
| | | <div className='mr-3 select-none'> |
| | | <HeaderBillingBtn onClick={handlePlanClick} /> |
| | | </div> |
| | | )} |
| | | <WorkspaceProvider> |
| | | )} */} |
| | | {/* <WorkspaceProvider> |
| | | <AccountDropdown isMobile={isMobile} /> |
| | | </WorkspaceProvider> |
| | | </WorkspaceProvider> */} |
| | | </div> |
| | | {(isMobile && isShowNavMenu) && ( |
| | | <div className='w-full flex flex-col p-2 gap-y-1'> |
| | |
| | | {text} |
| | | </div> |
| | | </Link> |
| | | { |
| | | {/* { |
| | | curNav && isActivated && ( |
| | | <> |
| | | <div className='font-light text-gray-300 '>/</div> |
| | |
| | | /> |
| | | </> |
| | | ) |
| | | } |
| | | } */} |
| | | </div> |
| | | ) |
| | | } |
| | |
| | | import { useCallback, useEffect, useState } from 'react' |
| | | import type { ReactNode } from 'react' |
| | | import { usePathname, useRouter, useSearchParams } from 'next/navigation' |
| | | import { fetchSetupStatus } from '@/service/common' |
| | | import { fetchSetupStatus, regAndLogin } from '@/service/common' |
| | | import type { RegAndLoginResponse } from '@/models/common' |
| | | |
| | | type SwrInitorProps = { |
| | | children: ReactNode |
| | |
| | | const searchParams = useSearchParams() |
| | | const consoleToken = decodeURIComponent(searchParams.get('access_token') || '') |
| | | const refreshToken = decodeURIComponent(searchParams.get('refresh_token') || '') |
| | | const userId = decodeURIComponent(searchParams.get('userId') || '') |
| | | const consoleTokenFromLocalStorage = localStorage?.getItem('console_token') |
| | | const refreshTokenFromLocalStorage = localStorage?.getItem('refresh_token') |
| | | const pathname = usePathname() |
| | |
| | | useEffect(() => { |
| | | (async () => { |
| | | try { |
| | | const isFinished = await isSetupFinished() |
| | | if (!isFinished) { |
| | | router.replace('/install') |
| | | return |
| | | let currentUserId = '' |
| | | if (userId) { |
| | | currentUserId = userId |
| | | localStorage.setItem('userId', userId) |
| | | } |
| | | if (!((consoleToken && refreshToken) || (consoleTokenFromLocalStorage && refreshTokenFromLocalStorage))) { |
| | | router.replace('/signin') |
| | | return |
| | | else { |
| | | currentUserId = localStorage.getItem('userId') || '' |
| | | } |
| | | if (searchParams.has('access_token') || searchParams.has('refresh_token')) { |
| | | consoleToken && localStorage.setItem('console_token', consoleToken) |
| | | refreshToken && localStorage.setItem('refresh_token', refreshToken) |
| | | router.replace(pathname) |
| | | } |
| | | |
| | | const response: RegAndLoginResponse = await regAndLogin({ body: { userId: currentUserId } }) |
| | | localStorage.setItem('console_token', response.data.access_token) |
| | | localStorage.setItem('refresh_token', response.data.refresh_token) |
| | | setInit(true) |
| | | // if (!isFinished) { |
| | | // router.replace('/install') |
| | | // return |
| | | // } |
| | | // if (!((consoleToken && refreshToken) || (consoleTokenFromLocalStorage && refreshTokenFromLocalStorage))) { |
| | | // router.replace('/signin') |
| | | // return |
| | | // } |
| | | // if (searchParams.has('access_token') || searchParams.has('refresh_token')) { |
| | | // consoleToken && localStorage.setItem('console_token', consoleToken) |
| | | // refreshToken && localStorage.setItem('refresh_token', refreshToken) |
| | | // router.replace(pathname) |
| | | // } |
| | | |
| | | // setInit(true) |
| | | } |
| | | catch (error) { |
| | | router.replace('/signin') |
| | |
| | | import { languages } from '@/i18n/language' |
| | | import { type Locale } from '@/i18n' |
| | | import I18n from '@/context/i18n' |
| | | import LogoSite from '@/app/components/base/logo/logo-site' |
| | | // import LogoSite from '@/app/components/base/logo/logo-site' |
| | | |
| | | const Header = () => { |
| | | const { locale, setLocaleOnClient } = useContext(I18n) |
| | | |
| | | return <div className='flex items-center justify-between p-6 w-full'> |
| | | <LogoSite /> |
| | | {/* <LogoSite /> */} |
| | | <Select |
| | | value={locale} |
| | | items={languages.filter(item => item.supported)} |
| | |
| | | {children} |
| | | </div> |
| | | </div> |
| | | <div className='px-8 py-6 system-xs-regular text-text-tertiary'> |
| | | {/* <div className='px-8 py-6 system-xs-regular text-text-tertiary'> |
| | | © {new Date().getFullYear()} LangGenius, Inc. All rights reserved. |
| | | </div> |
| | | </div> */} |
| | | </div> |
| | | </div> |
| | | </> |
| | |
| | | edit: '编辑标注', |
| | | }, |
| | | dataSet: { |
| | | title: '上下文', |
| | | noData: '您可以导入知识库作为上下文', |
| | | title: '知识库', |
| | | noData: '您可以添加已有或创建新的知识库', |
| | | words: '词', |
| | | textBlocks: '文本块', |
| | | selectTitle: '选择引用知识库', |
| | |
| | | nextIteration: '下一次迭代', |
| | | promptPlaceholder: '在这里写下您的提示词', |
| | | tools: { |
| | | name: '工具', |
| | | name: '插件或工作流', |
| | | description: '使用工具可以扩展代理的能力,比如搜索互联网或科学计算', |
| | | enabled: '启用', |
| | | }, |
| | |
| | | types: { |
| | | all: '全部', |
| | | chatbot: '聊天助手', |
| | | agent: 'Agent', |
| | | agent: '智能体', |
| | | workflow: '工作流', |
| | | completion: '文本生成', |
| | | advanced: 'Chatflow', |
| | |
| | | const translation = { |
| | | pageTitle: '嗨,近来可好', |
| | | welcome: '👋 欢迎来到 Dify, 登录以继续', |
| | | welcome: '👋 欢迎来到 Qxueyou, 登录以继续', |
| | | email: '邮箱', |
| | | emailPlaceholder: '输入邮箱地址', |
| | | password: '密码', |
| | |
| | | nextIteration: '下一次迭代', |
| | | promptPlaceholder: '在這裡寫下您的提示詞', |
| | | tools: { |
| | | name: '工具', |
| | | name: '插件或工作流', |
| | | description: '使用工具可以擴充套件代理的能力,比如搜尋網際網路或科學計算', |
| | | enabled: '啟用', |
| | | }, |
| | |
| | | result: 'success' | 'fail' |
| | | } |
| | | |
| | | export type RegAndLoginResponse = { |
| | | result: 'success' | 'fail' |
| | | data: RegAndLoginData |
| | | } |
| | | |
| | | type RegAndLoginData = { |
| | | access_token: string |
| | | refresh_token: string |
| | | } |
| | | |
| | | export type OauthResponse = { |
| | | redirect_url: string |
| | | } |
| | |
| | | return get<SetupStatusResponse>('/setup') |
| | | } |
| | | |
| | | export const regAndLogin: Fetcher<CommonResponse, { body: Record<string, any> }> = ({ body }) => { |
| | | return post<CommonResponse>('/regAndLogin', { body }) |
| | | } |
| | | |
| | | export const fetchUserProfile: Fetcher<UserProfileOriginResponse, { url: string; params: Record<string, any> }> = ({ url, params }) => { |
| | | return get<UserProfileOriginResponse>(url, params, { needAllResponseContent: true }) |
| | | } |