mirror of
https://github.com/langgenius/dify.git
synced 2026-04-28 11:56:55 +08:00
features
This commit is contained in:
parent
4edaa95cbf
commit
0164dec438
@ -1,9 +1,10 @@
|
|||||||
'use client'
|
'use client'
|
||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import React from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import cn from 'classnames'
|
import cn from 'classnames'
|
||||||
import s from './style.module.css'
|
import s from './style.module.css'
|
||||||
import Switch from '@/app/components/base/switch'
|
import Switch from '@/app/components/base/switch'
|
||||||
|
import type { FeatureEnum } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
export type IFeatureItemProps = {
|
export type IFeatureItemProps = {
|
||||||
icon: React.ReactNode
|
icon: React.ReactNode
|
||||||
@ -11,7 +12,8 @@ export type IFeatureItemProps = {
|
|||||||
title: string
|
title: string
|
||||||
description: string
|
description: string
|
||||||
value: boolean
|
value: boolean
|
||||||
onChange: (value: boolean) => void
|
onChange: (type: FeatureEnum, value: boolean) => void
|
||||||
|
type: FeatureEnum
|
||||||
}
|
}
|
||||||
|
|
||||||
const FeatureItem: FC<IFeatureItemProps> = ({
|
const FeatureItem: FC<IFeatureItemProps> = ({
|
||||||
@ -21,7 +23,12 @@ const FeatureItem: FC<IFeatureItemProps> = ({
|
|||||||
description,
|
description,
|
||||||
value,
|
value,
|
||||||
onChange,
|
onChange,
|
||||||
|
type,
|
||||||
}) => {
|
}) => {
|
||||||
|
const handleChange = useCallback((newValue: boolean) => {
|
||||||
|
onChange(type, newValue)
|
||||||
|
}, [type, onChange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={cn(s.wrap, 'relative flex justify-between p-3 rounded-xl border border-transparent bg-gray-50 hover:border-gray-200 cursor-pointer')}>
|
<div className={cn(s.wrap, 'relative flex justify-between p-3 rounded-xl border border-transparent bg-gray-50 hover:border-gray-200 cursor-pointer')}>
|
||||||
<div className='flex space-x-3 mr-2'>
|
<div className='flex space-x-3 mr-2'>
|
||||||
@ -40,7 +47,7 @@ const FeatureItem: FC<IFeatureItemProps> = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Switch onChange={onChange} defaultValue={value} />
|
<Switch onChange={handleChange} defaultValue={value} />
|
||||||
{
|
{
|
||||||
previewImgClassName && (
|
previewImgClassName && (
|
||||||
<div className={cn(s.preview, s[previewImgClassName])}>
|
<div className={cn(s.preview, s[previewImgClassName])}>
|
||||||
|
|||||||
@ -3,7 +3,10 @@ import type { FC } from 'react'
|
|||||||
import React, { useCallback } from 'react'
|
import React, { useCallback } from 'react'
|
||||||
import produce from 'immer'
|
import produce from 'immer'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useFeatures } from '../hooks'
|
import {
|
||||||
|
useFeatures,
|
||||||
|
useFeaturesStore,
|
||||||
|
} from '../hooks'
|
||||||
import FeatureGroup from './feature-group'
|
import FeatureGroup from './feature-group'
|
||||||
import FeatureItem from './feature-item'
|
import FeatureItem from './feature-item'
|
||||||
import Modal from '@/app/components/base/modal'
|
import Modal from '@/app/components/base/modal'
|
||||||
@ -15,37 +18,43 @@ import {
|
|||||||
MessageFast,
|
MessageFast,
|
||||||
MessageHeartCircle,
|
MessageHeartCircle,
|
||||||
} from '@/app/components/base/icons/src/vender/solid/communication'
|
} from '@/app/components/base/icons/src/vender/solid/communication'
|
||||||
|
import { FeatureEnum } from '@/app/components/base/features/types'
|
||||||
|
import type { Features } from '@/app/components/base/features/types'
|
||||||
|
|
||||||
export type ChooseFeatureProps = {
|
export type FeatureModalProps = {
|
||||||
|
onChange?: (features: Features) => void
|
||||||
showTextToSpeechItem?: boolean
|
showTextToSpeechItem?: boolean
|
||||||
showSpeechToTextItem?: boolean
|
showSpeechToTextItem?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
const ChooseFeature: FC<ChooseFeatureProps> = ({
|
const FeatureModal: FC<FeatureModalProps> = ({
|
||||||
|
onChange,
|
||||||
showTextToSpeechItem,
|
showTextToSpeechItem,
|
||||||
showSpeechToTextItem,
|
showSpeechToTextItem,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
const featuresStore = useFeaturesStore()
|
||||||
const setShowFeaturesModal = useFeatures(s => s.setShowFeaturesModal)
|
const setShowFeaturesModal = useFeatures(s => s.setShowFeaturesModal)
|
||||||
const openingStatement = useFeatures(s => s.openingStatement)
|
const features = useFeatures(s => s.features)
|
||||||
const setOpeningStatement = useFeatures(s => s.setOpeningStatement)
|
|
||||||
const suggestedQuestionsAfterAnswer = useFeatures(s => s.suggestedQuestionsAfterAnswer)
|
|
||||||
const setSuggestedQuestionsAfterAnswer = useFeatures(s => s.setSuggestedQuestionsAfterAnswer)
|
|
||||||
const textToSpeech = useFeatures(s => s.textToSpeech)
|
|
||||||
const setTextToSpeech = useFeatures(s => s.setTextToSpeech)
|
|
||||||
const speechToText = useFeatures(s => s.speechToText)
|
|
||||||
const setSpeechToText = useFeatures(s => s.setSpeechToText)
|
|
||||||
const citation = useFeatures(s => s.citation)
|
|
||||||
const setCitation = useFeatures(s => s.setCitation)
|
|
||||||
const moderation = useFeatures(s => s.moderation)
|
|
||||||
const setModeration = useFeatures(s => s.setModeration)
|
|
||||||
const annotation = useFeatures(s => s.annotation)
|
|
||||||
const setAnnotation = useFeatures(s => s.setAnnotation)
|
|
||||||
|
|
||||||
const handleCancelModal = useCallback(() => {
|
const handleCancelModal = useCallback(() => {
|
||||||
setShowFeaturesModal(false)
|
setShowFeaturesModal(false)
|
||||||
}, [setShowFeaturesModal])
|
}, [setShowFeaturesModal])
|
||||||
|
|
||||||
|
const handleChange = useCallback((type: FeatureEnum, enabled: boolean) => {
|
||||||
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = featuresStore!.getState()
|
||||||
|
|
||||||
|
const newFeatures = produce(features, (draft) => {
|
||||||
|
draft[type].enabled = enabled
|
||||||
|
})
|
||||||
|
setFeatures(newFeatures)
|
||||||
|
if (onChange)
|
||||||
|
onChange(newFeatures)
|
||||||
|
}, [featuresStore, onChange])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Modal
|
<Modal
|
||||||
isShow
|
isShow
|
||||||
@ -67,20 +76,18 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
previewImgClassName='openingStatementPreview'
|
previewImgClassName='openingStatementPreview'
|
||||||
title={t('appDebug.feature.conversationOpener.title')}
|
title={t('appDebug.feature.conversationOpener.title')}
|
||||||
description={t('appDebug.feature.conversationOpener.description')}
|
description={t('appDebug.feature.conversationOpener.description')}
|
||||||
value={openingStatement.enabled}
|
value={!!features.opening.enabled}
|
||||||
onChange={value => setOpeningStatement(produce(openingStatement, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.opening}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
<FeatureItem
|
<FeatureItem
|
||||||
icon={<SuggestedQuestionsAfterAnswerIcon />}
|
icon={<SuggestedQuestionsAfterAnswerIcon />}
|
||||||
previewImgClassName='suggestedQuestionsAfterAnswerPreview'
|
previewImgClassName='suggestedQuestionsAfterAnswerPreview'
|
||||||
title={t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
|
title={t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
|
||||||
description={t('appDebug.feature.suggestedQuestionsAfterAnswer.description')}
|
description={t('appDebug.feature.suggestedQuestionsAfterAnswer.description')}
|
||||||
value={suggestedQuestionsAfterAnswer.enabled}
|
value={!!features.suggested.enabled}
|
||||||
onChange={value => setSuggestedQuestionsAfterAnswer(produce(suggestedQuestionsAfterAnswer, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.suggested}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
{
|
{
|
||||||
showTextToSpeechItem && (
|
showTextToSpeechItem && (
|
||||||
@ -89,10 +96,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
previewImgClassName='textToSpeechPreview'
|
previewImgClassName='textToSpeechPreview'
|
||||||
title={t('appDebug.feature.textToSpeech.title')}
|
title={t('appDebug.feature.textToSpeech.title')}
|
||||||
description={t('appDebug.feature.textToSpeech.description')}
|
description={t('appDebug.feature.textToSpeech.description')}
|
||||||
value={textToSpeech.enabled}
|
value={!!features.text2speech.enabled}
|
||||||
onChange={value => setTextToSpeech(produce(textToSpeech, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.text2speech}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -103,10 +109,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
previewImgClassName='speechToTextPreview'
|
previewImgClassName='speechToTextPreview'
|
||||||
title={t('appDebug.feature.speechToText.title')}
|
title={t('appDebug.feature.speechToText.title')}
|
||||||
description={t('appDebug.feature.speechToText.description')}
|
description={t('appDebug.feature.speechToText.description')}
|
||||||
value={speechToText.enabled}
|
value={!!features.speech2text.enabled}
|
||||||
onChange={value => setSpeechToText(produce(speechToText, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.speech2text}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -115,10 +120,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
previewImgClassName='citationPreview'
|
previewImgClassName='citationPreview'
|
||||||
title={t('appDebug.feature.citation.title')}
|
title={t('appDebug.feature.citation.title')}
|
||||||
description={t('appDebug.feature.citation.description')}
|
description={t('appDebug.feature.citation.description')}
|
||||||
value={citation.enabled}
|
value={!!features.citation.enabled}
|
||||||
onChange={value => setCitation(produce(citation, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.citation}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</FeatureGroup>
|
</FeatureGroup>
|
||||||
@ -130,19 +134,17 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
previewImgClassName=''
|
previewImgClassName=''
|
||||||
title={t('appDebug.feature.moderation.title')}
|
title={t('appDebug.feature.moderation.title')}
|
||||||
description={t('appDebug.feature.moderation.description')}
|
description={t('appDebug.feature.moderation.description')}
|
||||||
value={moderation.enabled}
|
value={!!features.moderation.enabled}
|
||||||
onChange={value => setModeration(produce(moderation, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.moderation}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
<FeatureItem
|
<FeatureItem
|
||||||
icon={<MessageFast className='w-4 h-4 text-[#444CE7]' />}
|
icon={<MessageFast className='w-4 h-4 text-[#444CE7]' />}
|
||||||
title={t('appDebug.feature.annotation.title')}
|
title={t('appDebug.feature.annotation.title')}
|
||||||
description={t('appDebug.feature.annotation.description')}
|
description={t('appDebug.feature.annotation.description')}
|
||||||
value={annotation.enabled}
|
value={!!features.annotation.enabled}
|
||||||
onChange={value => setAnnotation(produce(annotation, (draft) => {
|
onChange={handleChange}
|
||||||
draft.enabled = value
|
type={FeatureEnum.annotation}
|
||||||
}))}
|
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
</FeatureGroup>
|
</FeatureGroup>
|
||||||
@ -150,4 +152,4 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||||||
</Modal>
|
</Modal>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
export default React.memo(ChooseFeature)
|
export default React.memo(FeatureModal)
|
||||||
|
|||||||
@ -23,21 +23,15 @@ const FeaturePanel = ({
|
|||||||
annotationProps,
|
annotationProps,
|
||||||
}: FeaturePanelProps) => {
|
}: FeaturePanelProps) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const openingStatement = useFeatures(s => s.openingStatement)
|
const features = useFeatures(s => s.features)
|
||||||
const suggestedQuestionsAfterAnswer = useFeatures(s => s.suggestedQuestionsAfterAnswer)
|
|
||||||
const textToSpeech = useFeatures(s => s.textToSpeech)
|
|
||||||
const speechToText = useFeatures(s => s.speechToText)
|
|
||||||
const citation = useFeatures(s => s.citation)
|
|
||||||
const moderation = useFeatures(s => s.moderation)
|
|
||||||
const annotation = useFeatures(s => s.annotation)
|
|
||||||
|
|
||||||
const showAdvanceFeature = useMemo(() => {
|
const showAdvanceFeature = useMemo(() => {
|
||||||
return openingStatement.enabled || suggestedQuestionsAfterAnswer.enabled || textToSpeech.enabled || speechToText.enabled || citation.enabled
|
return features.opening.enabled || features.suggested.enabled || features.speech2text.enabled || features.text2speech.enabled || features.citation.enabled
|
||||||
}, [openingStatement, suggestedQuestionsAfterAnswer, textToSpeech, speechToText, citation])
|
}, [features])
|
||||||
|
|
||||||
const showToolFeature = useMemo(() => {
|
const showToolFeature = useMemo(() => {
|
||||||
return moderation.enabled || annotation.enabled
|
return features.moderation.enabled || features.annotation.enabled
|
||||||
}, [moderation, annotation])
|
}, [features])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='space-y-3'>
|
<div className='space-y-3'>
|
||||||
@ -55,27 +49,27 @@ const FeaturePanel = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className='py-2 space-y-2'>
|
<div className='py-2 space-y-2'>
|
||||||
{
|
{
|
||||||
openingStatement.enabled && (
|
features.opening.enabled && (
|
||||||
<OpeningStatement {...openingStatementProps} />
|
<OpeningStatement {...openingStatementProps} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
suggestedQuestionsAfterAnswer.enabled && (
|
features.suggested.enabled && (
|
||||||
<SuggestedQuestionsAfterAnswer />
|
<SuggestedQuestionsAfterAnswer />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
textToSpeech.enabled && (
|
features.text2speech.enabled && (
|
||||||
<TextToSpeech />
|
<TextToSpeech />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
speechToText.enabled && (
|
features.speech2text.enabled && (
|
||||||
<SpeechToText />
|
<SpeechToText />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
citation.enabled && (
|
features.citation.enabled && (
|
||||||
<Citation />
|
<Citation />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -97,12 +91,12 @@ const FeaturePanel = ({
|
|||||||
</div>
|
</div>
|
||||||
<div className='py-2 space-y-2'>
|
<div className='py-2 space-y-2'>
|
||||||
{
|
{
|
||||||
moderation.enabled && (
|
features.moderation.enabled && (
|
||||||
<Moderation />
|
<Moderation />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
{
|
{
|
||||||
annotation.enabled && (
|
features.annotation.enabled && (
|
||||||
<Annotation {...annotationProps} />
|
<Annotation {...annotationProps} />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,8 +1,12 @@
|
|||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import useSWR from 'swr'
|
import useSWR from 'swr'
|
||||||
|
import produce from 'immer'
|
||||||
import { useContext } from 'use-context-selector'
|
import { useContext } from 'use-context-selector'
|
||||||
import { useFeatures } from '../../hooks'
|
import {
|
||||||
|
useFeatures,
|
||||||
|
useFeaturesStore,
|
||||||
|
} from '../../hooks'
|
||||||
import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
|
import { FileSearch02 } from '@/app/components/base/icons/src/vender/solid/files'
|
||||||
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
import { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import { useModalContext } from '@/context/modal-context'
|
import { useModalContext } from '@/context/modal-context'
|
||||||
@ -13,8 +17,8 @@ const Moderation = () => {
|
|||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const { setShowModerationSettingModal } = useModalContext()
|
const { setShowModerationSettingModal } = useModalContext()
|
||||||
const { locale } = useContext(I18n)
|
const { locale } = useContext(I18n)
|
||||||
const moderation = useFeatures(s => s.moderation)
|
const featuresStore = useFeaturesStore()
|
||||||
const setModeration = useFeatures(s => s.setModeration)
|
const moderation = useFeatures(s => s.features.moderation)
|
||||||
|
|
||||||
const { data: codeBasedExtensionList } = useSWR(
|
const { data: codeBasedExtensionList } = useSWR(
|
||||||
'/code-based-extension?module=moderation',
|
'/code-based-extension?module=moderation',
|
||||||
@ -22,9 +26,17 @@ const Moderation = () => {
|
|||||||
)
|
)
|
||||||
|
|
||||||
const handleOpenModerationSettingModal = () => {
|
const handleOpenModerationSettingModal = () => {
|
||||||
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = featuresStore!.getState()
|
||||||
setShowModerationSettingModal({
|
setShowModerationSettingModal({
|
||||||
payload: moderation,
|
payload: moderation as any,
|
||||||
onSaveCallback: setModeration,
|
onSaveCallback: (newModeration) => {
|
||||||
|
setFeatures(produce(features, (draft) => {
|
||||||
|
draft.moderation = newModeration
|
||||||
|
}))
|
||||||
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -7,7 +7,10 @@ import cn from 'classnames'
|
|||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useBoolean } from 'ahooks'
|
import { useBoolean } from 'ahooks'
|
||||||
import { ReactSortable } from 'react-sortablejs'
|
import { ReactSortable } from 'react-sortablejs'
|
||||||
import { useFeatures } from '../../hooks'
|
import {
|
||||||
|
useFeatures,
|
||||||
|
useFeaturesStore,
|
||||||
|
} from '../../hooks'
|
||||||
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
import Panel from '@/app/components/app/configuration/base/feature-panel'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
||||||
@ -35,8 +38,8 @@ const OpeningStatement: FC<OpeningStatementProps> = ({
|
|||||||
onAutoAddPromptVariable,
|
onAutoAddPromptVariable,
|
||||||
}) => {
|
}) => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const openingStatement = useFeatures(s => s.openingStatement)
|
const featureStore = useFeaturesStore()
|
||||||
const setOpeningStatement = useFeatures(s => s.setOpeningStatement)
|
const openingStatement = useFeatures(s => s.features.opening)
|
||||||
const value = openingStatement.opening_statement || ''
|
const value = openingStatement.opening_statement || ''
|
||||||
const suggestedQuestions = openingStatement.suggested_questions || []
|
const suggestedQuestions = openingStatement.suggested_questions || []
|
||||||
const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
|
const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
|
||||||
@ -103,23 +106,41 @@ const OpeningStatement: FC<OpeningStatementProps> = ({
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
setBlur()
|
setBlur()
|
||||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
const { getState } = featureStore!
|
||||||
draft.opening_statement = tempValue
|
const {
|
||||||
draft.suggested_questions = tempSuggestedQuestions
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = getState()
|
||||||
|
|
||||||
|
setFeatures(produce(features, (draft) => {
|
||||||
|
draft.opening.opening_statement = tempValue
|
||||||
|
draft.opening.suggested_questions = tempSuggestedQuestions
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
const cancelAutoAddVar = () => {
|
const cancelAutoAddVar = () => {
|
||||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
const { getState } = featureStore!
|
||||||
draft.opening_statement = tempValue
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = getState()
|
||||||
|
|
||||||
|
setFeatures(produce(features, (draft) => {
|
||||||
|
draft.opening.opening_statement = tempValue
|
||||||
}))
|
}))
|
||||||
hideConfirmAddVar()
|
hideConfirmAddVar()
|
||||||
setBlur()
|
setBlur()
|
||||||
}
|
}
|
||||||
|
|
||||||
const autoAddVar = () => {
|
const autoAddVar = () => {
|
||||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
const { getState } = featureStore!
|
||||||
draft.opening_statement = tempValue
|
const {
|
||||||
|
features,
|
||||||
|
setFeatures,
|
||||||
|
} = getState()
|
||||||
|
|
||||||
|
setFeatures(produce(features, (draft) => {
|
||||||
|
draft.opening.opening_statement = tempValue
|
||||||
}))
|
}))
|
||||||
onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))])
|
onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))])
|
||||||
hideConfirmAddVar()
|
hideConfirmAddVar()
|
||||||
|
|||||||
@ -12,7 +12,7 @@ import AudioBtn from '@/app/components/base/audio-btn'
|
|||||||
|
|
||||||
const TextToSpeech: FC = () => {
|
const TextToSpeech: FC = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const textToSpeech = useFeatures(s => s.textToSpeech)
|
const textToSpeech = useFeatures(s => s.features.text2speech)
|
||||||
|
|
||||||
const pathname = usePathname()
|
const pathname = usePathname()
|
||||||
const matched = pathname.match(/\/app\/([^/]+)/)
|
const matched = pathname.match(/\/app\/([^/]+)/)
|
||||||
|
|||||||
@ -1,13 +1,5 @@
|
|||||||
import { createStore } from 'zustand'
|
import { createStore } from 'zustand'
|
||||||
import type {
|
import type { Features } from './types'
|
||||||
AnnotationReply,
|
|
||||||
OpeningStatement,
|
|
||||||
RetrieverResource,
|
|
||||||
SensitiveWordAvoidance,
|
|
||||||
SpeechToText,
|
|
||||||
SuggestedQuestionsAfterAnswer,
|
|
||||||
TextToSpeech,
|
|
||||||
} from './types'
|
|
||||||
|
|
||||||
export type FeaturesModal = {
|
export type FeaturesModal = {
|
||||||
showFeaturesModal: boolean
|
showFeaturesModal: boolean
|
||||||
@ -15,23 +7,11 @@ export type FeaturesModal = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type FeaturesState = {
|
export type FeaturesState = {
|
||||||
openingStatement: OpeningStatement
|
features: Features
|
||||||
suggestedQuestionsAfterAnswer: SuggestedQuestionsAfterAnswer
|
|
||||||
textToSpeech: TextToSpeech
|
|
||||||
speechToText: SpeechToText
|
|
||||||
citation: RetrieverResource
|
|
||||||
moderation: SensitiveWordAvoidance
|
|
||||||
annotation: AnnotationReply
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeaturesAction = {
|
export type FeaturesAction = {
|
||||||
setOpeningStatement: (openingStatement: OpeningStatement) => void
|
setFeatures: (features: Features) => void
|
||||||
setSuggestedQuestionsAfterAnswer: (suggestedQuestionsAfterAnswer: SuggestedQuestionsAfterAnswer) => void
|
|
||||||
setTextToSpeech: (textToSpeech: TextToSpeech) => void
|
|
||||||
setSpeechToText: (speechToText: SpeechToText) => void
|
|
||||||
setCitation: (citation: RetrieverResource) => void
|
|
||||||
setModeration: (moderation: SensitiveWordAvoidance) => void
|
|
||||||
setAnnotation: (annotation: AnnotationReply) => void
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export type FeatureStoreState = FeaturesState & FeaturesAction & FeaturesModal
|
export type FeatureStoreState = FeaturesState & FeaturesAction & FeaturesModal
|
||||||
@ -40,39 +20,34 @@ export type FeaturesStore = ReturnType<typeof createFeaturesStore>
|
|||||||
|
|
||||||
export const createFeaturesStore = (initProps?: Partial<FeaturesState>) => {
|
export const createFeaturesStore = (initProps?: Partial<FeaturesState>) => {
|
||||||
const DEFAULT_PROPS: FeaturesState = {
|
const DEFAULT_PROPS: FeaturesState = {
|
||||||
openingStatement: {
|
features: {
|
||||||
enabled: false,
|
opening: {
|
||||||
},
|
enabled: false,
|
||||||
suggestedQuestionsAfterAnswer: {
|
},
|
||||||
enabled: false,
|
suggested: {
|
||||||
},
|
enabled: false,
|
||||||
textToSpeech: {
|
},
|
||||||
enabled: false,
|
text2speech: {
|
||||||
},
|
enabled: false,
|
||||||
speechToText: {
|
},
|
||||||
enabled: false,
|
speech2text: {
|
||||||
},
|
enabled: false,
|
||||||
citation: {
|
},
|
||||||
enabled: false,
|
citation: {
|
||||||
},
|
enabled: false,
|
||||||
moderation: {
|
},
|
||||||
enabled: false,
|
moderation: {
|
||||||
},
|
enabled: false,
|
||||||
annotation: {
|
},
|
||||||
enabled: false,
|
annotation: {
|
||||||
|
enabled: false,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
return createStore<FeatureStoreState>()(set => ({
|
return createStore<FeatureStoreState>()(set => ({
|
||||||
...DEFAULT_PROPS,
|
...DEFAULT_PROPS,
|
||||||
...initProps,
|
...initProps,
|
||||||
setOpeningStatement: openingStatement => set(() => ({ openingStatement })),
|
setFeatures: features => set(() => ({ features })),
|
||||||
setSuggestedQuestionsAfterAnswer: suggestedQuestionsAfterAnswer => set(() => ({ suggestedQuestionsAfterAnswer })),
|
|
||||||
setSpeechToText: speechToText => set(() => ({ speechToText })),
|
|
||||||
setTextToSpeech: textToSpeech => set(() => ({ textToSpeech })),
|
|
||||||
setCitation: citation => set(() => ({ citation })),
|
|
||||||
setModeration: moderation => set(() => ({ moderation })),
|
|
||||||
setAnnotation: annotation => set(() => ({ annotation })),
|
|
||||||
|
|
||||||
showFeaturesModal: false,
|
showFeaturesModal: false,
|
||||||
setShowFeaturesModal: showFeaturesModal => set(() => ({ showFeaturesModal })),
|
setShowFeaturesModal: showFeaturesModal => set(() => ({ showFeaturesModal })),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -31,3 +31,23 @@ export type AnnotationReply = EnabledOrDisabled & {
|
|||||||
embedding_provider_name: string
|
embedding_provider_name: string
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export enum FeatureEnum {
|
||||||
|
opening = 'opening',
|
||||||
|
suggested = 'suggested',
|
||||||
|
text2speech = 'text2speech',
|
||||||
|
speech2text = 'speech2text',
|
||||||
|
citation = 'citation',
|
||||||
|
moderation = 'moderation',
|
||||||
|
annotation = 'annotation',
|
||||||
|
}
|
||||||
|
|
||||||
|
export type Features = {
|
||||||
|
[FeatureEnum.opening]: OpeningStatement
|
||||||
|
[FeatureEnum.suggested]: SuggestedQuestionsAfterAnswer
|
||||||
|
[FeatureEnum.text2speech]: TextToSpeech
|
||||||
|
[FeatureEnum.speech2text]: SpeechToText
|
||||||
|
[FeatureEnum.citation]: RetrieverResource
|
||||||
|
[FeatureEnum.moderation]: SensitiveWordAvoidance
|
||||||
|
[FeatureEnum.annotation]: AnnotationReply
|
||||||
|
}
|
||||||
|
|||||||
@ -0,0 +1,5 @@
|
|||||||
|
<svg width="12" height="12" viewBox="0 0 12 12" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||||
|
<g id="play">
|
||||||
|
<path id="Solid" fill-rule="evenodd" clip-rule="evenodd" d="M4.00312 1.40109C4.0091 1.40508 4.0151 1.40907 4.02111 1.41309L9.29548 4.92933C9.44809 5.03105 9.58959 5.12537 9.69827 5.21301C9.81168 5.30448 9.94538 5.43132 10.0223 5.61687C10.124 5.86212 10.124 6.13775 10.0223 6.38301C9.94538 6.56856 9.81168 6.6954 9.69827 6.78686C9.5896 6.8745 9.44811 6.96881 9.2955 7.07053L4.00314 10.5988C3.8166 10.7232 3.64886 10.835 3.50652 10.9121C3.36409 10.9893 3.16859 11.0775 2.9404 11.0639C2.64852 11.0465 2.3789 10.9022 2.20249 10.669C2.06458 10.4867 2.02952 10.2751 2.01474 10.1138C1.99997 9.95254 1.99999 9.75094 2 9.52674L2 2.49475C2 2.48752 2 2.48031 2 2.47313C1.99999 2.24893 1.99997 2.04733 2.01474 1.88612C2.02952 1.72479 2.06458 1.5132 2.20249 1.33089C2.3789 1.0977 2.64852 0.953401 2.9404 0.935973C3.16859 0.922349 3.36409 1.01055 3.50652 1.08774C3.64885 1.16488 3.81659 1.27672 4.00312 1.40109Z" fill="#155EEF"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 1.0 KiB |
@ -0,0 +1,38 @@
|
|||||||
|
{
|
||||||
|
"icon": {
|
||||||
|
"type": "element",
|
||||||
|
"isRootNode": true,
|
||||||
|
"name": "svg",
|
||||||
|
"attributes": {
|
||||||
|
"width": "12",
|
||||||
|
"height": "12",
|
||||||
|
"viewBox": "0 0 12 12",
|
||||||
|
"fill": "none",
|
||||||
|
"xmlns": "http://www.w3.org/2000/svg"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "g",
|
||||||
|
"attributes": {
|
||||||
|
"id": "play"
|
||||||
|
},
|
||||||
|
"children": [
|
||||||
|
{
|
||||||
|
"type": "element",
|
||||||
|
"name": "path",
|
||||||
|
"attributes": {
|
||||||
|
"id": "Solid",
|
||||||
|
"fill-rule": "evenodd",
|
||||||
|
"clip-rule": "evenodd",
|
||||||
|
"d": "M4.00312 1.40109C4.0091 1.40508 4.0151 1.40907 4.02111 1.41309L9.29548 4.92933C9.44809 5.03105 9.58959 5.12537 9.69827 5.21301C9.81168 5.30448 9.94538 5.43132 10.0223 5.61687C10.124 5.86212 10.124 6.13775 10.0223 6.38301C9.94538 6.56856 9.81168 6.6954 9.69827 6.78686C9.5896 6.8745 9.44811 6.96881 9.2955 7.07053L4.00314 10.5988C3.8166 10.7232 3.64886 10.835 3.50652 10.9121C3.36409 10.9893 3.16859 11.0775 2.9404 11.0639C2.64852 11.0465 2.3789 10.9022 2.20249 10.669C2.06458 10.4867 2.02952 10.2751 2.01474 10.1138C1.99997 9.95254 1.99999 9.75094 2 9.52674L2 2.49475C2 2.48752 2 2.48031 2 2.47313C1.99999 2.24893 1.99997 2.04733 2.01474 1.88612C2.02952 1.72479 2.06458 1.5132 2.20249 1.33089C2.3789 1.0977 2.64852 0.953401 2.9404 0.935973C3.16859 0.922349 3.36409 1.01055 3.50652 1.08774C3.64885 1.16488 3.81659 1.27672 4.00312 1.40109Z",
|
||||||
|
"fill": "currentColor"
|
||||||
|
},
|
||||||
|
"children": []
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"name": "Play"
|
||||||
|
}
|
||||||
@ -0,0 +1,16 @@
|
|||||||
|
// GENERATE BY script
|
||||||
|
// DON NOT EDIT IT MANUALLY
|
||||||
|
|
||||||
|
import * as React from 'react'
|
||||||
|
import data from './Play.json'
|
||||||
|
import IconBase from '@/app/components/base/icons/IconBase'
|
||||||
|
import type { IconBaseProps, IconData } from '@/app/components/base/icons/IconBase'
|
||||||
|
|
||||||
|
const Icon = React.forwardRef<React.MutableRefObject<SVGElement>, Omit<IconBaseProps, 'data'>>((
|
||||||
|
props,
|
||||||
|
ref,
|
||||||
|
) => <IconBase {...props} ref={ref} data={data as IconData} />)
|
||||||
|
|
||||||
|
Icon.displayName = 'Play'
|
||||||
|
|
||||||
|
export default Icon
|
||||||
@ -2,6 +2,7 @@ export { default as MagicBox } from './MagicBox'
|
|||||||
export { default as MagicEyes } from './MagicEyes'
|
export { default as MagicEyes } from './MagicEyes'
|
||||||
export { default as MagicWand } from './MagicWand'
|
export { default as MagicWand } from './MagicWand'
|
||||||
export { default as Microphone01 } from './Microphone01'
|
export { default as Microphone01 } from './Microphone01'
|
||||||
|
export { default as Play } from './Play'
|
||||||
export { default as Robot } from './Robot'
|
export { default as Robot } from './Robot'
|
||||||
export { default as Sliders02 } from './Sliders02'
|
export { default as Sliders02 } from './Sliders02'
|
||||||
export { default as Speaker } from './Speaker'
|
export { default as Speaker } from './Speaker'
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useStore } from './store'
|
import { useStore } from './store'
|
||||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
import {
|
import {
|
||||||
@ -7,12 +8,13 @@ import {
|
|||||||
} from '@/app/components/base/features'
|
} from '@/app/components/base/features'
|
||||||
|
|
||||||
const Features = () => {
|
const Features = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='fixed top-16 left-2 bottom-2 w-[600px] rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl z-10'>
|
<div className='fixed top-16 left-2 bottom-2 w-[600px] rounded-2xl border-[0.5px] border-gray-200 bg-white shadow-xl z-10'>
|
||||||
<div className='flex items-center justify-between px-4 pt-3'>
|
<div className='flex items-center justify-between px-4 pt-3'>
|
||||||
Features
|
{t('workflow.common.features')}
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
<FeaturesChoose />
|
<FeaturesChoose />
|
||||||
<div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
|
<div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
|
||||||
|
|||||||
34
web/app/components/workflow/header/editing-title.tsx
Normal file
34
web/app/components/workflow/header/editing-title.tsx
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import dayjs from 'dayjs'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
||||||
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
|
||||||
|
const EditingTitle = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
|
||||||
|
const publishedAt = useStore(state => state.publishedAt)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex items-center h-[18px] text-xs text-gray-500'>
|
||||||
|
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
||||||
|
{t('workflow.common.editing')}
|
||||||
|
{
|
||||||
|
draftUpdatedAt && (
|
||||||
|
<>
|
||||||
|
<span className='flex items-center mx-1'>·</span>
|
||||||
|
{t('workflow.common.autoSaved')} {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
<span className='flex items-center mx-1'>·</span>
|
||||||
|
{
|
||||||
|
publishedAt
|
||||||
|
? `${t('workflow.common.published')} ${dayjs(publishedAt).fromNow()}`
|
||||||
|
: t('workflow.common.unpublished')
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(EditingTitle)
|
||||||
@ -3,26 +3,27 @@ import {
|
|||||||
memo,
|
memo,
|
||||||
useCallback,
|
useCallback,
|
||||||
} from 'react'
|
} from 'react'
|
||||||
import dayjs from 'dayjs'
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import RunAndHistory from './run-and-history'
|
import RunAndHistory from './run-and-history'
|
||||||
|
import EditingTitle from './editing-title'
|
||||||
|
import RunningTitle from './running-title'
|
||||||
import Publish from './publish'
|
import Publish from './publish'
|
||||||
import { Edit03 } from '@/app/components/base/icons/src/vender/solid/general'
|
|
||||||
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
|
import { Grid01 } from '@/app/components/base/icons/src/vender/line/layout'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import { Mode } from '@/app/components/workflow/types'
|
||||||
|
|
||||||
const Header: FC = () => {
|
const Header: FC = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const appDetail = useAppStore(state => state.appDetail)
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
const mode = useStore(state => state.mode)
|
||||||
const runStaus = useStore(state => state.runStaus)
|
const runTaskId = useStore(state => state.runTaskId)
|
||||||
const setRunStaus = useStore(state => state.setRunStaus)
|
|
||||||
const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
|
|
||||||
|
|
||||||
const handleShowFeatures = useCallback(() => {
|
const handleShowFeatures = useCallback(() => {
|
||||||
setShowFeaturesPanel(true)
|
useStore.setState({ showFeaturesPanel: true })
|
||||||
}, [setShowFeaturesPanel])
|
}, [])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
@ -33,35 +34,25 @@ const Header: FC = () => {
|
|||||||
>
|
>
|
||||||
<div>
|
<div>
|
||||||
<div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div>
|
<div className='text-xs font-medium text-gray-700'>{appDetail?.name}</div>
|
||||||
<div className='flex items-center'>
|
{
|
||||||
<div className='flex items-center text-xs text-gray-500'>
|
mode === Mode.Editing && !runTaskId && <EditingTitle />
|
||||||
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
}
|
||||||
Editing
|
{
|
||||||
{
|
(mode === Mode.Running || runTaskId) && <RunningTitle />
|
||||||
draftUpdatedAt && (
|
}
|
||||||
<>
|
|
||||||
<span className='flex items-center mx-1'>·</span>
|
|
||||||
<span>
|
|
||||||
Auto-Saved {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
|
||||||
</span>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center'>
|
<div className='flex items-center'>
|
||||||
{
|
{
|
||||||
runStaus && (
|
(mode === Mode.Running || runTaskId) && (
|
||||||
<Button
|
<Button
|
||||||
className={`
|
className={`
|
||||||
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
|
mr-2 px-3 py-0 h-8 bg-white text-[13px] font-medium text-primary-600
|
||||||
border-[0.5px] border-gray-200 shadow-xs
|
border-[0.5px] border-gray-200 shadow-xs
|
||||||
`}
|
`}
|
||||||
onClick={() => setRunStaus('')}
|
onClick={() => useStore.setState({ mode: Mode.Editing, runTaskId: '' })}
|
||||||
>
|
>
|
||||||
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
|
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
|
||||||
Go back to editor
|
{t('workflow.common.goBackToEdit')}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -77,7 +68,7 @@ const Header: FC = () => {
|
|||||||
onClick={handleShowFeatures}
|
onClick={handleShowFeatures}
|
||||||
>
|
>
|
||||||
<Grid01 className='mr-1 w-4 h-4 text-gray-500' />
|
<Grid01 className='mr-1 w-4 h-4 text-gray-500' />
|
||||||
Features
|
{t('workflow.common.features')}
|
||||||
</Button>
|
</Button>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
import { useState } from 'react'
|
import { useState } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import Button from '@/app/components/base/button'
|
import Button from '@/app/components/base/button'
|
||||||
import {
|
import {
|
||||||
PortalToFollowElem,
|
PortalToFollowElem,
|
||||||
@ -7,6 +8,7 @@ import {
|
|||||||
} from '@/app/components/base/portal-to-follow-elem'
|
} from '@/app/components/base/portal-to-follow-elem'
|
||||||
|
|
||||||
const Publish = () => {
|
const Publish = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
const [open, setOpen] = useState(false)
|
const [open, setOpen] = useState(false)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@ -24,35 +26,35 @@ const Publish = () => {
|
|||||||
type='primary'
|
type='primary'
|
||||||
className='px-3 py-0 h-8 text-[13px] font-medium'
|
className='px-3 py-0 h-8 text-[13px] font-medium'
|
||||||
>
|
>
|
||||||
publish
|
{t('workflow.common.publish')}
|
||||||
</Button>
|
</Button>
|
||||||
</PortalToFollowElemTrigger>
|
</PortalToFollowElemTrigger>
|
||||||
<PortalToFollowElemContent className='z-[11]'>
|
<PortalToFollowElemContent className='z-[11]'>
|
||||||
<div className='w-[320px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
|
<div className='w-[320px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
|
||||||
<div className='p-4 pt-3'>
|
<div className='p-4 pt-3'>
|
||||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
||||||
Current Draft
|
{t('workflow.common.currentDraft').toLocaleUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex items-center h-[18px] text-[13px] font-medium text-gray-700'>
|
<div className='flex items-center h-[18px] text-[13px] font-medium text-gray-700'>
|
||||||
Auto-Saved 3 min ago · Evan
|
{t('workflow.common.autoSaved')} 3 min ago · Evan
|
||||||
</div>
|
</div>
|
||||||
<Button
|
<Button
|
||||||
type='primary'
|
type='primary'
|
||||||
className='mt-3 px-3 py-0 w-full h-8 border-[0.5px] border-primary-700 rounded-lg text-[13px] font-medium'
|
className='mt-3 px-3 py-0 w-full h-8 border-[0.5px] border-primary-700 rounded-lg text-[13px] font-medium'
|
||||||
>
|
>
|
||||||
Publish
|
{t('workflow.common.publish')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
<div className='p-4 pt-3 border-t-[0.5px] border-t-black/5'>
|
<div className='p-4 pt-3 border-t-[0.5px] border-t-black/5'>
|
||||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
||||||
Latest Published
|
{t('workflow.common.latestPublished').toLocaleUpperCase()}
|
||||||
</div>
|
</div>
|
||||||
<div className='flex justify-between'>
|
<div className='flex justify-between'>
|
||||||
<div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-gray-700'>
|
<div className='flex items-center mt-[3px] mb-[3px] leading-[18px] text-[13px] font-medium text-gray-700'>
|
||||||
Auto-Saved 3 min ago · Evan
|
{t('workflow.common.autoSaved')} 3 min ago · Evan
|
||||||
</div>
|
</div>
|
||||||
<Button className='ml-2 px-2 py-0 h-6 shadow-xs rounded-md text-xs font-medium text-gray-700 border-[0.5px] border-gray-200'>
|
<Button className='ml-2 px-2 py-0 h-6 shadow-xs rounded-md text-xs font-medium text-gray-700 border-[0.5px] border-gray-200'>
|
||||||
Restore
|
{t('workflow.common.restore')}
|
||||||
</Button>
|
</Button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -1,16 +1,19 @@
|
|||||||
import type { FC } from 'react'
|
import type { FC } from 'react'
|
||||||
import { memo } from 'react'
|
import { memo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
import { useStore } from '../store'
|
import { useStore } from '../store'
|
||||||
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||||
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
|
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
|
||||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||||
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
|
import { Loading02 } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
|
import { Mode } from '@/app/components/workflow/types'
|
||||||
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
|
||||||
const RunAndHistory: FC = () => {
|
const RunAndHistory: FC = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
|
const mode = useStore(state => state.mode)
|
||||||
const showRunHistory = useStore(state => state.showRunHistory)
|
const showRunHistory = useStore(state => state.showRunHistory)
|
||||||
const setShowRunHistory = useStore(state => state.setShowRunHistory)
|
|
||||||
const runStaus = useStore(state => state.runStaus)
|
|
||||||
const setRunStaus = useStore(state => state.setRunStaus)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'>
|
<div className='flex items-center px-0.5 h-8 rounded-lg border-[0.5px] border-gray-200 bg-white shadow-xs'>
|
||||||
@ -18,38 +21,51 @@ const RunAndHistory: FC = () => {
|
|||||||
className={`
|
className={`
|
||||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||||
hover:bg-primary-50 cursor-pointer
|
hover:bg-primary-50 cursor-pointer
|
||||||
${runStaus === 'running' && 'bg-primary-50 !cursor-not-allowed'}
|
${mode === 'running' && 'bg-primary-50 !cursor-not-allowed'}
|
||||||
|
${mode === 'running' && appDetail?.mode !== 'workflow' && 'opacity-50'}
|
||||||
`}
|
`}
|
||||||
onClick={() => runStaus !== 'running' && setRunStaus('running')}
|
onClick={() => mode !== 'running' && useStore.setState({ mode: Mode.Running })}
|
||||||
>
|
>
|
||||||
{
|
{
|
||||||
runStaus === 'running'
|
mode === 'running'
|
||||||
? (
|
? (
|
||||||
<>
|
<>
|
||||||
<Loading02 className='mr-1 w-4 h-4 animate-spin' />
|
{
|
||||||
Running
|
appDetail?.mode === 'workflow' && (
|
||||||
|
<Loading02 className='mr-1 w-4 h-4 animate-spin' />
|
||||||
|
)
|
||||||
|
}
|
||||||
|
{
|
||||||
|
appDetail?.mode === 'workflow'
|
||||||
|
? t('workflow.common.running')
|
||||||
|
: t('workflow.common.inPreview')
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
: (
|
: (
|
||||||
<>
|
<>
|
||||||
<Play className='mr-1 w-4 h-4' />
|
<Play className='mr-1 w-4 h-4' />
|
||||||
Run
|
{
|
||||||
|
appDetail?.mode === 'workflow'
|
||||||
|
? t('workflow.common.run')
|
||||||
|
: t('workflow.common.preview')
|
||||||
|
}
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
</div>
|
</div>
|
||||||
<div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
|
<div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
|
||||||
<TooltipPlus
|
<TooltipPlus
|
||||||
popupContent='View run history'
|
popupContent={t('workflow.common.viewRunHistory')}
|
||||||
>
|
>
|
||||||
<div
|
<div
|
||||||
className={`
|
className={`
|
||||||
flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer
|
flex items-center justify-center w-7 h-7 rounded-md hover:bg-black/5 cursor-pointer
|
||||||
${showRunHistory && 'bg-black/5'}
|
${showRunHistory && 'bg-primary-50'}
|
||||||
`}
|
`}
|
||||||
onClick={() => setShowRunHistory(true)}
|
onClick={() => useStore.setState({ showRunHistory: true })}
|
||||||
>
|
>
|
||||||
<ClockPlay className='w-4 h-4 text-gray-500' />
|
<ClockPlay className={`w-4 h-4 ${showRunHistory ? 'text-primary-600' : 'text-gray-500'}`} />
|
||||||
</div>
|
</div>
|
||||||
</TooltipPlus>
|
</TooltipPlus>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
24
web/app/components/workflow/header/running-title.tsx
Normal file
24
web/app/components/workflow/header/running-title.tsx
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import { memo } from 'react'
|
||||||
|
import { useTranslation } from 'react-i18next'
|
||||||
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
import { Play } from '@/app/components/base/icons/src/vender/solid/mediaAndDevices'
|
||||||
|
|
||||||
|
const RunningTitle = () => {
|
||||||
|
const { t } = useTranslation()
|
||||||
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className='flex items-center h-[18px] text-xs text-primary-600'>
|
||||||
|
<Play className='mr-1 w-3 h-3' />
|
||||||
|
{
|
||||||
|
appDetail?.mode === 'advanced-chat'
|
||||||
|
? t('workflow.common.inPreviewMode')
|
||||||
|
: t('workflow.common.inRunMode')
|
||||||
|
}
|
||||||
|
<span className='mx-1'>·</span>
|
||||||
|
<span className='text-gray-500'>Test Run#2</span>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default memo(RunningTitle)
|
||||||
@ -40,6 +40,7 @@ import {
|
|||||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
import Loading from '@/app/components/base/loading'
|
import Loading from '@/app/components/base/loading'
|
||||||
import { FeaturesProvider } from '@/app/components/base/features'
|
import { FeaturesProvider } from '@/app/components/base/features'
|
||||||
|
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||||
import { fetchCollectionList } from '@/service/tools'
|
import { fetchCollectionList } from '@/service/tools'
|
||||||
|
|
||||||
const nodeTypes = {
|
const nodeTypes = {
|
||||||
@ -205,9 +206,24 @@ const WorkflowWrap: FC<WorkflowProps> = ({
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const features = data?.features || {}
|
||||||
|
const initialFeatures: FeaturesData = {
|
||||||
|
opening: {
|
||||||
|
enabled: !!features.opening_statement,
|
||||||
|
opening_statement: features.opening_statement,
|
||||||
|
suggested_questions: features.suggested_questions,
|
||||||
|
},
|
||||||
|
suggested: features.suggested_questions_after_answer || { enabled: false },
|
||||||
|
speech2text: features.speech_to_text || { enabled: false },
|
||||||
|
text2speech: features.text_to_speech || { enabled: false },
|
||||||
|
citation: features.retriever_resource || { enabled: false },
|
||||||
|
moderation: features.sensitive_word_avoidance || { enabled: false },
|
||||||
|
annotation: features.annotation_reply || { enabled: false },
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ReactFlowProvider>
|
<ReactFlowProvider>
|
||||||
<FeaturesProvider>
|
<FeaturesProvider features={initialFeatures}>
|
||||||
<Workflow
|
<Workflow
|
||||||
nodes={nodesData}
|
nodes={nodesData}
|
||||||
edges={edgesData}
|
edges={edgesData}
|
||||||
|
|||||||
@ -48,7 +48,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||||||
}, [handleNodeDataUpdate, id, data])
|
}, [handleNodeDataUpdate, id, data])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
<div className='w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||||
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5 z-10'>
|
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5 z-10'>
|
||||||
<div className='flex items-center px-4 pt-4 pb-1'>
|
<div className='flex items-center px-4 pt-4 pb-1'>
|
||||||
{
|
{
|
||||||
|
|||||||
@ -15,7 +15,7 @@ import { useStore as useAppStore } from '@/app/components/app/store'
|
|||||||
|
|
||||||
const Panel: FC = () => {
|
const Panel: FC = () => {
|
||||||
const appDetail = useAppStore(state => state.appDetail)
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
const runStaus = useStore(state => state.runStaus)
|
const runTaskId = useStore(state => state.runTaskId)
|
||||||
const nodes = useNodes<CommonNodeType>()
|
const nodes = useNodes<CommonNodeType>()
|
||||||
const selectedNode = nodes.find(node => node.data._selected)
|
const selectedNode = nodes.find(node => node.data._selected)
|
||||||
const showRunHistory = useStore(state => state.showRunHistory)
|
const showRunHistory = useStore(state => state.showRunHistory)
|
||||||
@ -25,16 +25,16 @@ const Panel: FC = () => {
|
|||||||
showDebugAndPreviewPanel,
|
showDebugAndPreviewPanel,
|
||||||
} = useMemo(() => {
|
} = useMemo(() => {
|
||||||
return {
|
return {
|
||||||
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode,
|
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode && !runTaskId,
|
||||||
showNodePanel: !!selectedNode,
|
showNodePanel: !!selectedNode && !runTaskId,
|
||||||
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode,
|
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode && !runTaskId,
|
||||||
}
|
}
|
||||||
}, [selectedNode, appDetail])
|
}, [selectedNode, appDetail, runTaskId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='absolute top-14 right-0 bottom-2 flex z-10'>
|
<div className='absolute top-14 right-0 bottom-2 flex z-10'>
|
||||||
{
|
{
|
||||||
runStaus && (
|
runTaskId && (
|
||||||
<Record />
|
<Record />
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,45 +1,33 @@
|
|||||||
import { useStore } from '../store'
|
import { memo } from 'react'
|
||||||
import {
|
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||||
CheckCircle,
|
|
||||||
XClose,
|
|
||||||
} from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
import { AlertCircle } from '@/app/components/base/icons/src/vender/line/alertsAndFeedback'
|
||||||
|
import { useStore } from '@/app/components/workflow/store'
|
||||||
|
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||||
|
|
||||||
const RunHistory = () => {
|
const RunHistory = () => {
|
||||||
const mode = useStore(state => state.mode)
|
const appDetail = useAppStore(state => state.appDetail)
|
||||||
const setShowRunHistory = useStore(state => state.setShowRunHistory)
|
|
||||||
const setRunStaus = useStore(state => state.setRunStaus)
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'>
|
<div className='ml-2 w-[200px] h-full bg-white border-[0.5px] border-gray-200 shadow-xl rounded-l-2xl'>
|
||||||
<div className='flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'>
|
<div className='flex items-center justify-between px-4 pt-3 text-base font-semibold text-gray-900'>
|
||||||
Run History
|
Run History
|
||||||
<div
|
<div
|
||||||
className='flex items-center justify-center w-6 h-6 cursor-pointer'
|
className='flex items-center justify-center w-6 h-6 cursor-pointer'
|
||||||
onClick={() => setShowRunHistory(false)}
|
onClick={() => useStore.setState({ showRunHistory: false })}
|
||||||
>
|
>
|
||||||
<XClose className='w-4 h-4 text-gray-500' />
|
<XClose className='w-4 h-4 text-gray-500' />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className='p-2'>
|
<div className='p-2'>
|
||||||
{
|
|
||||||
mode === 'workflow' && (
|
|
||||||
<div className='flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'>
|
|
||||||
<CheckCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#12B76A]' />
|
|
||||||
<div>
|
|
||||||
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#7</div>
|
|
||||||
<div className='flex items-center text-xs text-gray-500 leading-[18px]'>
|
|
||||||
Evan · 2 min ago
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div
|
<div
|
||||||
className='flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
className='flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
||||||
onClick={() => setRunStaus('finished')}
|
onClick={() => useStore.setState({ runTaskId: '1' })}
|
||||||
>
|
>
|
||||||
<AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' />
|
{
|
||||||
|
appDetail?.mode === 'advanced-chat' && (
|
||||||
|
<AlertCircle className='mt-0.5 mr-1.5 w-3.5 h-3.5 text-[#F79009]' />
|
||||||
|
)
|
||||||
|
}
|
||||||
<div>
|
<div>
|
||||||
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#6</div>
|
<div className='flex items-center text-[13px] font-medium text-primary-600 leading-[18px]'>Test Run#6</div>
|
||||||
<div className='flex items-center text-xs text-gray-500 leading-[18px]'>
|
<div className='flex items-center text-xs text-gray-500 leading-[18px]'>
|
||||||
@ -52,4 +40,4 @@ const RunHistory = () => {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
export default RunHistory
|
export default memo(RunHistory)
|
||||||
|
|||||||
@ -14,7 +14,7 @@ const WorkflowInfo: FC = () => {
|
|||||||
return null
|
return null
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className='mr-2 w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
<div className='w-[420px] h-full bg-white shadow-lg border-[0.5px] border-gray-200 rounded-2xl overflow-y-auto'>
|
||||||
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5'>
|
<div className='sticky top-0 bg-white border-b-[0.5px] border-black/5'>
|
||||||
<div className='flex pt-4 px-4 pb-1'>
|
<div className='flex pt-4 px-4 pb-1'>
|
||||||
<AppIcon
|
<AppIcon
|
||||||
|
|||||||
@ -5,38 +5,43 @@ import type {
|
|||||||
ToolInWorkflow,
|
ToolInWorkflow,
|
||||||
ToolsMap,
|
ToolsMap,
|
||||||
} from './block-selector/types'
|
} from './block-selector/types'
|
||||||
|
import { Mode } from './types'
|
||||||
|
|
||||||
type State = {
|
type State = {
|
||||||
mode: string
|
mode: Mode
|
||||||
|
runTaskId: string
|
||||||
showRunHistory: boolean
|
showRunHistory: boolean
|
||||||
showFeaturesPanel: boolean
|
showFeaturesPanel: boolean
|
||||||
runStaus: string
|
|
||||||
isDragging: boolean
|
isDragging: boolean
|
||||||
helpLine?: HelpLinePosition
|
helpLine?: HelpLinePosition
|
||||||
toolsets: CollectionWithExpanded[]
|
toolsets: CollectionWithExpanded[]
|
||||||
toolsMap: ToolsMap
|
toolsMap: ToolsMap
|
||||||
draftUpdatedAt: number
|
draftUpdatedAt: number
|
||||||
|
publishedAt: number
|
||||||
}
|
}
|
||||||
|
|
||||||
type Action = {
|
type Action = {
|
||||||
|
setMode: (mode: Mode) => void
|
||||||
|
setRunTaskId: (runTaskId: string) => void
|
||||||
setShowRunHistory: (showRunHistory: boolean) => void
|
setShowRunHistory: (showRunHistory: boolean) => void
|
||||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||||
setRunStaus: (runStaus: string) => void
|
|
||||||
setIsDragging: (isDragging: boolean) => void
|
setIsDragging: (isDragging: boolean) => void
|
||||||
setHelpLine: (helpLine?: HelpLinePosition) => void
|
setHelpLine: (helpLine?: HelpLinePosition) => void
|
||||||
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
||||||
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
||||||
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
||||||
|
setPublishedAt: (publishedAt: number) => void
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useStore = create<State & Action>(set => ({
|
export const useStore = create<State & Action>(set => ({
|
||||||
mode: 'workflow',
|
mode: Mode.Editing,
|
||||||
|
runTaskId: '',
|
||||||
|
setRunTaskId: runTaskId => set(() => ({ runTaskId })),
|
||||||
|
setMode: mode => set(() => ({ mode })),
|
||||||
showRunHistory: false,
|
showRunHistory: false,
|
||||||
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
|
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
|
||||||
showFeaturesPanel: false,
|
showFeaturesPanel: false,
|
||||||
setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })),
|
setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })),
|
||||||
runStaus: '',
|
|
||||||
setRunStaus: runStaus => set(() => ({ runStaus })),
|
|
||||||
isDragging: false,
|
isDragging: false,
|
||||||
setIsDragging: isDragging => set(() => ({ isDragging })),
|
setIsDragging: isDragging => set(() => ({ isDragging })),
|
||||||
helpLine: undefined,
|
helpLine: undefined,
|
||||||
@ -47,4 +52,6 @@ export const useStore = create<State & Action>(set => ({
|
|||||||
setToolsMap: toolsMap => set(() => ({ toolsMap })),
|
setToolsMap: toolsMap => set(() => ({ toolsMap })),
|
||||||
draftUpdatedAt: 0,
|
draftUpdatedAt: 0,
|
||||||
setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt })),
|
setDraftUpdatedAt: draftUpdatedAt => set(() => ({ draftUpdatedAt })),
|
||||||
|
publishedAt: 0,
|
||||||
|
setPublishedAt: publishedAt => set(() => ({ publishedAt })),
|
||||||
}))
|
}))
|
||||||
|
|||||||
@ -139,3 +139,8 @@ export type NodeDefault<T> = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void
|
export type OnSelectBlock = (type: BlockEnum, toolDefaultValue?: ToolDefaultValue) => void
|
||||||
|
|
||||||
|
export enum Mode {
|
||||||
|
Editing = 'editing',
|
||||||
|
Running = 'running',
|
||||||
|
}
|
||||||
|
|||||||
@ -1,110 +0,0 @@
|
|||||||
import type { FC } from 'react'
|
|
||||||
import {
|
|
||||||
Fragment,
|
|
||||||
memo,
|
|
||||||
useState,
|
|
||||||
} from 'react'
|
|
||||||
import { useReactFlow } from 'reactflow'
|
|
||||||
import {
|
|
||||||
PortalToFollowElem,
|
|
||||||
PortalToFollowElemContent,
|
|
||||||
PortalToFollowElemTrigger,
|
|
||||||
} from '@/app/components/base/portal-to-follow-elem'
|
|
||||||
import { SearchLg } from '@/app/components/base/icons/src/vender/line/general'
|
|
||||||
import { ChevronDown } from '@/app/components/base/icons/src/vender/line/arrows'
|
|
||||||
|
|
||||||
const ZOOM_IN_OUT_OPTIONS = [
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: 'in',
|
|
||||||
text: 'Zoom In',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'out',
|
|
||||||
text: 'Zoom Out',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: 'to50',
|
|
||||||
text: 'Zoom to 50%',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
key: 'to100',
|
|
||||||
text: 'Zoom to 100%',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
[
|
|
||||||
{
|
|
||||||
key: 'fit',
|
|
||||||
text: 'Zoom to Fit',
|
|
||||||
},
|
|
||||||
],
|
|
||||||
]
|
|
||||||
|
|
||||||
const ZoomInOut: FC = () => {
|
|
||||||
const reactFlow = useReactFlow()
|
|
||||||
const [open, setOpen] = useState(false)
|
|
||||||
|
|
||||||
const handleZoom = (type: string) => {
|
|
||||||
if (type === 'in')
|
|
||||||
reactFlow.zoomIn()
|
|
||||||
|
|
||||||
if (type === 'out')
|
|
||||||
reactFlow.zoomOut()
|
|
||||||
|
|
||||||
if (type === 'fit')
|
|
||||||
reactFlow.fitView()
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<PortalToFollowElem
|
|
||||||
placement='top-start'
|
|
||||||
open={open}
|
|
||||||
onOpenChange={setOpen}
|
|
||||||
offset={4}
|
|
||||||
>
|
|
||||||
<PortalToFollowElemTrigger asChild onClick={() => setOpen(v => !v)}>
|
|
||||||
<div className={`
|
|
||||||
absolute left-6 bottom-6
|
|
||||||
flex items-center px-2.5 h-9 cursor-pointer rounded-lg border-[0.5px] border-gray-100 bg-white shadow-lg
|
|
||||||
text-[13px] text-gray-500 z-10
|
|
||||||
`}>
|
|
||||||
<SearchLg className='mr-1 w-4 h-4' />
|
|
||||||
100%
|
|
||||||
<ChevronDown className='ml-1 w-4 h-4' />
|
|
||||||
</div>
|
|
||||||
</PortalToFollowElemTrigger>
|
|
||||||
<PortalToFollowElemContent>
|
|
||||||
<div className='w-[168px] rounded-lg border-[0.5px] border-gray-200 bg-white shadow-lg'>
|
|
||||||
{
|
|
||||||
ZOOM_IN_OUT_OPTIONS.map((options, i) => (
|
|
||||||
<Fragment key={i}>
|
|
||||||
{
|
|
||||||
i !== 0 && (
|
|
||||||
<div className='h-[1px] bg-gray-100' />
|
|
||||||
)
|
|
||||||
}
|
|
||||||
<div className='p-1'>
|
|
||||||
{
|
|
||||||
options.map(option => (
|
|
||||||
<div
|
|
||||||
key={option.key}
|
|
||||||
className='flex items-center px-3 h-8 rounded-lg hover:bg-gray-50 cursor-pointer text-sm text-gray-700'
|
|
||||||
onClick={() => handleZoom(option.key)}
|
|
||||||
>
|
|
||||||
{option.text}
|
|
||||||
</div>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</Fragment>
|
|
||||||
))
|
|
||||||
}
|
|
||||||
</div>
|
|
||||||
</PortalToFollowElemContent>
|
|
||||||
</PortalToFollowElem>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
export default memo(ZoomInOut)
|
|
||||||
@ -6,6 +6,7 @@ const translation = {
|
|||||||
published: 'Published',
|
published: 'Published',
|
||||||
publish: 'Publish',
|
publish: 'Publish',
|
||||||
run: 'Run',
|
run: 'Run',
|
||||||
|
running: 'Running',
|
||||||
inRunMode: 'In Run Mode',
|
inRunMode: 'In Run Mode',
|
||||||
inPreview: 'In Preview',
|
inPreview: 'In Preview',
|
||||||
inPreviewMode: 'In Preview Mode',
|
inPreviewMode: 'In Preview Mode',
|
||||||
|
|||||||
@ -6,7 +6,8 @@ const translation = {
|
|||||||
published: '已发布',
|
published: '已发布',
|
||||||
publish: '发布',
|
publish: '发布',
|
||||||
run: '运行',
|
run: '运行',
|
||||||
inRunMode: '运行中',
|
running: '运行中',
|
||||||
|
inRunMode: '在运行模式中',
|
||||||
inPreview: '预览中',
|
inPreview: '预览中',
|
||||||
inPreviewMode: '预览中',
|
inPreviewMode: '预览中',
|
||||||
preview: '预览',
|
preview: '预览',
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user