Commit 0164dec4 authored by StyleZhang's avatar StyleZhang

features

parent 4edaa95c
'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 = { ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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' ...@@ -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 { ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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 = ({ ...@@ -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 = ({ ...@@ -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 = ({ ...@@ -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} />
) )
} }
......
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 = () => { ...@@ -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 = () => { ...@@ -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' ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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' ...@@ -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\/([^/]+)/)
......
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 = { ...@@ -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> ...@@ -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 & { ...@@ -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
}
<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>
{
"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"
}
\ No newline at end of file
// 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' ...@@ -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'
......
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 { ...@@ -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>
......
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 { ...@@ -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 = () => { ...@@ -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 = () => { ...@@ -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>
) )
} }
......
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 { ...@@ -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 = () => { ...@@ -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>
......
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 = () => { ...@@ -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>
......
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 { ...@@ -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> = ({ ...@@ -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> = ({ ...@@ -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' ...@@ -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 = () => { ...@@ -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 />
) )
} }
......
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 = () => { ...@@ -52,4 +40,4 @@ const RunHistory = () => {
) )
} }
export default RunHistory export default memo(RunHistory)
...@@ -14,7 +14,7 @@ const WorkflowInfo: FC = () => { ...@@ -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 { ...@@ -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 => ({ ...@@ -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> = { ...@@ -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',
}
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 = { ...@@ -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 = { ...@@ -6,7 +6,8 @@ const translation = {
published: '已发布', published: '已发布',
publish: '发布', publish: '发布',
run: '运行', run: '运行',
inRunMode: '运行中', running: '运行中',
inRunMode: '在运行模式中',
inPreview: '预览中', inPreview: '预览中',
inPreviewMode: '预览中', inPreviewMode: '预览中',
preview: '预览', preview: '预览',
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment