mirror of https://github.com/langgenius/dify.git
features
This commit is contained in:
parent
4edaa95cbf
commit
0164dec438
|
|
@ -1,9 +1,10 @@
|
|||
'use client'
|
||||
import type { FC } from 'react'
|
||||
import React from 'react'
|
||||
import React, { useCallback } from 'react'
|
||||
import cn from 'classnames'
|
||||
import s from './style.module.css'
|
||||
import Switch from '@/app/components/base/switch'
|
||||
import type { FeatureEnum } from '@/app/components/base/features/types'
|
||||
|
||||
export type IFeatureItemProps = {
|
||||
icon: React.ReactNode
|
||||
|
|
@ -11,7 +12,8 @@ export type IFeatureItemProps = {
|
|||
title: string
|
||||
description: string
|
||||
value: boolean
|
||||
onChange: (value: boolean) => void
|
||||
onChange: (type: FeatureEnum, value: boolean) => void
|
||||
type: FeatureEnum
|
||||
}
|
||||
|
||||
const FeatureItem: FC<IFeatureItemProps> = ({
|
||||
|
|
@ -21,7 +23,12 @@ const FeatureItem: FC<IFeatureItemProps> = ({
|
|||
description,
|
||||
value,
|
||||
onChange,
|
||||
type,
|
||||
}) => {
|
||||
const handleChange = useCallback((newValue: boolean) => {
|
||||
onChange(type, newValue)
|
||||
}, [type, onChange])
|
||||
|
||||
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='flex space-x-3 mr-2'>
|
||||
|
|
@ -40,7 +47,7 @@ const FeatureItem: FC<IFeatureItemProps> = ({
|
|||
</div>
|
||||
</div>
|
||||
|
||||
<Switch onChange={onChange} defaultValue={value} />
|
||||
<Switch onChange={handleChange} defaultValue={value} />
|
||||
{
|
||||
previewImgClassName && (
|
||||
<div className={cn(s.preview, s[previewImgClassName])}>
|
||||
|
|
|
|||
|
|
@ -3,7 +3,10 @@ import type { FC } from 'react'
|
|||
import React, { useCallback } from 'react'
|
||||
import produce from 'immer'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useFeatures } from '../hooks'
|
||||
import {
|
||||
useFeatures,
|
||||
useFeaturesStore,
|
||||
} from '../hooks'
|
||||
import FeatureGroup from './feature-group'
|
||||
import FeatureItem from './feature-item'
|
||||
import Modal from '@/app/components/base/modal'
|
||||
|
|
@ -15,37 +18,43 @@ import {
|
|||
MessageFast,
|
||||
MessageHeartCircle,
|
||||
} 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
|
||||
showSpeechToTextItem?: boolean
|
||||
}
|
||||
|
||||
const ChooseFeature: FC<ChooseFeatureProps> = ({
|
||||
const FeatureModal: FC<FeatureModalProps> = ({
|
||||
onChange,
|
||||
showTextToSpeechItem,
|
||||
showSpeechToTextItem,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const featuresStore = useFeaturesStore()
|
||||
const setShowFeaturesModal = useFeatures(s => s.setShowFeaturesModal)
|
||||
const openingStatement = useFeatures(s => s.openingStatement)
|
||||
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 features = useFeatures(s => s.features)
|
||||
|
||||
const handleCancelModal = useCallback(() => {
|
||||
setShowFeaturesModal(false)
|
||||
}, [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 (
|
||||
<Modal
|
||||
isShow
|
||||
|
|
@ -67,20 +76,18 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
previewImgClassName='openingStatementPreview'
|
||||
title={t('appDebug.feature.conversationOpener.title')}
|
||||
description={t('appDebug.feature.conversationOpener.description')}
|
||||
value={openingStatement.enabled}
|
||||
onChange={value => setOpeningStatement(produce(openingStatement, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.opening.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.opening}
|
||||
/>
|
||||
<FeatureItem
|
||||
icon={<SuggestedQuestionsAfterAnswerIcon />}
|
||||
previewImgClassName='suggestedQuestionsAfterAnswerPreview'
|
||||
title={t('appDebug.feature.suggestedQuestionsAfterAnswer.title')}
|
||||
description={t('appDebug.feature.suggestedQuestionsAfterAnswer.description')}
|
||||
value={suggestedQuestionsAfterAnswer.enabled}
|
||||
onChange={value => setSuggestedQuestionsAfterAnswer(produce(suggestedQuestionsAfterAnswer, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.suggested.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.suggested}
|
||||
/>
|
||||
{
|
||||
showTextToSpeechItem && (
|
||||
|
|
@ -89,10 +96,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
previewImgClassName='textToSpeechPreview'
|
||||
title={t('appDebug.feature.textToSpeech.title')}
|
||||
description={t('appDebug.feature.textToSpeech.description')}
|
||||
value={textToSpeech.enabled}
|
||||
onChange={value => setTextToSpeech(produce(textToSpeech, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.text2speech.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.text2speech}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -103,10 +109,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
previewImgClassName='speechToTextPreview'
|
||||
title={t('appDebug.feature.speechToText.title')}
|
||||
description={t('appDebug.feature.speechToText.description')}
|
||||
value={speechToText.enabled}
|
||||
onChange={value => setSpeechToText(produce(speechToText, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.speech2text.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.speech2text}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
|
@ -115,10 +120,9 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
previewImgClassName='citationPreview'
|
||||
title={t('appDebug.feature.citation.title')}
|
||||
description={t('appDebug.feature.citation.description')}
|
||||
value={citation.enabled}
|
||||
onChange={value => setCitation(produce(citation, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.citation.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.citation}
|
||||
/>
|
||||
</>
|
||||
</FeatureGroup>
|
||||
|
|
@ -130,19 +134,17 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
previewImgClassName=''
|
||||
title={t('appDebug.feature.moderation.title')}
|
||||
description={t('appDebug.feature.moderation.description')}
|
||||
value={moderation.enabled}
|
||||
onChange={value => setModeration(produce(moderation, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.moderation.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.moderation}
|
||||
/>
|
||||
<FeatureItem
|
||||
icon={<MessageFast className='w-4 h-4 text-[#444CE7]' />}
|
||||
title={t('appDebug.feature.annotation.title')}
|
||||
description={t('appDebug.feature.annotation.description')}
|
||||
value={annotation.enabled}
|
||||
onChange={value => setAnnotation(produce(annotation, (draft) => {
|
||||
draft.enabled = value
|
||||
}))}
|
||||
value={!!features.annotation.enabled}
|
||||
onChange={handleChange}
|
||||
type={FeatureEnum.annotation}
|
||||
/>
|
||||
</>
|
||||
</FeatureGroup>
|
||||
|
|
@ -150,4 +152,4 @@ const ChooseFeature: FC<ChooseFeatureProps> = ({
|
|||
</Modal>
|
||||
)
|
||||
}
|
||||
export default React.memo(ChooseFeature)
|
||||
export default React.memo(FeatureModal)
|
||||
|
|
|
|||
|
|
@ -23,21 +23,15 @@ const FeaturePanel = ({
|
|||
annotationProps,
|
||||
}: FeaturePanelProps) => {
|
||||
const { t } = useTranslation()
|
||||
const openingStatement = useFeatures(s => s.openingStatement)
|
||||
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 features = useFeatures(s => s.features)
|
||||
|
||||
const showAdvanceFeature = useMemo(() => {
|
||||
return openingStatement.enabled || suggestedQuestionsAfterAnswer.enabled || textToSpeech.enabled || speechToText.enabled || citation.enabled
|
||||
}, [openingStatement, suggestedQuestionsAfterAnswer, textToSpeech, speechToText, citation])
|
||||
return features.opening.enabled || features.suggested.enabled || features.speech2text.enabled || features.text2speech.enabled || features.citation.enabled
|
||||
}, [features])
|
||||
|
||||
const showToolFeature = useMemo(() => {
|
||||
return moderation.enabled || annotation.enabled
|
||||
}, [moderation, annotation])
|
||||
return features.moderation.enabled || features.annotation.enabled
|
||||
}, [features])
|
||||
|
||||
return (
|
||||
<div className='space-y-3'>
|
||||
|
|
@ -55,27 +49,27 @@ const FeaturePanel = ({
|
|||
</div>
|
||||
<div className='py-2 space-y-2'>
|
||||
{
|
||||
openingStatement.enabled && (
|
||||
features.opening.enabled && (
|
||||
<OpeningStatement {...openingStatementProps} />
|
||||
)
|
||||
}
|
||||
{
|
||||
suggestedQuestionsAfterAnswer.enabled && (
|
||||
features.suggested.enabled && (
|
||||
<SuggestedQuestionsAfterAnswer />
|
||||
)
|
||||
}
|
||||
{
|
||||
textToSpeech.enabled && (
|
||||
features.text2speech.enabled && (
|
||||
<TextToSpeech />
|
||||
)
|
||||
}
|
||||
{
|
||||
speechToText.enabled && (
|
||||
features.speech2text.enabled && (
|
||||
<SpeechToText />
|
||||
)
|
||||
}
|
||||
{
|
||||
citation.enabled && (
|
||||
features.citation.enabled && (
|
||||
<Citation />
|
||||
)
|
||||
}
|
||||
|
|
@ -97,12 +91,12 @@ const FeaturePanel = ({
|
|||
</div>
|
||||
<div className='py-2 space-y-2'>
|
||||
{
|
||||
moderation.enabled && (
|
||||
features.moderation.enabled && (
|
||||
<Moderation />
|
||||
)
|
||||
}
|
||||
{
|
||||
annotation.enabled && (
|
||||
features.annotation.enabled && (
|
||||
<Annotation {...annotationProps} />
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,8 +1,12 @@
|
|||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import useSWR from 'swr'
|
||||
import produce from 'immer'
|
||||
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 { Settings01 } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { useModalContext } from '@/context/modal-context'
|
||||
|
|
@ -13,8 +17,8 @@ const Moderation = () => {
|
|||
const { t } = useTranslation()
|
||||
const { setShowModerationSettingModal } = useModalContext()
|
||||
const { locale } = useContext(I18n)
|
||||
const moderation = useFeatures(s => s.moderation)
|
||||
const setModeration = useFeatures(s => s.setModeration)
|
||||
const featuresStore = useFeaturesStore()
|
||||
const moderation = useFeatures(s => s.features.moderation)
|
||||
|
||||
const { data: codeBasedExtensionList } = useSWR(
|
||||
'/code-based-extension?module=moderation',
|
||||
|
|
@ -22,9 +26,17 @@ const Moderation = () => {
|
|||
)
|
||||
|
||||
const handleOpenModerationSettingModal = () => {
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = featuresStore!.getState()
|
||||
setShowModerationSettingModal({
|
||||
payload: moderation,
|
||||
onSaveCallback: setModeration,
|
||||
payload: moderation as any,
|
||||
onSaveCallback: (newModeration) => {
|
||||
setFeatures(produce(features, (draft) => {
|
||||
draft.moderation = newModeration
|
||||
}))
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -7,7 +7,10 @@ import cn from 'classnames'
|
|||
import { useTranslation } from 'react-i18next'
|
||||
import { useBoolean } from 'ahooks'
|
||||
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 Button from '@/app/components/base/button'
|
||||
import OperationBtn from '@/app/components/app/configuration/base/operation-btn'
|
||||
|
|
@ -35,8 +38,8 @@ const OpeningStatement: FC<OpeningStatementProps> = ({
|
|||
onAutoAddPromptVariable,
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const openingStatement = useFeatures(s => s.openingStatement)
|
||||
const setOpeningStatement = useFeatures(s => s.setOpeningStatement)
|
||||
const featureStore = useFeaturesStore()
|
||||
const openingStatement = useFeatures(s => s.features.opening)
|
||||
const value = openingStatement.opening_statement || ''
|
||||
const suggestedQuestions = openingStatement.suggested_questions || []
|
||||
const [notIncludeKeys, setNotIncludeKeys] = useState<string[]>([])
|
||||
|
|
@ -103,23 +106,41 @@ const OpeningStatement: FC<OpeningStatementProps> = ({
|
|||
return
|
||||
}
|
||||
setBlur()
|
||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
||||
draft.opening_statement = tempValue
|
||||
draft.suggested_questions = tempSuggestedQuestions
|
||||
const { getState } = featureStore!
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = getState()
|
||||
|
||||
setFeatures(produce(features, (draft) => {
|
||||
draft.opening.opening_statement = tempValue
|
||||
draft.opening.suggested_questions = tempSuggestedQuestions
|
||||
}))
|
||||
}
|
||||
|
||||
const cancelAutoAddVar = () => {
|
||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
||||
draft.opening_statement = tempValue
|
||||
const { getState } = featureStore!
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = getState()
|
||||
|
||||
setFeatures(produce(features, (draft) => {
|
||||
draft.opening.opening_statement = tempValue
|
||||
}))
|
||||
hideConfirmAddVar()
|
||||
setBlur()
|
||||
}
|
||||
|
||||
const autoAddVar = () => {
|
||||
setOpeningStatement(produce(openingStatement, (draft) => {
|
||||
draft.opening_statement = tempValue
|
||||
const { getState } = featureStore!
|
||||
const {
|
||||
features,
|
||||
setFeatures,
|
||||
} = getState()
|
||||
|
||||
setFeatures(produce(features, (draft) => {
|
||||
draft.opening.opening_statement = tempValue
|
||||
}))
|
||||
onAutoAddPromptVariable([...notIncludeKeys.map(key => getNewVar(key, 'string'))])
|
||||
hideConfirmAddVar()
|
||||
|
|
|
|||
|
|
@ -12,7 +12,7 @@ import AudioBtn from '@/app/components/base/audio-btn'
|
|||
|
||||
const TextToSpeech: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const textToSpeech = useFeatures(s => s.textToSpeech)
|
||||
const textToSpeech = useFeatures(s => s.features.text2speech)
|
||||
|
||||
const pathname = usePathname()
|
||||
const matched = pathname.match(/\/app\/([^/]+)/)
|
||||
|
|
|
|||
|
|
@ -1,13 +1,5 @@
|
|||
import { createStore } from 'zustand'
|
||||
import type {
|
||||
AnnotationReply,
|
||||
OpeningStatement,
|
||||
RetrieverResource,
|
||||
SensitiveWordAvoidance,
|
||||
SpeechToText,
|
||||
SuggestedQuestionsAfterAnswer,
|
||||
TextToSpeech,
|
||||
} from './types'
|
||||
import type { Features } from './types'
|
||||
|
||||
export type FeaturesModal = {
|
||||
showFeaturesModal: boolean
|
||||
|
|
@ -15,23 +7,11 @@ export type FeaturesModal = {
|
|||
}
|
||||
|
||||
export type FeaturesState = {
|
||||
openingStatement: OpeningStatement
|
||||
suggestedQuestionsAfterAnswer: SuggestedQuestionsAfterAnswer
|
||||
textToSpeech: TextToSpeech
|
||||
speechToText: SpeechToText
|
||||
citation: RetrieverResource
|
||||
moderation: SensitiveWordAvoidance
|
||||
annotation: AnnotationReply
|
||||
features: Features
|
||||
}
|
||||
|
||||
export type FeaturesAction = {
|
||||
setOpeningStatement: (openingStatement: OpeningStatement) => 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
|
||||
setFeatures: (features: Features) => void
|
||||
}
|
||||
|
||||
export type FeatureStoreState = FeaturesState & FeaturesAction & FeaturesModal
|
||||
|
|
@ -40,39 +20,34 @@ export type FeaturesStore = ReturnType<typeof createFeaturesStore>
|
|||
|
||||
export const createFeaturesStore = (initProps?: Partial<FeaturesState>) => {
|
||||
const DEFAULT_PROPS: FeaturesState = {
|
||||
openingStatement: {
|
||||
enabled: false,
|
||||
},
|
||||
suggestedQuestionsAfterAnswer: {
|
||||
enabled: false,
|
||||
},
|
||||
textToSpeech: {
|
||||
enabled: false,
|
||||
},
|
||||
speechToText: {
|
||||
enabled: false,
|
||||
},
|
||||
citation: {
|
||||
enabled: false,
|
||||
},
|
||||
moderation: {
|
||||
enabled: false,
|
||||
},
|
||||
annotation: {
|
||||
enabled: false,
|
||||
features: {
|
||||
opening: {
|
||||
enabled: false,
|
||||
},
|
||||
suggested: {
|
||||
enabled: false,
|
||||
},
|
||||
text2speech: {
|
||||
enabled: false,
|
||||
},
|
||||
speech2text: {
|
||||
enabled: false,
|
||||
},
|
||||
citation: {
|
||||
enabled: false,
|
||||
},
|
||||
moderation: {
|
||||
enabled: false,
|
||||
},
|
||||
annotation: {
|
||||
enabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
return createStore<FeatureStoreState>()(set => ({
|
||||
...DEFAULT_PROPS,
|
||||
...initProps,
|
||||
setOpeningStatement: openingStatement => set(() => ({ openingStatement })),
|
||||
setSuggestedQuestionsAfterAnswer: suggestedQuestionsAfterAnswer => set(() => ({ suggestedQuestionsAfterAnswer })),
|
||||
setSpeechToText: speechToText => set(() => ({ speechToText })),
|
||||
setTextToSpeech: textToSpeech => set(() => ({ textToSpeech })),
|
||||
setCitation: citation => set(() => ({ citation })),
|
||||
setModeration: moderation => set(() => ({ moderation })),
|
||||
setAnnotation: annotation => set(() => ({ annotation })),
|
||||
|
||||
setFeatures: features => set(() => ({ features })),
|
||||
showFeaturesModal: false,
|
||||
setShowFeaturesModal: showFeaturesModal => set(() => ({ showFeaturesModal })),
|
||||
}))
|
||||
|
|
|
|||
|
|
@ -31,3 +31,23 @@ export type AnnotationReply = EnabledOrDisabled & {
|
|||
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 MagicWand } from './MagicWand'
|
||||
export { default as Microphone01 } from './Microphone01'
|
||||
export { default as Play } from './Play'
|
||||
export { default as Robot } from './Robot'
|
||||
export { default as Sliders02 } from './Sliders02'
|
||||
export { default as Speaker } from './Speaker'
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from './store'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
import {
|
||||
|
|
@ -7,12 +8,13 @@ import {
|
|||
} from '@/app/components/base/features'
|
||||
|
||||
const Features = () => {
|
||||
const { t } = useTranslation()
|
||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
||||
|
||||
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='flex items-center justify-between px-4 pt-3'>
|
||||
Features
|
||||
{t('workflow.common.features')}
|
||||
<div className='flex items-center'>
|
||||
<FeaturesChoose />
|
||||
<div className='mx-3 w-[1px] h-[14px] bg-gray-200'></div>
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
useCallback,
|
||||
} from 'react'
|
||||
import dayjs from 'dayjs'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from '../store'
|
||||
import RunAndHistory from './run-and-history'
|
||||
import EditingTitle from './editing-title'
|
||||
import RunningTitle from './running-title'
|
||||
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 Button from '@/app/components/base/button'
|
||||
import { ArrowNarrowLeft } from '@/app/components/base/icons/src/vender/line/arrows'
|
||||
import { useStore as useAppStore } from '@/app/components/app/store'
|
||||
import { Mode } from '@/app/components/workflow/types'
|
||||
|
||||
const Header: FC = () => {
|
||||
const { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const setShowFeaturesPanel = useStore(state => state.setShowFeaturesPanel)
|
||||
const runStaus = useStore(state => state.runStaus)
|
||||
const setRunStaus = useStore(state => state.setRunStaus)
|
||||
const draftUpdatedAt = useStore(state => state.draftUpdatedAt)
|
||||
const mode = useStore(state => state.mode)
|
||||
const runTaskId = useStore(state => state.runTaskId)
|
||||
|
||||
const handleShowFeatures = useCallback(() => {
|
||||
setShowFeaturesPanel(true)
|
||||
}, [setShowFeaturesPanel])
|
||||
useStore.setState({ showFeaturesPanel: true })
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<div
|
||||
|
|
@ -33,35 +34,25 @@ const Header: FC = () => {
|
|||
>
|
||||
<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'>
|
||||
<Edit03 className='mr-1 w-3 h-3 text-gray-400' />
|
||||
Editing
|
||||
{
|
||||
draftUpdatedAt && (
|
||||
<>
|
||||
<span className='flex items-center mx-1'>·</span>
|
||||
<span>
|
||||
Auto-Saved {dayjs(draftUpdatedAt).format('HH:mm:ss')}
|
||||
</span>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
mode === Mode.Editing && !runTaskId && <EditingTitle />
|
||||
}
|
||||
{
|
||||
(mode === Mode.Running || runTaskId) && <RunningTitle />
|
||||
}
|
||||
</div>
|
||||
<div className='flex items-center'>
|
||||
{
|
||||
runStaus && (
|
||||
(mode === Mode.Running || runTaskId) && (
|
||||
<Button
|
||||
className={`
|
||||
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
|
||||
`}
|
||||
onClick={() => setRunStaus('')}
|
||||
onClick={() => useStore.setState({ mode: Mode.Editing, runTaskId: '' })}
|
||||
>
|
||||
<ArrowNarrowLeft className='mr-1 w-4 h-4' />
|
||||
Go back to editor
|
||||
{t('workflow.common.goBackToEdit')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
@ -77,7 +68,7 @@ const Header: FC = () => {
|
|||
onClick={handleShowFeatures}
|
||||
>
|
||||
<Grid01 className='mr-1 w-4 h-4 text-gray-500' />
|
||||
Features
|
||||
{t('workflow.common.features')}
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
import { useState } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import Button from '@/app/components/base/button'
|
||||
import {
|
||||
PortalToFollowElem,
|
||||
|
|
@ -7,6 +8,7 @@ import {
|
|||
} from '@/app/components/base/portal-to-follow-elem'
|
||||
|
||||
const Publish = () => {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
return (
|
||||
|
|
@ -24,35 +26,35 @@ const Publish = () => {
|
|||
type='primary'
|
||||
className='px-3 py-0 h-8 text-[13px] font-medium'
|
||||
>
|
||||
publish
|
||||
{t('workflow.common.publish')}
|
||||
</Button>
|
||||
</PortalToFollowElemTrigger>
|
||||
<PortalToFollowElemContent className='z-[11]'>
|
||||
<div className='w-[320px] bg-white rounded-2xl border-[0.5px] border-gray-200 shadow-xl'>
|
||||
<div className='p-4 pt-3'>
|
||||
<div className='flex items-center h-6 text-xs font-medium text-gray-500'>
|
||||
Current Draft
|
||||
{t('workflow.common.currentDraft').toLocaleUpperCase()}
|
||||
</div>
|
||||
<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>
|
||||
<Button
|
||||
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'
|
||||
>
|
||||
Publish
|
||||
{t('workflow.common.publish')}
|
||||
</Button>
|
||||
</div>
|
||||
<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'>
|
||||
Latest Published
|
||||
{t('workflow.common.latestPublished').toLocaleUpperCase()}
|
||||
</div>
|
||||
<div className='flex justify-between'>
|
||||
<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>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -1,16 +1,19 @@
|
|||
import type { FC } from 'react'
|
||||
import { memo } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
import { useStore } from '../store'
|
||||
import { Play } from '@/app/components/base/icons/src/vender/line/mediaAndDevices'
|
||||
import { ClockPlay } from '@/app/components/base/icons/src/vender/line/time'
|
||||
import TooltipPlus from '@/app/components/base/tooltip-plus'
|
||||
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 { t } = useTranslation()
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
const mode = useStore(state => state.mode)
|
||||
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 (
|
||||
<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={`
|
||||
flex items-center px-1.5 h-7 rounded-md text-[13px] font-medium text-primary-600
|
||||
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' />
|
||||
Run
|
||||
{
|
||||
appDetail?.mode === 'workflow'
|
||||
? t('workflow.common.run')
|
||||
: t('workflow.common.preview')
|
||||
}
|
||||
</>
|
||||
)
|
||||
}
|
||||
</div>
|
||||
<div className='mx-0.5 w-[0.5px] h-8 bg-gray-200'></div>
|
||||
<TooltipPlus
|
||||
popupContent='View run history'
|
||||
popupContent={t('workflow.common.viewRunHistory')}
|
||||
>
|
||||
<div
|
||||
className={`
|
||||
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>
|
||||
</TooltipPlus>
|
||||
</div>
|
||||
|
|
|
|||
|
|
@ -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 Loading from '@/app/components/base/loading'
|
||||
import { FeaturesProvider } from '@/app/components/base/features'
|
||||
import type { Features as FeaturesData } from '@/app/components/base/features/types'
|
||||
import { fetchCollectionList } from '@/service/tools'
|
||||
|
||||
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 (
|
||||
<ReactFlowProvider>
|
||||
<FeaturesProvider>
|
||||
<FeaturesProvider features={initialFeatures}>
|
||||
<Workflow
|
||||
nodes={nodesData}
|
||||
edges={edgesData}
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ const BasePanel: FC<BasePanelProps> = ({
|
|||
}, [handleNodeDataUpdate, id, data])
|
||||
|
||||
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='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 appDetail = useAppStore(state => state.appDetail)
|
||||
const runStaus = useStore(state => state.runStaus)
|
||||
const runTaskId = useStore(state => state.runTaskId)
|
||||
const nodes = useNodes<CommonNodeType>()
|
||||
const selectedNode = nodes.find(node => node.data._selected)
|
||||
const showRunHistory = useStore(state => state.showRunHistory)
|
||||
|
|
@ -25,16 +25,16 @@ const Panel: FC = () => {
|
|||
showDebugAndPreviewPanel,
|
||||
} = useMemo(() => {
|
||||
return {
|
||||
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode,
|
||||
showNodePanel: !!selectedNode,
|
||||
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode,
|
||||
showWorkflowInfoPanel: appDetail?.mode === 'workflow' && !selectedNode && !runTaskId,
|
||||
showNodePanel: !!selectedNode && !runTaskId,
|
||||
showDebugAndPreviewPanel: appDetail?.mode === 'advanced-chat' && !selectedNode && !runTaskId,
|
||||
}
|
||||
}, [selectedNode, appDetail])
|
||||
}, [selectedNode, appDetail, runTaskId])
|
||||
|
||||
return (
|
||||
<div className='absolute top-14 right-0 bottom-2 flex z-10'>
|
||||
{
|
||||
runStaus && (
|
||||
runTaskId && (
|
||||
<Record />
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,45 +1,33 @@
|
|||
import { useStore } from '../store'
|
||||
import {
|
||||
CheckCircle,
|
||||
XClose,
|
||||
} from '@/app/components/base/icons/src/vender/line/general'
|
||||
import { memo } from 'react'
|
||||
import { XClose } from '@/app/components/base/icons/src/vender/line/general'
|
||||
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 mode = useStore(state => state.mode)
|
||||
const setShowRunHistory = useStore(state => state.setShowRunHistory)
|
||||
const setRunStaus = useStore(state => state.setRunStaus)
|
||||
const appDetail = useAppStore(state => state.appDetail)
|
||||
|
||||
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'>
|
||||
Run History
|
||||
<div
|
||||
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' />
|
||||
</div>
|
||||
</div>
|
||||
<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
|
||||
className='flex px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
||||
onClick={() => setRunStaus('finished')}
|
||||
className='flex mb-0.5 px-2 py-[7px] rounded-lg hover:bg-primary-50 cursor-pointer'
|
||||
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 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]'>
|
||||
|
|
@ -52,4 +40,4 @@ const RunHistory = () => {
|
|||
)
|
||||
}
|
||||
|
||||
export default RunHistory
|
||||
export default memo(RunHistory)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,7 @@ const WorkflowInfo: FC = () => {
|
|||
return null
|
||||
|
||||
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='flex pt-4 px-4 pb-1'>
|
||||
<AppIcon
|
||||
|
|
|
|||
|
|
@ -5,38 +5,43 @@ import type {
|
|||
ToolInWorkflow,
|
||||
ToolsMap,
|
||||
} from './block-selector/types'
|
||||
import { Mode } from './types'
|
||||
|
||||
type State = {
|
||||
mode: string
|
||||
mode: Mode
|
||||
runTaskId: string
|
||||
showRunHistory: boolean
|
||||
showFeaturesPanel: boolean
|
||||
runStaus: string
|
||||
isDragging: boolean
|
||||
helpLine?: HelpLinePosition
|
||||
toolsets: CollectionWithExpanded[]
|
||||
toolsMap: ToolsMap
|
||||
draftUpdatedAt: number
|
||||
publishedAt: number
|
||||
}
|
||||
|
||||
type Action = {
|
||||
setMode: (mode: Mode) => void
|
||||
setRunTaskId: (runTaskId: string) => void
|
||||
setShowRunHistory: (showRunHistory: boolean) => void
|
||||
setShowFeaturesPanel: (showFeaturesPanel: boolean) => void
|
||||
setRunStaus: (runStaus: string) => void
|
||||
setIsDragging: (isDragging: boolean) => void
|
||||
setHelpLine: (helpLine?: HelpLinePosition) => void
|
||||
setToolsets: (toolsets: CollectionWithExpanded[]) => void
|
||||
setToolsMap: (toolsMap: Record<string, ToolInWorkflow[]>) => void
|
||||
setDraftUpdatedAt: (draftUpdatedAt: number) => void
|
||||
setPublishedAt: (publishedAt: number) => void
|
||||
}
|
||||
|
||||
export const useStore = create<State & Action>(set => ({
|
||||
mode: 'workflow',
|
||||
mode: Mode.Editing,
|
||||
runTaskId: '',
|
||||
setRunTaskId: runTaskId => set(() => ({ runTaskId })),
|
||||
setMode: mode => set(() => ({ mode })),
|
||||
showRunHistory: false,
|
||||
setShowRunHistory: showRunHistory => set(() => ({ showRunHistory })),
|
||||
showFeaturesPanel: false,
|
||||
setShowFeaturesPanel: showFeaturesPanel => set(() => ({ showFeaturesPanel })),
|
||||
runStaus: '',
|
||||
setRunStaus: runStaus => set(() => ({ runStaus })),
|
||||
isDragging: false,
|
||||
setIsDragging: isDragging => set(() => ({ isDragging })),
|
||||
helpLine: undefined,
|
||||
|
|
@ -47,4 +52,6 @@ export const useStore = create<State & Action>(set => ({
|
|||
setToolsMap: toolsMap => set(() => ({ toolsMap })),
|
||||
draftUpdatedAt: 0,
|
||||
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 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',
|
||||
publish: 'Publish',
|
||||
run: 'Run',
|
||||
running: 'Running',
|
||||
inRunMode: 'In Run Mode',
|
||||
inPreview: 'In Preview',
|
||||
inPreviewMode: 'In Preview Mode',
|
||||
|
|
|
|||
|
|
@ -6,7 +6,8 @@ const translation = {
|
|||
published: '已发布',
|
||||
publish: '发布',
|
||||
run: '运行',
|
||||
inRunMode: '运行中',
|
||||
running: '运行中',
|
||||
inRunMode: '在运行模式中',
|
||||
inPreview: '预览中',
|
||||
inPreviewMode: '预览中',
|
||||
preview: '预览',
|
||||
|
|
|
|||
Loading…
Reference in New Issue